diff --git a/backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/mapper/CwApprovalRecordMapper.java b/backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/mapper/CwApprovalRecordMapper.java index 38fbd29e..89087eeb 100644 --- a/backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/mapper/CwApprovalRecordMapper.java +++ b/backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/mapper/CwApprovalRecordMapper.java @@ -41,29 +41,30 @@ public interface CwApprovalRecordMapper extends BaseMapperPlus selectPendingByApproverId(@Param("approverId") Long approverId, @Param("nodeStatus") String nodeStatus); + List selectPendingByApproverId(@Param("approverId") Long approverId, + @Param("nodeStatus") String nodeStatus); /** * 根据审批人ID和节点编码查询审批记录 * * @param approverId 审批人ID - * @param nodeCode 节点编码 + * @param nodeCode 节点编码 * @param nodeStatus 节点状态 * @return 审批记录列表 */ List selectByApproverAndNode(@Param("approverId") Long approverId, - @Param("nodeCode") String nodeCode, - @Param("nodeStatus") String nodeStatus); + @Param("nodeCode") String nodeCode, + @Param("nodeStatus") String nodeStatus); /** * 根据节点编码查询审批记录 * - * @param nodeCode 节点编码 + * @param nodeCode 节点编码 * @param nodeStatus 节点状态 * @return 审批记录列表 */ List selectByNode(@Param("nodeCode") String nodeCode, - @Param("nodeStatus") String nodeStatus); + @Param("nodeStatus") String nodeStatus); /** * 查询财务审批节点的待审批记录(包含waiting和pending状态) @@ -91,11 +92,11 @@ public interface CwApprovalRecordMapper extends BaseMapperPlus selectPageWithJoin(IPage page, @Param("bo") CwApprovalRecordBo bo); @@ -140,4 +141,12 @@ public interface CwApprovalRecordMapper extends BaseMapperPlus> selectAvailableApprovers(); + } diff --git a/backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/service/impl/CwApprovalRecordServiceImpl.java b/backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/service/impl/CwApprovalRecordServiceImpl.java index f522adfd..84f2b634 100644 --- a/backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/service/impl/CwApprovalRecordServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/service/impl/CwApprovalRecordServiceImpl.java @@ -37,7 +37,6 @@ import java.util.*; import java.util.stream.Collectors; import org.dromara.common.core.utils.SpringUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.core.JdbcTemplate; /** * 审批记录Service业务层处理 @@ -56,9 +55,6 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { private final ISysUserService sysUserService; private final ISysDeptService sysDeptService; - @Autowired - private JdbcTemplate jdbcTemplate; - /** * 根据主键查询审批记录 */ @@ -90,7 +86,8 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { */ @Override public TableDataInfo queryPageList(CwApprovalRecordBo bo, PageQuery pageQuery) { - log.info("🔍 开始查询审批记录,查询参数:deptId={}, pageNum={}, pageSize={}", bo.getDeptId(), pageQuery.getPageNum(), pageQuery.getPageSize()); + log.info("🔍 开始查询审批记录,查询参数:deptId={}, pageNum={}, pageSize={}", bo.getDeptId(), pageQuery.getPageNum(), + pageQuery.getPageSize()); // 🔧 使用新的关联查询方法(支持完整字段显示) Page page = pageQuery.build(); @@ -167,7 +164,7 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { for (int i = 0; i < Math.min(result.getRecords().size(), 2); i++) { CwApprovalRecordVo record = result.getRecords().get(i); log.info("📋 记录{}详情:id={}, 申报编号={}, 项目名称={}, 申报人={}, 部门={}, 总投资={}, 当前审批人={}, 状态={}", - i+1, record.getId(), record.getApplicationNo(), record.getProjectName(), + i + 1, record.getId(), record.getApplicationNo(), record.getProjectName(), record.getApplicantName(), record.getDeptName(), record.getTotalInvestment(), record.getCurrentApproverName(), record.getApprovalStatus()); } @@ -196,7 +193,8 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { private LambdaQueryWrapper buildQueryWrapper(CwApprovalRecordBo bo) { LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(bo.getApplicationId() != null, CwApprovalRecord::getApplicationId, bo.getApplicationId()); - lqw.eq(StringUtils.isNotBlank(bo.getApplicationNo()), CwApprovalRecord::getApplicationNo, bo.getApplicationNo()); + lqw.eq(StringUtils.isNotBlank(bo.getApplicationNo()), CwApprovalRecord::getApplicationNo, + bo.getApplicationNo()); lqw.eq(StringUtils.isNotBlank(bo.getNodeCode()), CwApprovalRecord::getNodeCode, bo.getNodeCode()); lqw.eq(StringUtils.isNotBlank(bo.getNodeStatus()), CwApprovalRecord::getNodeStatus, bo.getNodeStatus()); lqw.eq(bo.getApproverId() != null, CwApprovalRecord::getApproverId, bo.getApproverId()); @@ -212,7 +210,8 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { if (currentDeptId != null) { // 查询该部门的项目申报ID列表 lqw.inSql(CwApprovalRecord::getApplicationId, - "SELECT id FROM cw_project_application WHERE department_id = " + currentDeptId + " AND deleted = 0"); + "SELECT id FROM cw_project_application WHERE department_id = " + currentDeptId + + " AND deleted = 0"); // 只显示部门审批节点的记录 lqw.eq(CwApprovalRecord::getNodeCode, "dept_approve"); @@ -234,7 +233,8 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { try { Long deptId = Long.valueOf(bo.getDeptId()); lqw.in(CwApprovalRecord::getApplicationId, - "SELECT id FROM cw_project_application WHERE department_id = " + deptId + " AND deleted = 0"); + "SELECT id FROM cw_project_application WHERE department_id = " + deptId + + " AND deleted = 0"); } catch (NumberFormatException e) { log.warn("无效的部门ID格式:{}", bo.getDeptId()); } @@ -245,11 +245,11 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { // 其他查询条件 - 暂时注释掉,避免复杂的关联查询导致编译错误 // if (StringUtils.isNotBlank(bo.getProjectName())) { - // // 项目名称查询需要关联查询,暂时跳过 + // // 项目名称查询需要关联查询,暂时跳过 // } // if (StringUtils.isNotBlank(bo.getApplicantName())) { - // // 申报人姓名查询需要关联查询,暂时跳过 + // // 申报人姓名查询需要关联查询,暂时跳过 // } if (StringUtils.isNotBlank(bo.getApproverName())) { @@ -304,8 +304,8 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { if (deptId != null) { // 通过关联查询,只显示本部门用户提交的申报对应的审批记录 lqw.exists("SELECT 1 FROM cw_project_application a " + - "WHERE a.id = cw_approval_record.application_id " + - "AND a.department_id = " + deptId); + "WHERE a.id = cw_approval_record.application_id " + + "AND a.department_id = " + deptId); // 🔧 关键修复:部门领导也需要过滤为自己作为审批人的记录 lqw.eq(CwApprovalRecord::getApproverId, currentUserId); log.debug("部门领导权限过滤完成:deptId={}, approverId={}", deptId, currentUserId); @@ -383,8 +383,8 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { // 查询当前用户已审批的记录(精确匹配已完成的状态) LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(CwApprovalRecord::getApproverId, currentUserId) - .in(CwApprovalRecord::getNodeStatus, Arrays.asList("approved", "rejected", "returned")) - .orderByDesc(CwApprovalRecord::getEndTime); + .in(CwApprovalRecord::getNodeStatus, Arrays.asList("approved", "rejected", "returned")) + .orderByDesc(CwApprovalRecord::getEndTime); return baseMapper.selectList(queryWrapper); }); @@ -544,8 +544,8 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { @Override @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) public Boolean createApprovalProcess(Long applicationId, String applicationNo, - Long currentUserId, String currentUserName, - Long currentDeptId, String currentTenantId) { + Long currentUserId, String currentUserName, + Long currentDeptId, String currentTenantId) { log.info("🚀 开始创建审批流程:applicationId={}, applicationNo={}", applicationId, applicationNo); log.info("📋 使用用户上下文:userId={}, userName={}, deptId={}, tenantId={}", currentUserId, currentUserName, currentDeptId, currentTenantId); @@ -565,15 +565,15 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { log.error("❌ 申报记录不存在:applicationId={}", applicationId); throw new RuntimeException("申报记录不存在"); } - log.info("✅ 成功获取申报信息:projectName={}, status={}", application.getProjectName(), application.getApprovalStatus()); + log.info("✅ 成功获取申报信息:projectName={}, status={}", application.getProjectName(), + application.getApprovalStatus()); // 🔧 重要修复:清理该申报的旧审批记录(避免重复数据) log.info("🧹 开始清理申报ID={}的旧审批记录...", applicationId); try { int deletedCount = baseMapper.delete( - Wrappers.lambdaQuery() - .eq(CwApprovalRecord::getApplicationId, applicationId) - ); + Wrappers.lambdaQuery() + .eq(CwApprovalRecord::getApplicationId, applicationId)); log.info("✅ 清理旧审批记录完成:删除{}条记录", deletedCount); } catch (Exception e) { log.error("❌ 清理旧审批记录失败:applicationId={}, error={}", applicationId, e.getMessage(), e); @@ -587,8 +587,8 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { // 第1节点:部门审批(提交时只创建这一个记录) CwApprovalRecord deptRecord = createApprovalRecord(applicationId, applicationNo, - "dept_approve", "部门审批", 1, "waiting", - currentUserId, currentUserName, currentDeptId, currentTenantId); + "dept_approve", "部门审批", 1, "waiting", + currentUserId, currentUserName, currentDeptId, currentTenantId); records.add(deptRecord); log.info("✅ 创建部门审批记录:ID={}, 审批人={}", deptRecord.getId(), deptRecord.getApproverName()); @@ -602,8 +602,8 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { for (int i = 0; i < records.size(); i++) { CwApprovalRecord record = records.get(i); log.info("📋 记录{}详情: ID={}, ApplicationId={}, NodeCode={}, ApproverId={}, CreateBy={}, TenantId={}", - i+1, record.getId(), record.getApplicationId(), record.getNodeCode(), - record.getApproverId(), record.getCreateBy(), record.getTenantId()); + i + 1, record.getId(), record.getApplicationId(), record.getNodeCode(), + record.getApproverId(), record.getCreateBy(), record.getTenantId()); } // 批量插入审批记录 @@ -626,11 +626,10 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { try { // 直接使用Mapper在当前事务中更新,避免调用Service层的新事务 int updateCount = applicationMapper.update(null, - Wrappers.lambdaUpdate() - .set(CwProjectApplication::getApprovalStatus, "approving") - .set(CwProjectApplication::getUpdateTime, new Date()) - .eq(CwProjectApplication::getId, applicationId) - ); + Wrappers.lambdaUpdate() + .set(CwProjectApplication::getApprovalStatus, "approving") + .set(CwProjectApplication::getUpdateTime, new Date()) + .eq(CwProjectApplication::getId, applicationId)); if (updateCount > 0) { log.info("✅ 申报状态直接更新成功:applicationId={} -> approving, 影响行数={}", applicationId, updateCount); @@ -673,10 +672,10 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { } private CwApprovalRecord createApprovalRecord(Long applicationId, String applicationNo, - String nodeCode, String nodeName, - Integer nodeOrder, String nodeStatus, - Long currentUserId, String currentUserName, - Long currentDeptId, String currentTenantId) { + String nodeCode, String nodeName, + Integer nodeOrder, String nodeStatus, + Long currentUserId, String currentUserName, + Long currentDeptId, String currentTenantId) { CwApprovalRecord record = new CwApprovalRecord(); // 生成唯一ID(批量插入需要手动生成) @@ -709,7 +708,7 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { record.setCreateDept(currentDeptId); record.setTenantId(currentTenantId); - // 设置其他必需字段(为批量插入提供完整数据) + // 设置其他必需字段(为批量插入提供完整数据) record.setIsDelegate(false); record.setIsOvertime(false); @@ -748,7 +747,7 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { * 根据节点类型设置审批人 */ private void setApproverByNodeCode(CwApprovalRecord record, String nodeCode, - Long currentUserId, String currentUserName) { + Long currentUserId, String currentUserName) { // 根据审批节点类型查找正确的审批人,而不是使用申报人 try { switch (nodeCode) { @@ -875,12 +874,12 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { try { // SQL查询:查找该部门内具有project_dept_manage角色的用户 String sql = "SELECT ur.user_id FROM sys_user_role ur " + - "INNER JOIN sys_role r ON ur.role_id = r.role_id " + - "INNER JOIN sys_user u ON ur.user_id = u.user_id " + - "WHERE r.role_key = 'project_dept_manage' " + - "AND u.dept_id = " + deptId + " " + - "AND u.del_flag = 0 AND u.status = 0 " + - "LIMIT 1"; + "INNER JOIN sys_role r ON ur.role_id = r.role_id " + + "INNER JOIN sys_user u ON ur.user_id = u.user_id " + + "WHERE r.role_key = 'project_dept_manage' " + + "AND u.dept_id = " + deptId + " " + + "AND u.del_flag = 0 AND u.status = 0 " + + "LIMIT 1"; log.info("查找部门审批人SQL:{}", sql); @@ -958,8 +957,8 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { // 更新当前审批记录 CwApprovalRecordBo updateBo = new CwApprovalRecordBo(); updateBo.setId(currentRecord.getId()); - updateBo.setNodeStatus("approve".equals(approveAction) ? "approved" : - "reject".equals(approveAction) ? "rejected" : "returned"); + updateBo.setNodeStatus("approve".equals(approveAction) ? "approved" + : "reject".equals(approveAction) ? "rejected" : "returned"); updateBo.setApproveAction(approveAction); updateBo.setApproveOpinion(approveOpinion); updateBo.setEndTime(new Date()); @@ -974,7 +973,7 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { } catch (Exception e) { log.error("处理审批失败:applicationId={}, nodeCode={}, error={}", - applicationId, nodeCode, e.getMessage(), e); + applicationId, nodeCode, e.getMessage(), e); throw new RuntimeException("处理审批失败:" + e.getMessage(), e); } } @@ -1020,8 +1019,8 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { // 根据节点类型创建对应的审批记录 CwApprovalRecord newRecord = createApprovalRecordForNode(applicationId, application.getApplicationNo(), - nextNodeCode, application.getCreateBy(), application.getApplicantName(), - application.getDepartmentId(), LoginHelper.getTenantId()); + nextNodeCode, application.getCreateBy(), application.getApplicantName(), + application.getDepartmentId(), LoginHelper.getTenantId()); if (newRecord != null) { // 插入新的审批记录 @@ -1046,7 +1045,7 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { return true; } catch (Exception e) { log.error("启动下一个审批节点失败:applicationId={}, nodeCode={}, error={}", - applicationId, nextNodeCode, e.getMessage(), e); + applicationId, nextNodeCode, e.getMessage(), e); throw new RuntimeException("启动下一个审批节点失败:" + e.getMessage()); } } @@ -1055,8 +1054,8 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { * 动态创建指定节点的审批记录 */ private CwApprovalRecord createApprovalRecordForNode(Long applicationId, String applicationNo, - String nodeCode, Long createBy, String createByName, - Long deptId, String tenantId) { + String nodeCode, Long createBy, String createByName, + Long deptId, String tenantId) { try { String nodeName; Integer nodeOrder; @@ -1077,11 +1076,11 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { // 创建审批记录 CwApprovalRecord record = createApprovalRecord(applicationId, applicationNo, - nodeCode, nodeName, nodeOrder, "waiting", - createBy, createByName, deptId, tenantId); + nodeCode, nodeName, nodeOrder, "waiting", + createBy, createByName, deptId, tenantId); log.info("✅ 动态创建审批记录:nodeCode={}, nodeName={}, approverId={}", - nodeCode, nodeName, record.getApproverId()); + nodeCode, nodeName, record.getApproverId()); return record; } catch (Exception e) { @@ -1124,7 +1123,7 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { return checkApprovalPermission(applicationId, "dept_approve"); } - /** + /** * 获取可选审批人列表(院领导) * 修改为基于权限角色查找:查找具有cw:approval:leader权限的用户 */ @@ -1135,46 +1134,22 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { List> approvers = new ArrayList<>(); try { - // 使用原生SQL查询具有cw:approval:leader权限的用户 - String sql = """ - SELECT DISTINCT - u.user_id, - u.user_name, - u.nick_name, - COALESCE(d.dept_name, '未分配部门') as dept_name, - COALESCE(d.dept_id, 0) as dept_id - FROM sys_user u - LEFT JOIN sys_dept d ON u.dept_id = d.dept_id - 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_role_menu rm ON r.role_id = rm.role_id - INNER JOIN sys_menu m ON rm.menu_id = m.menu_id - WHERE m.perms = 'cw:approval:leader' - AND u.status = '0' - AND u.del_flag = '0' - AND r.status = '0' - ORDER BY u.user_name - """; + // 🔧 使用Mapper方法查询具有cw:approval:leader权限的用户 + log.info("执行Mapper查询查找具有cw:approval:leader权限的用户"); - log.info("执行权限查询SQL查找具有cw:approval:leader权限的用户"); + // 直接调用Mapper方法,不需要手动转换结果 + List> results = baseMapper.selectAvailableApprovers(); - // 执行SQL查询 - List> results = jdbcTemplate.queryForList(sql); + log.info("Mapper查询结果:找到 {} 个具有院领导审批权限的用户", results.size()); - log.info("SQL查询结果:找到 {} 个具有院领导审批权限的用户", results.size()); - - // 转换查询结果为返回格式 - for (Map row : results) { - Map approver = new HashMap<>(); - approver.put("userId", row.get("user_id")); - approver.put("userName", row.get("user_name")); - approver.put("nickName", row.get("nick_name")); - approver.put("deptName", row.get("dept_name")); - approver.put("deptId", row.get("dept_id")); - approvers.add(approver); + // 直接使用查询结果,Mapper已经处理了字段映射 + approvers.addAll(results); + // 记录查询到的审批人信息 + for (Map approver : approvers) { log.info("添加院领导审批人: userId={}, userName={}, nickName={}, deptName={}", - row.get("user_id"), row.get("user_name"), row.get("nick_name"), row.get("dept_name")); + approver.get("userId"), approver.get("userName"), approver.get("nickName"), + approver.get("deptName")); } // 如果基于权限查询没有找到用户,使用原有的部门查询作为备选方案 @@ -1230,11 +1205,10 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { // 1. 查找当前用户的待审批记录 CwApprovalRecord currentRecord = baseMapper.selectOne( - Wrappers.lambdaQuery(CwApprovalRecord.class) - .eq(CwApprovalRecord::getApplicationId, applicationId) - .eq(CwApprovalRecord::getApproverId, currentUserId) - .eq(CwApprovalRecord::getNodeStatus, "waiting") - ); + Wrappers.lambdaQuery(CwApprovalRecord.class) + .eq(CwApprovalRecord::getApplicationId, applicationId) + .eq(CwApprovalRecord::getApproverId, currentUserId) + .eq(CwApprovalRecord::getNodeStatus, "waiting")); if (currentRecord == null) { throw new RuntimeException("未找到待审批记录或您没有审批权限"); @@ -1263,7 +1237,7 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { throw new RuntimeException("更新审批记录失败"); } - // 3. 更新项目申请状态 + // 3. 更新项目申请状态 String newStatus; if ("approve".equals(approveAction)) { // 分管领导审批通过,整个审批流程完成 @@ -1292,7 +1266,8 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { */ @Override @Transactional(rollbackFor = Exception.class) - public void financeApproval(Long applicationId, String approveAction, String approveOpinion, Long selectedApproverId) { + public void financeApproval(Long applicationId, String approveAction, String approveOpinion, + Long selectedApproverId) { Long currentUserId = LoginHelper.getUserId(); log.info("🔍 财务审批开始:applicationId={}, userId={}, action={}, selectedApproverId={}", applicationId, currentUserId, approveAction, selectedApproverId); @@ -1306,24 +1281,22 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { log.info("🔍 查询财务审批记录:applicationId={}, nodeCode=finance_approve, nodeStatus=waiting", applicationId); List records = baseMapper.selectList( - Wrappers.lambdaQuery(CwApprovalRecord.class) - .eq(CwApprovalRecord::getApplicationId, applicationId) - .eq(CwApprovalRecord::getNodeCode, "finance_approve") - .eq(CwApprovalRecord::getNodeStatus, "waiting") - .orderByDesc(CwApprovalRecord::getCreateTime) - ); + Wrappers.lambdaQuery(CwApprovalRecord.class) + .eq(CwApprovalRecord::getApplicationId, applicationId) + .eq(CwApprovalRecord::getNodeCode, "finance_approve") + .eq(CwApprovalRecord::getNodeStatus, "waiting") + .orderByDesc(CwApprovalRecord::getCreateTime)); if (records.isEmpty()) { log.error("❌ 未找到财务审批记录:applicationId={}", applicationId); // 查询所有相关记录用于调试 List allRecords = baseMapper.selectList( - Wrappers.lambdaQuery(CwApprovalRecord.class) - .eq(CwApprovalRecord::getApplicationId, applicationId) - ); + Wrappers.lambdaQuery(CwApprovalRecord.class) + .eq(CwApprovalRecord::getApplicationId, applicationId)); log.error("📋 该申请的所有审批记录:{}", allRecords.size()); for (CwApprovalRecord record : allRecords) { log.error("📋 记录详情:nodeCode={}, nodeStatus={}, approverId={}", - record.getNodeCode(), record.getNodeStatus(), record.getApproverId()); + record.getNodeCode(), record.getNodeStatus(), record.getApproverId()); } throw new RuntimeException("未找到待审批记录或该申请已被处理"); } @@ -1335,7 +1308,8 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { } log.info("✅ 找到财务审批记录:recordId={}, nodeCode={}, nodeStatus={}, approverId={}", - currentRecord.getId(), currentRecord.getNodeCode(), currentRecord.getNodeStatus(), currentRecord.getApproverId()); + currentRecord.getId(), currentRecord.getNodeCode(), currentRecord.getNodeStatus(), + currentRecord.getApproverId()); // 3. 更新当前审批记录 Date now = new Date(); @@ -1392,23 +1366,21 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { private void updateLeaderApproverAndStart(Long applicationId, Long selectedApproverId) { // 1. 查找分管领导审批记录(先按waiting状态查找) CwApprovalRecord leaderRecord = baseMapper.selectOne( - Wrappers.lambdaQuery(CwApprovalRecord.class) - .eq(CwApprovalRecord::getApplicationId, applicationId) - .eq(CwApprovalRecord::getNodeCode, "leader_approve") - .eq(CwApprovalRecord::getNodeStatus, "waiting") - ); + Wrappers.lambdaQuery(CwApprovalRecord.class) + .eq(CwApprovalRecord::getApplicationId, applicationId) + .eq(CwApprovalRecord::getNodeCode, "leader_approve") + .eq(CwApprovalRecord::getNodeStatus, "waiting")); // 2. 如果waiting状态未找到,尝试查找pending状态(兼容处理) if (leaderRecord == null) { leaderRecord = baseMapper.selectOne( - Wrappers.lambdaQuery(CwApprovalRecord.class) - .eq(CwApprovalRecord::getApplicationId, applicationId) - .eq(CwApprovalRecord::getNodeCode, "leader_approve") - .eq(CwApprovalRecord::getNodeStatus, "pending") - ); + Wrappers.lambdaQuery(CwApprovalRecord.class) + .eq(CwApprovalRecord::getApplicationId, applicationId) + .eq(CwApprovalRecord::getNodeCode, "leader_approve") + .eq(CwApprovalRecord::getNodeStatus, "pending")); } - // 3. 如果仍未找到,动态创建分管领导审批记录 + // 3. 如果仍未找到,动态创建分管领导审批记录 if (leaderRecord == null) { log.warn("未找到分管领导审批记录,开始动态创建:applicationId={}", applicationId); @@ -1426,17 +1398,16 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { // 创建分管领导审批记录 leaderRecord = createApprovalRecord( - applicationId, - applicationVo.getApplicationNo(), - "leader_approve", - "分管领导审批", - 3, // leader_approve 节点顺序 - "waiting", - currentUserId, - currentUserName, - currentDeptId, - currentTenantId - ); + applicationId, + applicationVo.getApplicationNo(), + "leader_approve", + "分管领导审批", + 3, // leader_approve 节点顺序 + "waiting", + currentUserId, + currentUserName, + currentDeptId, + currentTenantId); // 保存到数据库 int result = baseMapper.insert(leaderRecord); @@ -1447,7 +1418,7 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { log.info("✅ 动态创建分管领导审批记录成功:recordId={}", leaderRecord.getId()); } - // 2. 获取选择的院领导信息 + // 2. 获取选择的院领导信息 try { SysUserVo selectedUser = sysUserService.selectUserById(selectedApproverId); if (selectedUser == null) { @@ -1470,12 +1441,12 @@ public class CwApprovalRecordServiceImpl implements ICwApprovalRecordService { throw new RuntimeException("更新分管领导审批记录失败"); } - log.info("✅ 成功指定院领导审批人:userId={}, userName={}", + log.info("✅ 成功指定院领导审批人:userId={}, userName={}", selectedApproverId, selectedUser.getUserName()); } catch (Exception e) { log.error("❌ 更新分管领导审批人失败:applicationId={}, selectedApproverId={}, error={}", - applicationId, selectedApproverId, e.getMessage(), e); + applicationId, selectedApproverId, e.getMessage(), e); throw new RuntimeException("指定院领导审批人失败:" + e.getMessage()); } } diff --git a/backend/ruoyi-modules/ruoyi-cw/src/main/resources/mapper/CwApprovalRecordMapper.xml b/backend/ruoyi-modules/ruoyi-cw/src/main/resources/mapper/CwApprovalRecordMapper.xml index 22f49bc6..11e1c691 100644 --- a/backend/ruoyi-modules/ruoyi-cw/src/main/resources/mapper/CwApprovalRecordMapper.xml +++ b/backend/ruoyi-modules/ruoyi-cw/src/main/resources/mapper/CwApprovalRecordMapper.xml @@ -234,4 +234,25 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ORDER BY ar.create_time DESC + + + diff --git a/backend/ruoyi-modules/ruoyi-jcbt/src/main/java/org/dromara/jcbt/service/impl/JcbtScholarshipApplicationServiceImpl.java b/backend/ruoyi-modules/ruoyi-jcbt/src/main/java/org/dromara/jcbt/service/impl/JcbtScholarshipApplicationServiceImpl.java index 721b4798..fbf380e7 100644 --- a/backend/ruoyi-modules/ruoyi-jcbt/src/main/java/org/dromara/jcbt/service/impl/JcbtScholarshipApplicationServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-jcbt/src/main/java/org/dromara/jcbt/service/impl/JcbtScholarshipApplicationServiceImpl.java @@ -70,6 +70,7 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp * 查询奖学金申请记录详情 */ @Override + @Transactional(rollbackFor = Exception.class) public JcbtScholarshipApplicationVo queryById(Long id) { return baseMapper.selectByIdWithRelations(id); } @@ -78,10 +79,13 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp * 查询奖学金申请记录分页列表 */ @Override - public TableDataInfo queryPageList(JcbtScholarshipApplicationBo bo, PageQuery pageQuery) { + @Transactional(rollbackFor = Exception.class) + public TableDataInfo queryPageList(JcbtScholarshipApplicationBo bo, + PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); // 使用自定义分页查询(包含关联表信息) - Page result = baseMapper.selectJcbtScholarshipApplicationPage(pageQuery.build(), lqw); + Page result = baseMapper.selectJcbtScholarshipApplicationPage(pageQuery.build(), + lqw); return TableDataInfo.build(result); } @@ -89,6 +93,7 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp * 查询奖学金申请记录列表 */ @Override + @Transactional(rollbackFor = Exception.class) public List queryList(JcbtScholarshipApplicationBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); @@ -98,18 +103,21 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(ObjectUtil.isNotNull(bo.getBatchId()), JcbtScholarshipApplication::getBatchId, bo.getBatchId()); - lqw.eq(ObjectUtil.isNotNull(bo.getScholarshipTypeId()), JcbtScholarshipApplication::getScholarshipTypeId, bo.getScholarshipTypeId()); + lqw.eq(ObjectUtil.isNotNull(bo.getScholarshipTypeId()), JcbtScholarshipApplication::getScholarshipTypeId, + bo.getScholarshipTypeId()); lqw.eq(StringUtils.isNotBlank(bo.getStudentNo()), JcbtScholarshipApplication::getStudentNo, bo.getStudentNo()); - lqw.like(StringUtils.isNotBlank(bo.getStudentName()), JcbtScholarshipApplication::getStudentName, bo.getStudentName()); + lqw.like(StringUtils.isNotBlank(bo.getStudentName()), JcbtScholarshipApplication::getStudentName, + bo.getStudentName()); lqw.eq(ObjectUtil.isNotNull(bo.getStudentId()), JcbtScholarshipApplication::getStudentId, bo.getStudentId()); lqw.eq(ObjectUtil.isNotNull(bo.getDeptId()), JcbtScholarshipApplication::getDeptId, bo.getDeptId()); lqw.eq(ObjectUtil.isNotNull(bo.getClassId()), JcbtScholarshipApplication::getClassId, bo.getClassId()); // 已删除 applicantType 和 applicantId 字段 lqw.eq(StringUtils.isNotBlank(bo.getStatus()), JcbtScholarshipApplication::getCurrentStatus, bo.getStatus()); // 支持前端 applicationStatus 查询参数 - lqw.eq(StringUtils.isNotBlank(bo.getApplicationStatus()), JcbtScholarshipApplication::getCurrentStatus, bo.getApplicationStatus()); + lqw.eq(StringUtils.isNotBlank(bo.getApplicationStatus()), JcbtScholarshipApplication::getCurrentStatus, + bo.getApplicationStatus()); lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null, - JcbtScholarshipApplication::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime")); + JcbtScholarshipApplication::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime")); // 注意:排序在XML中处理,避免SQL语法错误 // lqw.orderByDesc(JcbtScholarshipApplication::getCreateTime); return lqw; @@ -122,7 +130,8 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp @Transactional(rollbackFor = Exception.class) public Boolean insertByBo(JcbtScholarshipApplicationBo bo) { // 检查申请资格 - Map eligibilityCheck = checkStudentEligibility(bo.getStudentId(), bo.getBatchId(), bo.getScholarshipTypeId()); + Map eligibilityCheck = checkStudentEligibility(bo.getStudentId(), bo.getBatchId(), + bo.getScholarshipTypeId()); if (!(Boolean) eligibilityCheck.get("eligible")) { throw new RuntimeException((String) eligibilityCheck.get("message")); } @@ -254,15 +263,14 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp @Override @Transactional(rollbackFor = Exception.class) public Boolean batchSubmitApplicationsByTeacher(Long batchId, Long scholarshipTypeId, Long classId, - List studentApplications) { + List studentApplications) { // 检查班级名额 - 使用和前端一致的查询方法 log.info("开始检查班级名额 - 批次ID: {}, 奖学金类型ID: {}, 班级ID: {}, 申请人数: {}", batchId, scholarshipTypeId, classId, studentApplications.size()); // 使用和前端getScholarshipTypesWithQuota一致的查询方法 JcbtScholarshipQuotaVo classQuota = quotaService.selectClassQuota(classId, scholarshipTypeId, batchId); - log.info("班级名额查询结果: {}", classQuota != null ? - String.format("ID=%s, 总名额=%s, 已用=%s, 可用=%s, 状态=%s", + log.info("班级名额查询结果: {}", classQuota != null ? String.format("ID=%s, 总名额=%s, 已用=%s, 可用=%s, 状态=%s", classQuota.getId(), classQuota.getTotalQuota(), classQuota.getUsedQuota(), classQuota.getAvailableQuota(), classQuota.getStatus()) : "null"); @@ -306,7 +314,8 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp if (classQuota != null) { // 有班级名额分配,更新班级名额 log.info("更新班级名额使用情况 - ID: {}, 原已用: {}, 原可用: {}, 新增申请: {}", - classQuota.getId(), classQuota.getUsedQuota(), classQuota.getAvailableQuota(), studentApplications.size()); + classQuota.getId(), classQuota.getUsedQuota(), classQuota.getAvailableQuota(), + studentApplications.size()); // 注意:这里需要调用全校名额表的更新方法,而不是班级名额表的方法 quotaService.updateQuotaUsage(classQuota.getId(), studentApplications.size()); @@ -334,6 +343,7 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp * 查询班主任负责班级的申请记录 */ @Override + @Transactional(rollbackFor = Exception.class) public List queryApplicationsByApplicant(Long applicantId, Long batchId) { // TODO: 重新设计查询逻辑,因为applicantId字段已删除 // 可能需要通过班级ID或其他方式查询班主任负责的申请记录 @@ -344,6 +354,7 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp * 查询班级的申请记录 */ @Override + @Transactional(rollbackFor = Exception.class) public List queryApplicationsByClass(Long classId, Long batchId) { return baseMapper.selectApplicationsByClass(classId, batchId); } @@ -352,6 +363,7 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp * 查询待审核的申请记录 */ @Override + @Transactional(rollbackFor = Exception.class) public List queryPendingApplications(Long reviewerId, String status) { return baseMapper.selectPendingApplications(reviewerId, status); } @@ -360,6 +372,7 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp * 检查学生申请资格和限制 */ @Override + @Transactional(rollbackFor = Exception.class) public Map checkStudentEligibility(Long studentId, Long batchId, Long scholarshipTypeId) { Map result = new HashMap<>(); @@ -383,7 +396,7 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp @Override @Transactional(rollbackFor = Exception.class) public Boolean batchReviewApplications(List applicationIds, String status, Long reviewerId, - String reviewerName, String reviewComment) { + String reviewerName, String reviewComment) { return baseMapper.batchUpdateStatus(applicationIds, status, reviewerId, reviewerName, reviewComment) > 0; } @@ -429,6 +442,7 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp * 获取申请统计数据 */ @Override + @Transactional(rollbackFor = Exception.class) public Map getApplicationStatistics(Long batchId, Long deptId) { List statistics = baseMapper.selectApplicationStatistics(batchId, deptId); @@ -443,6 +457,7 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp * 查询可申请的奖学金类型列表 */ @Override + @Transactional(rollbackFor = Exception.class) public List> queryAvailableScholarshipTypes(Long classId, Long batchId) { // 查询该班级在该批次的名额分配情况 List quotas = classQuotaService.queryQuotasByClass(classId, batchId); @@ -466,6 +481,7 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp * 获取班级名额使用情况 */ @Override + @Transactional(rollbackFor = Exception.class) public Map getClassQuotaUsage(Long classId, Long batchId) { List quotas = classQuotaService.queryQuotasByClass(classId, batchId); @@ -485,7 +501,7 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp // ========== 班主任申请专用接口实现 ========== - /** + /** * 根据班主任用户ID获取完整的班级信息 * 自包含方法,避免跨模块调用,直接使用SQL查询 */ @@ -493,26 +509,26 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp try { // 使用自定义SQL一次性关联查询所有需要的信息 String sql = """ - SELECT - cm.id as class_id, - cm.class_name, - cm.class_code, - cm.grade, - cm.college_id, - cm.college_name, - cm.major_id, - cm.major_name, - cm.class_teacher_id, - cm.class_teacher_name, - cm.current_student_count, - cm.class_status, - cm.class_type - FROM xg_class_msg cm - WHERE cm.class_teacher_id = ? - AND cm.deleted = 0 - AND cm.class_status = '1' - ORDER BY cm.grade DESC, cm.class_name ASC - """; + SELECT + cm.id as class_id, + cm.class_name, + cm.class_code, + cm.grade, + cm.college_id, + cm.college_name, + cm.major_id, + cm.major_name, + cm.class_teacher_id, + cm.class_teacher_name, + cm.current_student_count, + cm.class_status, + cm.class_type + FROM xg_class_msg cm + WHERE cm.class_teacher_id = ? + AND cm.deleted = 0 + AND cm.class_status = '1' + ORDER BY cm.grade DESC, cm.class_name ASC + """; // 执行查询 List> result = jdbcTemplate.queryForList(sql, teacherId); @@ -530,6 +546,7 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp * 重构后的获取班级信息方法 - 自包含,无跨模块依赖 */ @Override + @Transactional(rollbackFor = Exception.class) public Map getClassInfoByTeacher(Long userId) { Map result = new HashMap<>(); @@ -582,8 +599,9 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp /** * 从Map构建班级结果信息 */ - private void buildClassResultFromMap(Map result, Long teacherId, String teacherName, Map classInfo) { - result.put("teacherId", teacherId.toString()); + private void buildClassResultFromMap(Map result, Long teacherId, String teacherName, + Map classInfo) { + result.put("teacherId", teacherId.toString()); result.put("teacherName", teacherName); result.put("classId", classInfo.get("class_id") != null ? classInfo.get("class_id").toString() : ""); result.put("className", (String) classInfo.get("class_name")); @@ -613,36 +631,37 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp return option; } - /** + /** * 确认选择班级(当用户从多个班级中选择一个时调用) */ @Override + @Transactional(rollbackFor = Exception.class) public Map confirmClassSelection(Long teacherId, Long classId) { Map result = new HashMap<>(); try { // 验证该班级确实属于该老师 - 使用自包含查询 String sql = """ - SELECT - cm.id as class_id, - cm.class_name, - cm.class_code, - cm.grade, - cm.college_id, - cm.college_name, - cm.major_id, - cm.major_name, - cm.class_teacher_id, - cm.class_teacher_name, - cm.current_student_count, - cm.class_status, - cm.class_type - FROM xg_class_msg cm - WHERE cm.class_teacher_id = ? - AND cm.id = ? - AND cm.deleted = 0 - AND cm.class_status = '1' - """; + SELECT + cm.id as class_id, + cm.class_name, + cm.class_code, + cm.grade, + cm.college_id, + cm.college_name, + cm.major_id, + cm.major_name, + cm.class_teacher_id, + cm.class_teacher_name, + cm.current_student_count, + cm.class_status, + cm.class_type + FROM xg_class_msg cm + WHERE cm.class_teacher_id = ? + AND cm.id = ? + AND cm.deleted = 0 + AND cm.class_status = '1' + """; List> classList = jdbcTemplate.queryForList(sql, teacherId, classId); @@ -663,38 +682,39 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp } } - /** + /** * 获取班级学生列表(用于勾选)- 使用自包含查询 */ @Override + @Transactional(rollbackFor = Exception.class) public List> getClassStudents(Long classId) { List> students = new ArrayList<>(); try { // 使用自包含查询获取学生列表,关联班级表获取学院和专业信息 String sql = """ - SELECT - sm.id, - sm.student_no, - sm.name, - sm.gender, - sm.phone, - sm.email, - sm.student_status, - cm.college_id, - cm.college_name, - cm.major_id, - cm.major_name, - cm.class_name, - cm.grade - FROM xg_student_msg sm - LEFT JOIN xg_class_msg cm ON sm.class_id = cm.id - WHERE sm.class_id = ? - AND sm.deleted = 0 - AND sm.student_status = '1' - AND (cm.deleted IS NULL OR cm.deleted = 0) - ORDER BY sm.student_no ASC - """; + SELECT + sm.id, + sm.student_no, + sm.name, + sm.gender, + sm.phone, + sm.email, + sm.student_status, + cm.college_id, + cm.college_name, + cm.major_id, + cm.major_name, + cm.class_name, + cm.grade + FROM xg_student_msg sm + LEFT JOIN xg_class_msg cm ON sm.class_id = cm.id + WHERE sm.class_id = ? + AND sm.deleted = 0 + AND sm.student_status = '1' + AND (cm.deleted IS NULL OR cm.deleted = 0) + ORDER BY sm.student_no ASC + """; List> studentList = jdbcTemplate.queryForList(sql, classId); @@ -702,7 +722,8 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp for (Map student : studentList) { Map studentMap = new HashMap<>(); studentMap.put("id", student.get("id")); - studentMap.put("studentCode", student.get("student_no") != null ? student.get("student_no").toString() : ""); + studentMap.put("studentCode", + student.get("student_no") != null ? student.get("student_no").toString() : ""); studentMap.put("studentName", student.get("name") != null ? student.get("name").toString() : ""); // 性别转换:0-男,1-女 @@ -715,14 +736,20 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp studentMap.put("phone", student.get("phone") != null ? student.get("phone").toString() : ""); studentMap.put("email", student.get("email") != null ? student.get("email").toString() : ""); - studentMap.put("status", student.get("student_status") != null ? student.get("student_status").toString() : "1"); + studentMap.put("status", + student.get("student_status") != null ? student.get("student_status").toString() : "1"); // 添加学院和专业信息 - studentMap.put("collegeId", student.get("college_id") != null ? student.get("college_id").toString() : ""); - studentMap.put("collegeName", student.get("college_name") != null ? student.get("college_name").toString() : ""); - studentMap.put("majorId", student.get("major_id") != null ? student.get("major_id").toString() : ""); - studentMap.put("majorName", student.get("major_name") != null ? student.get("major_name").toString() : ""); - studentMap.put("className", student.get("class_name") != null ? student.get("class_name").toString() : ""); + studentMap.put("collegeId", + student.get("college_id") != null ? student.get("college_id").toString() : ""); + studentMap.put("collegeName", + student.get("college_name") != null ? student.get("college_name").toString() : ""); + studentMap.put("majorId", + student.get("major_id") != null ? student.get("major_id").toString() : ""); + studentMap.put("majorName", + student.get("major_name") != null ? student.get("major_name").toString() : ""); + studentMap.put("className", + student.get("class_name") != null ? student.get("class_name").toString() : ""); studentMap.put("grade", student.get("grade") != null ? student.get("grade").toString() : ""); students.add(studentMap); @@ -739,10 +766,11 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp } } - /** + /** * 根据批次获取可申请的奖学金类型 */ @Override + @Transactional(rollbackFor = Exception.class) public List> getScholarshipTypesByBatch(Long batchId) { List> scholarshipTypes = new ArrayList<>(); @@ -752,21 +780,24 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp if (batchTypeIds != null && !batchTypeIds.isEmpty()) { for (Long scholarshipTypeId : batchTypeIds) { - // 查询奖学金类型基本信息 - JcbtScholarshipTypeVo typeInfo = scholarshipTypeService.queryById(scholarshipTypeId); + // 查询奖学金类型基本信息 + JcbtScholarshipTypeVo typeInfo = scholarshipTypeService.queryById(scholarshipTypeId); if (typeInfo != null && typeInfo.getStatus() == 1) { // 只返回启用的类型 - Map scholarshipType = new HashMap<>(); - scholarshipType.put("id", scholarshipTypeId); - scholarshipType.put("name", typeInfo.getTypeName()); - scholarshipType.put("amount", typeInfo.getAwardAmount()); - scholarshipType.put("description", typeInfo.getDescription()); - scholarshipType.put("typeCategory", typeInfo.getTypeCategory()); - scholarshipType.put("levelType", typeInfo.getLevelType()); + Map scholarshipType = new HashMap<>(); + scholarshipType.put("id", scholarshipTypeId); + scholarshipType.put("name", typeInfo.getTypeName()); + scholarshipType.put("amount", typeInfo.getAwardAmount()); + scholarshipType.put("description", typeInfo.getDescription()); + scholarshipType.put("typeCategory", typeInfo.getTypeCategory()); + scholarshipType.put("levelType", typeInfo.getLevelType()); // 查询该类型在该批次下的名额统计信息 - List quotaList = quotaService.queryListByBatchAndType(batchId, scholarshipTypeId); - int totalQuota = quotaList.stream().mapToInt(q -> q.getTotalQuota() != null ? q.getTotalQuota() : 0).sum(); - int usedQuota = quotaList.stream().mapToInt(q -> q.getUsedQuota() != null ? q.getUsedQuota() : 0).sum(); + List quotaList = quotaService.queryListByBatchAndType(batchId, + scholarshipTypeId); + int totalQuota = quotaList.stream() + .mapToInt(q -> q.getTotalQuota() != null ? q.getTotalQuota() : 0).sum(); + int usedQuota = quotaList.stream() + .mapToInt(q -> q.getUsedQuota() != null ? q.getUsedQuota() : 0).sum(); int availableQuota = totalQuota - usedQuota; scholarshipType.put("totalQuota", totalQuota); @@ -805,6 +836,7 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp * 根据批次和班级获取带名额信息的奖学金类型(班主任专用) */ @Override + @Transactional(rollbackFor = Exception.class) public List> getScholarshipTypesWithQuota(Long batchId, Long classId) { List> scholarshipTypes = new ArrayList<>(); @@ -822,8 +854,7 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp // 查询奖学金类型基本信息 JcbtScholarshipTypeVo typeInfo = scholarshipTypeService.queryById(scholarshipTypeId); - log.info("奖学金类型基本信息: {}", typeInfo != null ? - String.format("ID=%s, 名称=%s, 状态=%s, 金额=%s, 等级=%s", + log.info("奖学金类型基本信息: {}", typeInfo != null ? String.format("ID=%s, 名称=%s, 状态=%s, 金额=%s, 等级=%s", typeInfo.getId(), typeInfo.getTypeName(), typeInfo.getStatus(), typeInfo.getAwardAmount(), typeInfo.getLevelType()) : "null"); @@ -837,14 +868,18 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp scholarshipType.put("levelType", typeInfo.getLevelType()); // 查询该班级在该奖学金类型下的名额分配信息(从jcbt_scholarship_quota表查询) - log.info("开始查询班级名额 - SQL参数: classId={}, scholarshipTypeId={}, batchId={}", classId, scholarshipTypeId, batchId); - log.info("执行SQL: SELECT * FROM jcbt_scholarship_quota WHERE allocation_level = 'CLASS' AND target_dept_id = {} AND scholarship_type_id = {} AND batch_id = {} AND status = 'ACTIVE'", classId, scholarshipTypeId, batchId); + log.info("开始查询班级名额 - SQL参数: classId={}, scholarshipTypeId={}, batchId={}", classId, + scholarshipTypeId, batchId); + log.info( + "执行SQL: SELECT * FROM jcbt_scholarship_quota WHERE allocation_level = 'CLASS' AND target_dept_id = {} AND scholarship_type_id = {} AND batch_id = {} AND status = 'ACTIVE'", + classId, scholarshipTypeId, batchId); - JcbtScholarshipQuotaVo classQuota = quotaService.selectClassQuota(classId, scholarshipTypeId, batchId); - log.info("班级名额查询结果: {}", classQuota != null ? - String.format("ID=%s, 总名额=%s, 已用=%s, 可用=%s, 状态=%s", - classQuota.getId(), classQuota.getTotalQuota(), classQuota.getUsedQuota(), - classQuota.getAvailableQuota(), classQuota.getStatus()) : "null"); + JcbtScholarshipQuotaVo classQuota = quotaService.selectClassQuota(classId, scholarshipTypeId, + batchId); + log.info("班级名额查询结果: {}", + classQuota != null ? String.format("ID=%s, 总名额=%s, 已用=%s, 可用=%s, 状态=%s", + classQuota.getId(), classQuota.getTotalQuota(), classQuota.getUsedQuota(), + classQuota.getAvailableQuota(), classQuota.getStatus()) : "null"); if (classQuota != null) { // 有班级名额分配 @@ -852,7 +887,8 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp scholarshipType.put("hasQuota", true); int totalQuota = classQuota.getTotalQuota() != null ? classQuota.getTotalQuota() : 0; int usedQuota = classQuota.getUsedQuota() != null ? classQuota.getUsedQuota() : 0; - int availableQuota = classQuota.getAvailableQuota() != null ? classQuota.getAvailableQuota() : Math.max(0, totalQuota - usedQuota); + int availableQuota = classQuota.getAvailableQuota() != null ? classQuota.getAvailableQuota() + : Math.max(0, totalQuota - usedQuota); scholarshipType.put("totalQuota", totalQuota); scholarshipType.put("usedQuota", usedQuota); @@ -860,44 +896,49 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp scholarshipType.put("quotaStatus", availableQuota > 0 ? "available" : "full"); log.info("班级名额处理结果 - 类型: {}, 总名额: {}, 已用: {}, 可用: {}, 状态: {}", - typeInfo.getTypeName(), totalQuota, usedQuota, availableQuota, availableQuota > 0 ? "available" : "full"); + typeInfo.getTypeName(), totalQuota, usedQuota, availableQuota, + availableQuota > 0 ? "available" : "full"); } else { // 没有班级名额分配,查询全校统计信息 log.info("未找到班级名额分配,开始查询全校名额"); log.info("执行全校名额查询SQL - 参数: batchId={}, scholarshipTypeId={}", batchId, scholarshipTypeId); - List quotaList = quotaService.queryListByBatchAndType(batchId, scholarshipTypeId); + List quotaList = quotaService.queryListByBatchAndType(batchId, + scholarshipTypeId); log.info("全校名额查询结果数量: {}", quotaList != null ? quotaList.size() : 0); if (quotaList != null && !quotaList.isEmpty()) { for (int i = 0; i < quotaList.size(); i++) { JcbtScholarshipQuotaVo quota = quotaList.get(i); log.info("全校名额[{}]: ID={}, 分配级别={}, 目标部门={}, 总名额={}, 已用={}, 可用={}", - i, quota.getId(), quota.getAllocationLevel(), quota.getTargetDeptName(), - quota.getTotalQuota(), quota.getUsedQuota(), quota.getAvailableQuota()); + i, quota.getId(), quota.getAllocationLevel(), quota.getTargetDeptName(), + quota.getTotalQuota(), quota.getUsedQuota(), quota.getAvailableQuota()); } } - int totalQuota = quotaList.stream().mapToInt(q -> q.getTotalQuota() != null ? q.getTotalQuota() : 0).sum(); - int usedQuota = quotaList.stream().mapToInt(q -> q.getUsedQuota() != null ? q.getUsedQuota() : 0).sum(); + int totalQuota = quotaList.stream() + .mapToInt(q -> q.getTotalQuota() != null ? q.getTotalQuota() : 0).sum(); + int usedQuota = quotaList.stream() + .mapToInt(q -> q.getUsedQuota() != null ? q.getUsedQuota() : 0).sum(); int availableQuota = Math.max(0, totalQuota - usedQuota); scholarshipType.put("hasQuota", false); scholarshipType.put("totalQuota", totalQuota); scholarshipType.put("usedQuota", usedQuota); scholarshipType.put("availableQuota", availableQuota); - scholarshipType.put("quotaStatus", totalQuota > 0 ? (availableQuota > 0 ? "available" : "full") : "unlimited"); + scholarshipType.put("quotaStatus", + totalQuota > 0 ? (availableQuota > 0 ? "available" : "full") : "unlimited"); log.info("全校名额统计结果 - 类型: {} (全校), 总名额: {}, 已用: {}, 可用: {}, 状态: {}", - typeInfo.getTypeName(), totalQuota, usedQuota, availableQuota, - totalQuota > 0 ? (availableQuota > 0 ? "available" : "full") : "unlimited"); + typeInfo.getTypeName(), totalQuota, usedQuota, availableQuota, + totalQuota > 0 ? (availableQuota > 0 ? "available" : "full") : "unlimited"); } scholarshipTypes.add(scholarshipType); log.info("奖学金类型 {} 处理完成,添加到结果列表", typeInfo.getTypeName()); } else { log.warn("奖学金类型被跳过 - ID: {}, 原因: {}", scholarshipTypeId, - typeInfo == null ? "类型信息不存在" : "状态未启用(status=" + typeInfo.getStatus() + ")"); + typeInfo == null ? "类型信息不存在" : "状态未启用(status=" + typeInfo.getStatus() + ")"); } } @@ -928,7 +969,8 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp for (int i = 0; i < scholarshipTypes.size(); i++) { Map type = scholarshipTypes.get(i); log.info("结果[{}]: 名称={}, 状态={}, 总名额={}, 可用名额={}", - i, type.get("name"), type.get("quotaStatus"), type.get("totalQuota"), type.get("availableQuota")); + i, type.get("name"), type.get("quotaStatus"), type.get("totalQuota"), + type.get("availableQuota")); } } else { log.warn("批次 {} 未关联任何奖学金类型", batchId); @@ -948,6 +990,7 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp * 测试班级名额查询(用于SQL日志调试) */ @Override + @Transactional(rollbackFor = Exception.class) public Map testClassQuotaQuery(Long classId, Long scholarshipTypeId, Long batchId) { Map result = new HashMap<>(); @@ -960,7 +1003,8 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp // 执行查询 log.info("💾 执行班级名额查询..."); - JcbtScholarshipClassQuotaVo classQuota = classQuotaService.queryAvailableQuota(classId, scholarshipTypeId, batchId); + JcbtScholarshipClassQuotaVo classQuota = classQuotaService.queryAvailableQuota(classId, scholarshipTypeId, + batchId); // 记录查询结束时间 long endTime = System.currentTimeMillis(); @@ -971,23 +1015,24 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp if (classQuota != null) { log.info("✅ 查询成功,找到班级名额记录"); log.info("📊 名额详情: ID={}, 批次ID={}, 奖学金类型ID={}, 班级ID={}", - classQuota.getId(), classQuota.getBatchId(), classQuota.getScholarshipTypeId(), classQuota.getClassId()); + classQuota.getId(), classQuota.getBatchId(), classQuota.getScholarshipTypeId(), + classQuota.getClassId()); log.info("📈 名额数据: 总名额={}, 已用名额={}, 可用名额={}, 状态={}", - classQuota.getTotalQuota(), classQuota.getUsedQuota(), classQuota.getAvailableQuota(), classQuota.getStatus()); + classQuota.getTotalQuota(), classQuota.getUsedQuota(), classQuota.getAvailableQuota(), + classQuota.getStatus()); result.put("found", true); result.put("data", Map.of( - "id", classQuota.getId(), - "batchId", classQuota.getBatchId(), - "scholarshipTypeId", classQuota.getScholarshipTypeId(), - "classId", classQuota.getClassId(), - "totalQuota", classQuota.getTotalQuota(), - "usedQuota", classQuota.getUsedQuota(), - "availableQuota", classQuota.getAvailableQuota(), - "status", classQuota.getStatus(), - "createTime", classQuota.getCreateTime(), - "updateTime", classQuota.getUpdateTime() - )); + "id", classQuota.getId(), + "batchId", classQuota.getBatchId(), + "scholarshipTypeId", classQuota.getScholarshipTypeId(), + "classId", classQuota.getClassId(), + "totalQuota", classQuota.getTotalQuota(), + "usedQuota", classQuota.getUsedQuota(), + "availableQuota", classQuota.getAvailableQuota(), + "status", classQuota.getStatus(), + "createTime", classQuota.getCreateTime(), + "updateTime", classQuota.getUpdateTime())); } else { log.warn("⚠️ 未找到班级名额记录"); log.info("🔍 可能原因:"); @@ -1033,6 +1078,7 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp * 测试全校名额查询(用于SQL日志调试) */ @Override + @Transactional(rollbackFor = Exception.class) public Map testSchoolQuotaQuery(Long batchId, Long scholarshipTypeId) { Map result = new HashMap<>(); @@ -1063,9 +1109,11 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp for (int i = 0; i < quotaList.size(); i++) { JcbtScholarshipQuotaVo quota = quotaList.get(i); log.info("📈 记录[{}]: ID={}, 分配级别={}, 目标部门ID={}, 目标部门={}", - i, quota.getId(), quota.getAllocationLevel(), quota.getTargetDeptId(), quota.getTargetDeptName()); + i, quota.getId(), quota.getAllocationLevel(), quota.getTargetDeptId(), + quota.getTargetDeptName()); log.info("📊 名额[{}]: 总名额={}, 已用={}, 预留={}, 可用={}, 状态={}", - i, quota.getTotalQuota(), quota.getUsedQuota(), quota.getReservedQuota(), quota.getAvailableQuota(), quota.getStatus()); + i, quota.getTotalQuota(), quota.getUsedQuota(), quota.getReservedQuota(), + quota.getAvailableQuota(), quota.getStatus()); Map quotaDetail = new HashMap<>(); quotaDetail.put("id", quota.getId()); @@ -1099,11 +1147,10 @@ public class JcbtScholarshipApplicationServiceImpl implements IJcbtScholarshipAp result.put("count", quotaList.size()); result.put("details", quotaDetails); result.put("summary", Map.of( - "totalQuota", totalQuota, - "usedQuota", usedQuota, - "availableQuota", availableQuota, - "calculatedAvailable", Math.max(0, totalQuota - usedQuota) - )); + "totalQuota", totalQuota, + "usedQuota", usedQuota, + "availableQuota", availableQuota, + "calculatedAvailable", Math.max(0, totalQuota - usedQuota))); } else { log.warn("⚠️ 未找到全校名额记录"); diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormBedStudentController.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormBedStudentController.java index f7deb169..c0bcd59c 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormBedStudentController.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormBedStudentController.java @@ -23,6 +23,7 @@ import org.dromara.xg.domain.bo.XgDormBedBatchAllocateBo; import org.dromara.xg.service.IXgDormBedStudentService; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.xg.domain.bo.XgDormBedBatchRecallBo; +import org.dromara.xg.domain.vo.DormBuildingTreeVo; /** * 床位学生分配关系 @@ -126,4 +127,15 @@ public class XgDormBedStudentController extends BaseController { public R batchRecallBeds(@Validated(AddGroup.class) @RequestBody XgDormBedBatchRecallBo bo) { return toAjax(xgDormBedStudentService.batchRecallBeds(bo)); } + + /** + * 获取当前用户二级学院的宿舍楼树形结构 + * 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择 + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:list") + @GetMapping("/currentUserCollegeDormTree") + public R> getCurrentUserCollegeDormBuildingTree() { + List treeData = xgDormBedStudentService.getCurrentUserCollegeDormBuildingTree(); + return R.ok(treeData); + } } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentMsgController.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentMsgController.java index 1662bc80..a41a700e 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentMsgController.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentMsgController.java @@ -1286,4 +1286,15 @@ public class XgStudentMsgController extends BaseController { List studentTree = xgStudentMsgService.getStudentTree(secondaryCollegeId); return R.ok(studentTree); } + + /** + * 获取当前用户所在学院的学生信息树 + */ + @SaCheckPermission("studentMsg:studentMsg:list") + @GetMapping("/getCurrentUserStudentTree") + public R> getCurrentUserStudentTree() { + log.info("获取当前用户所在学院的学生信息树"); + List studentTree = xgStudentMsgService.getCurrentUserStudentTree(); + return R.ok(studentTree); + } } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormBedStudentService.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormBedStudentService.java index ba031671..7c31b8a6 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormBedStudentService.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormBedStudentService.java @@ -10,6 +10,7 @@ import java.util.Collection; import java.util.List; import org.dromara.xg.domain.bo.XgDormBedBatchRecallBo; +import org.dromara.xg.domain.vo.DormBuildingTreeVo; /** * 床位学生分配关系Service接口 @@ -85,4 +86,12 @@ public interface IXgDormBedStudentService { * @return 是否成功 */ Boolean batchRecallBeds(XgDormBedBatchRecallBo bo); + + /** + * 获取当前用户二级学院的宿舍楼树形结构 + * 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择 + * + * @return 宿舍楼树形结构数据 + */ + List getCurrentUserCollegeDormBuildingTree(); } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgStudentMsgService.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgStudentMsgService.java index 35d3cb52..fe89c09c 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgStudentMsgService.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgStudentMsgService.java @@ -161,4 +161,11 @@ public interface IXgStudentMsgService { * @return 学生信息树 */ List getStudentTree(Long secondaryCollegeId); + + /** + * 获取当前用户所在学院的学生信息树 + * + * @return 学生信息树 + */ + List getCurrentUserStudentTree(); } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormBedStudentServiceImpl.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormBedStudentServiceImpl.java index 11cb945c..8dd3d955 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormBedStudentServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormBedStudentServiceImpl.java @@ -36,6 +36,15 @@ import org.dromara.xg.mapper.XgStudentMsgMapper; import org.dromara.xg.domain.XgStudentMsg; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.xg.domain.vo.DormBuildingTreeVo; +import org.dromara.xg.domain.XgDormCampus; +import org.dromara.xg.domain.XgDormArea; +import org.dromara.xg.mapper.XgDormCampusMapper; +import org.dromara.xg.mapper.XgDormAreaMapper; +import org.dromara.xg.mapper.XgSysDeptMapper; +import org.dromara.xg.domain.XgSysDept; +import java.util.stream.Collectors; +import java.util.LinkedHashMap; /** * 床位学生分配关系Service业务层处理 @@ -53,6 +62,9 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService { private final XgDormBuildingMapper xgDormBuildingMapper; private final XgDormRoomMapper xgDormRoomMapper; private final XgStudentMsgMapper xgStudentMsgMapper; + private final XgDormCampusMapper xgDormCampusMapper; + private final XgDormAreaMapper xgDormAreaMapper; + private final XgSysDeptMapper xgSysDeptMapper; /** * 查询床位学生分配关系 @@ -491,4 +503,230 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService { return true; } + + /** + * 获取当前用户二级学院的宿舍楼树形结构 + * 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择 + * + * @return 宿舍楼树形结构数据 + */ + @Override + public List getCurrentUserCollegeDormBuildingTree() { + try { + // 1. 获取当前用户的二级学院ID + Long collegeId = getCurrentUserCollegeId(); + if (collegeId == null) { + log.warn("无法获取当前用户的二级学院信息"); + return new ArrayList<>(); + } + + // 2. 通过xg_dorm_bed_student表获取分配给该学院的宿舍楼ID列表 + Set buildingIds = getCollegeAllocatedBuildingIds(collegeId); + if (buildingIds.isEmpty()) { + log.info("学院 {} 还没有分配任何宿舍楼", collegeId); + return new ArrayList<>(); + } + + // 3. 获取宿舍楼信息并构建树形结构 + return buildDormBuildingTree(buildingIds); + + } catch (Exception e) { + log.error("获取当前用户二级学院宿舍楼树失败", e); + return new ArrayList<>(); + } + } + + /** + * 获取当前用户的二级学院ID + * 通过当前用户的部门id,向上寻找包括他自己,找到dept_category前几位是xy-的就是二级学院 + * 或者parent_id = 101的部门就是二级学院 + */ + private Long getCurrentUserCollegeId() { + try { + Long currentUserId = LoginHelper.getUserId(); + Long currentDeptId = LoginHelper.getDeptId(); + + if (currentUserId == null || currentDeptId == null) { + log.warn("用户未登录或没有部门信息"); + return null; + } + + // 首先查找parent_id = 101的部门(使用LambdaQueryWrapper) + LambdaQueryWrapper parentQuery = Wrappers.lambdaQuery(); + parentQuery.eq(XgSysDept::getParentId, 101L) + .eq(XgSysDept::getStatus, "0") + .eq(XgSysDept::getDelFlag, "0") + .and(wrapper -> wrapper.eq(XgSysDept::getDeptId, currentDeptId) + .or().like(XgSysDept::getAncestors, "," + currentDeptId + ",") // 中间位置 + .or().likeRight(XgSysDept::getAncestors, currentDeptId + ",") // 开头位置 + .or().likeLeft(XgSysDept::getAncestors, "," + currentDeptId) // 末尾位置 + .or().eq(XgSysDept::getAncestors, currentDeptId.toString())) // 单独一个值 + .last("LIMIT 1"); + + List parentColleges = xgSysDeptMapper.selectList(parentQuery); + if (!parentColleges.isEmpty()) { + return parentColleges.get(0).getDeptId(); + } + + // 如果上面的查询没找到,尝试通过dept_category查找 + LambdaQueryWrapper categoryQuery = Wrappers.lambdaQuery(); + categoryQuery.likeRight(XgSysDept::getDeptCategory, "xy-") + .eq(XgSysDept::getStatus, "0") + .eq(XgSysDept::getDelFlag, "0") + .and(wrapper -> wrapper.eq(XgSysDept::getDeptId, currentDeptId) + .or().like(XgSysDept::getAncestors, "," + currentDeptId + ",") // 中间位置 + .or().likeRight(XgSysDept::getAncestors, currentDeptId + ",") // 开头位置 + .or().likeLeft(XgSysDept::getAncestors, "," + currentDeptId) // 末尾位置 + .or().eq(XgSysDept::getAncestors, currentDeptId.toString())) // 单独一个值 + .last("LIMIT 1"); + + List categoryColleges = xgSysDeptMapper.selectList(categoryQuery); + return categoryColleges.isEmpty() ? null : categoryColleges.get(0).getDeptId(); + + } catch (Exception e) { + log.error("获取当前用户二级学院ID失败", e); + return null; + } + } + + /** + * 获取分配给指定学院的宿舍楼ID列表 + */ + private Set getCollegeAllocatedBuildingIds(Long collegeId) { + try { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.select(XgDormBedStudent::getBuildingId) + .eq(XgDormBedStudent::getCollegeId, collegeId) + .eq(XgDormBedStudent::getStatus, "0") + .eq(XgDormBedStudent::getDeleted, "0") + .isNotNull(XgDormBedStudent::getBuildingId) + .groupBy(XgDormBedStudent::getBuildingId); + + List buildingRecords = baseMapper.selectList(queryWrapper); + return buildingRecords.stream() + .map(XgDormBedStudent::getBuildingId) + .collect(Collectors.toSet()); + + } catch (Exception e) { + log.error("获取学院 {} 分配的宿舍楼ID列表失败", collegeId, e); + return Set.of(); + } + } + + /** + * 根据宿舍楼ID列表构建宿舍楼树形结构 + */ + private List buildDormBuildingTree(Set buildingIds) { + if (buildingIds.isEmpty()) { + return new ArrayList<>(); + } + + try { + // 获取宿舍楼信息 + LambdaQueryWrapper buildingWrapper = Wrappers.lambdaQuery(); + buildingWrapper.in(XgDormBuilding::getId, buildingIds) + .eq(XgDormBuilding::getDeleted, "0") + .orderByAsc(XgDormBuilding::getSortOrder, XgDormBuilding::getId); + List buildings = xgDormBuildingMapper.selectList(buildingWrapper); + + if (buildings.isEmpty()) { + return new ArrayList<>(); + } + + // 获取涉及的宿舍区ID + Set areaIds = buildings.stream() + .map(XgDormBuilding::getAreaId) + .collect(Collectors.toSet()); + + // 获取宿舍区信息 + LambdaQueryWrapper areaWrapper = Wrappers.lambdaQuery(); + areaWrapper.in(XgDormArea::getId, areaIds) + .eq(XgDormArea::getDeleted, "0") + .orderByAsc(XgDormArea::getSortOrder, XgDormArea::getId); + List areas = xgDormAreaMapper.selectList(areaWrapper); + + // 获取涉及的校区ID + Set campusIds = areas.stream() + .map(XgDormArea::getCampusId) + .collect(Collectors.toSet()); + + // 获取校区信息 + LambdaQueryWrapper campusWrapper = Wrappers.lambdaQuery(); + campusWrapper.in(XgDormCampus::getId, campusIds) + .eq(XgDormCampus::getDeleted, "0") + .orderByAsc(XgDormCampus::getSortOrder, XgDormCampus::getId); + List campuses = xgDormCampusMapper.selectList(campusWrapper); + + // 构建树形结构 + return buildTreeStructure(campuses, areas, buildings); + + } catch (Exception e) { + log.error("构建宿舍楼树形结构失败", e); + return new ArrayList<>(); + } + } + + /** + * 构建树形结构:校区-宿舍区-宿舍楼 + */ + private List buildTreeStructure(List campuses, + List areas, + List buildings) { + + // 构建校区节点(第一层) + Map campusMap = new LinkedHashMap<>(); + for (XgDormCampus campus : campuses) { + // 只保留有宿舍楼的校区 + if (areas.stream().anyMatch(a -> a.getCampusId().equals(campus.getId()))) { + DormBuildingTreeVo campusNode = new DormBuildingTreeVo(); + campusNode.setId(campus.getId()); + campusNode.setLabel(campus.getCampusName()); + campusNode.setType("campus"); + campusNode.setDisabled(true); // 校区节点禁止点击 + campusNode.setChildren(new ArrayList<>()); + campusMap.put(campus.getId(), campusNode); + } + } + + // 构建宿舍区节点(第二层) + Map areaMap = new LinkedHashMap<>(); + for (XgDormArea area : areas) { + // 只保留有宿舍楼的宿舍区 + if (buildings.stream().anyMatch(b -> b.getAreaId().equals(area.getId()))) { + DormBuildingTreeVo areaNode = new DormBuildingTreeVo(); + areaNode.setId(area.getId()); + areaNode.setLabel(area.getAreaName()); + areaNode.setType("area"); + areaNode.setDisabled(true); // 宿舍区节点禁止点击 + areaNode.setChildren(new ArrayList<>()); + areaMap.put(area.getId(), areaNode); + + // 添加到对应的校区下 + DormBuildingTreeVo campusNode = campusMap.get(area.getCampusId()); + if (campusNode != null) { + campusNode.getChildren().add(areaNode); + } + } + } + + // 构建宿舍楼节点(第三层) + for (XgDormBuilding building : buildings) { + DormBuildingTreeVo buildingNode = new DormBuildingTreeVo(); + buildingNode.setId(building.getId()); + buildingNode.setLabel(building.getBuildingName()); + buildingNode.setType("building"); + buildingNode.setDisabled(false); // 宿舍楼节点可以点击 + buildingNode.setBuildingFloors(building.getFloors()); + buildingNode.setBuildingType(building.getBuildingType()); + buildingNode.setChildren(new ArrayList<>()); + + // 添加到对应的宿舍区下 + DormBuildingTreeVo areaNode = areaMap.get(building.getAreaId()); + if (areaNode != null) { + areaNode.getChildren().add(buildingNode); + } + } + + return new ArrayList<>(campusMap.values()); + } } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentManagerSourceServiceImpl.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentManagerSourceServiceImpl.java index 211cbf5b..bed0bb0a 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentManagerSourceServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentManagerSourceServiceImpl.java @@ -167,6 +167,7 @@ public class XgStudentManagerSourceServiceImpl implements IXgStudentManagerSourc } @Override + @Transactional(readOnly = true) public List getAuthSourceNames(Long managerId) { // 1. 查询xg_student_manager_source表,manager_id=managerId, deleted=0, // status=0,获取auth_source @@ -190,6 +191,7 @@ public class XgStudentManagerSourceServiceImpl implements IXgStudentManagerSourc } @Override + @Transactional(readOnly = true) public List getCurrentUserAuthDeptTree() { // 获取当前登录用户ID Long currentUserId = LoginHelper.getUserId(); diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentMsgServiceImpl.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentMsgServiceImpl.java index 81eaf206..d513f82d 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentMsgServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentMsgServiceImpl.java @@ -37,11 +37,13 @@ import org.dromara.xg.mapper.XgStudentManagerSourceMapper; import org.dromara.xg.mapper.XgClassTeacherCounselorRelMapper; import org.dromara.xg.mapper.XgClassMsgMapper; import org.dromara.xg.mapper.XgDormBedStudentMapper; +import org.dromara.xg.mapper.XgSysDeptMapper; import org.dromara.xg.domain.XgStudentManager; import org.dromara.xg.domain.XgStudentManagerSource; import org.dromara.xg.domain.XgClassTeacherCounselorRel; import org.dromara.xg.domain.XgClassMsg; import org.dromara.xg.domain.XgDormBedStudent; +import org.dromara.xg.domain.XgSysDept; import org.dromara.common.satoken.utils.LoginHelper; import java.util.stream.Collectors; @@ -73,6 +75,7 @@ public class XgStudentMsgServiceImpl implements IXgStudentMsgService { private final XgClassTeacherCounselorRelMapper xgClassTeacherCounselorRelMapper; private final XgClassMsgMapper xgClassMsgMapper; private final XgDormBedStudentMapper xgDormBedStudentMapper; + private final XgSysDeptMapper xgSysDeptMapper; /** * 查询学生信息 @@ -1317,4 +1320,83 @@ public class XgStudentMsgServiceImpl implements IXgStudentMsgService { return result; } + + /** + * 获取当前用户所在学院的学生信息树 + * + * @return 学生信息树 + */ + @Override + public List getCurrentUserStudentTree() { + try { + // 1. 获取当前用户的二级学院ID + Long collegeId = getCurrentUserCollegeId(); + if (collegeId == null) { + log.warn("无法获取当前用户的二级学院信息"); + return new ArrayList<>(); + } + + log.info("获取到当前用户的学院ID: {}", collegeId); + + // 2. 调用现有的getStudentTree方法 + return getStudentTree(collegeId); + + } catch (Exception e) { + log.error("获取当前用户学院的学生信息树失败", e); + return new ArrayList<>(); + } + } + + /** + * 获取当前用户的二级学院ID + * 通过当前用户的部门id,向上寻找包括他自己,找到dept_category前几位是xy-的就是二级学院 + * 或者parent_id = 101的部门就是二级学院 + */ + private Long getCurrentUserCollegeId() { + try { + Long currentUserId = LoginHelper.getUserId(); + Long currentDeptId = LoginHelper.getDeptId(); + + if (currentUserId == null || currentDeptId == null) { + log.warn("用户未登录或没有部门信息"); + return null; + } + + // 首先查找parent_id = 101的部门(使用LambdaQueryWrapper) + LambdaQueryWrapper parentQuery = Wrappers.lambdaQuery(); + parentQuery.eq(XgSysDept::getParentId, 101L) + .eq(XgSysDept::getStatus, "0") + .eq(XgSysDept::getDelFlag, "0") + .and(wrapper -> wrapper.eq(XgSysDept::getDeptId, currentDeptId) + .or().like(XgSysDept::getAncestors, "," + currentDeptId + ",") // 中间位置 + .or().likeRight(XgSysDept::getAncestors, currentDeptId + ",") // 开头位置 + .or().likeLeft(XgSysDept::getAncestors, "," + currentDeptId) // 末尾位置 + .or().eq(XgSysDept::getAncestors, currentDeptId.toString())) // 单独一个值 + .last("LIMIT 1"); + + List parentColleges = xgSysDeptMapper.selectList(parentQuery); + if (!parentColleges.isEmpty()) { + return parentColleges.get(0).getDeptId(); + } + + // 如果上面的查询没找到,尝试通过dept_category查找 + LambdaQueryWrapper categoryQuery = Wrappers.lambdaQuery(); + categoryQuery.likeRight(XgSysDept::getDeptCategory, "xy-") + .eq(XgSysDept::getStatus, "0") + .eq(XgSysDept::getDelFlag, "0") + .and(wrapper -> wrapper.eq(XgSysDept::getDeptId, currentDeptId) + .or().like(XgSysDept::getAncestors, "," + currentDeptId + ",") // 中间位置 + .or().likeRight(XgSysDept::getAncestors, currentDeptId + ",") // 开头位置 + .or().likeLeft(XgSysDept::getAncestors, "," + currentDeptId) // 末尾位置 + .or().eq(XgSysDept::getAncestors, currentDeptId.toString())) // 单独一个值 + .last("LIMIT 1"); + + List categoryColleges = xgSysDeptMapper.selectList(categoryQuery); + return categoryColleges.isEmpty() ? null : categoryColleges.get(0).getDeptId(); + + } catch (Exception e) { + log.error("获取当前用户二级学院ID失败", e); + return null; + } + } } diff --git a/web/src/api/xg/xgss/dormBedStudent/dormBedStudent/index.ts b/web/src/api/xg/xgss/dormBedStudent/dormBedStudent/index.ts index 59b286bb..372eca02 100644 --- a/web/src/api/xg/xgss/dormBedStudent/dormBedStudent/index.ts +++ b/web/src/api/xg/xgss/dormBedStudent/dormBedStudent/index.ts @@ -1,6 +1,6 @@ import request from '@/utils/request'; import { AxiosPromise } from 'axios'; -import { DormBedStudentVO, DormBedStudentForm, DormBedStudentQuery } from '@/api/xg/xgss/dormBedStudent/dormBedStudent/types'; +import { DormBedStudentVO, DormBedStudentForm, DormBedStudentQuery, DormBuildingTreeVO } from '@/api/xg/xgss/dormBedStudent/dormBedStudent/types'; /** * 查询床位学生分配关系列表 @@ -72,3 +72,14 @@ export const batchRecallDormBeds = (data: { bedIds: Array }) => data }); }; + +/** + * 获取当前用户二级学院的宿舍楼树形结构 + * 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择 + */ +export const getCurrentUserCollegeDormTree = (): AxiosPromise => { + return request({ + url: '/dormBedStudent/dormBedStudent/currentUserCollegeDormTree', + method: 'get' + }); +}; diff --git a/web/src/api/xg/xgss/dormBedStudent/dormBedStudent/types.ts b/web/src/api/xg/xgss/dormBedStudent/dormBedStudent/types.ts index df1ac28a..62811938 100644 --- a/web/src/api/xg/xgss/dormBedStudent/dormBedStudent/types.ts +++ b/web/src/api/xg/xgss/dormBedStudent/dormBedStudent/types.ts @@ -223,3 +223,43 @@ export interface DormBedStudentQuery extends PageQuery { */ params?: any; } + +/** + * 宿舍楼树形结构VO + */ +export interface DormBuildingTreeVO { + /** + * 节点ID + */ + id: string | number; + + /** + * 节点名称 + */ + label: string; + + /** + * 节点类型 (campus:校区, area:宿舍区, building:宿舍楼) + */ + type: string; + + /** + * 是否禁用节点(校区和宿舍区节点禁用点击) + */ + disabled?: boolean; + + /** + * 宿舍楼层数(仅宿舍楼节点有此字段) + */ + buildingFloors?: number; + + /** + * 宿舍楼类型(1男生楼 2女生楼 3混合楼)(仅宿舍楼节点有此字段) + */ + buildingType?: string; + + /** + * 子节点 + */ + children?: DormBuildingTreeVO[]; +} diff --git a/web/src/api/xg/xsjcxx/studentMsg/index.ts b/web/src/api/xg/xsjcxx/studentMsg/index.ts index 8abab8d0..65ac2221 100644 --- a/web/src/api/xg/xsjcxx/studentMsg/index.ts +++ b/web/src/api/xg/xsjcxx/studentMsg/index.ts @@ -207,6 +207,14 @@ export function getStudentTree(secondaryCollegeId: number = 101): AxiosPromise> { + return request({ + url: '/studentMsg/studentMsg/getCurrentUserStudentTree', + method: 'get' + }); +} + // 学生状态修改 export function changeStudentStatus(studentId: string | number, status: string): AxiosPromise> { // 实际使用时连接后端接口 diff --git a/web/src/views/xg/xgss/ejxyfp/components/BedDialog.vue b/web/src/views/xg/xgss/ejxyfp/components/BedDialog.vue new file mode 100644 index 00000000..28f99ee5 --- /dev/null +++ b/web/src/views/xg/xgss/ejxyfp/components/BedDialog.vue @@ -0,0 +1,150 @@ + + + diff --git a/web/src/views/xg/xgss/ejxyfp/components/CollegeAllocationDialog.vue b/web/src/views/xg/xgss/ejxyfp/components/CollegeAllocationDialog.vue new file mode 100644 index 00000000..78570767 --- /dev/null +++ b/web/src/views/xg/xgss/ejxyfp/components/CollegeAllocationDialog.vue @@ -0,0 +1,1174 @@ + + + + + diff --git a/web/src/views/xg/xgss/ejxyfp/components/RoomDialog.vue b/web/src/views/xg/xgss/ejxyfp/components/RoomDialog.vue new file mode 100644 index 00000000..e6b94692 --- /dev/null +++ b/web/src/views/xg/xgss/ejxyfp/components/RoomDialog.vue @@ -0,0 +1,171 @@ + + + diff --git a/web/src/views/xg/xgss/ejxyfp/ejxyfp.vue b/web/src/views/xg/xgss/ejxyfp/ejxyfp.vue new file mode 100644 index 00000000..731806e4 --- /dev/null +++ b/web/src/views/xg/xgss/ejxyfp/ejxyfp.vue @@ -0,0 +1,1377 @@ + + + + +