From 0745d287f59c0e0251964c3ce06b7a8e468d3e7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cwuzhen=E2=80=9D?= Date: Fri, 22 May 2026 11:17:26 +0800 Subject: [PATCH] =?UTF-8?q?wuzhen=E6=9B=B4=E6=96=B0=E7=A4=BE=E5=9B=A2?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E6=96=B0=E9=9C=80=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stgl/controller/StglClassController.java | 9 + .../stgl/controller/StglMemberController.java | 44 +++ .../org/dromara/stgl/domain/StglClub.java | 20 ++ .../stgl/domain/StglMemberApproval.java | 57 ++++ .../stgl/domain/bo/StglActivityBo.java | 7 +- .../dromara/stgl/domain/bo/StglClubBo.java | 21 ++ .../stgl/domain/bo/StglMemberApprovalBo.java | 22 ++ .../dromara/stgl/domain/vo/StglClubVo.java | 21 ++ .../stgl/domain/vo/StglMemberApprovalVo.java | 35 +++ .../dromara/stgl/mapper/StglClassMapper.java | 16 + .../stgl/mapper/StglMemberApprovalMapper.java | 11 + .../stgl/service/IStglClassService.java | 9 + .../service/IStglMemberApprovalService.java | 56 ++++ .../service/impl/StglActivityServiceImpl.java | 43 ++- .../service/impl/StglClassServiceImpl.java | 17 ++ .../impl/StglClubRegisterServiceImpl.java | 15 +- .../service/impl/StglClubServiceImpl.java | 177 ++++++++++- .../impl/StglMemberApprovalServiceImpl.java | 262 +++++++++++++++++ .../service/impl/StglMemberServiceImpl.java | 33 +-- .../main/resources/mapper/StglClubMapper.xml | 5 + .../resources/mapper/stgl/StglClassMapper.xml | 128 ++++++++ web/src/api/stgl/activity/types.ts | 5 + web/src/api/stgl/club/types.ts | 51 +++- web/src/api/stgl/member/index.ts | 40 ++- web/src/api/stgl/stclass/index.ts | 8 + web/src/api/stgl/stclass/types.ts | 34 +-- .../components/VicePresidentManager/index.vue | 1 + web/src/views/stgl/activity/index.vue | 79 ++++- .../info/components/PendingMembersTab.vue | 30 +- web/src/views/stgl/club/info/index.vue | 181 +++++++++++- web/src/views/stgl/club/member/index.vue | 2 +- .../views/stgl/stclass/current-activity.vue | 275 ++++++++++++++++++ 32 files changed, 1638 insertions(+), 76 deletions(-) create mode 100644 backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglMemberApproval.java create mode 100644 backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/bo/StglMemberApprovalBo.java create mode 100644 backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/StglMemberApprovalVo.java create mode 100644 backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/mapper/StglMemberApprovalMapper.java create mode 100644 backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/IStglMemberApprovalService.java create mode 100644 backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglMemberApprovalServiceImpl.java create mode 100644 web/src/views/stgl/stclass/current-activity.vue diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglClassController.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglClassController.java index 749bf41a..a486ec55 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglClassController.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglClassController.java @@ -98,4 +98,13 @@ public class StglClassController extends BaseController { List list = stglClassService.queryList(bo); ExcelUtil.exportExcel(list, "班级学生参与情况", StglClassVo.class, response); } + + /** + * 查询班主任所辖班级中当前正在参加活动的学生列表 + */ + //@SaCheckPermission("stgl:stclass:list") + @GetMapping("/current-activities") + public TableDataInfo> currentActivities(StglClassBo bo, PageQuery pageQuery) { + return stglClassService.getCurrentActiveStudents(bo, pageQuery); + } } \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglMemberController.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglMemberController.java index b55102e6..f079020e 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglMemberController.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/controller/StglMemberController.java @@ -29,6 +29,9 @@ import org.dromara.stgl.domain.vo.StglMemberVo; import org.dromara.stgl.domain.vo.StglMemberImportVo; import org.dromara.stgl.domain.bo.StglMemberBo; import org.dromara.stgl.service.IStglMemberService; +import org.dromara.stgl.service.IStglMemberApprovalService; +import org.dromara.stgl.domain.bo.StglMemberApprovalBo; +import org.dromara.stgl.domain.vo.StglMemberApprovalVo; import org.dromara.stgl.listener.StglMemberImportListener; import org.dromara.common.mybatis.core.page.TableDataInfo; import io.swagger.v3.oas.annotations.Operation; @@ -51,6 +54,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; public class StglMemberController extends BaseController { private final IStglMemberService stglMemberService; + private final IStglMemberApprovalService memberApprovalService; /** * 查询社团成员列表 @@ -232,4 +236,44 @@ public class StglMemberController extends BaseController { ExcelUtil.exportExcel(templateData, "社团成员导入模板", StglMemberImportVo.class, response); } + /** + * 社长审批成员申请 + */ + @Operation(summary = "社长审批成员申请") + @Log(title = "社长审批", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/presidentApprove") + public R presidentApprove(@RequestBody StglMemberApprovalBo bo) { + return toAjax(memberApprovalService.presidentApprove(bo.getMemberId(), bo.getApproved(), bo.getOpinion())); + } + + /** + * 指导老师审批成员申请 + */ + @Operation(summary = "指导老师审批成员申请") + @Log(title = "指导老师审批", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/advisorApprove") + public R advisorApprove(@RequestBody StglMemberApprovalBo bo) { + return toAjax(memberApprovalService.advisorApprove(bo.getMemberId(), bo.getApproved(), bo.getOpinion())); + } + + /** + * 获取成员申请的审批流程 + */ + @Operation(summary = "获取成员申请的审批流程") + @GetMapping("/approvalFlow/{memberId}") + public R> getApprovalFlow(@PathVariable Long memberId) { + return R.ok(memberApprovalService.getApprovalFlow(memberId)); + } + + /** + * 获取待当前用户审批的成员列表 + */ + @Operation(summary = "获取待当前用户审批的成员列表") + @GetMapping("/pendingApprovals") + public R> getPendingApprovals(@RequestParam(required = false) Long clubId) { + return R.ok(memberApprovalService.getPendingApprovals(clubId)); + } + } \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglClub.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglClub.java index a6d3b4e9..214f5120 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglClub.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglClub.java @@ -152,6 +152,11 @@ public class StglClub extends TenantEntity { */ private String advisorsJson; + /** + * 归属(部门ID) + */ + private Long affiliation; + /** * 成员规模上限 */ @@ -197,6 +202,21 @@ public class StglClub extends TenantEntity { */ private String sighOpen; + /** + * 社长用户ID + */ + private Long presidentUserId; + + /** + * 物资保管员用户ID + */ + private Long materialKeeperUserId; + + /** + * 物资保管员姓名 + */ + private String materialKeeperName; + /** * 备注 */ diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglMemberApproval.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglMemberApproval.java new file mode 100644 index 00000000..251fe51d --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/StglMemberApproval.java @@ -0,0 +1,57 @@ +package org.dromara.stgl.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import org.dromara.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 成员申请审批流程对象 stgl_member_approval + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("stgl_member_approval") +public class StglMemberApproval extends TenantEntity { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id") + private Long id; + + /** 成员记录ID(关联stgl_member) */ + private Long memberId; + + /** 社团ID */ + private Long clubId; + + /** 节点代码(president_approve/advisor_approve) */ + private String nodeCode; + + /** 节点名称(社长审批/指导老师审批) */ + private String nodeName; + + /** 节点顺序(1.0, 2.0) */ + private BigDecimal nodeOrder; + + /** 审批人ID */ + private Long approverId; + + /** 审批人姓名 */ + private String approverName; + + /** 审批结果(通过/不通过) */ + private String approveResult; + + /** 审批状态(pending/approved/rejected) */ + private String auditStatus; + + /** 审批意见 */ + private String auditOpinion; + + /** 审批时间 */ + private Date auditTime; +} diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/bo/StglActivityBo.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/bo/StglActivityBo.java index 594b85db..9d6a0667 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/bo/StglActivityBo.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/bo/StglActivityBo.java @@ -12,6 +12,7 @@ import org.dromara.common.mybatis.core.domain.BaseEntity; import org.dromara.stgl.domain.StglActivity; import java.util.Date; +import java.util.List; /** * 活动信息业务对象 stgl_activity @@ -229,4 +230,8 @@ public class StglActivityBo extends BaseEntity { */ private String remark; -} \ No newline at end of file + /** + * 邀请成员ID列表(非数据库字段,用于创建报名记录) + */ + private List invitedMemberIds; +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/bo/StglClubBo.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/bo/StglClubBo.java index 87540776..04907530 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/bo/StglClubBo.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/bo/StglClubBo.java @@ -184,6 +184,11 @@ public class StglClubBo extends BaseEntity { */ private String advisorsJson; + /** + * 归属(部门ID) + */ + private Long affiliation; + /** * 成员规模上限 */ @@ -240,6 +245,22 @@ public class StglClubBo extends BaseEntity { */ private String remark; + /** + * 社长用户ID(创建时选择学生) + */ + private Long presidentUserId; + + /** + * 物资保管员用户ID(创建时选择学生) + */ + private Long materialKeeperUserId; + + /** + * 物资保管员姓名 + */ + @Size(max = 50, message = "物资保管员姓名长度不能超过50个字符") + private String materialKeeperName; + /** * 社团ID列表(用于数据权限控制) */ diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/bo/StglMemberApprovalBo.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/bo/StglMemberApprovalBo.java new file mode 100644 index 00000000..f2caeada --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/bo/StglMemberApprovalBo.java @@ -0,0 +1,22 @@ +package org.dromara.stgl.domain.bo; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * 成员审批请求对象 + */ +@Data +public class StglMemberApprovalBo { + + /** 成员记录ID */ + @NotNull(message = "成员记录ID不能为空") + private Long memberId; + + /** 是否通过 */ + @NotNull(message = "审批结果不能为空") + private Boolean approved; + + /** 审批意见 */ + private String opinion; +} diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/StglClubVo.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/StglClubVo.java index 98ba7797..788e4c13 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/StglClubVo.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/StglClubVo.java @@ -178,6 +178,11 @@ public class StglClubVo implements Serializable { */ private String advisorsJson; + /** + * 归属(部门ID) + */ + private Long affiliation; + /** * 成员规模上限 */ @@ -232,6 +237,22 @@ public class StglClubVo implements Serializable { @ExcelProperty(value = "开启报名") private String sighOpen; + /** + * 社长用户ID + */ + private Long presidentUserId; + + /** + * 物资保管员用户ID + */ + private Long materialKeeperUserId; + + /** + * 物资保管员姓名 + */ + @ExcelProperty(value = "物资保管员") + private String materialKeeperName; + /** * 备注 */ diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/StglMemberApprovalVo.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/StglMemberApprovalVo.java new file mode 100644 index 00000000..031c1076 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/domain/vo/StglMemberApprovalVo.java @@ -0,0 +1,35 @@ +package org.dromara.stgl.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 成员申请审批流程视图对象 + */ +@Data +public class StglMemberApprovalVo implements Serializable { + + private static final long serialVersionUID = 1L; + + private Long id; + private Long memberId; + private Long clubId; + private String nodeCode; + private String nodeName; + private BigDecimal nodeOrder; + private Long approverId; + private String approverName; + private String approveResult; + private String auditStatus; + private String auditOpinion; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date auditTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; +} diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/mapper/StglClassMapper.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/mapper/StglClassMapper.java index 7e96245a..e0ae78cc 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/mapper/StglClassMapper.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/mapper/StglClassMapper.java @@ -62,4 +62,20 @@ public interface StglClassMapper extends BaseMapperPlus * @return 活动详情列表 */ List> selectStudentActivities(Long studentId); + + /** + * 查询指定班级中当前正在参加活动的学生列表 + * + * @param params 查询参数(classId, studentName, studentNo, offset, pageSize) + * @return 正在参加活动的学生列表 + */ + List> selectCurrentActiveStudents(Map params); + + /** + * 查询指定班级中当前正在参加活动的学生总数 + * + * @param params 查询参数(classId, studentName, studentNo) + * @return 学生总数 + */ + Integer selectCurrentActiveStudentsCount(Map params); } \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/mapper/StglMemberApprovalMapper.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/mapper/StglMemberApprovalMapper.java new file mode 100644 index 00000000..b0b228e4 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/mapper/StglMemberApprovalMapper.java @@ -0,0 +1,11 @@ +package org.dromara.stgl.mapper; + +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.stgl.domain.StglMemberApproval; +import org.dromara.stgl.domain.vo.StglMemberApprovalVo; + +/** + * 成员申请审批流程Mapper接口 + */ +public interface StglMemberApprovalMapper extends BaseMapperPlus { +} diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/IStglClassService.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/IStglClassService.java index 8b33c1f7..a546f728 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/IStglClassService.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/IStglClassService.java @@ -65,4 +65,13 @@ public interface IStglClassService { * @return 活动详情列表 */ List> getStudentActivities(Long studentId); + + /** + * 查询班主任所辖班级中当前正在参加活动的学生分页列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 分页结果 + */ + TableDataInfo> getCurrentActiveStudents(StglClassBo bo, PageQuery pageQuery); } \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/IStglMemberApprovalService.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/IStglMemberApprovalService.java new file mode 100644 index 00000000..87fa0335 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/IStglMemberApprovalService.java @@ -0,0 +1,56 @@ +package org.dromara.stgl.service; + +import org.dromara.stgl.domain.vo.StglMemberApprovalVo; + +import java.util.List; + +/** + * 成员申请审批流程Service接口 + */ +public interface IStglMemberApprovalService { + + /** + * 为成员申请创建审批流程(社长审批 → 指导老师审批) + * + * @param memberId 成员记录ID + * @param clubId 社团ID + * @return 是否创建成功 + */ + Boolean createApprovalFlow(Long memberId, Long clubId); + + /** + * 获取成员申请的审批流程列表 + * + * @param memberId 成员记录ID + * @return 审批节点列表 + */ + List getApprovalFlow(Long memberId); + + /** + * 获取待当前用户审批的列表 + * + * @param clubId 社团ID(可选) + * @return 待审批的成员列表 + */ + List getPendingApprovals(Long clubId); + + /** + * 社长审批 + * + * @param memberId 成员记录ID + * @param approved 是否通过 + * @param opinion 审批意见 + * @return 是否审批成功 + */ + Boolean presidentApprove(Long memberId, Boolean approved, String opinion); + + /** + * 指导老师审批 + * + * @param memberId 成员记录ID + * @param approved 是否通过 + * @param opinion 审批意见 + * @return 是否审批成功 + */ + Boolean advisorApprove(Long memberId, Boolean approved, String opinion); +} diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglActivityServiceImpl.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglActivityServiceImpl.java index 48d231f0..4984c37f 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglActivityServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglActivityServiceImpl.java @@ -47,10 +47,13 @@ import org.dromara.common.json.utils.JsonUtils; import com.fasterxml.jackson.core.type.TypeReference; import org.dromara.stgl.domain.StglClub; import org.dromara.stgl.domain.StglVolunteerTeam; +import org.dromara.stgl.domain.StglActivityRegistration; +import org.dromara.stgl.domain.StglMember; import org.dromara.stgl.domain.vo.StglClubVo; import org.dromara.stgl.domain.vo.StglVolunteerTeamVo; import org.dromara.stgl.mapper.StglClubMapper; import org.dromara.stgl.mapper.StglVolunteerTeamMapper; +import org.dromara.stgl.mapper.StglActivityRegistrationMapper; import org.dromara.system.domain.SysUser; import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.service.ISysUserService; @@ -76,6 +79,7 @@ public class StglActivityServiceImpl extends ServiceImpl 0; if (flag) { bo.setId(add.getId()); - // 为指导老师分配角色 assignAdvisorRoles(add); + createInvitedRegistrations(add.getId(), bo.getInvitedMemberIds()); } return flag; } @@ -311,8 +315,8 @@ public class StglActivityServiceImpl extends ServiceImpl 0; if (updateResult) { - // 处理指导老师角色分配 handleAdvisorRoleAssignment(oldActivity, update); + createInvitedRegistrations(update.getId(), bo.getInvitedMemberIds()); // 监听状态变化:如果状态从0(草稿)变为8(已提交),自动创建审批记录 if (oldActivity != null && @@ -1018,4 +1022,37 @@ public class StglActivityServiceImpl extends ServiceImpl invitedMemberIds) { + if (invitedMemberIds == null || invitedMemberIds.isEmpty() || activityId == null) { + return; + } + Date now = new Date(); + for (Long userId : invitedMemberIds) { + LambdaQueryWrapper existLqw = Wrappers.lambdaQuery(); + existLqw.eq(StglActivityRegistration::getActivityId, activityId) + .eq(StglActivityRegistration::getUserId, userId); + if (registrationMapper.selectCount(existLqw) > 0) { + continue; + } + StglMember member = stglMemberMapper.selectOne( + Wrappers.lambdaQuery() + .eq(StglMember::getUserId, userId) + .eq(StglMember::getStatus, 2) + .last("LIMIT 1") + ); + StglActivityRegistration reg = new StglActivityRegistration(); + reg.setActivityId(activityId); + reg.setUserId(userId); + reg.setStudentNo(member != null ? member.getStudentNo() : ""); + reg.setStudentName(member != null ? member.getStudentName() : ""); + reg.setRegistrationType("1"); + reg.setRegistrationTime(now); + reg.setApprovalStatus("1"); + reg.setApprovalTime(now); + reg.setRemark("活动邀请自动报名"); + registrationMapper.insert(reg); + } + log.info("活动邀请报名完成: activityId={}, 邀请人数={}", activityId, invitedMemberIds.size()); + } +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglClassServiceImpl.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglClassServiceImpl.java index 5c253529..e3f72431 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglClassServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglClassServiceImpl.java @@ -115,6 +115,23 @@ public class StglClassServiceImpl implements IStglClassService { return baseMapper.selectStudentActivities(studentId); } + @Override + public TableDataInfo> getCurrentActiveStudents(StglClassBo bo, PageQuery pageQuery) { + Map params = buildQueryParams(bo); + params.put("offset", (pageQuery.getPageNum() - 1) * pageQuery.getPageSize()); + params.put("pageSize", pageQuery.getPageSize()); + + List> list = baseMapper.selectCurrentActiveStudents(params); + + Map countParams = buildQueryParams(bo); + Integer total = baseMapper.selectCurrentActiveStudentsCount(countParams); + + TableDataInfo> result = new TableDataInfo<>(); + result.setRows(list); + result.setTotal(total != null ? total : 0); + return result; + } + /** * 构建查询参数 * diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglClubRegisterServiceImpl.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglClubRegisterServiceImpl.java index 9ac49df7..52e8a71f 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglClubRegisterServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglClubRegisterServiceImpl.java @@ -15,6 +15,7 @@ import org.dromara.stgl.domain.StglMember; import org.dromara.stgl.mapper.StglClubMapper; import org.dromara.stgl.mapper.StglMemberMapper; import org.dromara.stgl.service.IStglClubRegisterService; +import org.dromara.stgl.service.IStglMemberApprovalService; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.stgl.domain.vo.StglClubVo; import org.dromara.system.domain.SysUser; @@ -40,6 +41,7 @@ public class StglClubRegisterServiceImpl implements IStglClubRegisterService { private final StglMemberMapper memberMapper; private final SysUserMapper sysUserMapper; private final XgStudentMsgMapper xgStudentMsgMapper; + private final IStglMemberApprovalService memberApprovalService; /** * 分页查询社团报名大厅列表 @@ -247,10 +249,15 @@ public class StglClubRegisterServiceImpl implements IStglClubRegisterService { member.setStudentName(studentName != null ? studentName : user.getNickName()); // 如果没有学生信息,使用用户昵称 // 申请时不设置roleType,等待审核通过后再设置 member.setJoinTime(new Date()); - member.setStatus(0); // 待审核状态(stgl_member_status字典,新报名需审核) - member.setRemark(reason); // 将申请理由存储到备注字段 - - return memberMapper.insert(member) > 0; + member.setStatus(0); // 待审核状态 + member.setRemark(reason); + + boolean inserted = memberMapper.insert(member) > 0; + if (inserted) { + // 创建两级审批流程:社长审批 → 指导老师审批 + memberApprovalService.createApprovalFlow(member.getId(), clubId); + } + return inserted; } /** diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglClubServiceImpl.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglClubServiceImpl.java index 0d18877f..ea89eb1d 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglClubServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglClubServiceImpl.java @@ -334,23 +334,162 @@ public class StglClubServiceImpl implements IStglClubService { @Override @Transactional(rollbackFor = Exception.class) public Boolean insertByBo(StglClubBo bo) { + Long currentUserId = LoginHelper.getUserId(); + if (currentUserId == null) { + throw new RuntimeException("用户未登录"); + } + StglClub add = MapstructUtils.convert(bo, StglClub.class); + + // 根据社长用户ID从学生表查询并填充冗余字段 + if (add.getPresidentUserId() != null) { + fillPresidentInfoFromStudent(add); + } + + // 创建者自动设为首席指导老师 + add.setCreateBy(currentUserId); + if (StringUtils.isBlank(add.getAdvisorName())) { + // 从用户表获取创建者(老师)信息填充指导老师字段 + fillCreatorAsAdvisor(add, currentUserId); + } + validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setId(add.getId()); - // 自动创建社长和副社长的成员记录 createLeaderMemberRecords(add); - // 为指导老师分配角色 + createMaterialKeeperMemberRecord(add); assignAdvisorRoles(add); - // 为社长分配角色 assignPresidentRole(add); - // 创建指导老师历史记录 createAdvisorHistoryRecords(add); } return flag; } + /** + * 根据社长用户ID从学生表查询信息,自动填充冗余字段 + */ + private void fillPresidentInfoFromStudent(StglClub club) { + try { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(XgStudentMsg::getId, club.getPresidentUserId()) + .eq(XgStudentMsg::getDeleted, 0) + .last("LIMIT 1"); + XgStudentMsg student = studentMsgMapper.selectOne(wrapper); + if (student != null) { + club.setPresidentName(student.getName()); + club.setPresidentStudentNo(student.getStudentNo()); + club.setPresidentDept(student.getCollegeName()); + club.setPresidentMajor(student.getMajorName()); + club.setPresidentClass(student.getClassName()); + club.setPresidentPhone(student.getPhone()); + } + } catch (Exception e) { + log.error("根据用户ID查询社长学生信息失败: {}", e.getMessage()); + } + } + + /** + * 将创建者设为首席指导老师 + */ + private void fillCreatorAsAdvisor(StglClub club, Long userId) { + try { + String nickName = LoginHelper.getLoginUser().getNickname(); + club.setAdvisorName(nickName); + } catch (Exception e) { + log.warn("获取创建者老师信息失败: {}", e.getMessage()); + } + } + + /** + * 创建物资保管员成员记录 + */ + private void createMaterialKeeperMemberRecord(StglClub club) { + if (club.getMaterialKeeperUserId() == null) { + return; + } + try { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(XgStudentMsg::getId, club.getMaterialKeeperUserId()) + .eq(XgStudentMsg::getDeleted, 0) + .last("LIMIT 1"); + XgStudentMsg student = studentMsgMapper.selectOne(wrapper); + if (student == null) { + log.warn("物资保管员学生信息不存在, userId={}", club.getMaterialKeeperUserId()); + return; + } + + StglMember keeper = new StglMember(); + keeper.setMemberType(1); + keeper.setRelationId(club.getId()); + keeper.setUserId(club.getMaterialKeeperUserId()); + keeper.setStudentNo(student.getStudentNo()); + keeper.setStudentName(student.getName()); + keeper.setRoleType("物资保管员"); + keeper.setJoinTime(new Date()); + keeper.setStatus(2); + keeper.setRemark("社团创建时自动添加"); + memberMapper.insert(keeper); + + // 回填物资保管员姓名到社团表 + if (StringUtils.isBlank(club.getMaterialKeeperName())) { + StglClub updateClub = new StglClub(); + updateClub.setId(club.getId()); + updateClub.setMaterialKeeperName(student.getName()); + baseMapper.updateById(updateClub); + } + } catch (Exception e) { + log.error("创建物资保管员成员记录失败: {}", e.getMessage()); + } + } + + private void syncMaterialKeeperMemberRecord(StglClub newClub, StglClub oldClub) { + Long newKeeperId = newClub.getMaterialKeeperUserId(); + Long oldKeeperId = oldClub != null ? oldClub.getMaterialKeeperUserId() : null; + + if (java.util.Objects.equals(newKeeperId, oldKeeperId)) { + return; + } + + // 删除旧的物资保管员成员记录 + if (oldKeeperId != null) { + LambdaQueryWrapper delWrapper = Wrappers.lambdaQuery(); + delWrapper.eq(StglMember::getRelationId, newClub.getId()) + .eq(StglMember::getMemberType, 1) + .eq(StglMember::getRoleType, "物资保管员"); + memberMapper.delete(delWrapper); + } + + // 创建新的物资保管员成员记录 + if (newKeeperId != null) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(XgStudentMsg::getId, newKeeperId) + .eq(XgStudentMsg::getDeleted, 0) + .last("LIMIT 1"); + XgStudentMsg student = studentMsgMapper.selectOne(wrapper); + if (student != null) { + StglMember keeper = new StglMember(); + keeper.setMemberType(1); + keeper.setRelationId(newClub.getId()); + keeper.setUserId(newKeeperId); + keeper.setStudentNo(student.getStudentNo()); + keeper.setStudentName(student.getName()); + keeper.setRoleType("物资保管员"); + keeper.setJoinTime(new Date()); + keeper.setStatus(2); + keeper.setRemark("修改社团信息时更新"); + memberMapper.insert(keeper); + + if (StringUtils.isBlank(newClub.getMaterialKeeperName())) { + StglClub updateName = new StglClub(); + updateName.setId(newClub.getId()); + updateName.setMaterialKeeperName(student.getName()); + baseMapper.updateById(updateName); + } + } + } + } + /** * 修改社团信息 * @@ -375,6 +514,8 @@ public class StglClubServiceImpl implements IStglClubService { if (updateResult) { // 同步更新副社长的成员记录 syncVicePresidentsInMembers(update); + // 同步物资保管员成员记录 + syncMaterialKeeperMemberRecord(update, oldClub); // 处理指导老师角色分配 handleAdvisorRoleAssignment(oldClub, update); // 更新指导老师历史记录 @@ -522,25 +663,35 @@ public class StglClubServiceImpl implements IStglClubService { */ private void createLeaderMemberRecords(StglClub club) { Date now = new Date(); - - // 1. 创建社长成员记录 - if (StringUtils.isNotBlank(club.getPresidentStudentNo())) { + + // 1. 创建社长成员记录(优先用 presidentUserId) + if (club.getPresidentUserId() != null) { StglMember president = new StglMember(); - president.setMemberType(1); // 社团成员 + president.setMemberType(1); + president.setRelationId(club.getId()); + president.setUserId(club.getPresidentUserId()); + president.setStudentNo(club.getPresidentStudentNo()); + president.setStudentName(club.getPresidentName()); + president.setRoleType("社长"); + president.setJoinTime(now); + president.setStatus(2); + president.setRemark("社团创建时自动添加"); + memberMapper.insert(president); + } else if (StringUtils.isNotBlank(club.getPresidentStudentNo())) { + // 兼容旧逻辑:通过学号查找 + StglMember president = new StglMember(); + president.setMemberType(1); president.setRelationId(club.getId()); president.setStudentNo(club.getPresidentStudentNo()); president.setStudentName(club.getPresidentName()); - president.setRoleType("社长"); // 社长 + president.setRoleType("社长"); president.setJoinTime(now); - president.setStatus(2); // 已加入状态(stgl_member_status字典) + president.setStatus(2); president.setRemark("社团创建时自动添加"); - - // 根据学号查找用户ID Long presidentUserId = findUserIdByStudentNo(club.getPresidentStudentNo()); if (presidentUserId != null) { president.setUserId(presidentUserId); } - memberMapper.insert(president); } diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglMemberApprovalServiceImpl.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglMemberApprovalServiceImpl.java new file mode 100644 index 00000000..ee7db341 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglMemberApprovalServiceImpl.java @@ -0,0 +1,262 @@ +package org.dromara.stgl.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.stgl.domain.StglClub; +import org.dromara.stgl.domain.StglMember; +import org.dromara.stgl.domain.StglMemberApproval; +import org.dromara.stgl.domain.vo.StglMemberApprovalVo; +import org.dromara.stgl.mapper.StglClubMapper; +import org.dromara.stgl.mapper.StglMemberApprovalMapper; +import org.dromara.stgl.mapper.StglMemberMapper; +import org.dromara.stgl.service.IStglMemberApprovalService; +import com.fasterxml.jackson.core.type.TypeReference; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.*; + +/** + * 成员申请审批流程Service业务层处理 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class StglMemberApprovalServiceImpl implements IStglMemberApprovalService { + + private final StglMemberApprovalMapper approvalMapper; + private final StglMemberMapper memberMapper; + private final StglClubMapper clubMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean createApprovalFlow(Long memberId, Long clubId) { + StglClub club = clubMapper.selectById(clubId); + if (club == null) { + throw new RuntimeException("社团不存在"); + } + + // 获取社长信息(审批节点1) + Long presidentUserId = club.getPresidentUserId(); + String presidentName = club.getPresidentName(); + if (presidentUserId == null) { + // 兼容旧数据:从 stgl_member 表查找社长 + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(StglMember::getMemberType, 1) + .eq(StglMember::getRelationId, clubId) + .eq(StglMember::getRoleType, "社长") + .eq(StglMember::getStatus, 2) + .last("LIMIT 1"); + StglMember president = memberMapper.selectOne(lqw); + if (president != null) { + presidentUserId = president.getUserId(); + presidentName = president.getStudentName(); + } + } + if (presidentUserId == null) { + throw new RuntimeException("社团暂无社长,无法提交申请"); + } + + // 获取指导老师信息(审批节点2)- 从 advisors_json 中获取首席指导老师 + Long advisorUserId = null; + String advisorName = club.getAdvisorName(); + String advisorsJson = club.getAdvisorsJson(); + if (StringUtils.isNotBlank(advisorsJson)) { + try { + List> advisors = JsonUtils.parseObject(advisorsJson, new TypeReference>>() {}); + if (advisors != null && !advisors.isEmpty()) { + Map first = advisors.get(0); + Object uid = first.get("userId"); + if (uid != null) { + advisorUserId = Long.parseLong(uid.toString()); + } + if (first.get("teacher") instanceof Map) { + Map teacher = (Map) first.get("teacher"); + if (teacher.get("nickName") != null) { + advisorName = teacher.get("nickName").toString(); + } + } + } + } catch (Exception e) { + log.warn("解析advisors_json失败: {}", e.getMessage()); + } + } + if (advisorUserId == null) { + advisorUserId = club.getCreateBy(); + } + if (advisorUserId == null) { + throw new RuntimeException("社团暂无指导老师,无法提交申请"); + } + + Date now = new Date(); + Long currentUserId = LoginHelper.getUserId(); + + // 节点1:社长审批 + StglMemberApproval presidentNode = new StglMemberApproval(); + presidentNode.setMemberId(memberId); + presidentNode.setClubId(clubId); + presidentNode.setNodeCode("president_approve"); + presidentNode.setNodeName("社长审批"); + presidentNode.setNodeOrder(new BigDecimal("1.0")); + presidentNode.setApproverId(presidentUserId); + presidentNode.setApproverName(presidentName != null ? presidentName : ""); + presidentNode.setAuditStatus("pending"); + approvalMapper.insert(presidentNode); + + // 节点2:指导老师审批 + StglMemberApproval advisorNode = new StglMemberApproval(); + advisorNode.setMemberId(memberId); + advisorNode.setClubId(clubId); + advisorNode.setNodeCode("advisor_approve"); + advisorNode.setNodeName("指导老师审批"); + advisorNode.setNodeOrder(new BigDecimal("2.0")); + advisorNode.setApproverId(advisorUserId); + advisorNode.setApproverName(advisorName != null ? advisorName : ""); + advisorNode.setAuditStatus("pending"); + approvalMapper.insert(advisorNode); + + log.info("创建成员审批流程: memberId={}, clubId={}, 社长={}, 指导老师={}", + memberId, clubId, presidentUserId, advisorUserId); + return true; + } + + @Override + public List getApprovalFlow(Long memberId) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(StglMemberApproval::getMemberId, memberId) + .orderByAsc(StglMemberApproval::getNodeOrder); + return approvalMapper.selectVoList(lqw); + } + + @Override + public List getPendingApprovals(Long clubId) { + Long currentUserId = LoginHelper.getUserId(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(StglMemberApproval::getApproverId, currentUserId) + .eq(StglMemberApproval::getAuditStatus, "pending"); + if (clubId != null) { + lqw.eq(StglMemberApproval::getClubId, clubId); + } + lqw.orderByAsc(StglMemberApproval::getNodeOrder); + return approvalMapper.selectVoList(lqw); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean presidentApprove(Long memberId, Boolean approved, String opinion) { + Boolean result = doApprove(memberId, "president_approve", approved, opinion); + + if (approved) { + StglMember member = memberMapper.selectById(memberId); + if (member != null) { + StglMember updateMember = new StglMember(); + updateMember.setId(memberId); + updateMember.setStatus(1); // 审核中,等待指导老师审批 + memberMapper.updateById(updateMember); + log.info("社长审批通过,进入指导老师审批阶段: memberId={}", memberId); + } + } + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean advisorApprove(Long memberId, Boolean approved, String opinion) { + Boolean result = doApprove(memberId, "advisor_approve", approved, opinion); + + // 指导老师审批是最后一个节点,如果通过则更新成员状态为已加入 + if (approved) { + StglMember member = memberMapper.selectById(memberId); + if (member != null) { + StglMember updateMember = new StglMember(); + updateMember.setId(memberId); + updateMember.setStatus(2); // 已加入 + updateMember.setRoleType("普通成员"); + memberMapper.updateById(updateMember); + log.info("成员申请全部审批通过,已加入社团: memberId={}", memberId); + } + } + return result; + } + + /** + * 通用审批处理逻辑 + */ + private Boolean doApprove(Long memberId, String nodeCode, Boolean approved, String opinion) { + Long currentUserId = LoginHelper.getUserId(); + if (currentUserId == null) { + throw new RuntimeException("用户未登录"); + } + + // 查找待审批的节点 + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(StglMemberApproval::getMemberId, memberId) + .eq(StglMemberApproval::getNodeCode, nodeCode) + .eq(StglMemberApproval::getAuditStatus, "pending"); + StglMemberApproval approvalNode = approvalMapper.selectOne(lqw); + + if (approvalNode == null) { + throw new RuntimeException("未找到待审批记录或已审批过"); + } + + // 校验审批人身份 + log.info("审批人校验: approverId={}, currentUserId={}, equals={}", + approvalNode.getApproverId(), currentUserId, + approvalNode.getApproverId().equals(currentUserId)); + log.info("审批人校验类型: approverId.class={}, currentUserId.class={}", + approvalNode.getApproverId().getClass().getName(), + currentUserId.getClass().getName()); + + if (!approvalNode.getApproverId().equals(currentUserId)) { + throw new RuntimeException("您不是该节点的审批人(节点审批人:" + approvalNode.getApproverId() + ", 当前用户:" + currentUserId + ")"); + } + + // 更新审批记录 + StglMemberApproval updateNode = new StglMemberApproval(); + updateNode.setId(approvalNode.getId()); + updateNode.setApproveResult(approved ? "通过" : "不通过"); + updateNode.setAuditStatus(approved ? "approved" : "rejected"); + updateNode.setAuditOpinion(opinion); + updateNode.setAuditTime(new Date()); + approvalMapper.updateById(updateNode); + + // 如果拒绝,更新成员状态为已拒绝,并标记后续节点也结束 + if (!approved) { + StglMember member = memberMapper.selectById(memberId); + if (member != null) { + StglMember updateMember = new StglMember(); + updateMember.setId(memberId); + updateMember.setStatus(3); // 已拒绝 + updateMember.setRemark(opinion != null ? "审批拒绝: " + opinion : "审批拒绝"); + memberMapper.updateById(updateMember); + } + + // 将后续待审批节点也标记为结束 + LambdaQueryWrapper pendingLqw = Wrappers.lambdaQuery(); + pendingLqw.eq(StglMemberApproval::getMemberId, memberId) + .eq(StglMemberApproval::getAuditStatus, "pending") + .ne(StglMemberApproval::getId, approvalNode.getId()); + List pendingNodes = approvalMapper.selectList(pendingLqw); + for (StglMemberApproval pending : pendingNodes) { + StglMemberApproval cancelNode = new StglMemberApproval(); + cancelNode.setId(pending.getId()); + cancelNode.setAuditStatus("rejected"); + cancelNode.setApproveResult("前置节点拒绝,自动终止"); + cancelNode.setAuditTime(new Date()); + approvalMapper.updateById(cancelNode); + } + + log.info("成员申请被拒绝: memberId={}, nodeCode={}, 拒绝人={}", memberId, nodeCode, currentUserId); + } else { + log.info("成员申请节点审批通过: memberId={}, nodeCode={}, 审批人={}", memberId, nodeCode, currentUserId); + } + + return true; + } +} diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglMemberServiceImpl.java b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglMemberServiceImpl.java index 2eaf8007..ff916fc0 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglMemberServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/java/org/dromara/stgl/service/impl/StglMemberServiceImpl.java @@ -16,6 +16,7 @@ import org.dromara.stgl.domain.vo.StglMemberVo; import org.dromara.stgl.domain.StglMember; import org.dromara.stgl.mapper.StglMemberMapper; import org.dromara.stgl.service.IStglMemberService; +import org.dromara.stgl.service.IStglMemberApprovalService; import org.dromara.system.service.ISysUserService; import cn.hutool.core.collection.CollUtil; import org.dromara.common.satoken.utils.LoginHelper; @@ -44,6 +45,7 @@ public class StglMemberServiceImpl implements IStglMemberService { private final StglMemberMapper baseMapper; private final ISysUserService userService; + private final IStglMemberApprovalService memberApprovalService; /** * 查询社团成员 @@ -381,28 +383,25 @@ public class StglMemberServiceImpl implements IStglMemberService { if (member == null) { throw new RuntimeException("成员记录不存在"); } - - if (member.getStatus() != 0) { + + if (member.getStatus() != 0 && member.getStatus() != 1) { throw new RuntimeException("该申请已审核过,无法重复审核"); } - - // 更新成员状态和角色 - StglMember updateMember = new StglMember(); - updateMember.setId(id); - if (approved) { - updateMember.setStatus(2); // 已加入状态(stgl_member_status字典) - // 根据成员类型设置不同的角色 - if (member.getMemberType() == 1) { - updateMember.setRoleType("普通成员"); // 社团成员 - } else if (member.getMemberType() == 2) { - updateMember.setRoleType("队员"); // 志愿者队伍成员 + + Long currentUserId = LoginHelper.getUserId(); + java.util.Set roleKeys = LoginHelper.getLoginUser().getRolePermission(); + + if (roleKeys.contains("stgl_instructor")) { + if (member.getStatus() == 0) { + throw new RuntimeException("该申请需等待社长先审批,请稍后再试"); } + return memberApprovalService.advisorApprove(id, approved, null); } else { - updateMember.setStatus(3); // 已驳回状态(stgl_member_status字典) - updateMember.setRoleType(null); // 拒绝时清空角色 + if (member.getStatus() == 1) { + throw new RuntimeException("社长已审批通过,等待指导老师审批中"); + } + return memberApprovalService.presidentApprove(id, approved, null); } - - return baseMapper.updateById(updateMember) > 0; } /** diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/StglClubMapper.xml b/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/StglClubMapper.xml index 0de5ec19..6a0917a0 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/StglClubMapper.xml +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/StglClubMapper.xml @@ -31,6 +31,7 @@ + @@ -56,6 +57,7 @@ c.vice_presidents_json, c.advisor_name, c.advisor_dept, c.advisor_phone, c.advisor_email, c.advisors_json, + c.affiliation, c.member_limit, c.departments, c.recruit_cycle, c.recruit_time, c.activity_frequency, c.funding_sources, c.charter_url, c.status, c.sigh_open, c.remark, c.create_by, c.create_time, c.update_by, c.update_time, c.tenant_id, c.deleted, COALESCE(m.member_count, 0) as memberCount @@ -76,6 +78,7 @@ c.president_dept, c.president_major, c.president_class, c.president_phone, c.vice_president_name, c.vice_president_student_no, c.vice_president_dept, c.vice_president_major, c.vice_president_class, c.vice_president_phone, c.advisor_name, c.advisor_dept, c.advisor_phone, c.advisor_email, + c.affiliation, c.member_limit, c.departments, c.recruit_cycle, c.recruit_time, c.activity_frequency, c.funding_sources, c.charter_url, c.status, c.sigh_open, c.remark, c.create_by, c.create_time, c.update_by, c.update_time, @@ -98,6 +101,7 @@ c.president_dept, c.president_major, c.president_class, c.president_phone, c.vice_president_name, c.vice_president_student_no, c.vice_president_dept, c.vice_president_major, c.vice_president_class, c.vice_president_phone, c.advisor_name, c.advisor_dept, c.advisor_phone, c.advisor_email, + c.affiliation, c.member_limit, c.departments, c.recruit_cycle, c.recruit_time, c.activity_frequency, c.funding_sources, c.charter_url, c.status, c.sigh_open, c.remark, c.create_by, c.create_time, c.update_by, c.update_time, @@ -129,6 +133,7 @@ c.vice_presidents_json, c.advisor_name, c.advisor_dept, c.advisor_phone, c.advisor_email, c.advisors_json, + c.affiliation, c.member_limit, c.departments, c.recruit_cycle, c.recruit_time, c.activity_frequency, c.funding_sources, c.charter_url, c.status, c.sigh_open, c.remark, c.create_by, c.create_time, c.update_by, c.update_time, diff --git a/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/stgl/StglClassMapper.xml b/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/stgl/StglClassMapper.xml index 2ad5d4a0..9487b72f 100644 --- a/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/stgl/StglClassMapper.xml +++ b/backend/ruoyi-modules/ruoyi-stgl/src/main/resources/mapper/stgl/StglClassMapper.xml @@ -445,4 +445,132 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ORDER BY a.start_time DESC, a.create_time DESC + + + + + + \ No newline at end of file diff --git a/web/src/api/stgl/activity/types.ts b/web/src/api/stgl/activity/types.ts index 2b40215c..3611913a 100644 --- a/web/src/api/stgl/activity/types.ts +++ b/web/src/api/stgl/activity/types.ts @@ -461,6 +461,11 @@ export interface StglActivityForm { * 备注 */ remark?: string; + + /** + * 邀请成员ID列表(前端专用,提交时传给后端) + */ + invitedMemberIds?: (string | number)[]; } export interface StglActivityQuery extends PageQuery { diff --git a/web/src/api/stgl/club/types.ts b/web/src/api/stgl/club/types.ts index 8f4586c7..37723cc2 100644 --- a/web/src/api/stgl/club/types.ts +++ b/web/src/api/stgl/club/types.ts @@ -79,6 +79,21 @@ export interface StglClubVO { */ presidentPhone: string; + /** + * 社长用户ID(关联学生用户) + */ + presidentUserId?: string | number; + + /** + * 物资保管员用户ID + */ + materialKeeperUserId?: string | number; + + /** + * 物资保管员姓名 + */ + materialKeeperName?: string; + /** * 副社长列表 */ @@ -148,10 +163,7 @@ export interface StglClubVO { * 指导老师列表JSON格式存储 */ advisorsJson?: string; - - /** - * 成员规模上限 - */ + affiliation?: string; memberLimit: number; /** @@ -286,6 +298,36 @@ export interface StglClubForm { */ presidentPhone?: string; + /** + * 社长用户ID(关联学生用户) + */ + presidentUserId?: string | number; + + /** + * 物资保管员用户ID + */ + materialKeeperUserId?: string | number; + + /** + * 物资保管员姓名 + */ + materialKeeperName?: string; + + /** + * 物资保管员学生对象(前端选择器用) + */ + materialKeeperStudent?: StudentVO | null; + + /** + * 物资保管员学号(前端显示用) + */ + materialKeeperStudentNo?: string; + + /** + * 物资保管员联系方式(前端显示用) + */ + materialKeeperPhone?: string; + /** * 副社长列表 */ @@ -350,6 +392,7 @@ export interface StglClubForm { * 成员规模上限 */ memberLimit?: number; + affiliation?: string; /** * 组织架构 diff --git a/web/src/api/stgl/member/index.ts b/web/src/api/stgl/member/index.ts index 9792a30d..81b8df85 100644 --- a/web/src/api/stgl/member/index.ts +++ b/web/src/api/stgl/member/index.ts @@ -98,9 +98,41 @@ export const approveMember = (id: string | number, approved: boolean) => { return request({ url: `/stgl/member/approve/${id}`, method: 'put', - params: { approved }, - headers: { - isEncrypt: 'true' - } + params: { approved } + }); +}; + +export const presidentApprove = (memberId: number, approved: boolean, opinion?: string) => { + return request({ + url: '/stgl/member/presidentApprove', + method: 'put', + data: { memberId, approved, opinion }, + headers: { isEncrypt: 'true' } + }); +}; + +export const advisorApprove = (memberId: number, approved: boolean, opinion?: string) => { + return request({ + url: '/stgl/member/advisorApprove', + method: 'put', + data: { memberId, approved, opinion }, + headers: { isEncrypt: 'true' } + }); +}; + +export const getApprovalFlow = (memberId: number) => { + return request({ + url: `/stgl/member/approvalFlow/${memberId}`, + method: 'get', + headers: { isEncrypt: 'true' } + }); +}; + +export const getPendingApprovals = (clubId?: number) => { + return request({ + url: '/stgl/member/pendingApprovals', + method: 'get', + params: clubId ? { clubId } : {}, + headers: { isEncrypt: 'true' } }); }; diff --git a/web/src/api/stgl/stclass/index.ts b/web/src/api/stgl/stclass/index.ts index aad3ff60..3fdca794 100644 --- a/web/src/api/stgl/stclass/index.ts +++ b/web/src/api/stgl/stclass/index.ts @@ -62,3 +62,11 @@ export function exportStglClass(query: StglClassQuery) { params: query }); } + +export function getCurrentActivities(query: StglClassQuery) { + return request({ + url: '/stgl/stclass/current-activities', + method: 'get', + params: query + }); +} diff --git a/web/src/api/stgl/stclass/types.ts b/web/src/api/stgl/stclass/types.ts index d1e1ca85..f7721c92 100644 --- a/web/src/api/stgl/stclass/types.ts +++ b/web/src/api/stgl/stclass/types.ts @@ -290,28 +290,24 @@ export interface ActivityDetailVO { * 班级详情对象 */ export interface StglClassDetailVO { - /** - * 班级ID - */ id: string | number; - - /** - * 班级名称 - */ className: string; - - /** - * 学院名称 - */ collegeName: string; - - /** - * 专业名称 - */ majorName: string; - - /** - * 年级 - */ grade: string; } + +export interface CurrentActivityVO { + id: string | number; + studentNo: string; + studentName: string; + gender: string; + phone: string; + className: string; + activityId: string | number; + activityName: string; + startTime: string; + endTime: string; + location: string; + organizationName: string; +} diff --git a/web/src/components/VicePresidentManager/index.vue b/web/src/components/VicePresidentManager/index.vue index 4577deb8..3587be21 100644 --- a/web/src/components/VicePresidentManager/index.vue +++ b/web/src/components/VicePresidentManager/index.vue @@ -118,6 +118,7 @@ watch( (newValue) => { if (updateLock.value) return; updateLock.value = true; + emit('update:modelValue', newValue); emit('change', newValue); nextTick(() => { updateLock.value = false; diff --git a/web/src/views/stgl/activity/index.vue b/web/src/views/stgl/activity/index.vue index fafb5858..beda3ec9 100644 --- a/web/src/views/stgl/activity/index.vue +++ b/web/src/views/stgl/activity/index.vue @@ -610,6 +610,34 @@ + 邀请成员 + + + +
+
+ 全选 + + 已选 {{ form.invitedMemberIds?.length || 0 }} / {{ inviteMemberList.length }} 人 + 请先选择关联社团 +
+ +
+
暂无可邀请的成员
+ + {{ member.studentName }}({{ member.studentNo }}) + +
+
+
+
+
+
@@ -690,7 +718,7 @@