# 02-组织关系处理 ## 📋 概述 本分文档详细说明学生退学、休学、毕业、复学处理中的组织关系处理内容,包括班级干部信息和社团成员关系的变更逻辑。 ## 👥 组织关系处理 ### 1. 班级干部信息处理 #### 📊 数据库表结构 **表名**: `xg_class_pos_msg` **主键**: `id` (bigint(20), 雪花ID) **核心关联字段**: - `class_id`: 班级ID,关联 `xg_class_msg.id` - `student_id`: 学生ID,关联 `xg_student_msg.id` - `student_no`: 学号 - `student_name`: 学生姓名(冗余字段) #### 🎯 关键业务字段 基于数据库表结构 `xg_class_pos_msg`,关键字段如下: | 字段名 | 类型 | 说明 | 默认值 | |--------|------|------|--------| | `is_current` | tinyint(1) | 是否现任(0-非现任,1-现任) | 1 | | `cadre_status` | char(1) | 职务任职状态(关联stu_cadre_status字典) | 1 | | `appointment_date` | date | 任职日期 | - | | `term_start_date` | date | 任期开始日期 | - | | `term_end_date` | date | 任期结束日期 | NULL | | `term_duration` | int(11) | 任期时长(月) | 12 | | `dismissal_date` | date | 离任日期 | NULL | | `dismissal_reason` | varchar(200) | 离任原因 | NULL | | `appointment_method` | varchar(20) | 任命方式(appointment/election/recommendation) | appointment | #### 🎯 干部状态字典 系统使用 `stu_cadre_status` 字典类型,具体定义如下: | 状态值 | 状态名称 | 说明 | 样式类 | |--------|----------|------|--------| | `1` | 在任 | 正常任职状态 | primary | | `2` | 到期 | 任期自然到期 | success | | `3` | 辞职 | 干部主动辞职 | warning | | `4` | 免职 | 被免去职务 | danger | | `5` | 调任 | 职务调整调动 | info | | `6` | 休学 | 因学生休学离任 | danger | **关键字段说明** (基于实体类代码分析): - `isCurrent`: 是否现任 (0-非现任,1-现任) - `cadreStatus`: 学生职务任职状态 - `dismissalDate`: 离任日期 - `dismissalReason`: 离任原因 - `termStartDate`: 任期开始日期 - `termEndDate`: 任期结束日期 - `termDuration`: 任期时长(月) #### 🔄 离任业务流程 ##### 📋 完整离任流程 ``` 1. 验证干部信息存在性和状态 2. 计算任期时长 (term_start_date 到 term_end_date) 3. 更新干部状态为非现任 (is_current = 0) 4. 设置离任状态和原因 5. 更新任期结束日期和离任日期 6. 删除用户角色关联 (sys_user_role) 7. 记录操作日志 ``` ##### 💻 后端实现逻辑 **控制器接口**: `POST /xgClassPosMsg/xgClassPosMsg/dismiss/{id}` **核心方法**: `XgClassPosMsgServiceImpl.dismissCadre()` ```java @Override @Transactional(rollbackFor = Exception.class) public Boolean dismissCadre(Long id, Date termEndDate, String cadreStatus, String dismissalReason) { // 1. 查询并验证干部信息 XgClassPosMsg classPosMsg = baseMapper.selectById(id); // 2. 计算任期时长 Long termDuration = calculateTermDuration(classPosMsg.getTermStartDate(), termEndDate); // 3. 更新干部信息 XgClassPosMsg updateEntity = new XgClassPosMsg(); updateEntity.setId(id); updateEntity.setTermEndDate(termEndDate); updateEntity.setTermDuration(termDuration); updateEntity.setCadreStatus(cadreStatus); updateEntity.setDismissalReason(dismissalReason); updateEntity.setDismissalDate(termEndDate); updateEntity.setIsCurrent(0L); // 设置为非现任 baseMapper.updateById(updateEntity); // 4. 删除用户角色关联 sysUserRoleMapper.delete(new LambdaQueryWrapper() .eq(SysUserRole::getUserId, classPosMsg.getStudentId())); return true; } ``` ##### 🎨 前端离任界面 **文件位置**: `web/src/views/xg/xsjcxx/xgClassPosMsg/xgClassPosMsg/index.vue` **离任按钮**: 仅对 `is_current = 1` 的干部显示 ```vue ``` #### 🔄 学生状态变更时的干部处理逻辑 - **休学**: `cadre_status = '6'`(休学),`is_current = 0`,`dismissal_reason = '学生休学'` - **退学**: `cadre_status = '4'`(免职),`is_current = 0`,`dismissal_reason = '学生退学'` - **毕业**: `cadre_status = '4'`(免职),`is_current = 0`,`dismissal_reason = '学生毕业'` #### 💾 SQL批量处理脚本 ```sql -- 参数说明: -- @student_id: 学生ID -- @change_type: 变更类型 (XIU_XUE-休学, TUI_XUE-退学, BI_YE-毕业) -- @operator_id: 操作员ID -- @dismissal_date: 离任日期 (默认为当前日期) SET @dismissal_date = COALESCE(@dismissal_date, CURDATE()); -- 1. 更新干部状态(休学) UPDATE xg_class_pos_msg SET is_current = 0, cadre_status = '6', -- 休学状态 term_end_date = @dismissal_date, dismissal_date = @dismissal_date, dismissal_reason = '学生休学', term_duration = TIMESTAMPDIFF(MONTH, term_start_date, @dismissal_date), update_by = @operator_id, update_time = NOW() WHERE student_id = @student_id AND is_current = 1 AND deleted = 0 AND @change_type = 'XIU_XUE'; -- 2. 更新干部状态(退学/毕业) UPDATE xg_class_pos_msg SET is_current = 0, cadre_status = '4', -- 免职状态 term_end_date = @dismissal_date, dismissal_date = @dismissal_date, dismissal_reason = CASE WHEN @change_type = 'TUI_XUE' THEN '学生退学' WHEN @change_type = 'BI_YE' THEN '学生毕业' ELSE '学生状态变更' END, term_duration = TIMESTAMPDIFF(MONTH, term_start_date, @dismissal_date), update_by = @operator_id, update_time = NOW() WHERE student_id = @student_id AND is_current = 1 AND deleted = 0 AND @change_type IN ('TUI_XUE', 'BI_YE'); ``` ### 2. 社团成员关系处理 #### 📊 数据库表结构 **表名**: `stgl_member` (原名 `club_member`,现已重构) **主键**: `id` (bigint(20)) **核心关联字段**: - `user_id`: 用户ID,关联学生用户ID - `student_no`: 学号 - `student_name`: 学生姓名(冗余字段) - `relation_id`: 关联ID(社团ID或志愿者队伍ID) - `member_type`: 成员类型(1-社团成员,2-志愿者) #### 🎯 成员状态字典 系统使用 `stgl_member_status` 字典类型,具体定义如下: | 状态值 | 状态名称 | 说明 | 样式类 | |--------|----------|------|--------| | `0` | 待审核 | 申请加入,等待审核 | warning | | `1` | 审核中 | 正在审核中 | info | | `2` | 已加入 | 正式成员 | success | | `3` | 已驳回 | 审核未通过 | danger | | `4` | 已退出 | 已退出组织 | danger | **关键字段说明** (基于实体类代码分析): - `memberType`: 成员类型 (1-社团成员,2-志愿者) - `userId`: 用户ID,对应学生ID - `studentNo`: 学号 - `studentName`: 学生姓名 - `roleType`: 角色类型 (社长、副社长、部长、普通成员) - `joinTime`: 加入时间 - `status`: 状态 (0-待审核,1-审核中,2-已加入,3-已驳回,4-已退出) #### 🔄 退出处理逻辑 ##### 📋 退出业务规则 - **适用范围**: 对所有状态为"待审核"(0)、"审核中"(1)、"已加入"(2)的成员 - **处理方式**: 将状态更新为"已退出"(4) - **数据保留**: 保留历史记录,不物理删除 - **关联清理**: 不涉及其他关联数据的清理 ##### 💻 后端实现逻辑 **所在模块**: `ruoyi-stgl` (社团管理模块) **实体类**: `StglMember.java` **服务类**: `StglMemberServiceImpl.java` ```java // 社团成员实体类关键字段 @Data @TableName("stgl_member") public class StglMember extends TenantEntity { private Long userId; // 用户ID private String studentNo; // 学号 private String studentName; // 学生姓名 private Integer memberType; // 成员类型:1-社团成员,2-志愿者 private Long relationId; // 关联ID private Integer status; // 状态:0-待审核,1-审核中,2-已加入,3-已驳回,4-已退出 private String roleType; // 角色类型 private Date joinTime; // 加入时间 } ``` ##### 🎨 前端界面展示 **管理界面**: `stgl/member/index.vue` **状态显示**: 使用字典标签显示成员状态 ```vue ``` #### 🔄 学生状态变更时的成员处理逻辑 - **休学**: 成员状态改为 `4`(已退出),退出原因为"学生休学" - **退学**: 成员状态改为 `4`(已退出),退出原因为"学生退学" - **毕业**: 成员状态改为 `4`(已退出),退出原因为"学生毕业" #### 💾 SQL批量处理脚本 ```sql -- 参数说明: -- @student_id: 学生ID (对应user_id) -- @change_type: 变更类型 (XIU_XUE-休学, TUI_XUE-退学, BI_YE-毕业) -- @operator_id: 操作员ID -- 更新社团/志愿者成员状态为已退出 UPDATE stgl_member SET status = 4, -- 已退出状态 update_by = @operator_id, update_time = NOW(), remark = CONCAT(COALESCE(remark, ''), ' | 退出原因:', CASE WHEN @change_type = 'XIU_XUE' THEN '学生休学' WHEN @change_type = 'TUI_XUE' THEN '学生退学' WHEN @change_type = 'BI_YE' THEN '学生毕业' ELSE '学生状态变更' END, ' - ', NOW()) WHERE user_id = @student_id AND status IN (0, 1, 2) -- 待审核、审核中、已加入 AND deleted = 0; ``` ## 🔗 关联数据处理 ### 1. 用户角色权限清理 #### 📋 角色关联表 **表名**: `sys_user_role` **关联逻辑**: 当干部离任时,需要清理对应的用户角色关联 #### 🔄 清理逻辑 ```java // 在干部离任时执行角色清理 int deleteCount = sysUserRoleMapper.delete(new LambdaQueryWrapper() .eq(SysUserRole::getUserId, classPosMsg.getStudentId())); ``` #### ⚠️ 注意事项 - **仅限干部离任**: 普通社团成员退出时不清理角色权限 - **角色识别**: 根据用户ID清理所有相关角色关联 - **权限回收**: 确保干部权限及时回收,避免安全风险 ### 2. 数据一致性保证 #### 🛡️ 事务处理要求 ```sql -- 建议在学生状态变更的大事务中包含组织关系处理 START TRANSACTION; -- 1. 备份原始数据到his表 INSERT INTO xg_class_pos_msg_his SELECT * FROM xg_class_pos_msg WHERE student_id = ?; INSERT INTO stgl_member_his SELECT * FROM stgl_member WHERE user_id = ?; -- 2. 处理班级干部关系(仅退学、休学、毕业时) UPDATE xg_class_pos_msg SET is_current = 0, cadre_status = ?, term_end_date = NOW(), dismissal_date = NOW(), dismissal_reason = ? WHERE student_id = ?; -- 3. 处理社团成员关系(仅退学、休学、毕业时) UPDATE stgl_member SET status = 4 WHERE user_id = ?; -- 4. 清理用户角色(如果需要) DELETE FROM sys_user_role WHERE user_id = ?; COMMIT; ``` #### 📊 处理统计 | 处理类型 | 涉及表数量 | 主要操作 | |----------|------------|----------| | 班级干部 | 2 (干部表+角色表) | 更新状态+清理权限 | | 社团成员 | 1 (成员表) | 更新状态 | | **总计** | **3个表** | **状态更新+权限清理** | ## ⚠️ 重要注意事项 ### 1. 业务规则约束 - **干部离任**: 必须通过专门的离任接口,计算任期时长 - **成员退出**: 直接更新状态,不涉及权限清理 - **数据保留**: 所有操作都保留历史记录,便于审计 - **状态验证**: 操作前验证当前状态的合法性 ### 2. 权限与安全 - **操作权限**: 干部离任需要专门的权限验证 - **角色清理**: 确保干部权限及时回收,防止权限泄露 - **审计记录**: 记录所有组织关系变更的操作日志 - **数据完整性**: 保证关联数据的完整性约束 ### 3. 性能优化建议 - **批量处理**: 支持对多个学生的组织关系批量处理 - **索引优化**: 确保学生ID相关的查询有适当索引 - **事务控制**: 使用合适的事务隔离级别保证数据一致性 - **异步处理**: 大批量操作考虑异步处理机制 --- ## 📅 更新记录 - **2025-12-XX**: 分文档创建,包含组织关系处理逻辑 - **2025-12-XX**: 基于代码分析完善技术实现细节和业务逻辑 - **后续更新**: 根据实际业务需求持续完善 --- *本分文档详细说明学生状态变更时的组织关系处理逻辑和技术实现,包括班级干部职务和社团成员关系的清理。*