Parcourir la source

feat(leave): 一年级请假二级学院与分管领导改走人文学院

按入学年与每年9月1日切届判定一年级;代理学院 dept_id 通过 sys_config 配置,提交时覆写选人,流程定义与销假不变。

Co-authored-by: Cursor <cursoragent@cursor.com>
“wuzhen” il y a 1 mois
Parent
commit
a76fd32d71

+ 2 - 2
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentMsgController.java

@@ -829,7 +829,7 @@ public class XgStudentMsgController extends BaseController {
         log.info("根据学生ID获取学院请假审批人列表 - 学生ID: {}", studentId);
         List<Long> approverUserIds = xgStudentMsgService.getCollegeLeaveApproversByStudentId(studentId);
         if (approverUserIds.isEmpty()) {
-            return R.fail("未找到该学生所在学院的请假审批人");
+            return R.fail("未找到学院请假审批人(一年级查人文学院角色 xsqj-ejxyspr),请联系管理员配置");
         }
         return R.ok(approverUserIds);
     }
@@ -846,7 +846,7 @@ public class XgStudentMsgController extends BaseController {
         log.info("根据学生ID获取二级学院分管领导ID列表 - 学生ID: {}", studentId);
         List<Long> leaderIds = xgStudentMsgService.getCollegeLeadersByStudentId(studentId);
         if (leaderIds.isEmpty()) {
-            return R.fail("未找到该学生所在学院的分管领导");
+            return R.fail("未找到学院分管领导(一年级查人文学院 xg_college_leader),请联系管理员配置");
         }
         return R.ok(leaderIds);
     }

+ 8 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgStudentMsgMapper.java

@@ -68,6 +68,14 @@ public interface XgStudentMsgMapper extends BaseMapperPlus<XgStudentMsg, XgStude
      */
     String selectCollegeIdByStudentId(@Param("studentId") Long studentId);
 
+    /**
+     * 根据学生ID查询入学年份(优先班级 enrollment_year,其次学生 enrollment_year)
+     *
+     * @param studentId 学生ID
+     * @return 入学年份,均无则返回 null
+     */
+    Long selectEnrollmentYearByStudentId(@Param("studentId") Long studentId);
+
     /**
      * 根据学院ID和角色Key查询具备该角色的用户列表
      *

+ 84 - 41
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentMsgServiceImpl.java

@@ -1,5 +1,7 @@
 package org.dromara.xg.service.impl;
 
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.service.ConfigService;
 import org.dromara.common.core.utils.MapstructUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -49,12 +51,14 @@ import java.util.stream.Collectors;
 
 import java.util.List;
 import java.util.Map;
-import java.util.ArrayList;
 import java.util.Collection;
+import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.Set;
 import java.util.HashSet;
+import java.time.LocalDate;
+import java.time.ZoneId;
 
 /**
  * 学生信息Service业务层处理
@@ -67,6 +71,14 @@ import java.util.HashSet;
 @Service
 public class XgStudentMsgServiceImpl implements IXgStudentMsgService {
 
+    /** 一年级请假代理学院(人文学院)dept_id,配置于 sys_config */
+    private static final String FRESHMAN_PROXY_COLLEGE_CONFIG_KEY = "xg.studentLeave.freshmanProxyCollegeId";
+
+    /** 请假二级学院审批角色 */
+    private static final String LEAVE_COLLEGE_APPROVER_ROLE_KEY = "xsqj-ejxyspr";
+
+    private static final ZoneId SHANGHAI_ZONE = ZoneId.of("Asia/Shanghai");
+
     private final XgStudentMsgMapper baseMapper;
     private final ISysUserService sysUserService;
     private final SysDeptMapper sysDeptMapper;
@@ -76,6 +88,7 @@ public class XgStudentMsgServiceImpl implements IXgStudentMsgService {
     private final XgClassMsgMapper xgClassMsgMapper;
     private final XgDormBedStudentMapper xgDormBedStudentMapper;
     private final XgSysDeptMapper xgSysDeptMapper;
+    private final ConfigService configService;
 
     /**
      * 查询学生信息
@@ -1088,6 +1101,9 @@ public class XgStudentMsgServiceImpl implements IXgStudentMsgService {
 
     /**
      * 根据学生ID获取学院请假审批人列表
+     * <p>
+     * 一年级(按入学年、每年9月1日切届)走 sys_config 配置的代理学院(人文学院);
+     * 二年级及以上走学生本学院。入学年缺失时禁止提交。
      *
      * @param studentId 学生ID
      * @return 请假审批人用户ID列表
@@ -1095,34 +1111,16 @@ public class XgStudentMsgServiceImpl implements IXgStudentMsgService {
     @Override
     public List<Long> getCollegeLeaveApproversByStudentId(Long studentId) {
         log.info("根据学生ID获取学院请假审批人列表 - 学生ID: {}", studentId);
-
-        // 1. 根据学生ID查询学院ID
-        String collegeIdStr = baseMapper.selectCollegeIdByStudentId(studentId);
-        Long collegeId = null;
-        if (StringUtils.isNotBlank(collegeIdStr)) {
-            try {
-                collegeId = Long.valueOf(collegeIdStr);
-            } catch (NumberFormatException e) {
-                log.warn("学院ID格式错误,无法转换为Long类型 - 学生ID: {}, collegeIdStr: {}", studentId, collegeIdStr);
-                return new ArrayList<>();
-            }
-        }
-        if (collegeId == null) {
-            log.warn("未找到学生对应的学院信息 - 学生ID: {}", studentId);
-            return new ArrayList<>();
-        }
-
-        log.info("学生所在学院ID: {}", collegeId);
-
-        // 2. 根据学院ID和角色Key查询具备请假审批角色的用户列表
-        List<Long> approverUserIds = baseMapper.selectUserIdsByCollegeIdAndRoleKey(collegeId, "xsqj-ejxyspr");
-
-        log.info("找到的请假审批人数量: {}", approverUserIds.size());
+        Long collegeId = resolveLeaveCollegeId(studentId);
+        List<Long> approverUserIds = baseMapper.selectUserIdsByCollegeIdAndRoleKey(collegeId, LEAVE_COLLEGE_APPROVER_ROLE_KEY);
+        log.info("找到的请假审批人数量: {}, collegeId: {}", approverUserIds.size(), collegeId);
         return approverUserIds;
     }
 
     /**
      * 根据学生ID获取二级学院分管领导ID列表
+     * <p>
+     * 一年级走人文学院(配置代理学院),二年级及以上走本学院;入学年缺失禁止提交。
      *
      * @param studentId 学生ID
      * @return 分管领导ID列表
@@ -1130,30 +1128,75 @@ public class XgStudentMsgServiceImpl implements IXgStudentMsgService {
     @Override
     public List<Long> getCollegeLeadersByStudentId(Long studentId) {
         log.info("根据学生ID获取二级学院分管领导ID列表 - 学生ID: {}", studentId);
+        Long collegeId = resolveLeaveCollegeId(studentId);
+        List<Long> leaderIds = baseMapper.selectCollegeLeaderIdsByCollegeId(collegeId);
+        log.info("找到的二级学院分管领导数量: {}, collegeId: {}", leaderIds.size(), collegeId);
+        return leaderIds;
+    }
+
+    /**
+     * 解析请假流程使用的学院ID:一年级 → 代理学院;非一年级 → 学生本学院。
+     */
+    private Long resolveLeaveCollegeId(Long studentId) {
+        Long enrollmentYear = baseMapper.selectEnrollmentYearByStudentId(studentId);
+        if (enrollmentYear == null) {
+            throw new ServiceException("学生入学年信息不完整,无法提交请假,请联系管理员维护班级或学生的入学年份");
+        }
 
-        // 1. 根据学生ID查询学院ID
         String collegeIdStr = baseMapper.selectCollegeIdByStudentId(studentId);
-        Long collegeId = null;
-        if (StringUtils.isNotBlank(collegeIdStr)) {
-            try {
-                collegeId = Long.valueOf(collegeIdStr);
-            } catch (NumberFormatException e) {
-                log.warn("学院ID格式错误,无法转换为Long类型 - 学生ID: {}, collegeIdStr: {}", studentId, collegeIdStr);
-                return new ArrayList<>();
-            }
+        Long ownCollegeId = parseCollegeId(collegeIdStr, studentId);
+        if (ownCollegeId == null) {
+            throw new ServiceException("未找到该学生所在学院信息,无法提交请假");
         }
-        if (collegeId == null) {
-            log.warn("未找到学生对应的学院信息 - 学生ID: {}", studentId);
-            return new ArrayList<>();
+
+        if (!isFreshmanByEnrollmentYear(enrollmentYear)) {
+            log.info("非一年级请假,使用学生本学院 - studentId: {}, enrollmentYear: {}, collegeId: {}",
+                studentId, enrollmentYear, ownCollegeId);
+            return ownCollegeId;
         }
 
-        log.info("学生所在学院ID: {}", collegeId);
+        Long proxyCollegeId = resolveFreshmanProxyCollegeId();
+        log.info("一年级请假,使用代理学院 - studentId: {}, enrollmentYear: {}, proxyCollegeId: {}, ownCollegeId: {}",
+            studentId, enrollmentYear, proxyCollegeId, ownCollegeId);
+        return proxyCollegeId;
+    }
 
-        // 2. 根据学院ID查询二级学院分管领导ID列表
-        List<Long> leaderIds = baseMapper.selectCollegeLeaderIdsByCollegeId(collegeId);
+    /**
+     * 按入学年判断是否一年级。学年切换日:每年 9 月 1 日(Asia/Shanghai)。
+     */
+    private boolean isFreshmanByEnrollmentYear(Long enrollmentYear) {
+        LocalDate today = LocalDate.now(SHANGHAI_ZONE);
+        int currentCohortYear = today.getMonthValue() >= 9 ? today.getYear() : today.getYear() - 1;
+        return enrollmentYear.intValue() == currentCohortYear;
+    }
 
-        log.info("找到的二级学院分管领导数量: {}", leaderIds.size());
-        return leaderIds;
+    /**
+     * 从系统参数读取一年级请假代理学院 dept_id。
+     */
+    private Long resolveFreshmanProxyCollegeId() {
+        String configValue = configService.getConfigValue(FRESHMAN_PROXY_COLLEGE_CONFIG_KEY);
+        if (StringUtils.isBlank(configValue)) {
+            throw new ServiceException("一年级请假代理学院未配置,请在系统参数中配置 "
+                + FRESHMAN_PROXY_COLLEGE_CONFIG_KEY);
+        }
+        try {
+            return Long.valueOf(configValue.trim());
+        } catch (NumberFormatException e) {
+            throw new ServiceException("一年级请假代理学院配置无效(应为部门ID数字),请检查参数 "
+                + FRESHMAN_PROXY_COLLEGE_CONFIG_KEY);
+        }
+    }
+
+    private Long parseCollegeId(String collegeIdStr, Long studentId) {
+        if (StringUtils.isBlank(collegeIdStr)) {
+            return null;
+        }
+        try {
+            return Long.valueOf(collegeIdStr);
+        } catch (NumberFormatException e) {
+            log.warn("学院ID格式错误 - 学生ID: {}, collegeIdStr: {}", studentId, collegeIdStr);
+            return null;
+        }
     }
 
     /**

+ 10 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/studentMsg/XgStudentMsgMapper.xml

@@ -155,6 +155,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
           AND deleted = 0
     </select>
 
+    <!-- 根据学生ID查询入学年份:优先班级,其次学生本人 -->
+    <select id="selectEnrollmentYearByStudentId" resultType="java.lang.Long">
+        SELECT COALESCE(c.enrollment_year, s.enrollment_year)
+        FROM xg_student_msg s
+        LEFT JOIN xg_class_msg c ON s.class_id = c.id AND c.deleted = 0
+        WHERE s.id = #{studentId}
+          AND s.deleted = 0
+        LIMIT 1
+    </select>
+
     <!-- 根据学院ID和角色Key查询具备该角色的用户列表 -->
     <select id="selectUserIdsByCollegeIdAndRoleKey" resultType="java.lang.Long">
         SELECT DISTINCT u.user_id

+ 123 - 0
backend/script/sql/update/update_20260916_leave_freshman_proxy_college.sql

@@ -0,0 +1,123 @@
+-- =============================================================================
+-- 生产环境:一年级请假代理学院(人文学院)配置
+-- 日期:2026-09-16
+-- 配套代码:一年级请假时,二级学院审批人 / 分管领导改走 sys_config 配置的学院
+-- 销假流程无需执行本脚本
+-- =============================================================================
+-- 执行顺序:
+--   1)先跑「一、核对」全部 SELECT,确认人文学院 dept_id、审批人、分管领导
+--   2)若 1.1 的 dept_id 不是 202512231307170003,先改「二」里 INSERT/UPDATE 的 config_value
+--   3)再执行「二、写入参数」
+--   4)部署新后端并重启(或刷新系统参数缓存)
+-- =============================================================================
+
+-- -----------------------------------------------------------------------------
+-- 一、核对(只读,请先执行并人工确认)
+-- -----------------------------------------------------------------------------
+
+-- 1.1 人文学院 dept_id(生产参考:202512231307170003 / xy-19,以本查询为准)
+SELECT dept_id,
+       dept_name,
+       dept_category,
+       parent_id,
+       status,
+       del_flag
+FROM sys_dept
+WHERE dept_name = '人文学院'
+  AND del_flag = '0';
+
+-- 1.2 是否已存在配置
+SELECT config_id, config_key, config_value, remark
+FROM sys_config
+WHERE config_key = 'xg.studentLeave.freshmanProxyCollegeId';
+
+-- 1.3 人文学院请假二级学院审批人(role_key = xsqj-ejxyspr)必须有人,否则一年级无法提交
+--     将 @proxy_college_id 换成 1.1 查到的 dept_id
+SET @proxy_college_id = 202512231307170003;
+
+SELECT u.user_id,
+       u.user_name,
+       u.nick_name,
+       d.dept_id,
+       d.dept_name,
+       r.role_key
+FROM sys_user u
+INNER JOIN sys_user_role ur ON u.user_id = ur.user_id
+INNER JOIN sys_role r ON ur.role_id = r.role_id
+INNER JOIN sys_dept d ON u.dept_id = d.dept_id
+WHERE u.del_flag = '0'
+  AND u.status = '0'
+  AND u.account_type = '1'
+  AND d.del_flag = '0'
+  AND d.status = '0'
+  AND r.role_key = 'xsqj-ejxyspr'
+  AND (d.dept_id = @proxy_college_id OR FIND_IN_SET(@proxy_college_id, d.ancestors));
+
+-- 1.4 人文学院分管领导(xg_college_leader)必须有人,否则一年级长假无法提交
+SELECT id, college_id, college_name, leader_id, leader_name, deleted
+FROM xg_college_leader
+WHERE college_id = @proxy_college_id
+  AND deleted = 0;
+
+-- 1.5 入学年缺失的在籍学生(提交请假会被系统拒绝,建议上线前处理)
+SELECT s.id,
+       s.student_no,
+       s.name,
+       s.enrollment_year AS student_enrollment_year,
+       c.id AS class_id,
+       c.class_name,
+       c.enrollment_year AS class_enrollment_year
+FROM xg_student_msg s
+LEFT JOIN xg_class_msg c ON s.class_id = c.id AND c.deleted = 0
+WHERE s.deleted = 0
+  AND IFNULL(s.student_status, '1') = '1'
+  AND COALESCE(c.enrollment_year, s.enrollment_year) IS NULL
+LIMIT 200;
+
+-- -----------------------------------------------------------------------------
+-- 二、写入系统参数(确认 1.1 的 dept_id 后执行)
+-- -----------------------------------------------------------------------------
+-- !! 若生产人文学院 dept_id 与下方不一致,请先替换两处 '202512231307170003'
+
+INSERT INTO sys_config (
+    config_id,
+    tenant_id,
+    config_name,
+    config_key,
+    config_value,
+    config_type,
+    create_dept,
+    create_by,
+    create_time,
+    remark
+)
+SELECT
+    2026091601,
+    '000000',
+    '一年级请假代理学院ID',
+    'xg.studentLeave.freshmanProxyCollegeId',
+    '202512231307170003',
+    'N',
+    103,
+    1,
+    NOW(),
+    '一年级学生请假时,二级学院审批人与分管领导改走此人文学院 dept_id;每年9月1日按入学年切届'
+FROM DUAL
+WHERE NOT EXISTS (
+    SELECT 1 FROM sys_config WHERE config_key = 'xg.studentLeave.freshmanProxyCollegeId'
+);
+
+-- 若参数已存在但值不对,执行下面 UPDATE(按需取消注释并改 dept_id)
+-- UPDATE sys_config
+-- SET config_value = '202512231307170003',
+--     update_by = 1,
+--     update_time = NOW(),
+--     remark = '一年级学生请假时,二级学院审批人与分管领导改走此人文学院 dept_id;每年9月1日按入学年切届'
+-- WHERE config_key = 'xg.studentLeave.freshmanProxyCollegeId';
+
+-- 写入后复查
+SELECT config_id, config_key, config_value, remark
+FROM sys_config
+WHERE config_key = 'xg.studentLeave.freshmanProxyCollegeId';
+
+-- 完成后:部署本次后端代码并重启应用(或刷新系统参数缓存)