diff --git a/backend/pom.xml b/backend/pom.xml index 950c890a..66e4440d 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -407,6 +407,13 @@ ${revision} + + + org.dromara + ruoyi-zsjy + ${revision} + + - + org.dromara ruoyi-jcbt ${revision} - + 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-system/pom.xml b/backend/ruoyi-modules/ruoyi-system/pom.xml index 0fc6d551..6723ded9 100644 --- a/backend/ruoyi-modules/ruoyi-system/pom.xml +++ b/backend/ruoyi-modules/ruoyi-system/pom.xml @@ -1,7 +1,6 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> org.dromara ruoyi-modules @@ -100,6 +99,14 @@ ruoyi-common-sse + + + + + org.springframework.boot + spring-boot-starter-webflux + + diff --git a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/SysRegionController.java b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/SysRegionController.java index fc835a40..1631df4d 100644 --- a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/SysRegionController.java +++ b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/SysRegionController.java @@ -21,6 +21,9 @@ import org.dromara.system.domain.vo.SysRegionVo; import org.dromara.system.domain.bo.SysRegionBo; import org.dromara.system.service.ISysRegionService; import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.system.domain.bo.GeocodingRequestBo; +import org.dromara.system.domain.vo.GeocodingResponseVo; +import org.dromara.system.service.IAmapService; /** * 行政区划 @@ -35,6 +38,7 @@ import org.dromara.common.mybatis.core.page.TableDataInfo; public class SysRegionController extends BaseController { private final ISysRegionService sysRegionService; + private final IAmapService amapService; /** * 查询行政区划列表 @@ -110,4 +114,17 @@ public class SysRegionController extends BaseController { List tree = sysRegionService.getRegionTree(); return R.ok(tree); } + + /** + * 逆地理编码 + * + * @param request 请求参数 + * @return 逆地理编码结果 + */ + @SaCheckPermission("system:SysRegion:geocode") + @PostMapping("/reverse-geocode") + public R reverseGeocode(@Validated @RequestBody GeocodingRequestBo request) { + GeocodingResponseVo result = amapService.reverseGeocode(request); + return R.ok(result); + } } diff --git a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/GeocodingRequestBo.java b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/GeocodingRequestBo.java new file mode 100644 index 00000000..7e05ada3 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/GeocodingRequestBo.java @@ -0,0 +1,47 @@ +package org.dromara.system.domain.bo; + +import lombok.Data; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.DecimalMax; + +/** + * 逆地理编码请求参数 + * + * @author zlwu + * @date 2025-01-03 + */ +@Data +public class GeocodingRequestBo { + + /** + * 经度 + */ + @NotNull(message = "经度不能为空") + @DecimalMin(value = "-180.0", message = "经度范围必须在-180到180之间") + @DecimalMax(value = "180.0", message = "经度范围必须在-180到180之间") + private Double longitude; + + /** + * 纬度 + */ + @NotNull(message = "纬度不能为空") + @DecimalMin(value = "-90.0", message = "纬度范围必须在-90到90之间") + @DecimalMax(value = "90.0", message = "纬度范围必须在-90到90之间") + private Double latitude; + + /** + * 返回格式,默认json + */ + private String output = "json"; + + /** + * 批量查询控制,默认1 + */ + private Integer batch = 1; + + /** + * 道路等级,默认0 + */ + private Integer roadlevel = 0; +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/GeocodingResponseVo.java b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/GeocodingResponseVo.java new file mode 100644 index 00000000..6238cd6f --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/GeocodingResponseVo.java @@ -0,0 +1,268 @@ +package org.dromara.system.domain.vo; + +import lombok.Data; +import com.fasterxml.jackson.annotation.JsonFormat; +import java.util.Date; + +/** + * 逆地理编码响应结果 + * + * @author zlwu + * @date 2025-01-03 + */ +@Data +public class GeocodingResponseVo { + + /** + * 状态码 + */ + private String status; + + /** + * 状态信息 + */ + private String info; + + /** + * 信息码 + */ + private String infocode; + + /** + * 逆地理编码结果 + */ + private Regeocode regeocode; + + /** + * 创建者 + */ + private Long createBy; + + /** + * 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新者 + */ + private Long updateBy; + + /** + * 更新时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** + * 逆地理编码结果详情 + */ + @Data + public static class Regeocode { + /** + * 地址信息 + */ + private String formattedAddress; + + /** + * 地址组件 + */ + private AddressComponent addressComponent; + + /** + * 兴趣点信息 + */ + private Poi[] pois; + + /** + * 道路信息 + */ + private Road[] roads; + + /** + * 道路交叉口信息 + */ + private RoadIntersection[] roadIntersections; + + /** + * 兴趣区域信息 + */ + private Aoi[] aois; + } + + /** + * 地址组件 + */ + @Data + public static class AddressComponent { + /** + * 国家 + */ + private String country; + + /** + * 省份 + */ + private String province; + + /** + * 城市 + */ + private String city; + + /** + * 区县 + */ + private String district; + + /** + * 乡镇 + */ + private String township; + + /** + * 街道 + */ + private String street; + + /** + * 门牌号 + */ + private String number; + + /** + * 行政区划代码 + */ + private String adcode; + + /** + * 城市编码 + */ + private String citycode; + } + + /** + * 兴趣点信息 + */ + @Data + public static class Poi { + /** + * 兴趣点ID + */ + private String id; + + /** + * 兴趣点名称 + */ + private String name; + + /** + * 兴趣点类型 + */ + private String type; + + /** + * 兴趣点地址 + */ + private String address; + + /** + * 兴趣点距离 + */ + private String distance; + + /** + * 兴趣点位置 + */ + private String location; + } + + /** + * 道路信息 + */ + @Data + public static class Road { + /** + * 道路ID + */ + private String id; + + /** + * 道路名称 + */ + private String name; + + /** + * 道路距离 + */ + private String distance; + + /** + * 道路方向 + */ + private String direction; + + /** + * 道路位置 + */ + private String location; + } + + /** + * 道路交叉口信息 + */ + @Data + public static class RoadIntersection { + /** + * 交叉口ID + */ + private String id; + + /** + * 交叉口名称 + */ + private String name; + + /** + * 交叉口距离 + */ + private String distance; + + /** + * 交叉口位置 + */ + private String location; + } + + /** + * 兴趣区域信息 + */ + @Data + public static class Aoi { + /** + * 兴趣区域ID + */ + private String id; + + /** + * 兴趣区域名称 + */ + private String name; + + /** + * 兴趣区域面积 + */ + private String area; + + /** + * 兴趣区域距离 + */ + private String distance; + + /** + * 兴趣区域类型 + */ + private String type; + } +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/IAmapService.java b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/IAmapService.java new file mode 100644 index 00000000..eabb1890 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/IAmapService.java @@ -0,0 +1,21 @@ +package org.dromara.system.service; + +import org.dromara.system.domain.bo.GeocodingRequestBo; +import org.dromara.system.domain.vo.GeocodingResponseVo; + +/** + * 高德地图服务接口 + * + * @author zlwu + * @date 2025-01-03 + */ +public interface IAmapService { + + /** + * 逆地理编码 + * + * @param request 请求参数 + * @return 逆地理编码结果 + */ + GeocodingResponseVo reverseGeocode(GeocodingRequestBo request); +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/AmapServiceImpl.java b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/AmapServiceImpl.java new file mode 100644 index 00000000..3471b6a7 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/AmapServiceImpl.java @@ -0,0 +1,80 @@ +package org.dromara.system.service.impl; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.system.domain.bo.GeocodingRequestBo; +import org.dromara.system.domain.vo.GeocodingResponseVo; +import org.dromara.system.service.IAmapService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import cn.hutool.http.HttpUtil; + +/** + * 高德地图服务实现类 + * + * @author zlwu + * @date 2025-01-03 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class AmapServiceImpl implements IAmapService { + + @Value("${amap.key:}") + private String amapKey; + + @Value("${amap.url:https://restapi.amap.com}") + private String amapUrl; + + @Override + public GeocodingResponseVo reverseGeocode(GeocodingRequestBo request) { + try { + // 构建请求URL + String url = buildGeocodeUrl(request); + log.info("高德地图逆地理编码请求URL: {}", url); + + // 发送HTTP请求 + String response = HttpUtil.get(url); + log.info("高德地图逆地理编码响应: {}", response); + + if (StrUtil.isBlank(response)) { + throw new RuntimeException("高德地图API返回空响应"); + } + + // 解析JSON响应 + GeocodingResponseVo result = JSONUtil.toBean(response, GeocodingResponseVo.class); + + // 检查API响应状态 + if (!"1".equals(result.getStatus())) { + log.error("高德地图API返回错误: status={}, info={}", result.getStatus(), result.getInfo()); + throw new RuntimeException("高德地图API返回错误: " + result.getInfo()); + } + + return result; + + } catch (Exception e) { + log.error("高德地图逆地理编码失败", e); + throw new RuntimeException("逆地理编码失败: " + e.getMessage()); + } + } + + /** + * 构建逆地理编码请求URL + * + * @param request 请求参数 + * @return 完整的请求URL + */ + private String buildGeocodeUrl(GeocodingRequestBo request) { + StringBuilder urlBuilder = new StringBuilder(amapUrl); + urlBuilder.append("/v3/geocode/regeo"); + urlBuilder.append("?key=").append(amapKey); + urlBuilder.append("&location=").append(request.getLongitude()).append(",").append(request.getLatitude()); + urlBuilder.append("&output=").append(request.getOutput()); + urlBuilder.append("&batch=").append(request.getBatch()); + urlBuilder.append("&roadlevel=").append(request.getRoadlevel()); + + return urlBuilder.toString(); + } +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgClassMsgController.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgClassMsgController.java index 7e95800a..1cae46cb 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgClassMsgController.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgClassMsgController.java @@ -21,6 +21,7 @@ import org.dromara.xg.domain.vo.XgClassMsgVo; import org.dromara.xg.domain.bo.XgClassMsgBo; import org.dromara.xg.service.IXgClassMsgService; import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; /** * 班级信息-新版 @@ -63,8 +64,7 @@ public class XgClassMsgController extends BaseController { */ @SaCheckPermission("classMsg:classMsg:query") @GetMapping("/{id}") - public R getInfo(@NotNull(message = "主键不能为空") - @PathVariable Long id) { + public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { return R.ok(xgClassMsgService.queryById(id)); } @@ -98,8 +98,22 @@ public class XgClassMsgController extends BaseController { @SaCheckPermission("classMsg:classMsg:remove") @Log(title = "班级信息-新版", businessType = BusinessType.DELETE) @DeleteMapping("/{ids}") - public R remove(@NotEmpty(message = "主键不能为空") - @PathVariable Long[] ids) { + public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) { return toAjax(xgClassMsgService.deleteWithValidByIds(List.of(ids), true)); } + + /** + * 获取当前登录人作为班主任所管理的班级列表 + */ + @SaCheckPermission("classMsg:classMsg:list") + @GetMapping("/myClassListAsTeacher") + public R> myClassListAsTeacher() { + Long userId = LoginHelper.getUserId(); + XgClassMsgBo bo = new XgClassMsgBo(); + bo.setClassTeacherId(userId); + // 仅查询正常状态班级(1-正常) + bo.setClassStatus("1"); + List list = xgClassMsgService.queryList(bo); + return R.ok(list); + } } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormBedController.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormBedController.java index 5f956dc1..f3063c1f 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormBedController.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormBedController.java @@ -114,6 +114,19 @@ public class XgDormBedController extends BaseController { return R.ok(list); } + /** + * 查询当前用户所在学院的指定房间床位详细信息列表(包含学生信息) + * + * @param roomId 房间ID + */ + @SaCheckPermission("dormbed:dormBed:query") + @GetMapping("/details/college/room/{roomId}") + public R> getBedDetailsByRoomIdForCurrentUserCollege( + @NotNull(message = "房间ID不能为空") @PathVariable Long roomId) { + List list = xgDormBedService.queryBedDetailsByRoomIdForCurrentUserCollege(roomId); + return R.ok(list); + } + /** * 查询指定宿舍楼的床位详细信息列表(包含学生信息) * @@ -126,4 +139,19 @@ public class XgDormBedController extends BaseController { List list = xgDormBedService.queryBedDetailsByBuildingId(buildingId); return R.ok(list); } + + /** + * 查询指定房间中属于指定班级的床位详细信息列表(包含学生信息) + * + * @param roomId 房间ID + * @param classId 班级ID + */ + @SaCheckPermission("dormbed:dormBed:query") + @GetMapping("/details/room/{roomId}/class/{classId}") + public R> getBedDetailsByRoomIdAndClassId( + @NotNull(message = "房间ID不能为空") @PathVariable Long roomId, + @NotNull(message = "班级ID不能为空") @PathVariable Long classId) { + List list = xgDormBedService.queryBedDetailsByRoomIdAndClassId(roomId, classId); + return R.ok(list); + } } 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 78eeedf8..7de403d4 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 @@ -20,8 +20,12 @@ import org.dromara.common.excel.utils.ExcelUtil; import org.dromara.xg.domain.vo.XgDormBedStudentVo; import org.dromara.xg.domain.bo.XgDormBedStudentBo; import org.dromara.xg.domain.bo.XgDormBedBatchAllocateBo; +import org.dromara.xg.domain.bo.XgDormBedBatchAllocateToClassBo; 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; +import org.dromara.xg.domain.bo.XgDormSelectiveAllocationBo; /** * 床位学生分配关系 @@ -115,4 +119,217 @@ public class XgDormBedStudentController extends BaseController { return toAjax(xgDormBedStudentService.batchAllocateBedsToCollege(bo)); } + /** + * 批量分配床位给班级 + * + * @param bo 批量分配给班级参数 + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:add") + @Log(title = "批量分配床位给班级", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/batchAllocateToClass") + public R batchAllocateBedsToClass( + @Validated(AddGroup.class) @RequestBody XgDormBedBatchAllocateToClassBo bo) { + return toAjax(xgDormBedStudentService.batchAllocateBedsToClass(bo)); + } + + /** + * 批量收回宿舍床位 + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:edit") + @Log(title = "批量收回宿舍床位", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/batchRecall") + public R batchRecallBeds(@Validated(AddGroup.class) @RequestBody XgDormBedBatchRecallBo bo) { + return toAjax(xgDormBedStudentService.batchRecallBeds(bo)); + } + + /** + * 批量回收班级分配床位(保留学院分配) + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:edit") + @Log(title = "批量回收班级分配床位", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/batchRecallClassAllocation") + public R batchRecallClassAllocation(@Validated(AddGroup.class) @RequestBody XgDormBedBatchRecallBo bo) { + return toAjax(xgDormBedStudentService.batchRecallClassAllocation(bo)); + } + + /** + * 获取当前用户二级学院的宿舍楼树形结构 + * 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择 + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:list") + @GetMapping("/currentUserCollegeDormTree") + public R> getCurrentUserCollegeDormBuildingTree() { + List treeData = xgDormBedStudentService.getCurrentUserCollegeDormBuildingTree(); + return R.ok(treeData); + } + + /** + * 获取班主任管理的班级宿舍楼树形结构 + * 只显示班主任管理班级分配到的宿舍楼 + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:list") + @GetMapping("/getClassTeacherDormTree") + public R> getClassTeacherDormTree(@RequestParam(required = false) Long classId) { + List treeData = xgDormBedStudentService.getClassTeacherDormTree(classId); + return R.ok(treeData); + } + + /** + * 获取班主任管理班级的房间床位详情(带学生信息) + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:list") + @GetMapping("/getClassTeacherBedDetails") + public R> getClassTeacherBedDetails(@RequestParam(required = false) Long buildingId, + @RequestParam(required = false) Long classId) { + List bedDetails = xgDormBedStudentService.getClassTeacherBedDetails(buildingId, classId); + return R.ok(bedDetails); + } + + /** + * 获取班主任管理的未分配床位学生列表 + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:list") + @GetMapping("/getClassTeacherUnallocatedStudents") + public R> getClassTeacherUnallocatedStudents(@RequestParam(required = false) Long classId) { + List students = xgDormBedStudentService.getClassTeacherUnallocatedStudents(classId); + return R.ok(students); + } + + /** + * 班主任一键随机分配床位 + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:add") + @Log(title = "班主任随机分配床位", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/classTeacherRandomAllocation") + public R classTeacherRandomAllocation(@RequestParam(required = false) Long buildingId, + @RequestParam(required = false) Long classId) { + return toAjax(xgDormBedStudentService.classTeacherRandomAllocation(buildingId, classId)); + } + + /** + * 班主任一键按学号分配床位 + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:add") + @Log(title = "班主任按学号分配床位", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/classTeacherStudentNoAllocation") + public R classTeacherStudentNoAllocation(@RequestParam(required = false) Long buildingId, + @RequestParam(required = false) Long classId) { + return toAjax(xgDormBedStudentService.classTeacherStudentNoAllocation(buildingId, classId)); + } + + /** + * 班主任选择性分配床位(弹窗模式) + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:add") + @Log(title = "班主任选择性分配床位", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/classTeacherSelectiveAllocation") + public R classTeacherSelectiveAllocation(@Validated @RequestBody XgDormSelectiveAllocationBo allocationParams, + @RequestParam(required = false) Long classId) { + return toAjax(xgDormBedStudentService.classTeacherSelectiveAllocation(allocationParams, classId)); + } + + /** + * 班主任一键收回床位 + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:edit") + @Log(title = "班主任收回床位", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/classTeacherRecallBeds") + public R classTeacherRecallBeds(@RequestParam(required = false) Long buildingId, + @RequestParam(required = false) Long classId) { + return toAjax(xgDormBedStudentService.classTeacherRecallBeds(buildingId, classId)); + } + + /** + * 班主任收回选中床位 + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:edit") + @Log(title = "班主任收回选中床位", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/classTeacherRecallSelectedBeds") + public R classTeacherRecallSelectedBeds(@RequestBody List bedIds, + @RequestParam(required = false) Long classId) { + return toAjax(xgDormBedStudentService.classTeacherRecallSelectedBeds(bedIds, classId)); + } + + /** + * 班主任批量退宿 + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:edit") + @Log(title = "班主任批量退宿", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/classTeacherStayawaySelectedBeds") + public R classTeacherStayawaySelectedBeds(@RequestBody List bedIds, + @RequestParam(required = false) Long classId) { + return toAjax(xgDormBedStudentService.classTeacherStayawaySelectedBeds(bedIds, classId)); + } + + /** + * 班主任床位互换 + * 将两个学生的床位进行互换 + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:edit") + @Log(title = "班主任床位互换", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/classTeacherExchangeBeds") + public R classTeacherExchangeBeds(@RequestParam Long sourceStudentId, + @RequestParam Long targetStudentId, + @RequestParam(required = false) Long classId) { + return toAjax(xgDormBedStudentService.classTeacherExchangeBeds(sourceStudentId, targetStudentId, classId)); + } + + /** + * 班主任床位移动 + * 将学生从当前床位移动到指定的空闲床位 + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:edit") + @Log(title = "班主任床位移动", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/classTeacherMoveBed") + public R classTeacherMoveBed(@RequestParam Long sourceStudentId, + @RequestParam Long targetBedId, + @RequestParam(required = false) Long classId) { + return toAjax(xgDormBedStudentService.classTeacherMoveBed(sourceStudentId, targetBedId, classId)); + } + + /** + * 获取班级所有已入住宿舍的学生床位信息 + * 包含床位信息(校区名称、宿舍区名称、楼栋名称、房间名称、床位名称) + * 和学生信息(学生姓名、学号、性别) + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:list") + @GetMapping("/getClassOccupiedBedDetails") + public R> getClassOccupiedBedDetails(@RequestParam(required = false) Long classId) { + List occupiedBedDetails = xgDormBedStudentService.getClassOccupiedBedDetails(classId); + return R.ok(occupiedBedDetails); + } + + /** + * 获取班级所有未安排学生的床位信息 + * 包含床位信息(校区名称、宿舍区名称、楼栋名称、房间名称、床位名称) + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:list") + @GetMapping("/getClassUnoccupiedBedDetails") + public R> getClassUnoccupiedBedDetails(@RequestParam(required = false) Long classId) { + List unoccupiedBedDetails = xgDormBedStudentService.getClassUnoccupiedBedDetails(classId); + return R.ok(unoccupiedBedDetails); + } + + /** + * 根据房间ID获取住在当前房间的所有学生信息 + * 包含学生姓名、学号、性别、学院名称、专业名称、班级名称、床位编码、床位ID + */ + @SaCheckPermission("dormBedStudent:dormBedStudent:list") + @GetMapping("/getRoomStudents") + public R> getRoomStudents(@RequestParam Long roomId) { + List roomStudents = xgDormBedStudentService.getRoomStudents(roomId); + return R.ok(roomStudents); + } + } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormHygieneCheckController.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormHygieneCheckController.java new file mode 100644 index 00000000..c68f89b8 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormHygieneCheckController.java @@ -0,0 +1,152 @@ +package org.dromara.xg.controller; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.xg.domain.vo.DormHygieneCheckExportVo; +import org.dromara.xg.domain.vo.XgDormHygieneCheckVo; +import org.dromara.xg.domain.bo.XgDormHygieneCheckBo; +import org.dromara.xg.domain.bo.XgDormHygieneCheckCompleteBo; +import org.dromara.xg.service.IXgDormHygieneCheckService; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +/** + * 宿舍卫生检查 + * + * @author zlwu + * @date 2025-08-16 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/dormHygieneCheck/dormHygieneCheck") +public class XgDormHygieneCheckController extends BaseController { + + private final IXgDormHygieneCheckService xgDormHygieneCheckService; + + /** + * 查询宿舍卫生检查列表 + */ + @SaCheckPermission("dormHygieneCheck:dormHygieneCheck:list") + @GetMapping("/list") + public TableDataInfo list(XgDormHygieneCheckBo bo, PageQuery pageQuery) { + return xgDormHygieneCheckService.queryPageList(bo, pageQuery); + } + + /** + * 导出宿舍卫生检查列表 + */ + @SaCheckPermission("dormHygieneCheck:dormHygieneCheck:export") + @Log(title = "宿舍卫生检查", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(XgDormHygieneCheckBo bo, HttpServletResponse response) { + List list = xgDormHygieneCheckService.queryList(bo); + ExcelUtil.exportExcel(list, "宿舍卫生检查", XgDormHygieneCheckVo.class, response); + } + + /** + * 获取宿舍卫生检查详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("dormHygieneCheck:dormHygieneCheck:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { + return R.ok(xgDormHygieneCheckService.queryById(id)); + } + + /** + * 新增宿舍卫生检查 + */ + @SaCheckPermission("dormHygieneCheck:dormHygieneCheck:add") + @Log(title = "宿舍卫生检查", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody XgDormHygieneCheckBo bo) { + return toAjax(xgDormHygieneCheckService.insertByBo(bo)); + } + + /** + * 新增宿舍卫生检查(包含学生评分数据) + */ + @SaCheckPermission("dormHygieneCheck:dormHygieneCheck:add") + @Log(title = "宿舍卫生检查", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/addWithStudentScores") + public R addWithStudentScores(@Validated(AddGroup.class) @RequestBody XgDormHygieneCheckCompleteBo bo) { + return toAjax(xgDormHygieneCheckService.insertWithStudentScores(bo)); + } + + /** + * 修改宿舍卫生检查 + */ + @SaCheckPermission("dormHygieneCheck:dormHygieneCheck:edit") + @Log(title = "宿舍卫生检查", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody XgDormHygieneCheckBo bo) { + return toAjax(xgDormHygieneCheckService.updateByBo(bo)); + } + + /** + * 修改宿舍卫生检查(包含学生评分数据) + */ + @SaCheckPermission("dormHygieneCheck:dormHygieneCheck:edit") + @Log(title = "宿舍卫生检查", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/updateWithStudentScores") + public R updateWithStudentScores(@Validated(EditGroup.class) @RequestBody XgDormHygieneCheckCompleteBo bo) { + return toAjax(xgDormHygieneCheckService.updateWithStudentScores(bo)); + } + + /** + * 删除宿舍卫生检查 + * + * @param ids 主键串 + */ + @SaCheckPermission("dormHygieneCheck:dormHygieneCheck:remove") + @Log(title = "宿舍卫生检查", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) { + return toAjax(xgDormHygieneCheckService.deleteWithValidByIds(List.of(ids), true)); + } + + /** + * 导出宿舍卫生检查列表(包含学生卫生情况) + */ + @SaCheckPermission("dormHygieneCheck:dormHygieneCheck:export") + @Log(title = "宿舍卫生检查", businessType = BusinessType.EXPORT) + @PostMapping("/advancedExport") + public void advancedExport(XgDormHygieneCheckBo bo, HttpServletResponse response) { + List list = xgDormHygieneCheckService.queryExportList(bo); + ExcelUtil.exportExcel(list, "宿舍卫生检查数据", DormHygieneCheckExportVo.class, response); + } + + /** + * 根据学生ID查询该学生宿舍的卫生检查信息 + * + * @param studentId 学生ID + */ + @SaCheckPermission("dormHygieneCheck:dormHygieneCheck:query") + @GetMapping("/student/{studentId}") + public R> getHygieneChecksByStudentId( + @NotNull(message = "学生ID不能为空") @PathVariable Long studentId) { + List list = xgDormHygieneCheckService.queryByStudentId(studentId); + return R.ok(list); + } + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormHygieneStudentScoreController.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormHygieneStudentScoreController.java new file mode 100644 index 00000000..fae74188 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormHygieneStudentScoreController.java @@ -0,0 +1,127 @@ +package org.dromara.xg.controller; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.xg.domain.vo.XgDormHygieneStudentScoreVo; +import org.dromara.xg.domain.bo.XgDormHygieneStudentScoreBo; +import org.dromara.xg.service.IXgDormHygieneStudentScoreService; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +/** + * 宿舍卫生检查学生评分 + * + * @author zlwu + * @date 2025-08-16 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/dormHygieneStudentScore/dormHygieneStudentScore") +public class XgDormHygieneStudentScoreController extends BaseController { + + private final IXgDormHygieneStudentScoreService xgDormHygieneStudentScoreService; + + /** + * 查询宿舍卫生检查学生评分列表 + */ + @SaCheckPermission("dormHygieneStudentScore:dormHygieneStudentScore:list") + @GetMapping("/list") + public TableDataInfo list(XgDormHygieneStudentScoreBo bo, PageQuery pageQuery) { + return xgDormHygieneStudentScoreService.queryPageList(bo, pageQuery); + } + + /** + * 导出宿舍卫生检查学生评分列表 + */ + @SaCheckPermission("dormHygieneStudentScore:dormHygieneStudentScore:export") + @Log(title = "宿舍卫生检查学生评分", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(XgDormHygieneStudentScoreBo bo, HttpServletResponse response) { + List list = xgDormHygieneStudentScoreService.queryList(bo); + ExcelUtil.exportExcel(list, "宿舍卫生检查学生评分", XgDormHygieneStudentScoreVo.class, response); + } + + /** + * 获取宿舍卫生检查学生评分详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("dormHygieneStudentScore:dormHygieneStudentScore:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { + return R.ok(xgDormHygieneStudentScoreService.queryById(id)); + } + + /** + * 新增宿舍卫生检查学生评分 + */ + @SaCheckPermission("dormHygieneStudentScore:dormHygieneStudentScore:add") + @Log(title = "宿舍卫生检查学生评分", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody XgDormHygieneStudentScoreBo bo) { + return toAjax(xgDormHygieneStudentScoreService.insertByBo(bo)); + } + + /** + * 修改宿舍卫生检查学生评分 + */ + @SaCheckPermission("dormHygieneStudentScore:dormHygieneStudentScore:edit") + @Log(title = "宿舍卫生检查学生评分", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody XgDormHygieneStudentScoreBo bo) { + return toAjax(xgDormHygieneStudentScoreService.updateByBo(bo)); + } + + /** + * 删除宿舍卫生检查学生评分 + * + * @param ids 主键串 + */ + @SaCheckPermission("dormHygieneStudentScore:dormHygieneStudentScore:remove") + @Log(title = "宿舍卫生检查学生评分", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) { + return toAjax(xgDormHygieneStudentScoreService.deleteWithValidByIds(List.of(ids), true)); + } + + /** + * 根据卫生检查ID查询学生评分列表 + * + * @param hygieneCheckId 卫生检查ID + */ + @SaCheckPermission("dormHygieneStudentScore:dormHygieneStudentScore:query") + @GetMapping("/getByCheckId/{hygieneCheckId}") + public R> getByCheckId( + @NotNull(message = "卫生检查ID不能为空") @PathVariable Long hygieneCheckId) { + return R.ok(xgDormHygieneStudentScoreService.queryByHygieneCheckId(hygieneCheckId)); + } + + /** + * 批量删除指定卫生检查的学生评分记录 + * + * @param hygieneCheckId 卫生检查ID + */ + @SaCheckPermission("dormHygieneStudentScore:dormHygieneStudentScore:remove") + @Log(title = "批量删除学生评分", businessType = BusinessType.DELETE) + @DeleteMapping("/deleteByCheckId/{hygieneCheckId}") + public R deleteByCheckId(@NotNull(message = "卫生检查ID不能为空") @PathVariable Long hygieneCheckId) { + return toAjax(xgDormHygieneStudentScoreService.deleteByHygieneCheckId(hygieneCheckId)); + } +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormRoomController.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormRoomController.java index 65734779..a6a8337d 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormRoomController.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormRoomController.java @@ -45,6 +45,27 @@ public class XgDormRoomController extends BaseController { return xgDormRoomService.queryPageList(bo, pageQuery); } + /** + * 查询当前用户所在学院的宿舍房间信息列表 + */ + @SaCheckPermission("xgss:dormRoom:list") + @GetMapping("/college/list") + public TableDataInfo collegeList(XgDormRoomBo bo, PageQuery pageQuery) { + return xgDormRoomService.queryPageListByCurrentUserCollege(bo, pageQuery); + } + + /** + * 查询指定班级的宿舍房间信息列表 + */ + @SaCheckPermission("xgss:dormRoom:list") + @GetMapping("/class/{classId}/list") + public TableDataInfo classRoomList( + @NotNull(message = "班级ID不能为空") @PathVariable Long classId, + XgDormRoomBo bo, + PageQuery pageQuery) { + return xgDormRoomService.queryPageListByClassId(bo, pageQuery, classId); + } + /** * 导出宿舍房间信息列表 */ @@ -63,8 +84,7 @@ public class XgDormRoomController extends BaseController { */ @SaCheckPermission("xgss:dormRoom:query") @GetMapping("/{id}") - public R getInfo(@NotNull(message = "主键不能为空") - @PathVariable Long id) { + public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { return R.ok(xgDormRoomService.queryById(id)); } @@ -98,8 +118,7 @@ public class XgDormRoomController extends BaseController { @SaCheckPermission("xgss:dormRoom:remove") @Log(title = "宿舍房间信息", businessType = BusinessType.DELETE) @DeleteMapping("/{ids}") - public R remove(@NotEmpty(message = "主键不能为空") - @PathVariable Long[] ids) { + public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) { return toAjax(xgDormRoomService.deleteWithValidByIds(List.of(ids), true)); } } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormViolationController.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormViolationController.java new file mode 100644 index 00000000..9d22ce90 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormViolationController.java @@ -0,0 +1,207 @@ +package org.dromara.xg.controller; + +import java.util.ArrayList; +import java.util.List; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.beans.BeanUtils; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.xg.domain.vo.XgDormViolationVo; +import org.dromara.xg.domain.bo.XgDormViolationBo; +import org.dromara.xg.domain.vo.StudentOptionVo; +import org.dromara.xg.domain.vo.XgDormViolationExportVo; +import org.dromara.xg.domain.vo.XgDormViolationImportVo; +import org.dromara.xg.domain.bo.StudentOptionBo; +import org.dromara.xg.service.IXgDormViolationService; +import org.springframework.web.multipart.MultipartFile; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; + +/** + * 宿舍违纪记录 + * + * @author zlwu + * @date 2025-08-16 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/dormViolation/dormViolation") +@Slf4j +public class XgDormViolationController extends BaseController { + + private final IXgDormViolationService xgDormViolationService; + + /** + * 查询宿舍违纪记录列表 + */ + @SaCheckPermission("dormViolation:dormViolation:list") + @GetMapping("/list") + public TableDataInfo list(XgDormViolationBo bo, PageQuery pageQuery) { + return xgDormViolationService.queryPageList(bo, pageQuery); + } + + /** + * 导出宿舍违纪记录列表 + */ + @SaCheckPermission("dormViolation:dormViolation:export") + @Log(title = "宿舍违纪记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(XgDormViolationBo bo, HttpServletResponse response) { + List list = xgDormViolationService.queryList(bo); + List exportList = new ArrayList(); + for (XgDormViolationVo vo : list) { + XgDormViolationExportVo exportVo = new XgDormViolationExportVo(); + BeanUtils.copyProperties(vo, exportVo); + exportList.add(exportVo); + } + ExcelUtil.exportExcel(exportList, "宿舍违纪记录", XgDormViolationExportVo.class, response); + } + + /** + * 获取宿舍违纪记录详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("dormViolation:dormViolation:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { + return R.ok(xgDormViolationService.queryById(id)); + } + + /** + * 新增宿舍违纪记录 + */ + @SaCheckPermission("dormViolation:dormViolation:add") + @Log(title = "宿舍违纪记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody XgDormViolationBo bo) { + return toAjax(xgDormViolationService.insertByBo(bo)); + } + + /** + * 修改宿舍违纪记录 + */ + @SaCheckPermission("dormViolation:dormViolation:edit") + @Log(title = "宿舍违纪记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody XgDormViolationBo bo) { + return toAjax(xgDormViolationService.updateByBo(bo)); + } + + /** + * 删除宿舍违纪记录 + * + * @param ids 主键串 + */ + @SaCheckPermission("dormViolation:dormViolation:remove") + @Log(title = "宿舍违纪记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) { + return toAjax(xgDormViolationService.deleteWithValidByIds(List.of(ids), true)); + } + + /** + * 获取学生选择器选项列表(支持按学号、姓名检索) + */ + @SaCheckPermission("dormViolation:dormViolation:list") + @GetMapping("/studentOptions") + public TableDataInfo getStudentOptions(StudentOptionBo bo, PageQuery pageQuery) { + return xgDormViolationService.getStudentOptions(bo, pageQuery); + } + + /** + * 根据学生ID获取学生详细信息 + * + * @param studentId 学生ID + */ + @SaCheckPermission("dormViolation:dormViolation:list") + @GetMapping("/student/{studentId}") + public R getStudentById(@NotNull(message = "学生ID不能为空") @PathVariable Long studentId) { + return R.ok(xgDormViolationService.getStudentById(studentId)); + } + + /** + * 下载宿舍违纪记录导入模板 + */ + @Log(title = "宿舍违纪记录", businessType = BusinessType.EXPORT) + @SaCheckPermission("dormViolation:dormViolation:import") + @PostMapping("/importTemplate") + public void importTemplate(HttpServletResponse response) { + // ExcelUtil.exportExcel( + // List.of(new XgDormViolationImportVo()), + // "宿舍违纪记录", + // XgDormViolationImportVo.class, + // response); + try { + // 设置响应头 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + String fileName = "宿舍违纪信息导入模板.xlsx"; + fileName = java.net.URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", + "%20"); + response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + + fileName); + + // 读取模板文件 + org.springframework.core.io.Resource resource = new org.springframework.core.io.ClassPathResource( + "dormViolation_template.xlsx"); + java.io.InputStream inputStream = resource.getInputStream(); + java.io.OutputStream outputStream = response.getOutputStream(); + + // 复制文件内容到响应流 + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + + // 关闭流 + inputStream.close(); + outputStream.flush(); + outputStream.close(); + + } catch (Exception e) { + log.error("宿舍违纪信息导入模板失败", e); + try { + response.setContentType("text/html;charset=utf-8"); + response.getWriter().write("下载模板失败:" + e.getMessage()); + } catch (Exception ex) { + log.error("写入错误响应失败", ex); + } + } + } + + /** + * 导入宿舍违纪记录数据 + * + * @param file Excel文件 + * @param updateSupport 是否更新支持,如果已存在,则进行更新数据 + */ + @Log(title = "宿舍违纪记录", businessType = BusinessType.IMPORT) + @SaCheckPermission("dormViolation:dormViolation:import") + @PostMapping("/importData") + public R importData(MultipartFile file, boolean updateSupport) throws Exception { + List violationList = ExcelUtil.importExcel(file.getInputStream(), + XgDormViolationImportVo.class); + String operName = LoginHelper.getUsername(); + String message = xgDormViolationService.importDormViolation(violationList, updateSupport, operName); + return R.ok(message); + } +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgHolidayStayController.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgHolidayStayController.java new file mode 100644 index 00000000..cf1e6914 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgHolidayStayController.java @@ -0,0 +1,114 @@ +package org.dromara.xg.controller; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.xg.domain.vo.XgHolidayStayVo; +import org.dromara.xg.domain.bo.XgHolidayStayBo; +import org.dromara.xg.domain.vo.StudentOptionVo; +import org.dromara.xg.domain.bo.StudentOptionBo; +import org.dromara.xg.service.IXgHolidayStayService; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +/** + * 假期留宿管理 + * + * @author zlwu + * @date 2025-08-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/holidayStay/holidayStay") +public class XgHolidayStayController extends BaseController { + + private final IXgHolidayStayService xgHolidayStayService; + + /** + * 查询假期留宿管理列表 + */ + @SaCheckPermission("holidayStay:holidayStay:list") + @GetMapping("/list") + public TableDataInfo list(XgHolidayStayBo bo, PageQuery pageQuery) { + return xgHolidayStayService.queryPageList(bo, pageQuery); + } + + /** + * 导出假期留宿管理列表 + */ + @SaCheckPermission("holidayStay:holidayStay:export") + @Log(title = "假期留宿管理", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(XgHolidayStayBo bo, HttpServletResponse response) { + List list = xgHolidayStayService.queryList(bo); + ExcelUtil.exportExcel(list, "假期留宿管理", XgHolidayStayVo.class, response); + } + + /** + * 获取假期留宿管理详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("holidayStay:holidayStay:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { + return R.ok(xgHolidayStayService.queryById(id)); + } + + /** + * 新增假期留宿管理 + */ + @SaCheckPermission("holidayStay:holidayStay:add") + @Log(title = "假期留宿管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody XgHolidayStayBo bo) { + return toAjax(xgHolidayStayService.insertByBo(bo)); + } + + /** + * 修改假期留宿管理 + */ + @SaCheckPermission("holidayStay:holidayStay:edit") + @Log(title = "假期留宿管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody XgHolidayStayBo bo) { + return toAjax(xgHolidayStayService.updateByBo(bo)); + } + + /** + * 删除假期留宿管理 + * + * @param ids 主键串 + */ + @SaCheckPermission("holidayStay:holidayStay:remove") + @Log(title = "假期留宿管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) { + return toAjax(xgHolidayStayService.deleteWithValidByIds(List.of(ids), true)); + } + + /** + * 获取学生选择器选项列表(支持按学号、姓名检索) + */ + @SaCheckPermission("holidayStay:holidayStay:list") + @GetMapping("/studentOptions") + public TableDataInfo getStudentOptions(StudentOptionBo bo, PageQuery pageQuery) { + return xgHolidayStayService.getStudentOptions(bo, pageQuery); + } +} 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/convert/StudentInfoExcelConvert.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/convert/StudentInfoExcelConvert.java new file mode 100644 index 00000000..f405316e --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/convert/StudentInfoExcelConvert.java @@ -0,0 +1,138 @@ +package org.dromara.xg.convert; + +import cn.hutool.core.util.StrUtil; +import cn.idev.excel.converters.Converter; +import cn.idev.excel.enums.CellDataTypeEnum; +import cn.idev.excel.metadata.GlobalConfiguration; +import cn.idev.excel.metadata.data.ReadCellData; +import cn.idev.excel.metadata.data.WriteCellData; +import cn.idev.excel.metadata.property.ExcelContentProperty; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.xg.domain.vo.StudentOptionVo; +import org.dromara.xg.service.IXgDormViolationService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 学生信息Excel转换器 + * 将学生ID转换为学生详细信息(学号、姓名、学院名称、专业名称、班级名称) + * + * @author zlwu + * @date 2025-01-03 + */ +public class StudentInfoExcelConvert implements Converter { + + private static final Logger log = LoggerFactory.getLogger(StudentInfoExcelConvert.class); + + @Override + public Class supportJavaTypeKey() { + return String.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public String convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + String value = cellData.getStringValue(); + if (StrUtil.isBlank(value)) { + return ""; + } + + try { + // 获取服务实例 + IXgDormViolationService dormViolationService = SpringUtils.getBean(IXgDormViolationService.class); + if (dormViolationService == null) { + log.warn("无法获取宿舍违纪服务实例,返回原始值"); + return value; + } + + // 分割学号(逗号分隔) + String[] studentNoArray = value.split(","); + List studentIdList = Arrays.stream(studentNoArray) + .map(String::trim) + .filter(StrUtil::isNotBlank) + .map(studentNo -> { + try { + StudentOptionVo student = dormViolationService.getStudentByStudentNo(studentNo); + if (student != null) { + return student.getId().toString(); + } else { + log.warn("未找到学号为 {} 的学生", studentNo); + return null; + } + } catch (Exception e) { + log.error("根据学号查询学生信息失败,学号: {}", studentNo, e); + return null; + } + }) + .filter(id -> id != null) + .collect(Collectors.toList()); + + return String.join(",", studentIdList); + } catch (Exception e) { + log.error("学号转学生ID失败,原始值: {}", value, e); + return ""; + } + } + + @Override + public WriteCellData convertToExcelData(String value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (StrUtil.isBlank(value)) { + return new WriteCellData<>(""); + } + + try { + // 获取服务实例 + IXgDormViolationService dormViolationService = SpringUtils.getBean(IXgDormViolationService.class); + if (dormViolationService == null) { + log.warn("无法获取宿舍违纪服务实例,返回原始值"); + return new WriteCellData<>(value); + } + + // 分割学生ID(逗号分隔) + String[] idArray = value.split(","); + List studentInfoList = Arrays.stream(idArray) + .map(String::trim) + .filter(StrUtil::isNotBlank) + .map(idStr -> { + try { + Long studentId = Long.parseLong(idStr); + StudentOptionVo student = dormViolationService.getStudentById(studentId); + if (student != null) { + // 格式:学号-姓名(学院-专业-班级) + return String.format("%s-%s(%s-%s-%s)", + student.getStudentNo(), + student.getName(), + student.getCollegeName(), + student.getMajorName(), + student.getClassName()); + } else { + return idStr + "-未知学生"; + } + } catch (NumberFormatException e) { + log.warn("无效的学生ID格式: {}", idStr); + return idStr + "-格式错误"; + } catch (Exception e) { + log.error("查询学生信息失败,学生ID: {}", idStr, e); + return idStr + "-查询失败"; + } + }) + .collect(Collectors.toList()); + + String convertedValue = String.join("; ", studentInfoList); + return new WriteCellData<>(convertedValue); + } catch (Exception e) { + log.error("学生信息转换失败,原始值: {}", value, e); + return new WriteCellData<>(value + "-转换失败"); + } + } +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgDormHygieneCheck.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgDormHygieneCheck.java new file mode 100644 index 00000000..87fa3502 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgDormHygieneCheck.java @@ -0,0 +1,143 @@ +package org.dromara.xg.domain; + +import org.dromara.common.tenant.core.TenantEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serial; + +/** + * 宿舍卫生检查对象 xg_dorm_hygiene_check + * + * @author zlwu + * @date 2025-08-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("xg_dorm_hygiene_check") +public class XgDormHygieneCheck extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "id") + private Long id; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic(value = "0", delval = "1") + private Long deleted; + + /** + * 检查标题 + */ + private String checkTitle; + + /** + * 检查日期 + */ + private Date checkDate; + + /** + * 检查员ID,关联sys_user.id + */ + private Long checkerId; + + /** + * 检查员姓名 + */ + private String checkerName; + + /** + * 检查类型(1日常检查 2专项检查 3突击检查 4复查) + */ + private String checkType; + + /** + * 检查级别(1宿管员 2学院 3学校) + */ + private String checkLevel; + + /** + * 宿舍房间ID,关联xg_dorm_room.id + */ + private Long roomId; + + /** + * 房间编号 + */ + private String roomCode; + + /** + * 房间名称 + */ + private String roomName; + + /** + * 宿舍楼ID,关联xg_dorm_building.id + */ + private Long buildingId; + + /** + * 宿舍楼名称 + */ + private String buildingName; + + /** + * 宿舍区ID,关联xg_dorm_area.id + */ + private Long areaId; + + /** + * 宿舍区名称 + */ + private String areaName; + + /** + * 校区ID,关联xg_dorm_campus.id + */ + private Long campusId; + + /** + * 校区名称 + */ + private String campusName; + + /** + * 宿舍总分(0-100分) + */ + private Double totalScore; + + /** + * 卫生等级(A优秀 B良好 C合格 D不合格) + */ + private String hygieneGrade; + + /** + * 附件OSS ID列表,逗号分隔 + */ + private String attachmentIds; + + /** + * 检查总结 + */ + private String checkSummary; + + /** + * 整改建议 + */ + private String improvementSuggestions; + + /** + * 检查备注 + */ + private String checkRemarks; + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgDormHygieneStudentScore.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgDormHygieneStudentScore.java new file mode 100644 index 00000000..4bfc2c15 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgDormHygieneStudentScore.java @@ -0,0 +1,126 @@ +package org.dromara.xg.domain; + +import org.dromara.common.tenant.core.TenantEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 宿舍卫生检查学生评分对象 xg_dorm_hygiene_student_score + * + * @author zlwu + * @date 2025-08-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("xg_dorm_hygiene_student_score") +public class XgDormHygieneStudentScore extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "id") + private Long id; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic(value = "0", delval = "1") + private Long deleted; + + /** + * 卫生检查ID,关联xg_dorm_hygiene_check.id + */ + private Long hygieneCheckId; + + /** + * 学生ID,关联xg_student_msg.id + */ + private Long studentId; + + /** + * 学号 + */ + private String studentNo; + + /** + * 学生姓名 + */ + private String studentName; + + /** + * 班级ID + */ + private Long classId; + + /** + * 班级名称 + */ + private String className; + + /** + * 专业ID + */ + private Long majorId; + + /** + * 专业名称 + */ + private String majorName; + + /** + * 学院ID + */ + private Long collegeId; + + /** + * 学院名称 + */ + private String collegeName; + + /** + * 床位ID,关联xg_dorm_bed.id + */ + private Long bedId; + + /** + * 床位编号 + */ + private String bedCode; + + /** + * 学生个人得分(0-100分) + */ + private Double studentScore; + + /** + * 是否在寝(1在寝 2外出 3请假 4其他) + */ + private String attendanceStatus; + + /** + * 学生评级(A优秀 B良好 C合格 D不合格) + */ + private String studentGrade; + + /** + * 是否为寝室长(0否 1是) + */ + private Long isResponsiblePerson; + + /** + * 评分备注 + */ + private String scoreRemarks; + + /** + * 改进意见 + */ + private String improvementNotes; + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgDormViolation.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgDormViolation.java new file mode 100644 index 00000000..b67b5814 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgDormViolation.java @@ -0,0 +1,168 @@ +package org.dromara.xg.domain; + +import org.dromara.common.tenant.core.TenantEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serial; + +/** + * 宿舍违纪记录对象 xg_dorm_violation + * + * @author zlwu + * @date 2025-08-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("xg_dorm_violation") +public class XgDormViolation extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 违纪记录ID(主键) + */ + @TableId(value = "id") + private Long id; + + /** + * 学生ID + */ + private Long studentId; + + /** + * 学生姓名 + */ + private String studentName; + + /** + * 学号 + */ + private String studentNo; + + /** + * 学院ID + */ + private Long collegeId; + + /** + * 学院名称 + */ + private String collegeName; + + /** + * 专业ID + */ + private Long majorId; + + /** + * 专业名称 + */ + private String majorName; + + /** + * 班级ID + */ + private Long classId; + + /** + * 班级名称 + */ + private String className; + + /** + * 校区ID + */ + private Long campusId; + + /** + * 校区名称 + */ + private String campusName; + + /** + * 宿舍区域ID + */ + private Long areaId; + + /** + * 宿舍区域名称 + */ + private String areaName; + + /** + * 宿舍楼ID + */ + private Long buildingId; + + /** + * 宿舍楼名称 + */ + private String buildingName; + + /** + * 宿舍房间ID + */ + private Long roomId; + + /** + * 房间名称 + */ + private String roomName; + + /** + * 床位ID + */ + private Long bedId; + + /** + * 床位名称 + */ + private String bedName; + + /** + * 违纪类型 + */ + private String violationType; + + /** + * 违纪等级:1轻微 2一般 3严重 4特别严重 + */ + private String violationLevel; + + /** + * 违纪发生时间 + */ + private Date violationTime; + + /** + * 违纪行为描述 + */ + private String violationDescription; + + /** + * 涉及其他学生id(逗号分隔) + */ + private String involvedStudents; + + /** + * 附件OSS ID列表(逗号分隔) + */ + private String attachmentOssIds; + + /** + * 删除标志(0代表存在 1代表删除) + */ + private String deleted; + + /** + * 备注 + */ + private String remark; + + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgHolidayStay.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgHolidayStay.java new file mode 100644 index 00000000..88c2263e --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgHolidayStay.java @@ -0,0 +1,203 @@ +package org.dromara.xg.domain; + +import org.dromara.common.tenant.core.TenantEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serial; + +/** + * 假期留宿管理对象 xg_holiday_stay + * + * @author zlwu + * @date 2025-08-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("xg_holiday_stay") +public class XgHolidayStay extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "id") + private Long id; + + /** + * 删除标志(0代表存在 1代表删除) + */ + private Long deleted; + + /** + * 学生ID,关联xg_student_msg.id + */ + private Long studentId; + + /** + * 学生姓名 + */ + private String studentName; + + /** + * 学号 + */ + private String studentNo; + + /** + * 班级ID + */ + private Long classId; + + /** + * 班级名称 + */ + private String className; + + /** + * 专业ID + */ + private Long majorId; + + /** + * 专业名称 + */ + private String majorName; + + /** + * 学院ID + */ + private Long collegeId; + + /** + * 学院名称 + */ + private String collegeName; + + /** + * 校区ID,关联xg_dorm_campus.id + */ + private Long campusId; + + /** + * 校区名称 + */ + private String campusName; + + /** + * 宿舍区ID,关联xg_dorm_area.id + */ + private Long areaId; + + /** + * 宿舍区名称 + */ + private String areaName; + + /** + * 宿舍楼ID,关联xg_dorm_building.id + */ + private Long buildingId; + + /** + * 宿舍楼名称 + */ + private String buildingName; + + /** + * 房间ID,关联xg_dorm_room.id + */ + private Long roomId; + + /** + * 房间名称 + */ + private String roomName; + + /** + * 床位ID,关联xg_dorm_bed.id + */ + private Long bedId; + + /** + * 床位名称 + */ + private String bedName; + + /** + * 假期类型(寒假、暑假、春节假期、国庆假期等) + */ + private String holidayType; + + /** + * 申请留宿开始日期 + */ + private Date applyStartDate; + + /** + * 申请留宿结束日期 + */ + private Date applyEndDate; + + /** + * 留宿原因(必填) + */ + private String stayReason; + + /** + * 留宿原因类型(学习、实习、家庭困难、其他) + */ + private String stayReasonType; + + /** + * 学生联系电话 + */ + private String studentPhone; + + /** + * 紧急联系人姓名 + */ + private String emergencyContact; + + /** + * 紧急联系人电话 + */ + private String emergencyPhone; + + /** + * 家长同意(0否 1是) + */ + private String parentConsent; + + /** + * 附件OSS文件ID,多个用逗号分隔(申请材料、证明文件等) + */ + private String attachmentOssIds; + + /** + * 扩展字段1 + */ + private String extField1; + + /** + * 扩展字段2 + */ + private String extField2; + + /** + * 扩展字段3 + */ + private String extField3; + + /** + * 备注信息 + */ + private String remark; + + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgStudentMsg.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgStudentMsg.java index f0eb3310..6ad69c43 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgStudentMsg.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgStudentMsg.java @@ -516,7 +516,7 @@ public class XgStudentMsg extends TenantEntity { private String dormitoryRoom; /** - * 扩展字段1 + * 扩展字段1 - 床位id */ private String extField1; diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgStudentMsgBatch.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgStudentMsgBatch.java index 8c975ffa..06a3697c 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgStudentMsgBatch.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgStudentMsgBatch.java @@ -516,7 +516,7 @@ public class XgStudentMsgBatch extends TenantEntity { private String dormitoryRoom; /** - * 扩展字段1 + * 扩展字段1 - 床位id */ private String extField1; diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgSysDept.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgSysDept.java index 7ccb9cc6..eba15ea6 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgSysDept.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgSysDept.java @@ -1,5 +1,6 @@ package org.dromara.xg.domain; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; @@ -70,8 +71,9 @@ public class XgSysDept extends BaseEntity { private String delFlag; /** - * 父部门名称 + * 父部门名称(冗余字段,用于显示,不持久化到数据库) */ + @TableField(exist = false) private String parentName; /** diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/StudentOptionBo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/StudentOptionBo.java new file mode 100644 index 00000000..235289cb --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/StudentOptionBo.java @@ -0,0 +1,21 @@ +package org.dromara.xg.domain.bo; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 学生选择器查询业务对象 + * + * @author zlwu + * @date 2025-08-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class StudentOptionBo extends BaseEntity { + + /** + * 搜索关键字(学号或姓名) + */ + private String keyword; +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormBedBatchAllocateToClassBo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormBedBatchAllocateToClassBo.java new file mode 100644 index 00000000..e2ccf8fc --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormBedBatchAllocateToClassBo.java @@ -0,0 +1,49 @@ +package org.dromara.xg.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.List; + +/** + * 床位批量分配给班级业务对象 xg_dorm_bed_batch_allocate_to_class + * + * @author zlwu + * @date 2025-01-27 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class XgDormBedBatchAllocateToClassBo extends BaseEntity { + + /** + * 床位ID列表 + */ + @NotEmpty(message = "床位ID列表不能为空", groups = { AddGroup.class }) + private List bedIds; + + /** + * 班级ID列表 + */ + @NotEmpty(message = "班级ID列表不能为空", groups = { AddGroup.class }) + private List classIds; + + /** + * 班级人数列表(与classIds一一对应) + */ + private List classStudentCounts; + + /** + * 楼栋ID + */ + private Long buildingId; + + /** + * 楼栋类型(1-男生楼,2-女生楼) + */ + private String buildingType; + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormBedBatchRecallBo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormBedBatchRecallBo.java new file mode 100644 index 00000000..dde9b7d4 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormBedBatchRecallBo.java @@ -0,0 +1,23 @@ +package org.dromara.xg.domain.bo; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.List; + +/** + * 床位批量收回业务对象 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class XgDormBedBatchRecallBo extends BaseEntity { + + /** + * 床位ID列表 + */ + @NotEmpty(message = "床位ID列表不能为空", groups = { AddGroup.class }) + private List bedIds; +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormHygieneCheckBo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormHygieneCheckBo.java new file mode 100644 index 00000000..ca9e945d --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormHygieneCheckBo.java @@ -0,0 +1,151 @@ +package org.dromara.xg.domain.bo; + +import org.dromara.xg.domain.XgDormHygieneCheck; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * 宿舍卫生检查业务对象 xg_dorm_hygiene_check + * + * @author zlwu + * @date 2025-08-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = XgDormHygieneCheck.class, reverseConvertGenerate = false) +public class XgDormHygieneCheckBo extends BaseEntity { + + /** + * 主键ID + */ + @NotNull(message = "主键ID不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 删除标志(0代表存在 1代表删除) + */ + private Long deleted; + + /** + * 检查标题 + */ + @NotBlank(message = "检查标题不能为空", groups = { AddGroup.class, EditGroup.class }) + private String checkTitle; + + /** + * 检查日期 + */ + @NotNull(message = "检查日期不能为空", groups = { AddGroup.class, EditGroup.class }) + private Date checkDate; + + /** + * 检查员ID,关联sys_user.id + */ + @NotNull(message = "检查员ID,关联sys_user.id不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long checkerId; + + /** + * 检查员姓名 + */ + @NotBlank(message = "检查员姓名不能为空", groups = { AddGroup.class, EditGroup.class }) + private String checkerName; + + /** + * 检查类型(1日常检查 2专项检查 3突击检查 4复查) + */ + private String checkType; + + /** + * 检查级别(1宿管员 2学院 3学校) + */ + private String checkLevel; + + /** + * 宿舍房间ID,关联xg_dorm_room.id + */ + @NotNull(message = "宿舍房间ID,关联xg_dorm_room.id不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long roomId; + + /** + * 房间编号 + */ + @NotBlank(message = "房间编号不能为空", groups = { AddGroup.class, EditGroup.class }) + private String roomCode; + + /** + * 房间名称 + */ + @NotBlank(message = "房间名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String roomName; + + /** + * 宿舍楼ID,关联xg_dorm_building.id + */ + @NotNull(message = "宿舍楼ID,关联xg_dorm_building.id不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long buildingId; + + /** + * 宿舍楼名称 + */ + @NotBlank(message = "宿舍楼名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String buildingName; + + /** + * 宿舍区ID,关联xg_dorm_area.id + */ + private Long areaId; + + /** + * 宿舍区名称 + */ + private String areaName; + + /** + * 校区ID,关联xg_dorm_campus.id + */ + private Long campusId; + + /** + * 校区名称 + */ + private String campusName; + + /** + * 宿舍总分(0-100分) + */ + private Double totalScore; + + /** + * 卫生等级(A优秀 B良好 C合格 D不合格) + */ + private String hygieneGrade; + + /** + * 附件OSS ID列表,逗号分隔 + */ + private String attachmentIds; + + /** + * 检查总结 + */ + private String checkSummary; + + /** + * 整改建议 + */ + private String improvementSuggestions; + + /** + * 检查备注 + */ + private String checkRemarks; + + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormHygieneCheckCompleteBo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormHygieneCheckCompleteBo.java new file mode 100644 index 00000000..3a84a6a1 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormHygieneCheckCompleteBo.java @@ -0,0 +1,169 @@ +package org.dromara.xg.domain.bo; + +import org.dromara.xg.domain.XgDormHygieneCheck; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; +import java.util.List; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * 宿舍卫生检查完整表单业务对象(包含学生评分数据) + * + * @author zlwu + * @date 2025-01-28 + */ +@Data +@AutoMapper(target = XgDormHygieneCheck.class, reverseConvertGenerate = false) +public class XgDormHygieneCheckCompleteBo { + + /** + * 主键ID + */ + @NotNull(message = "主键ID不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 删除标志(0代表存在 1代表删除) + */ + private Long deleted; + + /** + * 检查标题 + */ + @NotBlank(message = "检查标题不能为空", groups = { AddGroup.class, EditGroup.class }) + private String checkTitle; + + /** + * 检查日期 + */ + @NotNull(message = "检查日期不能为空", groups = { AddGroup.class, EditGroup.class }) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date checkDate; + + /** + * 检查员ID,关联sys_user.id + */ + @NotNull(message = "检查员ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long checkerId; + + /** + * 检查员姓名 + */ + @NotBlank(message = "检查员姓名不能为空", groups = { AddGroup.class, EditGroup.class }) + private String checkerName; + + /** + * 检查类型(1日常检查 2专项检查 3突击检查 4复查) + */ + private String checkType; + + /** + * 检查级别(1宿管员 2学院 3学校) + */ + private String checkLevel; + + /** + * 宿舍房间ID,关联xg_dorm_room.id + */ + @NotNull(message = "宿舍房间ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long roomId; + + /** + * 房间编号 + */ + @NotBlank(message = "房间编号不能为空", groups = { AddGroup.class, EditGroup.class }) + private String roomCode; + + /** + * 房间名称 + */ + @NotBlank(message = "房间名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String roomName; + + /** + * 宿舍楼ID,关联xg_dorm_building.id + */ + @NotNull(message = "宿舍楼ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long buildingId; + + /** + * 宿舍楼名称 + */ + @NotBlank(message = "宿舍楼名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String buildingName; + + /** + * 宿舍区ID,关联xg_dorm_area.id + */ + private Long areaId; + + /** + * 宿舍区名称 + */ + private String areaName; + + /** + * 校区ID,关联xg_dorm_campus.id + */ + private Long campusId; + + /** + * 校区名称 + */ + private String campusName; + + /** + * 宿舍总分(0-100分) + */ + @Min(value = 0, message = "宿舍总分不能小于0") + @Max(value = 100, message = "宿舍总分不能大于100") + private Double totalScore; + + /** + * 卫生等级(A优秀 B良好 C合格 D不合格) + */ + private String hygieneGrade; + + /** + * 附件OSS ID列表,逗号分隔 + */ + private String attachmentIds; + + /** + * 检查总结 + */ + private String checkSummary; + + /** + * 整改建议 + */ + private String improvementSuggestions; + + /** + * 检查备注 + */ + private String checkRemarks; + + /** + * 学生评分数据列表 + */ + @Valid + @NotEmpty(message = "学生评分数据不能为空", groups = { AddGroup.class, EditGroup.class }) + private List studentScores; + + // 基础字段(用于VO类映射) + private Long createBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private Long updateBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormHygieneStudentScoreBo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormHygieneStudentScoreBo.java new file mode 100644 index 00000000..25a96bab --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormHygieneStudentScoreBo.java @@ -0,0 +1,129 @@ +package org.dromara.xg.domain.bo; + +import org.dromara.xg.domain.XgDormHygieneStudentScore; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * 宿舍卫生检查学生评分业务对象 xg_dorm_hygiene_student_score + * + * @author zlwu + * @date 2025-08-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = XgDormHygieneStudentScore.class, reverseConvertGenerate = false) +public class XgDormHygieneStudentScoreBo extends BaseEntity { + + /** + * 主键ID + */ + @NotNull(message = "主键ID不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 删除标志(0代表存在 1代表删除) + */ + private Long deleted; + + /** + * 卫生检查ID,关联xg_dorm_hygiene_check.id + */ + // @NotNull(message = "卫生检查ID,关联xg_dorm_hygiene_check.id不能为空", groups = { + // EditGroup.class }) + private Long hygieneCheckId; + + /** + * 学生ID,关联xg_student_msg.id + */ + @NotNull(message = "学生ID,关联xg_student_msg.id不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long studentId; + + /** + * 学号 + */ + @NotBlank(message = "学号不能为空", groups = { AddGroup.class, EditGroup.class }) + private String studentNo; + + /** + * 学生姓名 + */ + @NotBlank(message = "学生姓名不能为空", groups = { AddGroup.class, EditGroup.class }) + private String studentName; + + /** + * 班级ID + */ + private Long classId; + + /** + * 班级名称 + */ + private String className; + + /** + * 专业ID + */ + private Long majorId; + + /** + * 专业名称 + */ + private String majorName; + + /** + * 学院ID + */ + private Long collegeId; + + /** + * 学院名称 + */ + private String collegeName; + + /** + * 床位ID,关联xg_dorm_bed.id + */ + private Long bedId; + + /** + * 床位编号 + */ + private String bedCode; + + /** + * 学生个人得分(0-100分) + */ + private Double studentScore; + + /** + * 是否在寝(1在寝 2外出 3请假 4其他) + */ + private String attendanceStatus; + + /** + * 学生评级(A优秀 B良好 C合格 D不合格) + */ + private String studentGrade; + + /** + * 是否为寝室长(0否 1是) + */ + private Long isResponsiblePerson; + + /** + * 评分备注 + */ + private String scoreRemarks; + + /** + * 改进意见 + */ + private String improvementNotes; + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormSelectiveAllocationBo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormSelectiveAllocationBo.java new file mode 100644 index 00000000..50950274 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormSelectiveAllocationBo.java @@ -0,0 +1,35 @@ +package org.dromara.xg.domain.bo; + +import lombok.Data; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotBlank; + +import java.util.List; + +/** + * 班主任选择性分配床位请求参数 + * + * @author zlwu + * @date 2025-01-27 + */ +@Data +public class XgDormSelectiveAllocationBo { + + /** + * 选择的床位ID数组 + */ + @NotEmpty(message = "床位ID列表不能为空") + private List bedIds; + + /** + * 选择的学生ID数组 + */ + @NotEmpty(message = "学生ID列表不能为空") + private List studentIds; + + /** + * 分配类型:random-随机分配,studentNo-按学号分配 + */ + @NotBlank(message = "分配类型不能为空") + private String allocationType; +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormViolationBo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormViolationBo.java new file mode 100644 index 00000000..e09ac546 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgDormViolationBo.java @@ -0,0 +1,178 @@ +package org.dromara.xg.domain.bo; + +import org.dromara.xg.domain.XgDormViolation; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * 宿舍违纪记录业务对象 xg_dorm_violation + * + * @author zlwu + * @date 2025-08-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = XgDormViolation.class, reverseConvertGenerate = false) +public class XgDormViolationBo extends BaseEntity { + + /** + * 违纪记录ID(主键) + */ + @NotNull(message = "违纪记录ID(主键)不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 学生ID + */ + @NotNull(message = "学生ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long studentId; + + /** + * 学生姓名 + */ + @NotBlank(message = "学生姓名不能为空", groups = { AddGroup.class, EditGroup.class }) + private String studentName; + + /** + * 学号 + */ + @NotBlank(message = "学号不能为空", groups = { AddGroup.class, EditGroup.class }) + private String studentNo; + + /** + * 学院ID + */ + private Long collegeId; + + /** + * 学院名称 + */ + private String collegeName; + + /** + * 专业ID + */ + private Long majorId; + + /** + * 专业名称 + */ + private String majorName; + + /** + * 班级ID + */ + private Long classId; + + /** + * 班级名称 + */ + private String className; + + /** + * 校区ID + */ + private Long campusId; + + /** + * 校区名称 + */ + private String campusName; + + /** + * 宿舍区域ID + */ + private Long areaId; + + /** + * 宿舍区域名称 + */ + private String areaName; + + /** + * 宿舍楼ID + */ + @NotNull(message = "宿舍楼ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long buildingId; + + /** + * 宿舍楼名称 + */ + @NotBlank(message = "宿舍楼名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String buildingName; + + /** + * 宿舍房间ID + */ + @NotNull(message = "宿舍房间ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long roomId; + + /** + * 房间名称 + */ + @NotBlank(message = "房间名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String roomName; + + /** + * 床位ID + */ + private Long bedId; + + /** + * 床位名称 + */ + private String bedName; + + /** + * 违纪类型 + */ + @NotBlank(message = "违纪类型不能为空", groups = { AddGroup.class, EditGroup.class }) + private String violationType; + + /** + * 违纪等级:1轻微 2一般 3严重 4特别严重 + */ + @NotBlank(message = "违纪等级:1轻微 2一般 3严重 4特别严重不能为空", groups = { AddGroup.class, EditGroup.class }) + private String violationLevel; + + /** + * 违纪发生时间 + */ + @NotNull(message = "违纪发生时间不能为空", groups = { AddGroup.class, EditGroup.class }) + private Date violationTime; + + /** + * 违纪行为描述 + */ + @NotBlank(message = "违纪行为描述不能为空", groups = { AddGroup.class, EditGroup.class }) + private String violationDescription; + + /** + * 涉及其他学生id(逗号分隔) + */ + private String involvedStudents; + + /** + * 附件OSS ID列表(逗号分隔) + */ + private String attachmentOssIds; + + /** + * 删除标志(0代表存在 1代表删除) + */ + private String deleted; + + /** + * 备注 + */ + private String remark; + + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgHolidayStayBo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgHolidayStayBo.java new file mode 100644 index 00000000..a9b2e0ad --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgHolidayStayBo.java @@ -0,0 +1,213 @@ +package org.dromara.xg.domain.bo; + +import org.dromara.xg.domain.XgHolidayStay; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * 假期留宿管理业务对象 xg_holiday_stay + * + * @author zlwu + * @date 2025-08-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = XgHolidayStay.class, reverseConvertGenerate = false) +public class XgHolidayStayBo extends BaseEntity { + + /** + * 主键ID + */ + @NotNull(message = "主键ID不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 删除标志(0代表存在 1代表删除) + */ + private Long deleted; + + /** + * 学生ID,关联xg_student_msg.id + */ + @NotNull(message = "学生ID,关联xg_student_msg.id不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long studentId; + + /** + * 学生姓名 + */ + @NotBlank(message = "学生姓名不能为空", groups = { AddGroup.class, EditGroup.class }) + private String studentName; + + /** + * 学号 + */ + @NotBlank(message = "学号不能为空", groups = { AddGroup.class, EditGroup.class }) + private String studentNo; + + /** + * 班级ID + */ + private Long classId; + + /** + * 班级名称 + */ + private String className; + + /** + * 专业ID + */ + private Long majorId; + + /** + * 专业名称 + */ + private String majorName; + + /** + * 学院ID + */ + private Long collegeId; + + /** + * 学院名称 + */ + private String collegeName; + + /** + * 校区ID,关联xg_dorm_campus.id + */ + private Long campusId; + + /** + * 校区名称 + */ + private String campusName; + + /** + * 宿舍区ID,关联xg_dorm_area.id + */ + private Long areaId; + + /** + * 宿舍区名称 + */ + private String areaName; + + /** + * 宿舍楼ID,关联xg_dorm_building.id + */ + private Long buildingId; + + /** + * 宿舍楼名称 + */ + private String buildingName; + + /** + * 房间ID,关联xg_dorm_room.id + */ + private Long roomId; + + /** + * 房间名称 + */ + private String roomName; + + /** + * 床位ID,关联xg_dorm_bed.id + */ + private Long bedId; + + /** + * 床位名称 + */ + private String bedName; + + /** + * 假期类型(寒假、暑假、春节假期、国庆假期等) + */ + @NotBlank(message = "假期类型(寒假、暑假、春节假期、国庆假期等)不能为空", groups = { AddGroup.class, EditGroup.class }) + private String holidayType; + + /** + * 申请留宿开始日期 + */ + @NotNull(message = "申请留宿开始日期不能为空", groups = { AddGroup.class, EditGroup.class }) + private Date applyStartDate; + + /** + * 申请留宿结束日期 + */ + @NotNull(message = "申请留宿结束日期不能为空", groups = { AddGroup.class, EditGroup.class }) + private Date applyEndDate; + + /** + * 留宿原因(必填) + */ + @NotBlank(message = "留宿原因(必填)不能为空", groups = { AddGroup.class, EditGroup.class }) + private String stayReason; + + /** + * 留宿原因类型(学习、实习、家庭困难、其他) + */ + private String stayReasonType; + + /** + * 学生联系电话 + */ + @NotBlank(message = "学生联系电话不能为空", groups = { AddGroup.class, EditGroup.class }) + private String studentPhone; + + /** + * 紧急联系人姓名 + */ + @NotBlank(message = "紧急联系人姓名不能为空", groups = { AddGroup.class, EditGroup.class }) + private String emergencyContact; + + /** + * 紧急联系人电话 + */ + @NotBlank(message = "紧急联系人电话不能为空", groups = { AddGroup.class, EditGroup.class }) + private String emergencyPhone; + + /** + * 家长同意(0否 1是) + */ + @NotBlank(message = "家长同意(0否 1是)不能为空", groups = { AddGroup.class, EditGroup.class }) + private String parentConsent; + + /** + * 附件OSS文件ID,多个用逗号分隔(申请材料、证明文件等) + */ + private String attachmentOssIds; + + /** + * 扩展字段1 + */ + private String extField1; + + /** + * 扩展字段2 + */ + private String extField2; + + /** + * 扩展字段3 + */ + private String extField3; + + /** + * 备注信息 + */ + private String remark; + + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentMsgBatchBo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentMsgBatchBo.java index 5a8a042c..e110bd17 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentMsgBatchBo.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentMsgBatchBo.java @@ -517,7 +517,7 @@ public class XgStudentMsgBatchBo extends BaseEntity { private String dormitoryRoom; /** - * 扩展字段1 + * 扩展字段1 扩展字段1 - 床位id */ private String extField1; diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentMsgBo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentMsgBo.java index 3a260d47..20eb3ff5 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentMsgBo.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentMsgBo.java @@ -517,7 +517,7 @@ public class XgStudentMsgBo extends BaseEntity { private String dormitoryRoom; /** - * 扩展字段1 + * 扩展字段1 扩展字段1 - 床位id */ private String extField1; diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/DormHygieneCheckExportVo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/DormHygieneCheckExportVo.java new file mode 100644 index 00000000..2755f48c --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/DormHygieneCheckExportVo.java @@ -0,0 +1,183 @@ +package org.dromara.xg.domain.vo; + +import lombok.Data; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +import org.dromara.xg.convert.StudentInfoExcelConvert; +import org.dromara.xg.domain.XgDormHygieneCheck; +import org.dromara.xg.domain.XgDormViolation; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 宿舍卫生检查导出 视图对象 dorm_hygiene_check_export + * + * @author Lion Li + * @date 2025-01-29 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = XgDormHygieneCheck.class) +public class DormHygieneCheckExportVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 检查标题 + */ + @ExcelProperty(value = "检查标题") + private String checkTitle; + + /** + * 检查日期 + */ + @ExcelProperty(value = "检查日期") + private String checkDate; + + /** + * 检查员姓名 + */ + @ExcelProperty(value = "检查员") + private String checkerName; + + /** + * 检查类型 + */ + @ExcelProperty(value = "检查类型") + private String checkTypeText; + + /** + * 检查级别 + */ + @ExcelProperty(value = "检查级别") + private String checkLevelText; + + /** + * 校区名称 + */ + @ExcelProperty(value = "校区") + private String campusName; + + /** + * 宿舍区名称 + */ + @ExcelProperty(value = "宿舍区") + private String areaName; + + /** + * 宿舍楼名称 + */ + @ExcelProperty(value = "宿舍楼") + private String buildingName; + + /** + * 房间名称 + */ + @ExcelProperty(value = "房间号") + private String roomName; + + /** + * 宿舍总分 + */ + @ExcelProperty(value = "宿舍总分") + private Double totalScore; + + /** + * 卫生等级 + */ + @ExcelProperty(value = "卫生等级") + private String hygieneGradeText; + + /** + * 学号 + */ + @ExcelProperty(value = "学号") + private String studentNo; + + /** + * 学生姓名 + */ + @ExcelProperty(value = "学生姓名") + private String studentName; + + /** + * 班级名称 + */ + @ExcelProperty(value = "班级") + private String className; + + /** + * 专业名称 + */ + @ExcelProperty(value = "专业") + private String majorName; + + /** + * 学院名称 + */ + @ExcelProperty(value = "学院") + private String collegeName; + + /** + * 床位编号 + */ + @ExcelProperty(value = "床位号") + private String bedCode; + + /** + * 学生个人得分 + */ + @ExcelProperty(value = "个人得分") + private Integer studentScore; + + /** + * 出勤状态 + */ + @ExcelProperty(value = "出勤状态") + private String attendanceStatusText; + + /** + * 学生评级 + */ + @ExcelProperty(value = "个人评级") + private String studentGradeText; + + /** + * 是否为寝室长 + */ + // @ExcelProperty(value = "是否寝室长") + // private String isResponsiblePersonText; + + /** + * 评分备注 + */ + // @ExcelProperty(value = "评分备注") + // private String scoreRemarks; + + /** + * 改进意见 + */ + // @ExcelProperty(value = "改进意见") + // private String improvementNotes; + + /** + * 检查总结 + */ + @ExcelProperty(value = "检查总结") + private String checkSummary; + + /** + * 整改建议 + */ + @ExcelProperty(value = "整改建议") + private String improvementSuggestions; + +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/StudentOptionVo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/StudentOptionVo.java new file mode 100644 index 00000000..f70481fa --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/StudentOptionVo.java @@ -0,0 +1,156 @@ +package org.dromara.xg.domain.vo; + +import lombok.Data; +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 学生选择器选项视图对象 + * + * @author zlwu + * @date 2025-08-15 + */ +@Data +public class StudentOptionVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 学生ID + */ + private Long id; + + /** + * 学号 + */ + private String studentNo; + + /** + * 学生姓名 + */ + private String name; + + /** + * 性别:0-男,1-女 + */ + private String gender; + + /** + * 学院ID + */ + private Long collegeId; + + /** + * 学院名称 + */ + private String collegeName; + + /** + * 专业ID + */ + private Long majorId; + + /** + * 专业名称 + */ + private String majorName; + + /** + * 班级ID + */ + private Long classId; + + /** + * 班级名称 + */ + private String className; + + /** + * 学生联系电话 + */ + private String phone; + + /** + * 宿舍信息 + */ + private DormInfo dormInfo; + + /** + * 基础字段 - 创建者 + */ + private Long createBy; + + /** + * 基础字段 - 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 基础字段 - 更新者 + */ + private Long updateBy; + + /** + * 基础字段 - 更新时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + @Data + public static class DormInfo implements Serializable { + /** + * 校区ID + */ + private Long campusId; + + /** + * 校区名称 + */ + private String campusName; + + /** + * 宿舍区ID + */ + private Long areaId; + + /** + * 宿舍区名称 + */ + private String areaName; + + /** + * 宿舍楼ID + */ + private Long buildingId; + + /** + * 宿舍楼名称 + */ + private String buildingName; + + /** + * 房间ID + */ + private Long roomId; + + /** + * 房间名称 + */ + private String roomName; + + /** + * 床位ID + */ + private Long bedId; + + /** + * 床位名称 + */ + private String bedName; + } +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgDormHygieneCheckVo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgDormHygieneCheckVo.java new file mode 100644 index 00000000..43ba5278 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgDormHygieneCheckVo.java @@ -0,0 +1,177 @@ +package org.dromara.xg.domain.vo; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import org.dromara.xg.domain.XgDormHygieneCheck; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * 宿舍卫生检查视图对象 xg_dorm_hygiene_check + * + * @author zlwu + * @date 2025-08-16 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = XgDormHygieneCheck.class) +public class XgDormHygieneCheckVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @ExcelProperty(value = "主键ID") + private Long id; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @ExcelProperty(value = "删除标志", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=代表存在,1=代表删除") + private Long deleted; + + /** + * 检查标题 + */ + @ExcelProperty(value = "检查标题") + private String checkTitle; + + /** + * 检查日期 + */ + @ExcelProperty(value = "检查日期") + private Date checkDate; + + /** + * 检查员ID,关联sys_user.id + */ + @ExcelProperty(value = "检查员ID,关联sys_user.id") + private Long checkerId; + + /** + * 检查员姓名 + */ + @ExcelProperty(value = "检查员姓名") + private String checkerName; + + /** + * 检查类型(1日常检查 2专项检查 3突击检查 4复查) + */ + @ExcelProperty(value = "检查类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "xg_dorm_hygiene_check_type") + private String checkType; + + /** + * 检查级别(1宿管员 2学院 3学校) + */ + @ExcelProperty(value = "检查级别", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "xg_dorm_hygiene_check_level") + private String checkLevel; + + /** + * 宿舍房间ID,关联xg_dorm_room.id + */ + @ExcelProperty(value = "宿舍房间ID,关联xg_dorm_room.id") + private Long roomId; + + /** + * 房间编号 + */ + @ExcelProperty(value = "房间编号") + private String roomCode; + + /** + * 房间名称 + */ + @ExcelProperty(value = "房间名称") + private String roomName; + + /** + * 宿舍楼ID,关联xg_dorm_building.id + */ + @ExcelProperty(value = "宿舍楼ID,关联xg_dorm_building.id") + private Long buildingId; + + /** + * 宿舍楼名称 + */ + @ExcelProperty(value = "宿舍楼名称") + private String buildingName; + + /** + * 宿舍区ID,关联xg_dorm_area.id + */ + @ExcelProperty(value = "宿舍区ID,关联xg_dorm_area.id") + private Long areaId; + + /** + * 宿舍区名称 + */ + @ExcelProperty(value = "宿舍区名称") + private String areaName; + + /** + * 校区ID,关联xg_dorm_campus.id + */ + @ExcelProperty(value = "校区ID,关联xg_dorm_campus.id") + private Long campusId; + + /** + * 校区名称 + */ + @ExcelProperty(value = "校区名称") + private String campusName; + + /** + * 宿舍总分(0-100分) + */ + @ExcelProperty(value = "宿舍总分", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=-100分") + private Double totalScore; + + /** + * 卫生等级(A优秀 B良好 C合格 D不合格) + */ + @ExcelProperty(value = "卫生等级", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "xg_dorm_hygiene_grade") + private String hygieneGrade; + + /** + * 附件OSS ID列表,逗号分隔 + */ + @ExcelProperty(value = "附件OSS ID列表,逗号分隔") + private String attachmentIds; + + /** + * 检查总结 + */ + @ExcelProperty(value = "检查总结") + private String checkSummary; + + /** + * 整改建议 + */ + @ExcelProperty(value = "整改建议") + private String improvementSuggestions; + + /** + * 检查备注 + */ + @ExcelProperty(value = "检查备注") + private String checkRemarks; + + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgDormHygieneStudentScoreVo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgDormHygieneStudentScoreVo.java new file mode 100644 index 00000000..48ef6e7b --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgDormHygieneStudentScoreVo.java @@ -0,0 +1,157 @@ +package org.dromara.xg.domain.vo; + +import org.dromara.xg.domain.XgDormHygieneStudentScore; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * 宿舍卫生检查学生评分视图对象 xg_dorm_hygiene_student_score + * + * @author zlwu + * @date 2025-08-16 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = XgDormHygieneStudentScore.class) +public class XgDormHygieneStudentScoreVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @ExcelProperty(value = "主键ID") + private Long id; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @ExcelProperty(value = "删除标志", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=代表存在,1=代表删除") + private Long deleted; + + /** + * 卫生检查ID,关联xg_dorm_hygiene_check.id + */ + @ExcelProperty(value = "卫生检查ID,关联xg_dorm_hygiene_check.id") + private Long hygieneCheckId; + + /** + * 学生ID,关联xg_student_msg.id + */ + @ExcelProperty(value = "学生ID,关联xg_student_msg.id") + private Long studentId; + + /** + * 学号 + */ + @ExcelProperty(value = "学号") + private String studentNo; + + /** + * 学生姓名 + */ + @ExcelProperty(value = "学生姓名") + private String studentName; + + /** + * 班级ID + */ + @ExcelProperty(value = "班级ID") + private Long classId; + + /** + * 班级名称 + */ + @ExcelProperty(value = "班级名称") + private String className; + + /** + * 专业ID + */ + @ExcelProperty(value = "专业ID") + private Long majorId; + + /** + * 专业名称 + */ + @ExcelProperty(value = "专业名称") + private String majorName; + + /** + * 学院ID + */ + @ExcelProperty(value = "学院ID") + private Long collegeId; + + /** + * 学院名称 + */ + @ExcelProperty(value = "学院名称") + private String collegeName; + + /** + * 床位ID,关联xg_dorm_bed.id + */ + @ExcelProperty(value = "床位ID,关联xg_dorm_bed.id") + private Long bedId; + + /** + * 床位编号 + */ + @ExcelProperty(value = "床位编号") + private String bedCode; + + /** + * 学生个人得分(0-100分) + */ + @ExcelProperty(value = "学生个人得分", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=-100分") + private Double studentScore; + + /** + * 是否在寝(1在寝 2外出 3请假 4其他) + */ + @ExcelProperty(value = "是否在寝", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "xg_student_attendance_status") + private String attendanceStatus; + + /** + * 学生评级(A优秀 B良好 C合格 D不合格) + */ + @ExcelProperty(value = "学生评级", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "xg_dorm_hygiene_grade") + private String studentGrade; + + /** + * 是否为寝室长(0否 1是) + */ + @ExcelProperty(value = "是否为寝室长", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_yesno") + private Long isResponsiblePerson; + + /** + * 评分备注 + */ + @ExcelProperty(value = "评分备注") + private String scoreRemarks; + + /** + * 改进意见 + */ + @ExcelProperty(value = "改进意见") + private String improvementNotes; + + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgDormViolationExportVo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgDormViolationExportVo.java new file mode 100644 index 00000000..4fa23cc1 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgDormViolationExportVo.java @@ -0,0 +1,206 @@ +package org.dromara.xg.domain.vo; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +import org.dromara.xg.convert.StudentInfoExcelConvert; +import org.dromara.xg.domain.XgDormViolation; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 宿舍违纪记录视图对象 xg_dorm_violation + * + * @author zlwu + * @date 2025-08-16 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = XgDormViolation.class) +public class XgDormViolationExportVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 违纪记录ID(主键) + */ + @ExcelProperty(value = "违纪记录ID") + private Long id; + + /** + * 学生ID + */ + // @ExcelProperty(value = "学生ID") + // private Long studentId; + + /** + * 学生姓名 + */ + @ExcelProperty(value = "学生姓名") + private String studentName; + + /** + * 学号 + */ + @ExcelProperty(value = "学号") + private String studentNo; + + /** + * 学院ID + */ + // @ExcelProperty(value = "学院ID") + // private Long collegeId; + + /** + * 学院名称 + */ + @ExcelProperty(value = "学院名称") + private String collegeName; + + /** + * 专业ID + */ + // @ExcelProperty(value = "专业ID") + // private Long majorId; + + /** + * 专业名称 + */ + @ExcelProperty(value = "专业名称") + private String majorName; + + /** + * 班级ID + */ + // @ExcelProperty(value = "班级ID") + // private Long classId; + + /** + * 班级名称 + */ + @ExcelProperty(value = "班级名称") + private String className; + + /** + * 校区ID + */ + // @ExcelProperty(value = "校区ID") + // private Long campusId; + + /** + * 校区名称 + */ + @ExcelProperty(value = "校区名称") + private String campusName; + + /** + * 宿舍区域ID + */ + // @ExcelProperty(value = "宿舍区域ID") + // private Long areaId; + + /** + * 宿舍区域名称 + */ + @ExcelProperty(value = "宿舍区域名称") + private String areaName; + + /** + * 宿舍楼ID + */ + // @ExcelProperty(value = "宿舍楼ID") + // private Long buildingId; + + /** + * 宿舍楼名称 + */ + @ExcelProperty(value = "宿舍楼名称") + private String buildingName; + + /** + * 宿舍房间ID + */ + // @ExcelProperty(value = "宿舍房间ID") + // private Long roomId; + + /** + * 房间名称 + */ + @ExcelProperty(value = "房间名称") + private String roomName; + + /** + * 床位ID + */ + // @ExcelProperty(value = "床位ID") + // private Long bedId; + + /** + * 床位名称 + */ + @ExcelProperty(value = "床位名称") + private String bedName; + + /** + * 违纪类型 + */ + @ExcelProperty(value = "违纪类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "dorm_violation_type") + private String violationType; + + /** + * 违纪等级:1轻微 2一般 3严重 4特别严重 + */ + @ExcelProperty(value = "违纪等级", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "dorm_violation_level") + private String violationLevel; + + /** + * 违纪发生时间 + */ + @ExcelProperty(value = "违纪发生时间") + private Date violationTime; + + /** + * 违纪行为描述 + */ + @ExcelProperty(value = "违纪行为描述") + private String violationDescription; + + /** + * 涉及其他学生id(逗号分隔) + */ + @ExcelProperty(value = "涉及其他学生id", converter = StudentInfoExcelConvert.class) + // @ExcelDictFormat(readConverterExp = "逗=号分隔") + private String involvedStudents; + + /** + * 附件OSS ID列表(逗号分隔) + */ + // @ExcelProperty(value = "附件OSS ID列表", converter = ExcelDictConvert.class) + // @ExcelDictFormat(readConverterExp = "逗=号分隔") + // private String attachmentOssIds; + + /** + * 删除标志(0代表存在 1代表删除) + */ + // @ExcelProperty(value = "删除标志", converter = ExcelDictConvert.class) + // @ExcelDictFormat(readConverterExp = "0=代表存在,1=代表删除") + // private String deleted; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgDormViolationImportVo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgDormViolationImportVo.java new file mode 100644 index 00000000..41453ec4 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgDormViolationImportVo.java @@ -0,0 +1,96 @@ +package org.dromara.xg.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 宿舍违纪记录导入视图对象 xg_dorm_violation_import + * + * @author zlwu + * @date 2025-01-31 + */ +@Data +@ExcelIgnoreUnannotated +public class XgDormViolationImportVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 学生姓名 + */ + @NotBlank(message = "学生姓名不能为空") + @ExcelProperty(value = "学生姓名") + private String studentName; + + /** + * 学号 + */ + @NotBlank(message = "学号不能为空") + @ExcelProperty(value = "学号") + private String studentNo; + + /** + * 学院名称 + */ + @ExcelProperty(value = "学院名称") + private String collegeName; + + /** + * 专业名称 + */ + @ExcelProperty(value = "专业名称") + private String majorName; + + /** + * 班级名称 + */ + @ExcelProperty(value = "班级名称") + private String className; + + /** + * 违纪类型 + */ + @NotBlank(message = "违纪类型不能为空") + @ExcelProperty(value = "违纪类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "dorm_violation_type") + private String violationType; + + /** + * 违纪等级 + */ + @NotBlank(message = "违纪等级不能为空") + @ExcelProperty(value = "违纪等级", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "dorm_violation_level") + private String violationLevel; + + /** + * 违纪发生时间 + */ + @NotNull(message = "违纪发生时间不能为空") + @ExcelProperty(value = "违纪发生时间") + private Date violationTime; + + /** + * 违纪行为描述 + */ + @NotBlank(message = "违纪行为描述不能为空") + @ExcelProperty(value = "违纪行为描述") + private String violationDescription; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgDormViolationVo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgDormViolationVo.java new file mode 100644 index 00000000..dfb9469e --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgDormViolationVo.java @@ -0,0 +1,205 @@ +package org.dromara.xg.domain.vo; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import org.dromara.xg.domain.XgDormViolation; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 宿舍违纪记录视图对象 xg_dorm_violation + * + * @author zlwu + * @date 2025-08-16 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = XgDormViolation.class) +public class XgDormViolationVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 违纪记录ID(主键) + */ + @ExcelProperty(value = "违纪记录ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "主=键") + private Long id; + + /** + * 学生ID + */ + @ExcelProperty(value = "学生ID") + private Long studentId; + + /** + * 学生姓名 + */ + @ExcelProperty(value = "学生姓名") + private String studentName; + + /** + * 学号 + */ + @ExcelProperty(value = "学号") + private String studentNo; + + /** + * 学院ID + */ + @ExcelProperty(value = "学院ID") + private Long collegeId; + + /** + * 学院名称 + */ + @ExcelProperty(value = "学院名称") + private String collegeName; + + /** + * 专业ID + */ + @ExcelProperty(value = "专业ID") + private Long majorId; + + /** + * 专业名称 + */ + @ExcelProperty(value = "专业名称") + private String majorName; + + /** + * 班级ID + */ + @ExcelProperty(value = "班级ID") + private Long classId; + + /** + * 班级名称 + */ + @ExcelProperty(value = "班级名称") + private String className; + + /** + * 校区ID + */ + @ExcelProperty(value = "校区ID") + private Long campusId; + + /** + * 校区名称 + */ + @ExcelProperty(value = "校区名称") + private String campusName; + + /** + * 宿舍区域ID + */ + @ExcelProperty(value = "宿舍区域ID") + private Long areaId; + + /** + * 宿舍区域名称 + */ + @ExcelProperty(value = "宿舍区域名称") + private String areaName; + + /** + * 宿舍楼ID + */ + @ExcelProperty(value = "宿舍楼ID") + private Long buildingId; + + /** + * 宿舍楼名称 + */ + @ExcelProperty(value = "宿舍楼名称") + private String buildingName; + + /** + * 宿舍房间ID + */ + @ExcelProperty(value = "宿舍房间ID") + private Long roomId; + + /** + * 房间名称 + */ + @ExcelProperty(value = "房间名称") + private String roomName; + + /** + * 床位ID + */ + @ExcelProperty(value = "床位ID") + private Long bedId; + + /** + * 床位名称 + */ + @ExcelProperty(value = "床位名称") + private String bedName; + + /** + * 违纪类型 + */ + @ExcelProperty(value = "违纪类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "dorm_violation_type") + private String violationType; + + /** + * 违纪等级:1轻微 2一般 3严重 4特别严重 + */ + @ExcelProperty(value = "违纪等级:1轻微 2一般 3严重 4特别严重", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "dorm_violation_level") + private String violationLevel; + + /** + * 违纪发生时间 + */ + @ExcelProperty(value = "违纪发生时间") + private Date violationTime; + + /** + * 违纪行为描述 + */ + @ExcelProperty(value = "违纪行为描述") + private String violationDescription; + + /** + * 涉及其他学生id(逗号分隔) + */ + @ExcelProperty(value = "涉及其他学生id", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逗=号分隔") + private String involvedStudents; + + /** + * 附件OSS ID列表(逗号分隔) + */ + @ExcelProperty(value = "附件OSS ID列表", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逗=号分隔") + private String attachmentOssIds; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @ExcelProperty(value = "删除标志", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=代表存在,1=代表删除") + private String deleted; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgHolidayStayVo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgHolidayStayVo.java new file mode 100644 index 00000000..c7887d40 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgHolidayStayVo.java @@ -0,0 +1,250 @@ +package org.dromara.xg.domain.vo; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import org.dromara.xg.domain.XgHolidayStay; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * 假期留宿管理视图对象 xg_holiday_stay + * + * @author zlwu + * @date 2025-08-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = XgHolidayStay.class) +public class XgHolidayStayVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @ExcelProperty(value = "主键ID") + private Long id; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @ExcelProperty(value = "删除标志", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=代表存在,1=代表删除") + private Long deleted; + + /** + * 学生ID,关联xg_student_msg.id + */ + @ExcelProperty(value = "学生ID,关联xg_student_msg.id") + private Long studentId; + + /** + * 学生姓名 + */ + @ExcelProperty(value = "学生姓名") + private String studentName; + + /** + * 学号 + */ + @ExcelProperty(value = "学号") + private String studentNo; + + /** + * 班级ID + */ + @ExcelProperty(value = "班级ID") + private Long classId; + + /** + * 班级名称 + */ + @ExcelProperty(value = "班级名称") + private String className; + + /** + * 专业ID + */ + @ExcelProperty(value = "专业ID") + private Long majorId; + + /** + * 专业名称 + */ + @ExcelProperty(value = "专业名称") + private String majorName; + + /** + * 学院ID + */ + @ExcelProperty(value = "学院ID") + private Long collegeId; + + /** + * 学院名称 + */ + @ExcelProperty(value = "学院名称") + private String collegeName; + + /** + * 校区ID,关联xg_dorm_campus.id + */ + @ExcelProperty(value = "校区ID,关联xg_dorm_campus.id") + private Long campusId; + + /** + * 校区名称 + */ + @ExcelProperty(value = "校区名称") + private String campusName; + + /** + * 宿舍区ID,关联xg_dorm_area.id + */ + @ExcelProperty(value = "宿舍区ID,关联xg_dorm_area.id") + private Long areaId; + + /** + * 宿舍区名称 + */ + @ExcelProperty(value = "宿舍区名称") + private String areaName; + + /** + * 宿舍楼ID,关联xg_dorm_building.id + */ + @ExcelProperty(value = "宿舍楼ID,关联xg_dorm_building.id") + private Long buildingId; + + /** + * 宿舍楼名称 + */ + @ExcelProperty(value = "宿舍楼名称") + private String buildingName; + + /** + * 房间ID,关联xg_dorm_room.id + */ + @ExcelProperty(value = "房间ID,关联xg_dorm_room.id") + private Long roomId; + + /** + * 房间名称 + */ + @ExcelProperty(value = "房间名称") + private String roomName; + + /** + * 床位ID,关联xg_dorm_bed.id + */ + @ExcelProperty(value = "床位ID,关联xg_dorm_bed.id") + private Long bedId; + + /** + * 床位名称 + */ + @ExcelProperty(value = "床位名称") + private String bedName; + + /** + * 假期类型(寒假、暑假、春节假期、国庆假期等) + */ + @ExcelProperty(value = "假期类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "xg_holiday_type") + private String holidayType; + + /** + * 申请留宿开始日期 + */ + @ExcelProperty(value = "申请留宿开始日期") + private Date applyStartDate; + + /** + * 申请留宿结束日期 + */ + @ExcelProperty(value = "申请留宿结束日期") + private Date applyEndDate; + + /** + * 留宿原因(必填) + */ + @ExcelProperty(value = "留宿原因", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "必=填") + private String stayReason; + + /** + * 留宿原因类型(学习、实习、家庭困难、其他) + */ + @ExcelProperty(value = "留宿原因类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "xg_stay_reason_type") + private String stayReasonType; + + /** + * 学生联系电话 + */ + @ExcelProperty(value = "学生联系电话") + private String studentPhone; + + /** + * 紧急联系人姓名 + */ + @ExcelProperty(value = "紧急联系人姓名") + private String emergencyContact; + + /** + * 紧急联系人电话 + */ + @ExcelProperty(value = "紧急联系人电话") + private String emergencyPhone; + + /** + * 家长同意(0否 1是) + */ + @ExcelProperty(value = "家长同意", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_yesno") + private String parentConsent; + + /** + * 附件OSS文件ID,多个用逗号分隔(申请材料、证明文件等) + */ + @ExcelProperty(value = "附件OSS文件ID,多个用逗号分隔", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "申=请材料、证明文件等") + private String attachmentOssIds; + + /** + * 扩展字段1 + */ + @ExcelProperty(value = "扩展字段1") + private String extField1; + + /** + * 扩展字段2 + */ + @ExcelProperty(value = "扩展字段2") + private String extField2; + + /** + * 扩展字段3 + */ + @ExcelProperty(value = "扩展字段3") + private String extField3; + + /** + * 备注信息 + */ + @ExcelProperty(value = "备注信息") + private String remark; + + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentMsgBatchVo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentMsgBatchVo.java index 528d3273..83c5c17c 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentMsgBatchVo.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentMsgBatchVo.java @@ -623,7 +623,7 @@ public class XgStudentMsgBatchVo implements Serializable { private String dormitoryRoom; /** - * 扩展字段1 + * 扩展字段1 扩展字段1 - 床位id */ @ExcelProperty(value = "扩展字段1") private String extField1; diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentMsgVo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentMsgVo.java index 7ec84a4c..57f92e09 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentMsgVo.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentMsgVo.java @@ -623,7 +623,7 @@ public class XgStudentMsgVo implements Serializable { private String dormitoryRoom; /** - * 扩展字段1 + * 扩展字段1 扩展字段1 - 床位id */ @ExcelProperty(value = "扩展字段1") private String extField1; diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedMapper.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedMapper.java index f26684ca..382c3b6b 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedMapper.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedMapper.java @@ -129,4 +129,122 @@ public interface XgDormBedMapper extends BaseMapperPlus """) List selectBedDetailsByBuildingId(@Param("buildingId") Long buildingId); + /** + * 查询指定房间中属于指定学院的床位详细信息列表(包含学生信息) + * + * @param roomId 房间ID + * @param collegeId 学院ID + * @return 床位详细信息列表 + */ + @Select(""" + SELECT + b.id, + b.bed_code, + b.bed_name, + b.room_id, + r.room_name, + b.bed_type, + b.bed_status, + b.remark as bed_remark, + b.create_by, + b.create_time, + b.update_by, + b.update_time, + + bs.id as assignment_id, + bs.assign_time, + bs.status as assignment_status, + + bs.college_id, + college_dept.dept_name as college_name, + + bs.major_id, + major_dept.dept_name as major_name, + + bs.class_id, + class_dept.dept_name as class_name, + c.class_code, + c.grade, + + bs.student_id, + s.student_no, + s.name as student_name, + s.gender as student_gender, + s.phone as student_phone, + s.id_card as student_id_card + FROM xg_dorm_bed b + LEFT JOIN xg_dorm_room r ON b.room_id = r.id + INNER JOIN xg_dorm_bed_student bs ON b.id = bs.bed_id AND bs.status = '0' AND bs.deleted = '0' + LEFT JOIN sys_dept college_dept ON bs.college_id = college_dept.dept_id AND college_dept.del_flag = '0' + LEFT JOIN sys_dept major_dept ON bs.major_id = major_dept.dept_id AND major_dept.del_flag = '0' + LEFT JOIN sys_dept class_dept ON bs.class_id = class_dept.dept_id AND class_dept.del_flag = '0' + LEFT JOIN xg_class_msg c ON bs.class_id = c.id AND c.deleted = 0 + LEFT JOIN xg_student_msg s ON bs.student_id = s.id AND s.deleted = 0 + WHERE b.room_id = #{roomId} + AND bs.college_id = #{collegeId} + AND b.deleted = '0' + ORDER BY b.bed_code + """) + List selectBedDetailsByRoomIdAndCollegeId(@Param("roomId") Long roomId, + @Param("collegeId") Long collegeId); + + /** + * 查询指定房间中属于指定班级的床位详细信息列表(包含学生信息) + * + * @param roomId 房间ID + * @param classId 班级ID + * @return 床位详细信息列表 + */ + @Select(""" + SELECT + b.id, + b.bed_code, + b.bed_name, + b.room_id, + r.room_name, + b.bed_type, + b.bed_status, + b.remark as bed_remark, + b.create_by, + b.create_time, + b.update_by, + b.update_time, + + bs.id as assignment_id, + bs.assign_time, + bs.status as assignment_status, + + bs.college_id, + college_dept.dept_name as college_name, + + bs.major_id, + major_dept.dept_name as major_name, + + bs.class_id, + class_dept.dept_name as class_name, + c.class_code, + c.grade, + + bs.student_id, + s.student_no, + s.name as student_name, + s.gender as student_gender, + s.phone as student_phone, + s.id_card as student_id_card + FROM xg_dorm_bed b + LEFT JOIN xg_dorm_room r ON b.room_id = r.id + INNER JOIN xg_dorm_bed_student bs ON b.id = bs.bed_id AND bs.status = '0' AND bs.deleted = '0' + LEFT JOIN sys_dept college_dept ON bs.college_id = college_dept.dept_id AND college_dept.del_flag = '0' + LEFT JOIN sys_dept major_dept ON bs.major_id = major_dept.dept_id AND major_dept.del_flag = '0' + LEFT JOIN sys_dept class_dept ON bs.class_id = class_dept.dept_id AND class_dept.del_flag = '0' + LEFT JOIN xg_class_msg c ON bs.class_id = c.id AND c.deleted = 0 + LEFT JOIN xg_student_msg s ON bs.student_id = s.id AND s.deleted = 0 + WHERE b.room_id = #{roomId} + AND bs.class_id = #{classId} + AND b.deleted = '0' + ORDER BY b.bed_code + """) + List selectBedDetailsByRoomIdAndClassId(@Param("roomId") Long roomId, + @Param("classId") Long classId); + } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedStudentMapper.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedStudentMapper.java index 3ab6e5b4..7bd4d31d 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedStudentMapper.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedStudentMapper.java @@ -3,6 +3,11 @@ package org.dromara.xg.mapper; import org.dromara.xg.domain.XgDormBedStudent; import org.dromara.xg.domain.vo.XgDormBedStudentVo; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; +import java.util.Map; /** * 床位学生分配关系Mapper接口 @@ -12,4 +17,175 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; */ public interface XgDormBedStudentMapper extends BaseMapperPlus { + /** + * 查询班级已入住床位详情(通过SQL关联查询,按楼栋、房间、床位排序) + * + * @param classId 班级ID + * @return 班级已入住床位详情列表 + */ + @Select(""" + SELECT + bs.bed_id, + bs.student_id, + bs.class_id, + bs.college_id, + + -- 床位信息 + b.bed_name, + b.bed_type, + b.bed_code, + + -- 房间信息 + r.id as room_id, + r.room_name, + r.room_code, + + -- 楼栋信息 + building.id as building_id, + building.building_name, + building.building_code, + building.building_type, + + -- 宿舍区信息 + area.id as area_id, + area.area_name, + + -- 校区信息 + campus.id as campus_id, + campus.campus_name, + + -- 学生信息 + s.student_no, + s.name as student_name, + s.gender, + s.phone, + s.id_card + + FROM xg_dorm_bed_student bs + INNER JOIN xg_dorm_bed b ON bs.bed_id = b.id AND b.deleted = '0' + INNER JOIN xg_dorm_room r ON b.room_id = r.id AND r.deleted = '0' + INNER JOIN xg_dorm_building building ON r.building_id = building.id AND building.deleted = '0' + INNER JOIN xg_dorm_area area ON building.area_id = area.id AND area.deleted = '0' + INNER JOIN xg_dorm_campus campus ON area.campus_id = campus.id AND campus.deleted = '0' + INNER JOIN xg_student_msg s ON bs.student_id = s.id AND s.deleted = 0 + + WHERE bs.class_id = #{classId} + AND bs.student_id IS NOT NULL + AND bs.status = '0' + AND bs.deleted = '0' + AND s.student_status = '1' + + ORDER BY + campus.campus_name, + area.area_name, + building.building_code, + r.room_code, + b.bed_code + """) + List> selectClassOccupiedBedDetails(@Param("classId") Long classId); + + /** + * 查询班级未分配学生床位详情(通过SQL关联查询,按楼栋、房间、床位排序) + * + * @param classId 班级ID + * @return 班级未分配学生床位详情列表 + */ + @Select(""" + SELECT + bs.bed_id, + bs.class_id, + bs.college_id, + + -- 床位信息 + b.bed_name, + b.bed_type, + b.bed_code, + + -- 房间信息 + r.id as room_id, + r.room_name, + r.room_code, + + -- 楼栋信息 + building.id as building_id, + building.building_name, + building.building_code, + building.building_type, + + -- 宿舍区信息 + area.id as area_id, + area.area_name, + + -- 校区信息 + campus.id as campus_id, + campus.campus_name + + FROM xg_dorm_bed_student bs + INNER JOIN xg_dorm_bed b ON bs.bed_id = b.id AND b.deleted = '0' AND b.bed_status = '1' + INNER JOIN xg_dorm_room r ON b.room_id = r.id AND r.deleted = '0' + INNER JOIN xg_dorm_building building ON r.building_id = building.id AND building.deleted = '0' + INNER JOIN xg_dorm_area area ON building.area_id = area.id AND area.deleted = '0' + INNER JOIN xg_dorm_campus campus ON area.campus_id = campus.id AND campus.deleted = '0' + + WHERE bs.class_id = #{classId} + AND bs.student_id IS NULL + AND bs.status = '0' + AND bs.deleted = '0' + + ORDER BY + campus.campus_name, + area.area_name, + building.building_code, + r.room_code, + b.bed_code + """) + List> selectClassUnoccupiedBedDetails(@Param("classId") Long classId); + + /** + * 根据房间ID查询住在当前房间的所有学生信息 + * 包含学生姓名、学号、性别、学院名称、专业名称、班级名称、床位编码、床位ID + * + * @param roomId 房间ID + * @return 房间学生信息列表 + */ + @Select(""" + SELECT + bs.student_id, + bs.bed_id, + + -- 学生基本信息 + s.student_no, + s.name as student_name, + s.gender, + + -- 床位信息 + b.bed_name, + b.bed_code, + + -- 班级信息 + c.class_name, + + -- 学院信息 + d.dept_name as college_name, + + -- 专业信息(通过班级关联) + major.dept_name as major_name + + FROM xg_dorm_bed_student bs + INNER JOIN xg_dorm_bed b ON bs.bed_id = b.id AND b.deleted = '0' + INNER JOIN xg_student_msg s ON bs.student_id = s.id AND s.deleted = 0 + LEFT JOIN xg_class_msg c ON s.class_id = c.id AND c.deleted = 0 + LEFT JOIN sys_dept d ON c.college_id = d.dept_id AND d.del_flag = '0' + LEFT JOIN sys_dept major ON c.major_id = major.dept_id AND major.del_flag = '0' + + WHERE b.room_id = #{roomId} + AND bs.student_id IS NOT NULL + AND bs.status = '0' + AND bs.deleted = '0' + AND s.student_status = '1' + + ORDER BY b.bed_code, s.student_no + """) + List> selectRoomStudents(@Param("roomId") Long roomId); + } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormHygieneCheckMapper.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormHygieneCheckMapper.java new file mode 100644 index 00000000..9a86affd --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormHygieneCheckMapper.java @@ -0,0 +1,26 @@ +package org.dromara.xg.mapper; + +import org.dromara.xg.domain.XgDormHygieneCheck; +import org.dromara.xg.domain.bo.XgDormHygieneCheckBo; +import org.dromara.xg.domain.vo.DormHygieneCheckExportVo; +import org.dromara.xg.domain.vo.XgDormHygieneCheckVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import java.util.List; + +/** + * 宿舍卫生检查Mapper接口 + * + * @author zlwu + * @date 2025-08-16 + */ +public interface XgDormHygieneCheckMapper extends BaseMapperPlus { + + /** + * 查询宿舍卫生检查导出列表(包含学生卫生情况) + * + * @param bo 查询条件 + * @return 导出数据列表 + */ + List selectExportList(XgDormHygieneCheckBo bo); + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormHygieneStudentScoreMapper.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormHygieneStudentScoreMapper.java new file mode 100644 index 00000000..d8650481 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormHygieneStudentScoreMapper.java @@ -0,0 +1,15 @@ +package org.dromara.xg.mapper; + +import org.dromara.xg.domain.XgDormHygieneStudentScore; +import org.dromara.xg.domain.vo.XgDormHygieneStudentScoreVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 宿舍卫生检查学生评分Mapper接口 + * + * @author zlwu + * @date 2025-08-16 + */ +public interface XgDormHygieneStudentScoreMapper extends BaseMapperPlus { + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormViolationMapper.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormViolationMapper.java new file mode 100644 index 00000000..b175928d --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormViolationMapper.java @@ -0,0 +1,44 @@ +package org.dromara.xg.mapper; + +import org.dromara.xg.domain.XgDormViolation; +import org.dromara.xg.domain.vo.XgDormViolationVo; +import org.dromara.xg.domain.vo.StudentOptionVo; +import org.dromara.xg.domain.bo.StudentOptionBo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; + +/** + * 宿舍违纪记录Mapper接口 + * + * @author zlwu + * @date 2025-08-16 + */ +public interface XgDormViolationMapper extends BaseMapperPlus { + + /** + * 查询学生选择器选项列表(支持按学号、姓名检索) + * + * @param page 分页参数 + * @param bo 查询条件 + * @return 学生选项列表 + */ + Page selectStudentOptions(IPage page, @Param("bo") StudentOptionBo bo); + + /** + * 根据学生ID获取学生详细信息 + * + * @param studentId 学生ID + * @return 学生详细信息 + */ + StudentOptionVo selectStudentById(@Param("studentId") Long studentId); + + /** + * 根据学号获取学生详细信息 + * + * @param studentNo 学号 + * @return 学生详细信息 + */ + StudentOptionVo selectStudentByStudentNo(@Param("studentNo") String studentNo); +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgHolidayStayMapper.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgHolidayStayMapper.java new file mode 100644 index 00000000..0ac190b8 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgHolidayStayMapper.java @@ -0,0 +1,37 @@ +package org.dromara.xg.mapper; + +import org.dromara.xg.domain.XgHolidayStay; +import org.dromara.xg.domain.vo.XgHolidayStayVo; +import org.dromara.xg.domain.vo.StudentOptionVo; +import org.dromara.xg.domain.bo.StudentOptionBo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; + +/** + * 假期留宿管理Mapper接口 + * + * @author zlwu + * @date 2025-08-15 + */ +public interface XgHolidayStayMapper extends BaseMapperPlus { + + /** + * 查询学生选择器选项列表(支持按学号、姓名检索) + * + * @param page 分页参数 + * @param bo 查询条件 + * @return 学生选项列表 + */ + Page selectStudentOptions(IPage page, @Param("bo") StudentOptionBo bo); + + /** + * 测试查询:简化版本,只查询学生基本信息 + * + * @param page 分页参数 + * @param bo 查询条件 + * @return 学生选项列表 + */ + Page selectStudentOptionsSimple(IPage page, @Param("bo") StudentOptionBo bo); +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgStudentMsgMapper.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgStudentMsgMapper.java index b2bb03e4..5e3bfc8f 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgStudentMsgMapper.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgStudentMsgMapper.java @@ -19,71 +19,87 @@ import java.util.Map; */ public interface XgStudentMsgMapper extends BaseMapperPlus { - /** - * 根据组织ID列表分页查询学生信息 用于导出 - * - * @param page 分页参数 - * @param queryWrapper 查询条件 - * @param deptIds 组织ID列表 - * @return 学生信息分页结果 - */ - List queryPageListExportByDeptId( - @Param("ew") LambdaQueryWrapper queryWrapper, - @Param("deptIds") List deptIds); + /** + * 根据组织ID列表分页查询学生信息 用于导出 + * + * @param page 分页参数 + * @param queryWrapper 查询条件 + * @param deptIds 组织ID列表 + * @return 学生信息分页结果 + */ + List queryPageListExportByDeptId( + @Param("ew") LambdaQueryWrapper queryWrapper, + @Param("deptIds") List deptIds); - /** - * 根据组织ID列表分页查询学生信息 - * - * @param page 分页参数 - * @param queryWrapper 查询条件 - * @param deptIds 组织ID列表 - * @return 学生信息分页结果 - */ - Page selectPageByDeptIds(@Param("page") Page page, - @Param("ew") LambdaQueryWrapper queryWrapper, - @Param("deptIds") List deptIds); + /** + * 根据组织ID列表分页查询学生信息 + * + * @param page 分页参数 + * @param queryWrapper 查询条件 + * @param deptIds 组织ID列表 + * @return 学生信息分页结果 + */ + Page selectPageByDeptIds(@Param("page") Page page, + @Param("ew") LambdaQueryWrapper queryWrapper, + @Param("deptIds") List deptIds); - /** - * 根据班级ID和姓名查询学生信息列表 - * - * @param classId 班级ID - * @param name 学生姓名(模糊查询) - * @return 学生信息列表 - */ - List selectStudentsByClassId(@Param("classId") String classId, @Param("name") String name); + /** + * 根据班级ID和姓名查询学生信息列表 + * + * @param classId 班级ID + * @param name 学生姓名(模糊查询) + * @return 学生信息列表 + */ + List selectStudentsByClassId(@Param("classId") String classId, @Param("name") String name); - /** - * 根据学生ID查询班主任ID - * - * @param studentId 学生ID - * @return 班主任ID - */ - Long selectClassTeacherIdByStudentId(@Param("studentId") Long studentId); + /** + * 根据学生ID查询班主任ID + * + * @param studentId 学生ID + * @return 班主任ID + */ + Long selectClassTeacherIdByStudentId(@Param("studentId") Long studentId); - /** - * 根据学生ID查询学院ID - * - * @param studentId 学生ID - * @return 学院ID - */ - Long selectCollegeIdByStudentId(@Param("studentId") Long studentId); + /** + * 根据学生ID查询学院ID + * + * @param studentId 学生ID + * @return 学院ID + */ + Long selectCollegeIdByStudentId(@Param("studentId") Long studentId); - /** - * 根据学院ID和角色Key查询具备该角色的用户列表 - * - * @param collegeId 学院ID - * @param roleKey 角色Key - * @return 用户ID列表 - */ - List selectUserIdsByCollegeIdAndRoleKey(@Param("collegeId") Long collegeId, - @Param("roleKey") String roleKey); + /** + * 根据学院ID和角色Key查询具备该角色的用户列表 + * + * @param collegeId 学院ID + * @param roleKey 角色Key + * @return 用户ID列表 + */ + List selectUserIdsByCollegeIdAndRoleKey(@Param("collegeId") Long collegeId, + @Param("roleKey") String roleKey); - /** - * 查询二级学院下的学院、专业、班级学生统计信息 - * - * @param secondaryCollegeId 二级学院ID - * @return 学生统计信息列表 - */ - List> getCollegeStudentStats(@Param("secondaryCollegeId") Long secondaryCollegeId); + /** + * 查询二级学院下的学院、专业、班级学生统计信息 + * + * @param secondaryCollegeId 二级学院ID + * @return 学生统计信息列表 + */ + List> getCollegeStudentStats(@Param("secondaryCollegeId") Long secondaryCollegeId); + + /** + * 查询二级学院下的学院、专业、班级学生统计信息(按床位分配统计) + * + * @param secondaryCollegeId 二级学院ID + * @return 学生统计信息列表 + */ + List> getCollegeStudentStatsByClass(@Param("secondaryCollegeId") Long secondaryCollegeId); + + /** + * 查询二级学院下的学院床位分配统计信息(基于college_id统计) + * + * @param secondaryCollegeId 二级学院ID + * @return 学院床位分配统计信息列表 + */ + List> getCollegeBedAssignmentStats(@Param("secondaryCollegeId") Long secondaryCollegeId); } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormBedService.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormBedService.java index 9794dc1f..b3e41219 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormBedService.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormBedService.java @@ -75,6 +75,14 @@ public interface IXgDormBedService { */ List queryBedDetailsByRoomId(Long roomId); + /** + * 查询当前用户所在学院的指定房间床位详细信息列表(包含学生信息) + * + * @param roomId 房间ID + * @return 床位详细信息列表 + */ + List queryBedDetailsByRoomIdForCurrentUserCollege(Long roomId); + /** * 查询指定宿舍楼的床位详细信息列表(包含学生信息) * @@ -82,4 +90,13 @@ public interface IXgDormBedService { * @return 床位详细信息列表 */ List queryBedDetailsByBuildingId(Long buildingId); + + /** + * 查询指定房间中属于指定班级的床位详细信息列表(包含学生信息) + * + * @param roomId 房间ID + * @param classId 班级ID + * @return 床位详细信息列表 + */ + List queryBedDetailsByRoomIdAndClassId(Long roomId, Long classId); } 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 83aa1784..bc972308 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 @@ -3,12 +3,17 @@ package org.dromara.xg.service; import org.dromara.xg.domain.vo.XgDormBedStudentVo; import org.dromara.xg.domain.bo.XgDormBedStudentBo; import org.dromara.xg.domain.bo.XgDormBedBatchAllocateBo; +import org.dromara.xg.domain.bo.XgDormBedBatchAllocateToClassBo; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.PageQuery; import java.util.Collection; import java.util.List; +import org.dromara.xg.domain.bo.XgDormBedBatchRecallBo; +import org.dromara.xg.domain.vo.DormBuildingTreeVo; +import org.dromara.xg.domain.bo.XgDormSelectiveAllocationBo; + /** * 床位学生分配关系Service接口 * @@ -75,4 +80,166 @@ public interface IXgDormBedStudentService { */ Boolean batchAllocateBedsToCollege( XgDormBedBatchAllocateBo bo); + + /** + * 批量分配床位给班级 + * + * @param bo 批量分配给班级请求参数 + * @return 是否分配成功 + */ + Boolean batchAllocateBedsToClass(XgDormBedBatchAllocateToClassBo bo); + + /** + * 批量收回宿舍床位 + * + * @param bo 批量收回请求参数 + * @return 是否成功 + */ + Boolean batchRecallBeds(XgDormBedBatchRecallBo bo); + + /** + * 批量回收班级分配床位(保留学院分配) + * + * @param bo 批量回收请求参数 + * @return 是否成功 + */ + Boolean batchRecallClassAllocation(XgDormBedBatchRecallBo bo); + + /** + * 获取当前用户二级学院的宿舍楼树形结构 + * 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择 + * + * @return 宿舍楼树形结构数据 + */ + List getCurrentUserCollegeDormBuildingTree(); + + /** + * 获取班主任管理的班级宿舍楼树形结构 + * 只显示班主任管理班级分配到的宿舍楼 + * + * @param classId 班级ID,可为空;为空时取当前用户管理的首个班级 + * @return 宿舍楼树形结构数据 + */ + List getClassTeacherDormTree(Long classId); + + /** + * 获取班主任管理班级的房间床位详情(带学生信息) + * + * @param buildingId 宿舍楼ID,为空则查询所有楼栋 + * @param classId 班级ID,可为空 + * @return 房间床位详情列表 + */ + List getClassTeacherBedDetails(Long buildingId, Long classId); + + /** + * 获取班主任管理的未分配床位学生列表 + * + * @param classId 班级ID,可为空 + * @return 未分配床位的学生列表 + */ + List getClassTeacherUnallocatedStudents(Long classId); + + /** + * 班主任一键随机分配床位 + * + * @param buildingId 宿舍楼ID,为空则全部分配 + * @param classId 班级ID,可为空 + * @return 是否分配成功 + */ + Boolean classTeacherRandomAllocation(Long buildingId, Long classId); + + /** + * 班主任一键按学号分配床位 + * + * @param buildingId 宿舍楼ID,为空则全部分配 + * @param classId 班级ID,可为空 + * @return 是否分配成功 + */ + Boolean classTeacherStudentNoAllocation(Long buildingId, Long classId); + + /** + * 班主任选择性分配床位(弹窗模式) + * + * @param allocationParams 分配参数(含班级ID等) + * @param classId 班级ID,可为空 + * @return 是否分配成功 + */ + Boolean classTeacherSelectiveAllocation(XgDormSelectiveAllocationBo allocationParams, Long classId); + + /** + * 班主任一键收回床位 + * + * @param buildingId 宿舍楼ID,为空则全部收回 + * @param classId 班级ID,可为空 + * @return 是否收回成功 + */ + Boolean classTeacherRecallBeds(Long buildingId, Long classId); + + /** + * 班主任收回选中床位 + * + * @param bedIds 床位ID列表 + * @param classId 班级ID + * @return 是否收回成功 + */ + Boolean classTeacherRecallSelectedBeds(List bedIds, Long classId); + + /** + * 班主任批量退宿 + * + * @param bedIds 床位ID列表 + * @param classId 班级ID + * @return 是否退宿成功 + */ + Boolean classTeacherStayawaySelectedBeds(List bedIds, Long classId); + + /** + * 班主任床位互换 + * 将两个学生的床位进行互换 + * + * @param sourceStudentId 源学生ID + * @param targetStudentId 目标学生ID + * @param classId 班级ID + * @return 是否互换成功 + */ + Boolean classTeacherExchangeBeds(Long sourceStudentId, Long targetStudentId, Long classId); + + /** + * 班主任床位移动 + * 将学生从当前床位移动到指定的空闲床位 + * + * @param sourceBedId 源床位ID + * @param targetBedId 目标床位ID + * @param classId 班级ID + * @return 是否移动成功 + */ + Boolean classTeacherMoveBed(Long sourceStudentId, Long targetBedId, Long classId); + + /** + * 获取班级所有已入住宿舍的学生床位信息 + * 包含床位信息(校区名称、宿舍区名称、楼栋名称、房间名称、床位名称) + * 和学生信息(学生姓名、学号、性别) + * + * @param classId 班级ID,可为空 + * @return 已入住床位详情列表 + */ + List getClassOccupiedBedDetails(Long classId); + + /** + * 获取班级所有未安排学生的床位信息 + * 包含床位信息(校区名称、宿舍区名称、楼栋名称、房间名称、床位名称) + * + * @param classId 班级ID,可为空 + * @return 未安排学生的床位详情列表 + */ + List getClassUnoccupiedBedDetails(Long classId); + + /** + * 根据房间ID获取住在当前房间的所有学生信息 + * 包含学生姓名、学号、性别、学院名称、专业名称、班级名称、床位编码、床位ID + * + * @param roomId 房间ID + * @return 房间学生信息列表 + */ + List getRoomStudents(Long roomId); } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormHygieneCheckService.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormHygieneCheckService.java new file mode 100644 index 00000000..d3f74d45 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormHygieneCheckService.java @@ -0,0 +1,103 @@ +package org.dromara.xg.service; + +import org.dromara.xg.domain.vo.DormHygieneCheckExportVo; +import org.dromara.xg.domain.vo.XgDormHygieneCheckVo; +import org.dromara.xg.domain.bo.XgDormHygieneCheckBo; +import org.dromara.xg.domain.bo.XgDormHygieneCheckCompleteBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 宿舍卫生检查Service接口 + * + * @author zlwu + * @date 2025-08-16 + */ +public interface IXgDormHygieneCheckService { + + /** + * 查询宿舍卫生检查 + * + * @param id 主键 + * @return 宿舍卫生检查 + */ + XgDormHygieneCheckVo queryById(Long id); + + /** + * 分页查询宿舍卫生检查列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 宿舍卫生检查分页列表 + */ + TableDataInfo queryPageList(XgDormHygieneCheckBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的宿舍卫生检查列表 + * + * @param bo 查询条件 + * @return 宿舍卫生检查列表 + */ + List queryList(XgDormHygieneCheckBo bo); + + /** + * 新增宿舍卫生检查 + * + * @param bo 宿舍卫生检查 + * @return 是否新增成功 + */ + Boolean insertByBo(XgDormHygieneCheckBo bo); + + /** + * 修改宿舍卫生检查 + * + * @param bo 宿舍卫生检查 + * @return 是否修改成功 + */ + Boolean updateByBo(XgDormHygieneCheckBo bo); + + /** + * 校验并批量删除宿舍卫生检查信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 新增宿舍卫生检查(包含学生评分数据) + * + * @param bo 完整的宿舍卫生检查数据,包含学生评分信息 + * @return 是否新增成功 + */ + Boolean insertWithStudentScores(XgDormHygieneCheckCompleteBo bo); + + /** + * 修改宿舍卫生检查(包含学生评分数据) + * + * @param bo 完整的宿舍卫生检查数据,包含学生评分信息 + * @return 是否修改成功 + */ + Boolean updateWithStudentScores(XgDormHygieneCheckCompleteBo bo); + + /** + * 查询宿舍卫生检查导出列表(包含学生卫生情况) + * + * @param bo 查询条件 + * @return 导出数据列表 + */ + List queryExportList(XgDormHygieneCheckBo bo); + + /** + * 根据学生ID查询该学生宿舍的卫生检查信息 + * + * @param studentId 学生ID + * @return 卫生检查信息列表 + */ + List queryByStudentId(Long studentId); + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormHygieneStudentScoreService.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormHygieneStudentScoreService.java new file mode 100644 index 00000000..f9e0375f --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormHygieneStudentScoreService.java @@ -0,0 +1,84 @@ +package org.dromara.xg.service; + +import org.dromara.xg.domain.vo.XgDormHygieneStudentScoreVo; +import org.dromara.xg.domain.bo.XgDormHygieneStudentScoreBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 宿舍卫生检查学生评分Service接口 + * + * @author zlwu + * @date 2025-08-16 + */ +public interface IXgDormHygieneStudentScoreService { + + /** + * 查询宿舍卫生检查学生评分 + * + * @param id 主键 + * @return 宿舍卫生检查学生评分 + */ + XgDormHygieneStudentScoreVo queryById(Long id); + + /** + * 分页查询宿舍卫生检查学生评分列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 宿舍卫生检查学生评分分页列表 + */ + TableDataInfo queryPageList(XgDormHygieneStudentScoreBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的宿舍卫生检查学生评分列表 + * + * @param bo 查询条件 + * @return 宿舍卫生检查学生评分列表 + */ + List queryList(XgDormHygieneStudentScoreBo bo); + + /** + * 新增宿舍卫生检查学生评分 + * + * @param bo 宿舍卫生检查学生评分 + * @return 是否新增成功 + */ + Boolean insertByBo(XgDormHygieneStudentScoreBo bo); + + /** + * 修改宿舍卫生检查学生评分 + * + * @param bo 宿舍卫生检查学生评分 + * @return 是否修改成功 + */ + Boolean updateByBo(XgDormHygieneStudentScoreBo bo); + + /** + * 校验并批量删除宿舍卫生检查学生评分信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 根据卫生检查ID查询学生评分列表 + * + * @param hygieneCheckId 卫生检查ID + * @return 学生评分列表 + */ + List queryByHygieneCheckId(Long hygieneCheckId); + + /** + * 根据卫生检查ID删除学生评分记录 + * + * @param hygieneCheckId 卫生检查ID + * @return 是否删除成功 + */ + Boolean deleteByHygieneCheckId(Long hygieneCheckId); +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormRoomService.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormRoomService.java index d4cf9f9c..f5b8cd41 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormRoomService.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormRoomService.java @@ -33,6 +33,25 @@ public interface IXgDormRoomService { */ TableDataInfo queryPageList(XgDormRoomBo bo, PageQuery pageQuery); + /** + * 分页查询当前用户所在学院的宿舍房间信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 宿舍房间信息分页列表 + */ + TableDataInfo queryPageListByCurrentUserCollege(XgDormRoomBo bo, PageQuery pageQuery); + + /** + * 分页查询指定班级的宿舍房间信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @param classId 班级ID + * @return 宿舍房间信息分页列表 + */ + TableDataInfo queryPageListByClassId(XgDormRoomBo bo, PageQuery pageQuery, Long classId); + /** * 查询符合条件的宿舍房间信息列表 * diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormViolationService.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormViolationService.java new file mode 100644 index 00000000..41336518 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormViolationService.java @@ -0,0 +1,106 @@ +package org.dromara.xg.service; + +import org.dromara.xg.domain.vo.XgDormViolationVo; +import org.dromara.xg.domain.bo.XgDormViolationBo; +import org.dromara.xg.domain.vo.StudentOptionVo; +import org.dromara.xg.domain.bo.StudentOptionBo; +import org.dromara.xg.domain.vo.XgDormViolationImportVo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 宿舍违纪记录Service接口 + * + * @author zlwu + * @date 2025-08-16 + */ +public interface IXgDormViolationService { + + /** + * 查询宿舍违纪记录 + * + * @param id 主键 + * @return 宿舍违纪记录 + */ + XgDormViolationVo queryById(Long id); + + /** + * 分页查询宿舍违纪记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 宿舍违纪记录分页列表 + */ + TableDataInfo queryPageList(XgDormViolationBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的宿舍违纪记录列表 + * + * @param bo 查询条件 + * @return 宿舍违纪记录列表 + */ + List queryList(XgDormViolationBo bo); + + /** + * 新增宿舍违纪记录 + * + * @param bo 宿舍违纪记录 + * @return 是否新增成功 + */ + Boolean insertByBo(XgDormViolationBo bo); + + /** + * 修改宿舍违纪记录 + * + * @param bo 宿舍违纪记录 + * @return 是否修改成功 + */ + Boolean updateByBo(XgDormViolationBo bo); + + /** + * 校验并批量删除宿舍违纪记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 获取学生选择器选项列表(支持按学号、姓名检索) + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 学生选项分页列表 + */ + TableDataInfo getStudentOptions(StudentOptionBo bo, PageQuery pageQuery); + + /** + * 根据学生ID获取学生详细信息 + * + * @param studentId 学生ID + * @return 学生详细信息 + */ + StudentOptionVo getStudentById(Long studentId); + + /** + * 根据学号获取学生详细信息 + * + * @param studentNo 学号 + * @return 学生详细信息 + */ + StudentOptionVo getStudentByStudentNo(String studentNo); + + /** + * 导入宿舍违纪记录数据 + * + * @param importList 导入的宿舍违纪记录列表 + * @param isUpdateSupport 是否支持更新已存在的记录 + * @param operName 操作人员名称 + * @return 导入结果信息 + */ + String importDormViolation(List importList, boolean isUpdateSupport, String operName); +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgHolidayStayService.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgHolidayStayService.java new file mode 100644 index 00000000..449b5842 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgHolidayStayService.java @@ -0,0 +1,80 @@ +package org.dromara.xg.service; + +import org.dromara.xg.domain.vo.XgHolidayStayVo; +import org.dromara.xg.domain.bo.XgHolidayStayBo; +import org.dromara.xg.domain.vo.StudentOptionVo; +import org.dromara.xg.domain.bo.StudentOptionBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 假期留宿管理Service接口 + * + * @author zlwu + * @date 2025-08-15 + */ +public interface IXgHolidayStayService { + + /** + * 查询假期留宿管理 + * + * @param id 主键 + * @return 假期留宿管理 + */ + XgHolidayStayVo queryById(Long id); + + /** + * 分页查询假期留宿管理列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 假期留宿管理分页列表 + */ + TableDataInfo queryPageList(XgHolidayStayBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的假期留宿管理列表 + * + * @param bo 查询条件 + * @return 假期留宿管理列表 + */ + List queryList(XgHolidayStayBo bo); + + /** + * 新增假期留宿管理 + * + * @param bo 假期留宿管理 + * @return 是否新增成功 + */ + Boolean insertByBo(XgHolidayStayBo bo); + + /** + * 修改假期留宿管理 + * + * @param bo 假期留宿管理 + * @return 是否修改成功 + */ + Boolean updateByBo(XgHolidayStayBo bo); + + /** + * 校验并批量删除假期留宿管理信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 获取学生选择器选项列表(支持按学号、姓名检索) + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 学生选项分页列表 + */ + TableDataInfo getStudentOptions(StudentOptionBo bo, PageQuery pageQuery); + +} 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/XgClassMsgServiceImpl.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgClassMsgServiceImpl.java index 71ce6f06..6ad59c7a 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgClassMsgServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgClassMsgServiceImpl.java @@ -88,6 +88,7 @@ public class XgClassMsgServiceImpl implements IXgClassMsgService { lqw.eq(StringUtils.isNotBlank(bo.getClassType()), XgClassMsg::getClassType, bo.getClassType()); lqw.eq(StringUtils.isNotBlank(bo.getGrade()), XgClassMsg::getGrade, bo.getGrade()); lqw.eq(StringUtils.isNotBlank(bo.getClassStatus()), XgClassMsg::getClassStatus, bo.getClassStatus()); + lqw.eq(bo.getClassTeacherId() != null, XgClassMsg::getClassTeacherId, bo.getClassTeacherId()); return lqw; } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormBedServiceImpl.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormBedServiceImpl.java index 62a85373..76221a79 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormBedServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormBedServiceImpl.java @@ -14,12 +14,22 @@ import org.dromara.xg.domain.bo.XgDormBedBo; import org.dromara.xg.domain.vo.XgDormBedVo; import org.dromara.xg.domain.vo.XgDormBedDetailVo; import org.dromara.xg.domain.XgDormBed; +import org.dromara.xg.domain.XgDormRoom; +import org.dromara.xg.domain.XgDormBedStudent; +import org.dromara.xg.domain.XgSysDept; import org.dromara.xg.mapper.XgDormBedMapper; +import org.dromara.xg.mapper.XgDormRoomMapper; +import org.dromara.xg.mapper.XgDormBedStudentMapper; +import org.dromara.xg.mapper.XgSysDeptMapper; import org.dromara.xg.service.IXgDormBedService; +import org.dromara.common.satoken.utils.LoginHelper; import java.util.List; import java.util.Map; import java.util.Collection; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.ArrayList; /** * 宿舍床位信息Service业务层处理 @@ -33,6 +43,9 @@ import java.util.Collection; public class XgDormBedServiceImpl implements IXgDormBedService { private final XgDormBedMapper baseMapper; + private final XgDormRoomMapper xgDormRoomMapper; + private final XgDormBedStudentMapper xgDormBedStudentMapper; + private final XgSysDeptMapper xgSysDeptMapper; /** * 查询宿舍床位信息 @@ -41,7 +54,7 @@ public class XgDormBedServiceImpl implements IXgDormBedService { * @return 宿舍床位信息 */ @Override - public XgDormBedVo queryById(Long id){ + public XgDormBedVo queryById(Long id) { return baseMapper.selectVoById(id); } @@ -117,8 +130,8 @@ public class XgDormBedServiceImpl implements IXgDormBedService { /** * 保存前的数据校验 */ - private void validEntityBeforeSave(XgDormBed entity){ - //TODO 做一些数据校验,如唯一约束 + private void validEntityBeforeSave(XgDormBed entity) { + // TODO 做一些数据校验,如唯一约束 } /** @@ -130,8 +143,8 @@ public class XgDormBedServiceImpl implements IXgDormBedService { */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { - if(isValid){ - //TODO 做一些业务上的校验,判断是否需要校验 + if (isValid) { + // TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteByIds(ids) > 0; } @@ -147,6 +160,26 @@ public class XgDormBedServiceImpl implements IXgDormBedService { return baseMapper.selectBedDetailsByRoomId(roomId); } + /** + * 查询当前用户所在学院的指定房间床位详细信息列表(包含学生信息) + * + * @param roomId 房间ID + * @return 床位详细信息列表 + */ + @Override + public List queryBedDetailsByRoomIdForCurrentUserCollege(Long roomId) { + // 1. 获取当前用户的学院ID + Long currentUserCollegeId = getCurrentUserCollegeId(); + if (currentUserCollegeId == null) { + log.warn("无法获取当前用户的学院信息,返回空结果"); + return new ArrayList<>(); + } + + // 2. 直接查询指定房间中属于当前学院的床位详细信息 + // 通过SQL关联xg_dorm_bed_student表,只返回分配给当前学院的床位 + return baseMapper.selectBedDetailsByRoomIdAndCollegeId(roomId, currentUserCollegeId); + } + /** * 查询指定宿舍楼的床位详细信息列表(包含学生信息) * @@ -157,4 +190,117 @@ public class XgDormBedServiceImpl implements IXgDormBedService { public List queryBedDetailsByBuildingId(Long buildingId) { return baseMapper.selectBedDetailsByBuildingId(buildingId); } + + /** + * 查询指定房间中属于指定班级的床位详细信息列表(包含学生信息) + * + * @param roomId 房间ID + * @param classId 班级ID + * @return 床位详细信息列表 + */ + @Override + public List queryBedDetailsByRoomIdAndClassId(Long roomId, Long classId) { + return baseMapper.selectBedDetailsByRoomIdAndClassId(roomId, classId); + } + + /** + * 获取当前用户的二级学院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 getCollegeAllocatedRoomIds(Long collegeId) { + try { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.select(XgDormBedStudent::getRoomId) + .eq(XgDormBedStudent::getCollegeId, collegeId) + .eq(XgDormBedStudent::getStatus, "0") + .eq(XgDormBedStudent::getDeleted, "0") + .isNotNull(XgDormBedStudent::getRoomId) + .groupBy(XgDormBedStudent::getRoomId); + + List roomRecords = xgDormBedStudentMapper.selectList(queryWrapper); + return roomRecords.stream() + .map(XgDormBedStudent::getRoomId) + .collect(Collectors.toSet()); + + } catch (Exception e) { + log.error("获取学院 {} 分配的房间ID列表失败", collegeId, e); + return Set.of(); + } + } + + /** + * 获取分配给指定学院的宿舍楼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 = xgDormBedStudentMapper.selectList(queryWrapper); + return buildingRecords.stream() + .map(XgDormBedStudent::getBuildingId) + .collect(Collectors.toSet()); + + } catch (Exception e) { + log.error("获取学院 {} 分配的宿舍楼ID列表失败", collegeId, e); + return Set.of(); + } + } } 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 c3b59f55..3f634fe5 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 @@ -30,6 +30,30 @@ import java.util.Set; import java.util.Collection; import java.util.Date; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Objects; +import java.util.Collections; + +import org.dromara.xg.domain.bo.XgDormBedBatchRecallBo; +import org.dromara.xg.domain.bo.XgDormSelectiveAllocationBo; +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; +import org.dromara.xg.domain.bo.XgDormBedBatchAllocateToClassBo; +import org.dromara.xg.mapper.XgClassMsgMapper; +import org.dromara.xg.domain.XgClassMsg; +import org.dromara.xg.domain.vo.XgDormBedDetailVo; +import org.dromara.xg.domain.bo.XgDormSelectiveAllocationBo; /** * 床位学生分配关系Service业务层处理 @@ -46,6 +70,11 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService { private final XgDormBedMapper xgDormBedMapper; private final XgDormBuildingMapper xgDormBuildingMapper; private final XgDormRoomMapper xgDormRoomMapper; + private final XgStudentMsgMapper xgStudentMsgMapper; + private final XgDormCampusMapper xgDormCampusMapper; + private final XgDormAreaMapper xgDormAreaMapper; + private final XgSysDeptMapper xgSysDeptMapper; + private final XgClassMsgMapper xgClassMsgMapper; /** * 查询床位学生分配关系 @@ -421,4 +450,1868 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService { allocationRecords.add(allocation); } } + + /** + * 批量收回宿舍床位 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean batchRecallBeds(XgDormBedBatchRecallBo bo) { + Date now = new Date(); + Long loginUserId = LoginHelper.getUserId(); + + // 1) 查出这些床位当前有效的分配关系(status=0, deleted=0) + LambdaQueryWrapper activeAssignQuery = Wrappers.lambdaQuery(); + activeAssignQuery.in(XgDormBedStudent::getBedId, bo.getBedIds()) + .eq(XgDormBedStudent::getStatus, "0") + .eq(XgDormBedStudent::getDeleted, "0"); + List activeAssignments = baseMapper.selectList(activeAssignQuery); + + if (activeAssignments.isEmpty()) { + return true; // 没有需要处理的数据 + } + + // 2) 更新分配关系:status=1(已退宿), checkout_time=now, update_time=now, + // update_by=loginUserId + LambdaUpdateWrapper updateAssign = Wrappers.lambdaUpdate(); + updateAssign.in(XgDormBedStudent::getBedId, bo.getBedIds()) + .eq(XgDormBedStudent::getStatus, "0") + .eq(XgDormBedStudent::getDeleted, "0") + .set(XgDormBedStudent::getStatus, "1") + .set(XgDormBedStudent::getCheckoutTime, now) + .set(XgDormBedStudent::getUpdateTime, now) + .set(XgDormBedStudent::getUpdateBy, loginUserId); + baseMapper.update(null, updateAssign); + + // 3) 更新床位:bed_status=0(空闲), update_time=now, update_by=loginUserId + LambdaUpdateWrapper updateBed = Wrappers.lambdaUpdate(); + updateBed.in(XgDormBed::getId, bo.getBedIds()) + .eq(XgDormBed::getDeleted, "0") + .set(XgDormBed::getBedStatus, "0") + .set(XgDormBed::getUpdateTime, now) + .set(XgDormBed::getUpdateBy, loginUserId); + xgDormBedMapper.update(null, updateBed); + + // 4) 更新学生信息:找到受影响的studentId,设置 student_status=1,清空 + // ext_field1/dormitory_room/dormitory_building + Set affectedStudentIds = activeAssignments.stream() + .map(XgDormBedStudent::getStudentId) + .filter(java.util.Objects::nonNull) + .collect(java.util.stream.Collectors.toSet()); + if (!affectedStudentIds.isEmpty()) { + LambdaUpdateWrapper updateStudent = Wrappers.lambdaUpdate(); + updateStudent.in(XgStudentMsg::getId, affectedStudentIds) + .eq(XgStudentMsg::getDeleted, 0L) + .eq(XgStudentMsg::getStudentStatus, "1") + .set(XgStudentMsg::getExtField1, null) + .set(XgStudentMsg::getDormitoryRoom, null) + .set(XgStudentMsg::getDormitoryBuilding, null) + .set(XgStudentMsg::getUpdateTime, now) + .set(XgStudentMsg::getUpdateBy, loginUserId); + xgStudentMsgMapper.update(null, updateStudent); + } + + return true; + } + + /** + * 批量回收班级分配床位(保留学院分配) + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean batchRecallClassAllocation(XgDormBedBatchRecallBo bo) { + Date now = new Date(); + Long loginUserId = LoginHelper.getUserId(); + + // 1) 查出这些床位当前有效的分配关系(status=0, deleted=0,并且已分配给班级) + LambdaQueryWrapper activeAssignQuery = Wrappers.lambdaQuery(); + activeAssignQuery.in(XgDormBedStudent::getBedId, bo.getBedIds()) + .eq(XgDormBedStudent::getStatus, "0") + .eq(XgDormBedStudent::getDeleted, "0") + .isNotNull(XgDormBedStudent::getClassId); // 只处理已分配给班级的记录 + List activeAssignments = baseMapper.selectList(activeAssignQuery); + + if (activeAssignments.isEmpty()) { + return true; // 没有需要处理的数据 + } + + // 2) 更新分配关系:清空班级相关字段,但保留学院分配信息 + LambdaUpdateWrapper updateAssign = Wrappers.lambdaUpdate(); + updateAssign.in(XgDormBedStudent::getBedId, bo.getBedIds()) + .eq(XgDormBedStudent::getStatus, "0") + .eq(XgDormBedStudent::getDeleted, "0") + .isNotNull(XgDormBedStudent::getClassId) + .set(XgDormBedStudent::getMajorId, null) + .set(XgDormBedStudent::getClassId, null) + .set(XgDormBedStudent::getStudentId, null) + // .set(XgDormBedStudent::getAssignTime, null) + .set(XgDormBedStudent::getCheckoutTime, null) + .set(XgDormBedStudent::getUpdateTime, now) + .set(XgDormBedStudent::getUpdateBy, loginUserId); + baseMapper.update(null, updateAssign); + + // 3) XgDormBed 状态保持不变(仍然是已分配给学院的状态) + + // 4) 更新学生信息:找到受影响的studentId,设置 student_status=1,清空 + // ext_field1/dormitory_room/dormitory_building + Set affectedStudentIds = activeAssignments.stream() + .map(XgDormBedStudent::getStudentId) + .filter(java.util.Objects::nonNull) + .collect(java.util.stream.Collectors.toSet()); + if (!affectedStudentIds.isEmpty()) { + LambdaUpdateWrapper updateStudent = Wrappers.lambdaUpdate(); + updateStudent.in(XgStudentMsg::getId, affectedStudentIds) + .eq(XgStudentMsg::getDeleted, 0L) + .eq(XgStudentMsg::getStudentStatus, "1") + .set(XgStudentMsg::getExtField1, null) + .set(XgStudentMsg::getDormitoryRoom, null) + .set(XgStudentMsg::getDormitoryBuilding, null) + .set(XgStudentMsg::getUpdateTime, now) + .set(XgStudentMsg::getUpdateBy, loginUserId); + xgStudentMsgMapper.update(null, updateStudent); + } + + return true; + } + + /** + * 批量分配床位给班级 + * + * @param bo 批量分配给班级请求参数 + * @return 是否分配成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean batchAllocateBedsToClass(XgDormBedBatchAllocateToClassBo bo) { + try { + // 1. 获取床位信息 + List beds = xgDormBedMapper.selectBatchIds(bo.getBedIds()); + + // 2. 校验床位是否已分配给班级(检查xg_dorm_bed_student表中的classId) + LambdaQueryWrapper classCheckWrapper = new LambdaQueryWrapper<>(); + classCheckWrapper.in(XgDormBedStudent::getBedId, bo.getBedIds()) + .isNotNull(XgDormBedStudent::getClassId) + .eq(XgDormBedStudent::getStatus, "0"); // 正常状态,未退宿 + + List existingClassAllocations = baseMapper.selectList(classCheckWrapper); + + if (!existingClassAllocations.isEmpty()) { + // 找出已分配的床位名称 + Set allocatedBedIds = existingClassAllocations.stream() + .map(XgDormBedStudent::getBedId) + .collect(java.util.stream.Collectors.toSet()); + + List allocatedBedNames = beds.stream() + .filter(bed -> allocatedBedIds.contains(bed.getId())) + .map(XgDormBed::getBedName) + .collect(java.util.stream.Collectors.toList()); + + throw new RuntimeException("以下床位已分配给班级,无法重复分配:" + String.join("、", allocatedBedNames)); + } + + // 3. 检查床位是否已分配给学院(必须先分配给学院才能分配给班级) + LambdaQueryWrapper collegeCheckWrapper = new LambdaQueryWrapper<>(); + collegeCheckWrapper.in(XgDormBedStudent::getBedId, bo.getBedIds()) + .isNotNull(XgDormBedStudent::getCollegeId) + .isNull(XgDormBedStudent::getClassId) // 只分配了学院,还没分配班级 + .eq(XgDormBedStudent::getStatus, "0"); // 正常状态,未退宿 + + List existingCollegeAllocations = baseMapper.selectList(collegeCheckWrapper); + + if (existingCollegeAllocations.isEmpty()) { + throw new RuntimeException("选择的床位尚未分配给学院,请先分配给学院后再分配给班级"); + } + + // 4. 验证所有床位都已分配给学院 + Set collegeAllocatedBedIds = existingCollegeAllocations.stream() + .map(XgDormBedStudent::getBedId) + .collect(java.util.stream.Collectors.toSet()); + + List unallocatedBedIds = bo.getBedIds().stream() + .filter(bedId -> !collegeAllocatedBedIds.contains(bedId)) + .collect(java.util.stream.Collectors.toList()); + + if (!unallocatedBedIds.isEmpty()) { + List unallocatedBedNames = beds.stream() + .filter(bed -> unallocatedBedIds.contains(bed.getId())) + .map(XgDormBed::getBedName) + .collect(java.util.stream.Collectors.toList()); + + throw new RuntimeException("以下床位尚未分配给学院,无法分配给班级:" + String.join("、", unallocatedBedNames)); + } + + // 5. 获取楼栋信息,用于获取校区ID和宿舍区ID + XgDormBuilding building = xgDormBuildingMapper.selectById(bo.getBuildingId()); + if (building == null) { + throw new RuntimeException("楼栋信息不存在,buildingId: " + bo.getBuildingId()); + } + + // 6. 查询班级信息,获取专业ID和学院ID + List classes = xgClassMsgMapper.selectBatchIds(bo.getClassIds()); + if (classes.size() != bo.getClassIds().size()) { + throw new RuntimeException("部分班级信息不存在,请检查班级ID"); + } + + // 建立班级ID到班级信息的映射 + Map classMap = classes.stream() + .collect(java.util.stream.Collectors.toMap(XgClassMsg::getId, cls -> cls)); + + // 7. 计算每个班级分配的床位数量 + Date now = new Date(); + Long loginUserId = LoginHelper.getUserId(); + int totalAvailableBeds = collegeAllocatedBedIds.size(); + + // 如果没有提供班级人数信息,则平均分配 + List classCounts = bo.getClassStudentCounts(); + if (classCounts == null || classCounts.isEmpty()) { + // 平均分配:每个班级分配的床位数 + int bedsPerClass = totalAvailableBeds / bo.getClassIds().size(); + int remainingBeds = totalAvailableBeds % bo.getClassIds().size(); + + classCounts = new ArrayList<>(); + for (int i = 0; i < bo.getClassIds().size(); i++) { + int count = bedsPerClass + (i < remainingBeds ? 1 : 0); + classCounts.add(count); + } + } + + // 验证班级人数总和不能超过可用床位总数 + int totalStudents = classCounts.stream().mapToInt(Integer::intValue).sum(); + if (totalStudents > totalAvailableBeds) { + throw new RuntimeException( + "班级人数总和(" + totalStudents + ")超过了可用床位总数(" + totalAvailableBeds + "),请调整分配数量"); + } + + log.info("床位分配验证通过:可用床位数{},分配学生数{},剩余床位数{}", + totalAvailableBeds, totalStudents, totalAvailableBeds - totalStudents); + + // 8. 获取房间信息,建立roomId到roomCode的映射 + Set roomIds = beds.stream().map(XgDormBed::getRoomId).collect(java.util.stream.Collectors.toSet()); + List rooms = xgDormRoomMapper.selectBatchIds(roomIds); + Map roomCodeMap = rooms.stream() + .collect(java.util.stream.Collectors.toMap(XgDormRoom::getId, XgDormRoom::getRoomCode)); + + // 9. 将床位按房间顺序排列,实现房间优先分配 + // 获取已分配给学院的床位详细信息 + List availableBeds = beds.stream() + .filter(bed -> collegeAllocatedBedIds.contains(bed.getId())) + .collect(java.util.stream.Collectors.toList()); + + // 按房间编码排序床位,确保同一房间的床位连续 + List sortedBeds = availableBeds.stream() + .sorted((b1, b2) -> { + // 首先按房间编码排序 + String roomCode1 = roomCodeMap.get(b1.getRoomId()); + String roomCode2 = roomCodeMap.get(b2.getRoomId()); + int roomCompare = roomCode1.compareTo(roomCode2); + if (roomCompare != 0) { + return roomCompare; + } + // 同一房间内按床位编码排序 + return b1.getBedCode().compareTo(b2.getBedCode()); + }) + .collect(java.util.stream.Collectors.toList()); + + // 10. 按房间分组床位,实现房间优先分配策略 + Map> bedsByRoom = sortedBeds.stream() + .collect(java.util.stream.Collectors.groupingBy(XgDormBed::getRoomId)); + + // 获取排序后的房间ID列表 + List sortedRoomIds = new ArrayList<>(bedsByRoom.keySet()); + sortedRoomIds.sort((r1, r2) -> roomCodeMap.get(r1).compareTo(roomCodeMap.get(r2))); + + // 11. 房间优先分配算法(四轮分配策略) + // 房间使用状态跟踪 + int[] roomUsage = new int[sortedRoomIds.size()]; // 记录每个房间已使用床位数 + Map roomClassMap = new java.util.HashMap<>(); // 记录房间主要分配给哪个班级 + + // 按班级顺序,采用房间优先分配策略 + for (int classIndex = 0; classIndex < bo.getClassIds().size(); classIndex++) { + Long classId = bo.getClassIds().get(classIndex); + XgClassMsg classInfo = classMap.get(classId); + int remainingStudents = classCounts.get(classIndex); + + log.info("开始为班级{}分配{}个床位,采用房间优先策略", classInfo.getClassName(), remainingStudents); + + // 第一轮:优先分配完整房间 + for (int roomIndex = 0; roomIndex < sortedRoomIds.size() && remainingStudents > 0; roomIndex++) { + Long roomId = sortedRoomIds.get(roomIndex); + List roomBeds = bedsByRoom.get(roomId); + int roomCapacity = roomBeds.size(); + + // 如果房间空闲且学生数足够分配整个房间 + if (roomUsage[roomIndex] == 0 && remainingStudents >= roomCapacity) { + // 分配整个房间给当前班级 + roomUsage[roomIndex] = roomCapacity; + roomClassMap.put(roomIndex, classId); + remainingStudents -= roomCapacity; + + log.info("为班级{}分配完整房间{},房间编码{},床位数{}", + classInfo.getClassName(), roomId, roomCodeMap.get(roomId), roomCapacity); + } + } + + // 第二轮:处理剩余学生(优先分配到已有该班级学生的房间) + for (int roomIndex = 0; roomIndex < sortedRoomIds.size() && remainingStudents > 0; roomIndex++) { + Long roomId = sortedRoomIds.get(roomIndex); + List roomBeds = bedsByRoom.get(roomId); + int availableBedsCount = roomBeds.size() - roomUsage[roomIndex]; + + // 优先分配到已有该班级学生的房间 + if (availableBedsCount > 0 && classId.equals(roomClassMap.get(roomIndex))) { + int bedsToAllocate = Math.min(remainingStudents, availableBedsCount); + roomUsage[roomIndex] += bedsToAllocate; + remainingStudents -= bedsToAllocate; + + log.info("为班级{}在已分配房间{}补充{}个床位", + classInfo.getClassName(), roomId, bedsToAllocate); + } + } + + // 第三轮:如果还有剩余学生,分配到空闲房间(避免混住) + for (int roomIndex = 0; roomIndex < sortedRoomIds.size() && remainingStudents > 0; roomIndex++) { + Long roomId = sortedRoomIds.get(roomIndex); + List roomBeds = bedsByRoom.get(roomId); + int availableBedsCount = roomBeds.size() - roomUsage[roomIndex]; + + // 分配到完全空闲的房间 + if (availableBedsCount > 0 && roomClassMap.get(roomIndex) == null) { + int bedsToAllocate = Math.min(remainingStudents, availableBedsCount); + roomUsage[roomIndex] += bedsToAllocate; + roomClassMap.put(roomIndex, classId); + remainingStudents -= bedsToAllocate; + + log.info("为班级{}分配空闲房间{}的{}个床位", + classInfo.getClassName(), roomId, bedsToAllocate); + } + } + + // 第四轮:确保所有学生都能分配到床位(允许混住) + for (int roomIndex = 0; roomIndex < sortedRoomIds.size() && remainingStudents > 0; roomIndex++) { + Long roomId = sortedRoomIds.get(roomIndex); + List roomBeds = bedsByRoom.get(roomId); + int availableBedsCount = roomBeds.size() - roomUsage[roomIndex]; + + // 分配到任何有空闲床位的房间,即使会产生混住 + if (availableBedsCount > 0) { + int bedsToAllocate = Math.min(remainingStudents, availableBedsCount); + roomUsage[roomIndex] += bedsToAllocate; + remainingStudents -= bedsToAllocate; + + Long existingClassId = roomClassMap.get(roomIndex); + if (existingClassId != null && !existingClassId.equals(classId)) { + XgClassMsg existingClass = classMap.get(existingClassId); + log.warn("房间{}产生混住:班级{}和班级{}", + roomCodeMap.get(roomId), existingClass.getClassName(), classInfo.getClassName()); + } + + log.info("为班级{}分配房间{}的{}个床位(可能混住)", + classInfo.getClassName(), roomId, bedsToAllocate); + } + } + + // 检查是否还有未分配的学生 + if (remainingStudents > 0) { + throw new RuntimeException( + "班级" + classInfo.getClassName() + "还有" + remainingStudents + "个学生未能分配床位,床位资源不足"); + } + + log.info("班级{}床位分配完成", classInfo.getClassName()); + } + + // 12. 批量更新床位学生记录,补充班级和专业信息 + for (int i = 0; i < bo.getClassIds().size(); i++) { + Long classId = bo.getClassIds().get(i); + XgClassMsg classInfo = classMap.get(classId); + + // 计算应该分配给该班级的床位数量 + int classStudentCount = classCounts.get(i); + + // 获取该班级应该分配的床位ID列表(按分配算法确定的顺序) + List classBedIds = new ArrayList<>(); + int allocatedCount = 0; + + // 按房间分配顺序找出分配给该班级的床位 + for (int roomIndex = 0; roomIndex < sortedRoomIds.size() + && allocatedCount < classStudentCount; roomIndex++) { + if (classId.equals(roomClassMap.get(roomIndex))) { + Long roomId = sortedRoomIds.get(roomIndex); + List roomBeds = bedsByRoom.get(roomId); + + // 计算这个房间分配给该班级的床位数 + int roomBedsForThisClass = Math.min(classStudentCount - allocatedCount, roomBeds.size()); + + for (int j = 0; j < roomBedsForThisClass; j++) { + classBedIds.add(roomBeds.get(j).getId()); + allocatedCount++; + } + } + } + + if (!classBedIds.isEmpty()) { + // 批量更新该班级分配的床位记录 + LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(); + updateWrapper.in(XgDormBedStudent::getBedId, classBedIds) + .eq(XgDormBedStudent::getStatus, "0") // 正常状态 + .isNull(XgDormBedStudent::getClassId) // 只更新还没分配班级的记录 + .isNotNull(XgDormBedStudent::getCollegeId) // 已分配给学院 + .set(XgDormBedStudent::getMajorId, classInfo.getMajorId()) + .set(XgDormBedStudent::getClassId, classInfo.getId()) + .set(XgDormBedStudent::getUpdateTime, now) + .set(XgDormBedStudent::getUpdateBy, loginUserId); + + int updateCount = baseMapper.update(null, updateWrapper); + log.info("为班级{}({})更新了{}条床位分配记录", classInfo.getClassName(), classId, updateCount); + } + } + + log.info("成功更新{}个床位分配给{}个班级,总可用床位:{}", + bo.getBedIds().size(), bo.getClassIds().size(), totalAvailableBeds); + return true; + + } catch (Exception e) { + log.error("批量分配床位给班级失败", e); + throw new RuntimeException("床位分配失败:" + e.getMessage()); + } + } + + /** + * 获取当前用户二级学院的宿舍楼树形结构 + * 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择 + * + * @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()); + } + + /** + * 获取班主任管理的班级宿舍楼树 + * 通过当前用户所在的班级,获取该班级分配的宿舍楼 + * + * @return 宿舍楼树形结构数据 + */ + @Override + public List getClassTeacherDormTree(Long classId) { + try { + // 1. 获取班级ID(优先使用入参,其次回退到当前用户) + Long targetClassId = classId != null ? classId : getCurrentUserClassId(); + if (targetClassId == null) { + log.warn("无法获取当前用户管理的班级信息"); + return new ArrayList<>(); + } + + // 2. 通过xg_dorm_bed_student表获取分配给该班级的宿舍楼ID列表 + Set buildingIds = getClassAllocatedBuildingIds(targetClassId); + if (buildingIds.isEmpty()) { + log.info("班级 {} 还没有分配任何宿舍楼", targetClassId); + return new ArrayList<>(); + } + + // 3. 获取宿舍楼信息并构建树形结构 + return buildDormBuildingTree(buildingIds); + + } catch (Exception e) { + log.error("获取班主任管理的班级宿舍楼树失败", e); + return new ArrayList<>(); + } + } + + /** + * 获取当前用户管理的班级ID(班主任身份) + */ + private Long getCurrentUserClassId() { + try { + Long currentUserId = LoginHelper.getUserId(); + if (currentUserId == null) { + log.warn("用户未登录"); + return null; + } + + // 查询当前用户是否是班主任,通过班级表查找 + LambdaQueryWrapper classQuery = Wrappers.lambdaQuery(); + classQuery.eq(XgClassMsg::getClassTeacherId, currentUserId) + .eq(XgClassMsg::getClassStatus, "1") + .eq(XgClassMsg::getDeleted, 0L) + .last("LIMIT 1"); + + List classes = xgClassMsgMapper.selectList(classQuery); + return classes.isEmpty() ? null : classes.get(0).getId(); + + } catch (Exception e) { + log.error("获取当前用户管理的班级ID失败", e); + return null; + } + } + + /** + * 获取分配给指定班级的宿舍楼ID列表 + */ + private Set getClassAllocatedBuildingIds(Long classId) { + try { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.select(XgDormBedStudent::getBuildingId) + .eq(XgDormBedStudent::getClassId, classId) + .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列表失败", classId, e); + return Set.of(); + } + } + + /** + * 获取班主任管理班级的房间床位详情 + * + * @param buildingId 宿舍楼ID + * @return 房间床位详情列表 + */ + @Override + public List getClassTeacherBedDetails(Long buildingId, Long classId) { + try { + Long targetClassId = classId != null ? classId : getCurrentUserClassId(); + if (targetClassId == null) { + log.warn("无法获取班级信息,请确认用户是否为班主任或传入正确的班级ID"); + return new ArrayList<>(); + } + + log.info("获取班主任管理班级的房间床位详情,宿舍楼ID: {},classId:{}", buildingId, targetClassId); + + // 1. 构建房间查询条件 + LambdaQueryWrapper roomQuery = Wrappers.lambdaQuery(); + roomQuery.eq(XgDormRoom::getDeleted, "0") + .eq(XgDormRoom::getRoomStatus, "0"); + + if (buildingId != null) { + roomQuery.eq(XgDormRoom::getBuildingId, buildingId); + } + + // 2. 获取该班级分配到的房间ID列表 + LambdaQueryWrapper bedStudentQuery = Wrappers.lambdaQuery(); + bedStudentQuery.select(XgDormBedStudent::getRoomId) + .eq(XgDormBedStudent::getClassId, targetClassId) + .eq(XgDormBedStudent::getStatus, "0") + .eq(XgDormBedStudent::getDeleted, "0") + .isNotNull(XgDormBedStudent::getRoomId) + .groupBy(XgDormBedStudent::getRoomId); + + List roomRecords = baseMapper.selectList(bedStudentQuery); + Set allocatedRoomIds = roomRecords.stream() + .map(XgDormBedStudent::getRoomId) + .collect(Collectors.toSet()); + + if (allocatedRoomIds.isEmpty()) { + log.info("班级 {} 没有分配的房间", targetClassId); + return new ArrayList<>(); + } + + // 3. 查询这些房间的信息 + roomQuery.in(XgDormRoom::getId, allocatedRoomIds); + List rooms = xgDormRoomMapper.selectList(roomQuery); + + // 4. 为每个房间构建详细信息(房间+床位) + List result = new ArrayList<>(); + for (XgDormRoom room : rooms) { + // 查询该房间中属于指定班级的床位详细信息 + List bedDetails = xgDormBedMapper.selectBedDetailsByRoomIdAndClassId(room.getId(), + targetClassId); + + // 构建房间记录 + Map roomRecord = new HashMap<>(); + roomRecord.put("id", room.getId()); + roomRecord.put("roomName", room.getRoomName()); + roomRecord.put("buildingId", room.getBuildingId()); + roomRecord.put("floorNum", room.getFloorNum()); + roomRecord.put("roomType", room.getRoomType()); + roomRecord.put("roomStatus", room.getRoomStatus()); + roomRecord.put("remark", room.getRemark()); + roomRecord.put("isBed", false); + roomRecord.put("children", bedDetails); + + result.add(roomRecord); + } + + log.info("成功获取班级 {} 的房间床位详情,共 {} 个房间", targetClassId, result.size()); + return result; + + } catch (Exception e) { + log.error("获取班主任管理班级的房间床位详情失败", e); + return new ArrayList<>(); + } + } + + /** + * 获取班主任管理班级的未分配学生列表 + * + * @param classId 班级ID,为空时使用当前用户管理的班级 + * @return 未分配学生列表 + */ + @Override + public List getClassTeacherUnallocatedStudents(Long classId) { + try { + Long targetClassId = classId != null ? classId : getCurrentUserClassId(); + if (targetClassId == null) { + log.warn("无法获取班级信息,请确认用户是否为班主任或传入正确的班级ID"); + return new ArrayList<>(); + } + + log.info("获取班主任管理班级的未分配学生列表,classId: {}", targetClassId); + + // 1. 查询该班级所有需要住校的学生 + LambdaQueryWrapper studentQuery = Wrappers.lambdaQuery(); + studentQuery.eq(XgStudentMsg::getStudentStatus, "1") // 学生状态为正常 + .eq(XgStudentMsg::getDeleted, 0L) // 未删除 + .eq(XgStudentMsg::getIsBoarder, "1") // 需要住校 + .eq(XgStudentMsg::getClassId, targetClassId) // 指定班级 + .orderByAsc(XgStudentMsg::getStudentNo); // 按学号排序 + + List boardingStudents = xgStudentMsgMapper.selectList(studentQuery); + + if (boardingStudents.isEmpty()) { + log.info("班级 {} 没有需要住校的学生", targetClassId); + return new ArrayList<>(); + } + + log.info("班级 {} 共有 {} 名学生需要住校", targetClassId, boardingStudents.size()); + + // 2. 查询已经分配床位的学生ID列表 + LambdaQueryWrapper bedQuery = Wrappers.lambdaQuery(); + bedQuery.select(XgDormBedStudent::getStudentId) + .eq(XgDormBedStudent::getClassId, targetClassId) // 指定班级 + .eq(XgDormBedStudent::getStatus, "0") // 正常状态(未退宿) + .eq(XgDormBedStudent::getDeleted, "0") // 未删除 + .isNotNull(XgDormBedStudent::getStudentId); // 学生ID不为空 + + List allocatedBedRecords = baseMapper.selectList(bedQuery); + Set allocatedStudentIds = allocatedBedRecords.stream() + .map(XgDormBedStudent::getStudentId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + log.info("班级 {} 已分配床位的学生数量: {}", targetClassId, allocatedStudentIds.size()); + + // 3. 计算未分配床位的学生(需要住校但未分配床位的学生) + List unallocatedStudents = boardingStudents.stream() + .filter(student -> !allocatedStudentIds.contains(student.getId())) + .collect(Collectors.toList()); + + log.info("班级 {} 统计结果 - 总住校学生: {}人,已分配床位: {}人,未分配床位: {}人", + targetClassId, boardingStudents.size(), allocatedStudentIds.size(), unallocatedStudents.size()); + + // 4. 转换为合适的返回格式,包含学生基本信息 + return unallocatedStudents.stream() + .map(student -> { + Map studentInfo = new HashMap<>(); + studentInfo.put("id", student.getId()); + studentInfo.put("studentNo", student.getStudentNo()); + studentInfo.put("studentName", student.getName()); + studentInfo.put("gender", student.getGender()); + studentInfo.put("classId", student.getClassId()); + studentInfo.put("majorId", student.getMajorId()); + studentInfo.put("collegeId", student.getCollegeId()); + studentInfo.put("phone", student.getPhone()); + studentInfo.put("idCard", student.getIdCard()); + return studentInfo; + }) + .collect(Collectors.toList()); + + } catch (Exception e) { + log.error("获取班主任管理班级的未分配学生列表失败,classId: {}", classId, e); + return new ArrayList<>(); + } + } + + /** + * 班主任一键随机分配床位 + * + * @param buildingId 宿舍楼ID,为空则全部分配 + * @return 是否分配成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean classTeacherRandomAllocation(Long buildingId, Long classId) { + try { + Long targetClassId = classId != null ? classId : getCurrentUserClassId(); + // TODO: 实现班主任一键随机分配床位 + log.info("班主任一键随机分配床位,宿舍楼ID: {},classId:{}", buildingId, targetClassId); + return true; + } catch (Exception e) { + log.error("班主任一键随机分配床位失败", e); + throw new RuntimeException("随机分配床位失败:" + e.getMessage()); + } + } + + /** + * 班主任一键按学号分配床位 + * + * @param buildingId 宿舍楼ID,为空则全部分配 + * @return 是否分配成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean classTeacherStudentNoAllocation(Long buildingId, Long classId) { + try { + Long targetClassId = classId != null ? classId : getCurrentUserClassId(); + // TODO: 实现班主任一键按学号分配床位 + log.info("班主任一键按学号分配床位,宿舍楼ID: {},classId:{}", buildingId, targetClassId); + return true; + } catch (Exception e) { + log.error("班主任一键按学号分配床位失败", e); + throw new RuntimeException("按学号分配床位失败:" + e.getMessage()); + } + } + + /** + * 班主任选择性分配床位(弹窗模式) + * + * @param allocationParams 分配参数 + * @return 是否分配成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean classTeacherSelectiveAllocation(XgDormSelectiveAllocationBo allocationParams, Long classId) { + try { + Long targetClassId = classId != null ? classId : getCurrentUserClassId(); + log.info("班主任选择性分配床位,classId:{},分配参数: {}", targetClassId, allocationParams); + + List bedIds = allocationParams.getBedIds(); + List studentIds = allocationParams.getStudentIds(); + String allocationType = allocationParams.getAllocationType(); + + // 参数校验 + if (bedIds == null || bedIds.isEmpty()) { + throw new RuntimeException("床位ID列表不能为空"); + } + if (studentIds == null || studentIds.isEmpty()) { + throw new RuntimeException("学生ID列表不能为空"); + } + if (bedIds.size() < studentIds.size()) { + throw new RuntimeException("床位数量不能少于学生数量"); + } + + Date now = new Date(); + Long loginUserId = LoginHelper.getUserId(); + + // 1. 验证床位状态 - 确保所有床位都是空闲且有效的 + LambdaQueryWrapper bedQuery = Wrappers.lambdaQuery(); + bedQuery.in(XgDormBed::getId, bedIds) + .eq(XgDormBed::getDeleted, "0"); + List availableBeds = xgDormBedMapper.selectList(bedQuery); + + if (availableBeds.size() != bedIds.size()) { + throw new RuntimeException("部分床位不存在或已被删除"); + } + + // 检查床位是否已被学生占用(床位已分配给学院/班级,但student_id有值表示被学生占用) + LambdaQueryWrapper existingQuery = Wrappers.lambdaQuery(); + existingQuery.in(XgDormBedStudent::getBedId, bedIds) + .eq(XgDormBedStudent::getStatus, "0") + .eq(XgDormBedStudent::getDeleted, "0") + .isNotNull(XgDormBedStudent::getStudentId); // 检查是否已有学生占用 + List existingAllocations = baseMapper.selectList(existingQuery); + + if (!existingAllocations.isEmpty()) { + // 找出已被占用的床位名称 + Set occupiedBedIds = existingAllocations.stream() + .map(XgDormBedStudent::getBedId) + .collect(Collectors.toSet()); + List occupiedBedNames = availableBeds.stream() + .filter(bed -> occupiedBedIds.contains(bed.getId())) + .map(XgDormBed::getBedName) + .collect(Collectors.toList()); + throw new RuntimeException("部分床位已被学生占用:" + String.join("、", occupiedBedNames) + ",请刷新后重试"); + } + + // 2. 验证学生状态 - 确保所有学生都未分配床位 + LambdaQueryWrapper studentQuery = Wrappers.lambdaQuery(); + studentQuery.in(XgStudentMsg::getId, studentIds) + .eq(XgStudentMsg::getDeleted, 0L); + List students = xgStudentMsgMapper.selectList(studentQuery); + + if (students.size() != studentIds.size()) { + throw new RuntimeException("部分学生不存在或已被删除"); + } + + // 检查学生是否已分配床位(通过dormitory_building、dormitory_room、ext_field1三个字段判断) + List alreadyAllocatedStudents = students.stream() + .filter(student -> (student.getDormitoryBuilding() != null + && !student.getDormitoryBuilding().trim().isEmpty()) || + (student.getDormitoryRoom() != null && !student.getDormitoryRoom().trim().isEmpty()) || + (student.getExtField1() != null && !student.getExtField1().trim().isEmpty())) + .collect(Collectors.toList()); + + if (!alreadyAllocatedStudents.isEmpty()) { + String allocatedStudentNames = alreadyAllocatedStudents.stream() + .map(student -> student.getName() + "(" + student.getStudentNo() + ")") + .collect(Collectors.joining("、")); + throw new RuntimeException("学生 " + allocatedStudentNames + " 已分配宿舍,请刷新后重试"); + } + + // 3. 根据分配类型进行床位分配 + List finalBedIds; + List finalStudentIds; + + if ("random".equals(allocationType)) { + // 随机分配:打乱床位顺序 + finalBedIds = new ArrayList<>(bedIds); + Collections.shuffle(finalBedIds); + finalStudentIds = new ArrayList<>(studentIds); + } else if ("studentNo".equals(allocationType)) { + // 按学号分配:学生按学号排序,床位按名称排序 + Map studentNoMap = students.stream() + .collect(Collectors.toMap(XgStudentMsg::getId, XgStudentMsg::getStudentNo)); + + finalStudentIds = studentIds.stream() + .sorted((id1, id2) -> { + String no1 = studentNoMap.get(id1); + String no2 = studentNoMap.get(id2); + return no1.compareToIgnoreCase(no2); + }) + .collect(Collectors.toList()); + + // 创建床位ID到床位信息的映射 + Map bedInfoMap = availableBeds.stream() + .collect(Collectors.toMap(XgDormBed::getId, bed -> bed)); + + // 创建房间ID到房间名称的映射 + Set roomIds = availableBeds.stream() + .map(XgDormBed::getRoomId) + .collect(Collectors.toSet()); + LambdaQueryWrapper roomQuery = Wrappers.lambdaQuery(); + roomQuery.in(XgDormRoom::getId, roomIds) + .eq(XgDormRoom::getDeleted, "0"); + List rooms = xgDormRoomMapper.selectList(roomQuery); + Map roomNameMap = rooms.stream() + .collect(Collectors.toMap(XgDormRoom::getId, XgDormRoom::getRoomName)); + + finalBedIds = bedIds.stream() + .sorted((id1, id2) -> { + XgDormBed bed1 = bedInfoMap.get(id1); + XgDormBed bed2 = bedInfoMap.get(id2); + String roomName1 = roomNameMap.get(bed1.getRoomId()); + String roomName2 = roomNameMap.get(bed2.getRoomId()); + + // 先按房间名称排序 + int roomCompare = roomName1.compareToIgnoreCase(roomName2); + if (roomCompare != 0) { + return roomCompare; + } + // 房间相同时,再按床位名称排序 + return bed1.getBedName().compareToIgnoreCase(bed2.getBedName()); + }) + .collect(Collectors.toList()); + } else { + throw new RuntimeException("不支持的分配类型:" + allocationType); + } + + // 创建床位-学生映射关系 + Map bedStudentMap = new HashMap<>(); + for (int i = 0; i < finalStudentIds.size(); i++) { + bedStudentMap.put(finalBedIds.get(i), finalStudentIds.get(i)); + } + + // 4. 更新或插入xg_dorm_bed_student表 + Map bedMap = availableBeds.stream() + .collect(Collectors.toMap(XgDormBed::getId, bed -> bed)); + Map studentMap = students.stream() + .collect(Collectors.toMap(XgStudentMsg::getId, student -> student)); + + // 查询现有的床位分配记录(已分配给班级但没有学生的记录) + LambdaQueryWrapper existingBedQuery = Wrappers.lambdaQuery(); + existingBedQuery.in(XgDormBedStudent::getBedId, bedIds) + .eq(XgDormBedStudent::getClassId, targetClassId) + .eq(XgDormBedStudent::getStatus, "0") + .eq(XgDormBedStudent::getDeleted, "0") + .isNull(XgDormBedStudent::getStudentId); // 没有学生的记录 + List existingBedRecords = baseMapper.selectList(existingBedQuery); + + // 创建床位ID到现有记录的映射 + Map existingBedMap = existingBedRecords.stream() + .collect(Collectors.toMap(XgDormBedStudent::getBedId, record -> record)); + + List bedStudentAllocations = new ArrayList<>(); + + for (Map.Entry entry : bedStudentMap.entrySet()) { + Long bedId = entry.getKey(); + Long studentId = entry.getValue(); + XgDormBed bed = bedMap.get(bedId); + XgStudentMsg student = studentMap.get(studentId); + + XgDormBedStudent existingRecord = existingBedMap.get(bedId); + if (existingRecord != null) { + // 更新现有记录:添加学生信息 + LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(); + updateWrapper.eq(XgDormBedStudent::getId, existingRecord.getId()) + .set(XgDormBedStudent::getStudentId, studentId) + .set(XgDormBedStudent::getAssignTime, now) + .set(XgDormBedStudent::getUpdateBy, loginUserId) + .set(XgDormBedStudent::getUpdateTime, now); + baseMapper.update(null, updateWrapper); + } else { + // 创建新的分配记录 + XgDormBedStudent allocation = new XgDormBedStudent(); + allocation.setBedId(bedId); + allocation.setStudentId(studentId); + allocation.setRoomId(bed.getRoomId()); + allocation.setBuildingId(getBuildingIdByBedId(bedId)); + allocation.setAreaId(getAreaIdByBedId(bedId)); + allocation.setCampusId(getCampusIdByBedId(bedId)); + + // 从学生信息获取学院、专业、班级信息 + allocation.setCollegeId(getCollegeIdByClassId(targetClassId)); + allocation.setMajorId(getMajorIdByClassId(targetClassId)); + allocation.setClassId(targetClassId); + + allocation.setStatus("0"); // 0正常 + allocation.setAssignTime(now); + allocation.setCreateBy(loginUserId); + allocation.setCreateTime(now); + allocation.setUpdateBy(loginUserId); + allocation.setUpdateTime(now); + allocation.setDeleted("0"); + + bedStudentAllocations.add(allocation); + } + } + + // 批量插入新的分配关系 + if (!bedStudentAllocations.isEmpty()) { + baseMapper.insertBatch(bedStudentAllocations); + } + + // 5. 批量更新xg_student_msg表 + for (Map.Entry entry : bedStudentMap.entrySet()) { + Long bedId = entry.getKey(); + Long studentId = entry.getValue(); + XgDormBed bed = bedMap.get(bedId); + + LambdaUpdateWrapper updateStudent = Wrappers.lambdaUpdate(); + updateStudent.eq(XgStudentMsg::getId, studentId) + .eq(XgStudentMsg::getDeleted, 0L) + .set(XgStudentMsg::getDormitoryBuilding, getBuildingIdByBedId(bedId).toString()) + .set(XgStudentMsg::getDormitoryRoom, bed.getRoomId().toString()) + .set(XgStudentMsg::getExtField1, bedId.toString()) + .set(XgStudentMsg::getUpdateBy, loginUserId) + .set(XgStudentMsg::getUpdateTime, now); + + xgStudentMsgMapper.update(null, updateStudent); + } + + // 6. 更新床位状态为已分配 + LambdaUpdateWrapper updateBed = Wrappers.lambdaUpdate(); + updateBed.in(XgDormBed::getId, bedStudentMap.keySet()) + .eq(XgDormBed::getDeleted, "0") + .set(XgDormBed::getBedStatus, "1") // 1已分配 + .set(XgDormBed::getUpdateBy, loginUserId) + .set(XgDormBed::getUpdateTime, now); + + xgDormBedMapper.update(null, updateBed); + + log.info("班主任选择性分配床位成功,分配了{}个床位给{}名学生", bedStudentMap.size(), finalStudentIds.size()); + return true; + + } catch (Exception e) { + log.error("班主任选择性分配床位失败", e); + throw new RuntimeException("选择性分配床位失败:" + e.getMessage()); + } + } + + /** + * 根据床位ID获取楼栋ID + */ + private Long getBuildingIdByBedId(Long bedId) { + // 通过床位ID查询房间ID,再通过房间ID查询楼栋ID + LambdaQueryWrapper bedQuery = Wrappers.lambdaQuery(); + bedQuery.eq(XgDormBed::getId, bedId).eq(XgDormBed::getDeleted, "0"); + XgDormBed bed = xgDormBedMapper.selectOne(bedQuery); + + if (bed != null && bed.getRoomId() != null) { + LambdaQueryWrapper roomQuery = Wrappers.lambdaQuery(); + roomQuery.eq(XgDormRoom::getId, bed.getRoomId()).eq(XgDormRoom::getDeleted, "0"); + XgDormRoom room = xgDormRoomMapper.selectOne(roomQuery); + + if (room != null) { + return room.getBuildingId(); + } + } + + return null; + } + + /** + * 根据床位ID获取宿舍区ID + */ + private Long getAreaIdByBedId(Long bedId) { + Long buildingId = getBuildingIdByBedId(bedId); + if (buildingId != null) { + LambdaQueryWrapper buildingQuery = Wrappers.lambdaQuery(); + buildingQuery.eq(XgDormBuilding::getId, buildingId).eq(XgDormBuilding::getDeleted, "0"); + XgDormBuilding building = xgDormBuildingMapper.selectOne(buildingQuery); + + if (building != null) { + return building.getAreaId(); + } + } + + return null; + } + + /** + * 根据床位ID获取校区ID + */ + private Long getCampusIdByBedId(Long bedId) { + Long buildingId = getBuildingIdByBedId(bedId); + if (buildingId != null) { + LambdaQueryWrapper buildingQuery = Wrappers.lambdaQuery(); + buildingQuery.eq(XgDormBuilding::getId, buildingId).eq(XgDormBuilding::getDeleted, "0"); + XgDormBuilding building = xgDormBuildingMapper.selectOne(buildingQuery); + + if (building != null) { + return building.getCampusId(); + } + } + + return null; + } + + /** + * 根据班级ID获取学院ID + */ + private Long getCollegeIdByClassId(Long classId) { + LambdaQueryWrapper classQuery = Wrappers.lambdaQuery(); + classQuery.eq(XgClassMsg::getId, classId).eq(XgClassMsg::getDeleted, 0L); + XgClassMsg classMsg = xgClassMsgMapper.selectOne(classQuery); + + if (classMsg != null) { + return classMsg.getCollegeId(); + } + + return null; + } + + /** + * 根据班级ID获取专业ID + */ + private Long getMajorIdByClassId(Long classId) { + LambdaQueryWrapper classQuery = Wrappers.lambdaQuery(); + classQuery.eq(XgClassMsg::getId, classId).eq(XgClassMsg::getDeleted, 0L); + XgClassMsg classMsg = xgClassMsgMapper.selectOne(classQuery); + + if (classMsg != null) { + return classMsg.getMajorId(); + } + + return null; + } + + /** + * 班主任一键收回床位 + * + * @param buildingId 宿舍楼ID,为空则全部收回 + * @return 是否收回成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean classTeacherRecallBeds(Long buildingId, Long classId) { + try { + Long targetClassId = classId != null ? classId : getCurrentUserClassId(); + // TODO: 实现班主任一键收回床位 + log.info("班主任一键收回床位,宿舍楼ID: {},classId:{}", buildingId, targetClassId); + return true; + } catch (Exception e) { + log.error("班主任一键收回床位失败", e); + throw new RuntimeException("收回床位失败:" + e.getMessage()); + } + } + + /** + * 班主任收回选中床位 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean classTeacherRecallSelectedBeds(List bedIds, Long classId) { + if (bedIds == null || bedIds.isEmpty()) { + throw new IllegalArgumentException("床位ID列表不能为空"); + } + + try { + Long targetClassId = classId != null ? classId : getCurrentUserClassId(); + Date now = new Date(); + Long loginUserId = LoginHelper.getUserId(); + + log.info("班主任收回选中床位,床位IDs: {},classId:{}", bedIds, targetClassId); + + // 1. 更新 xg_student_msg 表:根据 ext_field1 = 床位id,deleted = 0,student_status = 1 + // 的数据 + // 将 dormitory_building、dormitory_room、ext_field1 置空 + if (!bedIds.isEmpty()) { + LambdaUpdateWrapper updateStudentWrapper = Wrappers.lambdaUpdate(); + updateStudentWrapper + .in(XgStudentMsg::getExtField1, + bedIds.stream().map(String::valueOf).collect(java.util.stream.Collectors.toList())) + .eq(XgStudentMsg::getDeleted, 0L) + .eq(XgStudentMsg::getStudentStatus, "1") + .set(XgStudentMsg::getDormitoryBuilding, null) + .set(XgStudentMsg::getDormitoryRoom, null) + .set(XgStudentMsg::getExtField1, null) + .set(XgStudentMsg::getUpdateTime, now) + .set(XgStudentMsg::getUpdateBy, loginUserId); + + int studentUpdateCount = xgStudentMsgMapper.update(null, updateStudentWrapper); + log.info("更新学生信息表记录数: {}", studentUpdateCount); + } + + // 2. 更新 xg_dorm_bed_student 表:根据 bed_id = 床位id,deleted = 0,status = 0 + // 将 student_id、assign_time、checkout_time 置空 + LambdaUpdateWrapper updateBedStudentWrapper = Wrappers.lambdaUpdate(); + updateBedStudentWrapper.in(XgDormBedStudent::getBedId, bedIds) + .eq(XgDormBedStudent::getDeleted, "0") + .eq(XgDormBedStudent::getStatus, "0") + .set(XgDormBedStudent::getStudentId, null) + // .set(XgDormBedStudent::getAssignTime, null) + .set(XgDormBedStudent::getCheckoutTime, null) + .set(XgDormBedStudent::getUpdateTime, now) + .set(XgDormBedStudent::getUpdateBy, loginUserId); + + int bedStudentUpdateCount = baseMapper.update(null, updateBedStudentWrapper); + log.info("更新床位学生分配表记录数: {}", bedStudentUpdateCount); + + return true; + } catch (Exception e) { + log.error("班主任收回选中床位失败", e); + throw new RuntimeException("收回选中床位失败:" + e.getMessage()); + } + } + + /** + * 班主任退宿床位 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean classTeacherStayawaySelectedBeds(List bedIds, Long classId) { + if (bedIds == null || bedIds.isEmpty()) { + throw new IllegalArgumentException("床位ID列表不能为空"); + } + + try { + Long targetClassId = classId != null ? classId : getCurrentUserClassId(); + Date now = new Date(); + Long loginUserId = LoginHelper.getUserId(); + + log.info("班主任退宿选中床位,床位IDs: {},classId:{}", bedIds, targetClassId); + + // 1. 更新 xg_student_msg 表:根据 ext_field1 = 床位id,deleted = 0,student_status = 1 + // 的数据 + // 将 dormitory_building、dormitory_room、ext_field1 置空 + if (!bedIds.isEmpty()) { + LambdaUpdateWrapper updateStudentWrapper = Wrappers.lambdaUpdate(); + updateStudentWrapper + .in(XgStudentMsg::getExtField1, + bedIds.stream().map(String::valueOf).collect(java.util.stream.Collectors.toList())) + .eq(XgStudentMsg::getDeleted, 0L) + .eq(XgStudentMsg::getStudentStatus, "1") + .set(XgStudentMsg::getDormitoryBuilding, null) + .set(XgStudentMsg::getDormitoryRoom, null) + .set(XgStudentMsg::getExtField1, null) + .set(XgStudentMsg::getUpdateTime, now) + .set(XgStudentMsg::getUpdateBy, loginUserId); + + int studentUpdateCount = xgStudentMsgMapper.update(null, updateStudentWrapper); + log.info("更新学生信息表记录数: {}", studentUpdateCount); + } + + // 2. 更新 xg_dorm_bed_student 表:根据 bed_id = 床位id,deleted = 0,status = 0 + // 将 status = 1 , checkout_time = 当前时间 + LambdaUpdateWrapper updateBedStudentWrapper = Wrappers.lambdaUpdate(); + updateBedStudentWrapper.in(XgDormBedStudent::getBedId, bedIds) + .eq(XgDormBedStudent::getDeleted, "0") + .eq(XgDormBedStudent::getStatus, "0") + .set(XgDormBedStudent::getStatus, "1") + // .set(XgDormBedStudent::getAssignTime, null) + .set(XgDormBedStudent::getCheckoutTime, now) + .set(XgDormBedStudent::getUpdateTime, now) + .set(XgDormBedStudent::getUpdateBy, loginUserId); + + int bedStudentUpdateCount = baseMapper.update(null, updateBedStudentWrapper); + log.info("更新床位学生分配表记录数: {}", bedStudentUpdateCount); + + // 根据id跟床位id更新床位状态为空闲 + LambdaUpdateWrapper updateBedWrapper = Wrappers.lambdaUpdate(); + updateBedWrapper.in(XgDormBed::getId, bedIds) + .eq(XgDormBed::getDeleted, "0") + .set(XgDormBed::getBedStatus, "0"); + xgDormBedMapper.update(null, updateBedWrapper); + + return true; + } catch (Exception e) { + log.error("班主任退宿选中床位失败", e); + throw new RuntimeException("退宿选中床位失败:" + e.getMessage()); + } + } + + /** + * 班主任床位互换 + * 将两个学生的床位进行互换 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean classTeacherExchangeBeds(Long sourceStudentId, Long targetStudentId, Long classId) { + try { + log.info("班主任床位互换开始,源学生ID: {}, 目标学生ID: {}, 班级ID: {}", sourceStudentId, targetStudentId, classId); + + // 1. 校验参数 + if (sourceStudentId == null || targetStudentId == null) { + throw new RuntimeException("学生ID不能为空"); + } + if (sourceStudentId.equals(targetStudentId)) { + throw new RuntimeException("不能选择相同的学生进行互换"); + } + + Date now = new Date(); + Long loginUserId = LoginHelper.getUserId(); + + // 2. 查询两个学生当前的床位分配信息 + LambdaQueryWrapper sourceQueryWrapper = Wrappers.lambdaQuery(); + sourceQueryWrapper.eq(XgDormBedStudent::getStudentId, sourceStudentId) + .eq(XgDormBedStudent::getDeleted, "0") + .eq(XgDormBedStudent::getStatus, "0"); + XgDormBedStudent sourceBedStudent = baseMapper.selectOne(sourceQueryWrapper); + + LambdaQueryWrapper targetQueryWrapper = Wrappers.lambdaQuery(); + targetQueryWrapper.eq(XgDormBedStudent::getStudentId, targetStudentId) + .eq(XgDormBedStudent::getDeleted, "0") + .eq(XgDormBedStudent::getStatus, "0"); + XgDormBedStudent targetBedStudent = baseMapper.selectOne(targetQueryWrapper); + + if (sourceBedStudent == null || targetBedStudent == null) { + throw new RuntimeException("学生必须都已分配床位才能互换"); + } + + // 3. 验证班级权限(如果指定了班级ID) + if (classId != null) { + if (!classId.equals(sourceBedStudent.getClassId()) || !classId.equals(targetBedStudent.getClassId())) { + throw new RuntimeException("没有权限操作这些学生床位"); + } + } + + // 4. 查询两个学生在学生信息表中的宿舍信息 + XgStudentMsg sourceStudent = xgStudentMsgMapper.selectById(sourceStudentId); + XgStudentMsg targetStudent = xgStudentMsgMapper.selectById(targetStudentId); + + if (sourceStudent == null || targetStudent == null) { + throw new RuntimeException("未找到学生信息"); + } + + // 5. 互换学生信息表中的宿舍信息 + String tempDormitoryBuilding = sourceStudent.getDormitoryBuilding(); + String tempDormitoryRoom = sourceStudent.getDormitoryRoom(); + String tempExtField1 = sourceStudent.getExtField1(); + + // 更新源学生的宿舍信息 + sourceStudent.setDormitoryBuilding(targetStudent.getDormitoryBuilding()); + sourceStudent.setDormitoryRoom(targetStudent.getDormitoryRoom()); + sourceStudent.setExtField1(targetStudent.getExtField1()); + sourceStudent.setUpdateTime(now); + sourceStudent.setUpdateBy(loginUserId); + + // 更新目标学生的宿舍信息 + targetStudent.setDormitoryBuilding(tempDormitoryBuilding); + targetStudent.setDormitoryRoom(tempDormitoryRoom); + targetStudent.setExtField1(tempExtField1); + targetStudent.setUpdateTime(now); + targetStudent.setUpdateBy(loginUserId); + + // 6. 更新学生信息表 + xgStudentMsgMapper.updateById(sourceStudent); + xgStudentMsgMapper.updateById(targetStudent); + + // 7. 更新床位分配表中原记录的状态为已退宿 + sourceBedStudent.setStatus("1"); + sourceBedStudent.setCheckoutTime(now); + sourceBedStudent.setUpdateTime(now); + sourceBedStudent.setUpdateBy(loginUserId); + + targetBedStudent.setStatus("1"); + targetBedStudent.setCheckoutTime(now); + targetBedStudent.setUpdateTime(now); + targetBedStudent.setUpdateBy(loginUserId); + + baseMapper.updateById(sourceBedStudent); + baseMapper.updateById(targetBedStudent); + + // 8. 插入两条新的床位分配记录 + XgDormBedStudent newSourceBedStudent = new XgDormBedStudent(); + newSourceBedStudent.setCollegeId(targetBedStudent.getCollegeId()); + newSourceBedStudent.setMajorId(targetBedStudent.getMajorId()); + newSourceBedStudent.setClassId(targetBedStudent.getClassId()); + newSourceBedStudent.setStudentId(sourceStudentId); + newSourceBedStudent.setBedId(targetBedStudent.getBedId()); + newSourceBedStudent.setRoomId(targetBedStudent.getRoomId()); + newSourceBedStudent.setBuildingId(targetBedStudent.getBuildingId()); + newSourceBedStudent.setAreaId(targetBedStudent.getAreaId()); + newSourceBedStudent.setCampusId(targetBedStudent.getCampusId()); + newSourceBedStudent.setStatus("0"); + newSourceBedStudent.setAssignTime(now); + newSourceBedStudent.setDeleted("0"); + newSourceBedStudent.setCreateTime(now); + newSourceBedStudent.setCreateBy(loginUserId); + + XgDormBedStudent newTargetBedStudent = new XgDormBedStudent(); + newTargetBedStudent.setCollegeId(sourceBedStudent.getCollegeId()); + newTargetBedStudent.setMajorId(sourceBedStudent.getMajorId()); + newTargetBedStudent.setClassId(sourceBedStudent.getClassId()); + newTargetBedStudent.setStudentId(targetStudentId); + newTargetBedStudent.setBedId(sourceBedStudent.getBedId()); + newTargetBedStudent.setRoomId(sourceBedStudent.getRoomId()); + newTargetBedStudent.setBuildingId(sourceBedStudent.getBuildingId()); + newTargetBedStudent.setAreaId(sourceBedStudent.getAreaId()); + newTargetBedStudent.setCampusId(sourceBedStudent.getCampusId()); + newTargetBedStudent.setStatus("0"); + newTargetBedStudent.setAssignTime(now); + newTargetBedStudent.setDeleted("0"); + newTargetBedStudent.setCreateTime(now); + newTargetBedStudent.setCreateBy(loginUserId); + + baseMapper.insert(newSourceBedStudent); + baseMapper.insert(newTargetBedStudent); + + log.info("班主任床位互换成功,源学生{}分配到床位{},目标学生{}分配到床位{}", + sourceStudentId, targetBedStudent.getBedId(), + targetStudentId, sourceBedStudent.getBedId()); + return true; + + } catch (Exception e) { + log.error("班主任床位互换失败", e); + throw new RuntimeException("床位互换失败:" + e.getMessage()); + } + } + + /** + * 班主任床位移动 + * 将学生从当前床位移动到指定的空闲床位 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean classTeacherMoveBed(Long sourceStudentId, Long targetBedId, Long classId) { + try { + log.info("班主任床位移动开始,源学生ID: {}, 目标床位ID: {}, 班级ID: {}", sourceStudentId, targetBedId, classId); + + // 1. 校验参数 + if (sourceStudentId == null || targetBedId == null) { + throw new RuntimeException("学生ID和目标床位ID不能为空"); + } + + Date now = new Date(); + Long loginUserId = LoginHelper.getUserId(); + + // 2. 查询学生当前的床位分配信息 + LambdaQueryWrapper sourceQueryWrapper = Wrappers.lambdaQuery(); + sourceQueryWrapper.eq(XgDormBedStudent::getStudentId, sourceStudentId) + .eq(XgDormBedStudent::getDeleted, "0") + .eq(XgDormBedStudent::getStatus, "0"); + XgDormBedStudent sourceBedStudent = baseMapper.selectOne(sourceQueryWrapper); + + if (sourceBedStudent == null) { + throw new RuntimeException("学生未分配床位"); + } + + // 3. 验证班级权限(如果指定了班级ID) + if (classId != null && !classId.equals(sourceBedStudent.getClassId())) { + throw new RuntimeException("没有权限操作此学生床位"); + } + + // 4. 检查目标床位是否空闲 + LambdaQueryWrapper targetQueryWrapper = Wrappers.lambdaQuery(); + targetQueryWrapper.eq(XgDormBedStudent::getBedId, targetBedId) + .eq(XgDormBedStudent::getDeleted, "0") + .eq(XgDormBedStudent::getStatus, "0") + .isNotNull(XgDormBedStudent::getStudentId); + XgDormBedStudent targetBedStudent = baseMapper.selectOne(targetQueryWrapper); + + if (targetBedStudent != null) { + throw new RuntimeException("目标床位已被占用"); + } + + // 5. 检查目标床位是否存在且可用 + XgDormBed targetBed = xgDormBedMapper.selectById(targetBedId); + if (targetBed == null || !"0".equals(targetBed.getDeleted())) { + throw new RuntimeException("目标床位不存在"); + } + if (!"1".equals(targetBed.getBedStatus())) { + throw new RuntimeException("目标床位不可用(维修中或其他状态)"); + } + + // 6. 检查是否是移动到相同床位 + if (sourceBedStudent.getBedId().equals(targetBedId)) { + throw new RuntimeException("学生已在目标床位,无需移动"); + } + + // 7. 查询学生信息,准备更新宿舍信息 + XgStudentMsg student = xgStudentMsgMapper.selectById(sourceStudentId); + if (student == null) { + throw new RuntimeException("未找到学生信息"); + } + + // 8. 查询目标床位的详细信息以更新学生宿舍信息 + XgDormRoom targetRoom = xgDormRoomMapper.selectById(targetBed.getRoomId()); + if (targetRoom == null) { + throw new RuntimeException("目标床位房间信息不存在"); + } + + // 获取目标床位的楼栋ID、宿舍区ID、校区ID + Long targetBuildingId = getBuildingIdByBedId(targetBedId); + Long targetAreaId = getAreaIdByBedId(targetBedId); + Long targetCampusId = getCampusIdByBedId(targetBedId); + + if (targetBuildingId == null || targetAreaId == null || targetCampusId == null) { + throw new RuntimeException("目标床位层级信息不完整"); + } + + // 9. 更新学生信息表中的宿舍信息 + student.setDormitoryBuilding(String.valueOf(targetBuildingId)); + student.setDormitoryRoom(String.valueOf(targetBed.getRoomId())); + student.setExtField1(String.valueOf(targetBedId)); + student.setUpdateTime(now); + student.setUpdateBy(loginUserId); + xgStudentMsgMapper.updateById(student); + + // 10. 更新原床位分配记录状态为已退宿 + sourceBedStudent.setStatus("1"); + sourceBedStudent.setCheckoutTime(now); + sourceBedStudent.setUpdateTime(now); + sourceBedStudent.setUpdateBy(loginUserId); + baseMapper.updateById(sourceBedStudent); + + // 11. 为原床位创建一个新的空闲分配记录(保持分配给班级但没有学生) + XgDormBedStudent newSourceBedRecord = new XgDormBedStudent(); + newSourceBedRecord.setCollegeId(sourceBedStudent.getCollegeId()); + newSourceBedRecord.setMajorId(sourceBedStudent.getMajorId()); + newSourceBedRecord.setClassId(sourceBedStudent.getClassId()); + newSourceBedRecord.setStudentId(null); // 空闲状态,没有学生 + newSourceBedRecord.setBedId(sourceBedStudent.getBedId()); + newSourceBedRecord.setRoomId(sourceBedStudent.getRoomId()); + newSourceBedRecord.setBuildingId(sourceBedStudent.getBuildingId()); + newSourceBedRecord.setAreaId(sourceBedStudent.getAreaId()); + newSourceBedRecord.setCampusId(sourceBedStudent.getCampusId()); + newSourceBedRecord.setStatus("0"); // 正常状态 + newSourceBedRecord.setAssignTime(sourceBedStudent.getAssignTime()); // 没有分配给具体学生 + newSourceBedRecord.setDeleted("0"); + newSourceBedRecord.setCreateTime(now); + newSourceBedRecord.setCreateBy(loginUserId); + newSourceBedRecord.setUpdateTime(now); + newSourceBedRecord.setUpdateBy(loginUserId); + baseMapper.insert(newSourceBedRecord); + + // 12. 查找目标床位的现有分配记录并更新student_id + LambdaQueryWrapper targetBedQuery = Wrappers.lambdaQuery(); + targetBedQuery.eq(XgDormBedStudent::getBedId, targetBedId) + .eq(XgDormBedStudent::getDeleted, "0") + .eq(XgDormBedStudent::getStatus, "0") + .isNull(XgDormBedStudent::getStudentId); // 已分配给班级但没有学生的记录 + XgDormBedStudent targetBedRecord = baseMapper.selectOne(targetBedQuery); + + if (targetBedRecord != null) { + // 更新现有记录,添加学生信息 + LambdaUpdateWrapper updateTargetWrapper = Wrappers.lambdaUpdate(); + updateTargetWrapper.eq(XgDormBedStudent::getId, targetBedRecord.getId()) + .set(XgDormBedStudent::getStudentId, sourceStudentId) + .set(XgDormBedStudent::getAssignTime, now) + .set(XgDormBedStudent::getUpdateBy, loginUserId) + .set(XgDormBedStudent::getUpdateTime, now); + baseMapper.update(null, updateTargetWrapper); + } else { + // 如果目标床位没有现有的分配记录,创建新的分配记录 + XgDormBedStudent newTargetBedRecord = new XgDormBedStudent(); + newTargetBedRecord.setCollegeId(sourceBedStudent.getCollegeId()); + newTargetBedRecord.setMajorId(sourceBedStudent.getMajorId()); + newTargetBedRecord.setClassId(sourceBedStudent.getClassId()); + newTargetBedRecord.setStudentId(sourceStudentId); + newTargetBedRecord.setBedId(targetBedId); + newTargetBedRecord.setRoomId(targetBed.getRoomId()); + newTargetBedRecord.setBuildingId(targetBuildingId); + newTargetBedRecord.setAreaId(targetAreaId); + newTargetBedRecord.setCampusId(targetCampusId); + newTargetBedRecord.setStatus("0"); // 正常状态 + newTargetBedRecord.setAssignTime(now); + newTargetBedRecord.setDeleted("0"); + newTargetBedRecord.setCreateTime(now); + newTargetBedRecord.setCreateBy(loginUserId); + newTargetBedRecord.setUpdateTime(now); + newTargetBedRecord.setUpdateBy(loginUserId); + baseMapper.insert(newTargetBedRecord); + } + + log.info("班主任床位移动成功,学生{}从床位{}移动到床位{}", + sourceStudentId, sourceBedStudent.getBedId(), targetBedId); + return true; + + } catch (Exception e) { + log.error("班主任床位移动失败", e); + throw new RuntimeException("床位移动失败:" + e.getMessage()); + } + } + + /** + * 获取班级所有已入住宿舍的学生床位信息 + * 包含床位信息(校区名称、宿舍区名称、楼栋名称、房间名称、床位名称) + * 和学生信息(学生姓名、学号、性别) + */ + @Override + public List getClassOccupiedBedDetails(Long classId) { + try { + log.info("开始获取班级已入住床位详情,班级ID: {}", classId); + + // 使用SQL关联查询,一次性获取所有信息并按楼栋、房间、床位排序 + List> bedDetails = baseMapper.selectClassOccupiedBedDetails(classId); + + List result = new ArrayList<>(); + + for (Map detailMap : bedDetails) { + // 组装结果数据 + Map detailInfo = new LinkedHashMap<>(); + + // 床位信息 + detailInfo.put("bedId", detailMap.get("bed_id")); + detailInfo.put("campusName", detailMap.get("campus_name")); + detailInfo.put("areaName", detailMap.get("area_name")); + detailInfo.put("buildingName", detailMap.get("building_name")); + detailInfo.put("roomName", detailMap.get("room_name")); + detailInfo.put("bedName", detailMap.get("bed_name")); + detailInfo.put("bedType", detailMap.get("bed_type")); + + // 床位性别类型(基于楼栋类型) + String bedGenderType = (String) detailMap.get("building_type"); // 1男生楼 2女生楼 3混合楼 + detailInfo.put("bedGenderType", bedGenderType); + + // 学生信息 + detailInfo.put("studentId", detailMap.get("student_id")); + detailInfo.put("studentName", detailMap.get("student_name")); + detailInfo.put("studentNo", detailMap.get("student_no")); + detailInfo.put("gender", detailMap.get("gender")); + + result.add(detailInfo); + } + + log.info("获取班级已入住床位详情成功,共{}条记录", result.size()); + return result; + + } catch (Exception e) { + log.error("获取班级已入住床位详情失败", e); + return Collections.emptyList(); + } + } + + /** + * 获取班级所有未安排学生的床位信息 + * 包含床位信息(校区名称、宿舍区名称、楼栋名称、房间名称、床位名称) + */ + @Override + public List getClassUnoccupiedBedDetails(Long classId) { + try { + log.info("开始获取班级未分配学生床位详情,班级ID: {}", classId); + + // 使用SQL关联查询,一次性获取所有信息并按楼栋、房间、床位排序 + List> bedDetails = baseMapper.selectClassUnoccupiedBedDetails(classId); + + List result = new ArrayList<>(); + + for (Map detailMap : bedDetails) { + // 组装结果数据 + Map detailInfo = new LinkedHashMap<>(); + + // 床位信息 + detailInfo.put("bedId", detailMap.get("bed_id")); + detailInfo.put("campusName", detailMap.get("campus_name")); + detailInfo.put("areaName", detailMap.get("area_name")); + detailInfo.put("buildingName", detailMap.get("building_name")); + detailInfo.put("roomName", detailMap.get("room_name")); + detailInfo.put("bedName", detailMap.get("bed_name")); + detailInfo.put("bedType", detailMap.get("bed_type")); + + // 床位性别类型(基于楼栋类型) + String bedGenderType = (String) detailMap.get("building_type"); // 1男生楼 2女生楼 3混合楼 + detailInfo.put("bedGenderType", bedGenderType); + + result.add(detailInfo); + } + + log.info("获取班级未分配学生床位详情成功,共{}条记录", result.size()); + return result; + + } catch (Exception e) { + log.error("获取班级未分配学生床位详情失败", e); + return Collections.emptyList(); + } + } + + /** + * 根据房间ID获取住在当前房间的所有学生信息 + * 包含学生姓名、学号、性别、学院名称、专业名称、班级名称、床位编码、床位ID + */ + @Override + public List getRoomStudents(Long roomId) { + try { + log.info("开始获取房间学生信息,房间ID: {}", roomId); + + // 使用SQL关联查询,一次性获取所有信息 + List> studentDetails = baseMapper.selectRoomStudents(roomId); + + List result = new ArrayList<>(); + + for (Map detailMap : studentDetails) { + // 组装结果数据 + Map studentInfo = new LinkedHashMap<>(); + + // 学生基本信息 + studentInfo.put("studentId", detailMap.get("student_id")); + studentInfo.put("studentName", detailMap.get("student_name")); + studentInfo.put("studentNo", detailMap.get("student_no")); + studentInfo.put("gender", detailMap.get("gender")); + + // 学院、专业、班级信息 + studentInfo.put("collegeName", detailMap.get("college_name")); + studentInfo.put("majorName", detailMap.get("major_name")); + studentInfo.put("className", detailMap.get("class_name")); + + // 床位信息 + studentInfo.put("bedName", detailMap.get("bed_name")); + studentInfo.put("bedCode", detailMap.get("bed_code")); + studentInfo.put("bedId", detailMap.get("bed_id")); + + result.add(studentInfo); + } + + log.info("获取房间学生信息成功,共{}条记录", result.size()); + return result; + + } catch (Exception e) { + log.error("获取房间学生信息失败", e); + return Collections.emptyList(); + } + } + } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormHygieneCheckServiceImpl.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormHygieneCheckServiceImpl.java new file mode 100644 index 00000000..207cf006 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormHygieneCheckServiceImpl.java @@ -0,0 +1,511 @@ +package org.dromara.xg.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.dromara.xg.domain.bo.XgDormHygieneCheckBo; +import org.dromara.xg.domain.bo.XgDormHygieneCheckCompleteBo; +import org.dromara.xg.domain.bo.XgDormHygieneStudentScoreBo; +import org.dromara.xg.domain.vo.DormHygieneCheckExportVo; +import org.dromara.xg.domain.vo.XgDormHygieneCheckVo; +import org.dromara.xg.domain.XgDormHygieneCheck; +import org.dromara.xg.domain.XgDormHygieneStudentScore; +import org.dromara.xg.mapper.XgDormHygieneCheckMapper; +import org.dromara.xg.mapper.XgDormHygieneStudentScoreMapper; +import org.dromara.xg.service.IXgDormHygieneCheckService; +import org.dromara.xg.service.IXgStudentMsgService; +import org.dromara.system.service.ISysDictDataService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 宿舍卫生检查Service业务层处理 + * + * @author zlwu + * @date 2025-08-16 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class XgDormHygieneCheckServiceImpl implements IXgDormHygieneCheckService { + + private final XgDormHygieneCheckMapper baseMapper; + private final XgDormHygieneStudentScoreMapper studentScoreMapper; + private final IXgStudentMsgService xgStudentMsgService; + private final ISysDictDataService sysDictDataService; + + /** + * 查询宿舍卫生检查 + * + * @param id 主键 + * @return 宿舍卫生检查 + */ + @Override + public XgDormHygieneCheckVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询宿舍卫生检查列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 宿舍卫生检查分页列表 + */ + @Override + public TableDataInfo queryPageList(XgDormHygieneCheckBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的宿舍卫生检查列表 + * + * @param bo 查询条件 + * @return 宿舍卫生检查列表 + */ + @Override + public List queryList(XgDormHygieneCheckBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(XgDormHygieneCheckBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + // lqw.orderByAsc(XgDormHygieneCheck::getId); + lqw.orderByDesc(XgDormHygieneCheck::getCreateTime); + lqw.eq(bo.getDeleted() != null, XgDormHygieneCheck::getDeleted, bo.getDeleted()); + lqw.like(StringUtils.isNotBlank(bo.getCheckTitle()), XgDormHygieneCheck::getCheckTitle, bo.getCheckTitle()); + lqw.eq(bo.getCheckDate() != null, XgDormHygieneCheck::getCheckDate, bo.getCheckDate()); + lqw.eq(bo.getCheckerId() != null, XgDormHygieneCheck::getCheckerId, bo.getCheckerId()); + lqw.like(StringUtils.isNotBlank(bo.getCheckerName()), XgDormHygieneCheck::getCheckerName, bo.getCheckerName()); + lqw.eq(StringUtils.isNotBlank(bo.getCheckType()), XgDormHygieneCheck::getCheckType, bo.getCheckType()); + lqw.eq(StringUtils.isNotBlank(bo.getCheckLevel()), XgDormHygieneCheck::getCheckLevel, bo.getCheckLevel()); + lqw.eq(bo.getRoomId() != null, XgDormHygieneCheck::getRoomId, bo.getRoomId()); + lqw.like(StringUtils.isNotBlank(bo.getRoomCode()), XgDormHygieneCheck::getRoomCode, bo.getRoomCode()); + lqw.like(StringUtils.isNotBlank(bo.getRoomName()), XgDormHygieneCheck::getRoomName, bo.getRoomName()); + lqw.eq(bo.getBuildingId() != null, XgDormHygieneCheck::getBuildingId, bo.getBuildingId()); + lqw.like(StringUtils.isNotBlank(bo.getBuildingName()), XgDormHygieneCheck::getBuildingName, + bo.getBuildingName()); + lqw.eq(bo.getAreaId() != null, XgDormHygieneCheck::getAreaId, bo.getAreaId()); + lqw.like(StringUtils.isNotBlank(bo.getAreaName()), XgDormHygieneCheck::getAreaName, bo.getAreaName()); + lqw.eq(bo.getCampusId() != null, XgDormHygieneCheck::getCampusId, bo.getCampusId()); + lqw.like(StringUtils.isNotBlank(bo.getCampusName()), XgDormHygieneCheck::getCampusName, bo.getCampusName()); + lqw.eq(bo.getTotalScore() != null, XgDormHygieneCheck::getTotalScore, bo.getTotalScore()); + lqw.eq(StringUtils.isNotBlank(bo.getHygieneGrade()), XgDormHygieneCheck::getHygieneGrade, bo.getHygieneGrade()); + lqw.eq(StringUtils.isNotBlank(bo.getAttachmentIds()), XgDormHygieneCheck::getAttachmentIds, + bo.getAttachmentIds()); + lqw.like(StringUtils.isNotBlank(bo.getCheckSummary()), XgDormHygieneCheck::getCheckSummary, + bo.getCheckSummary()); + lqw.like(StringUtils.isNotBlank(bo.getImprovementSuggestions()), XgDormHygieneCheck::getImprovementSuggestions, + bo.getImprovementSuggestions()); + lqw.like(StringUtils.isNotBlank(bo.getCheckRemarks()), XgDormHygieneCheck::getCheckRemarks, + bo.getCheckRemarks()); + return lqw; + } + + /** + * 新增宿舍卫生检查 + * + * @param bo 宿舍卫生检查 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(XgDormHygieneCheckBo bo) { + XgDormHygieneCheck add = MapstructUtils.convert(bo, XgDormHygieneCheck.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改宿舍卫生检查 + * + * @param bo 宿舍卫生检查 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(XgDormHygieneCheckBo bo) { + XgDormHygieneCheck update = MapstructUtils.convert(bo, XgDormHygieneCheck.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(XgDormHygieneCheck entity) { + // TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除宿舍卫生检查信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + /** + * 新增宿舍卫生检查(包含学生评分数据) + * + * @param bo 完整的宿舍卫生检查数据,包含学生评分信息 + * @return 是否新增成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertWithStudentScores(XgDormHygieneCheckCompleteBo bo) { + // 1. 转换并保存主表数据 + XgDormHygieneCheck add = MapstructUtils.convert(bo, XgDormHygieneCheck.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + log.info("add: {}", add); + log.info("bo: {}", bo); + log.info("flag: {}", flag); + + if (flag && bo.getStudentScores() != null && !bo.getStudentScores().isEmpty()) { + // 2. 保存学生评分数据 + for (XgDormHygieneStudentScoreBo studentScoreBo : bo.getStudentScores()) { + XgDormHygieneStudentScore studentScore = MapstructUtils.convert(studentScoreBo, + XgDormHygieneStudentScore.class); + studentScore.setHygieneCheckId(add.getId()); + + // 根据学生ID查询学生完整信息,补充学院、专业、班级信息 + if (studentScoreBo.getStudentId() != null) { + try { + var studentInfo = xgStudentMsgService.queryById(studentScoreBo.getStudentId()); + if (studentInfo != null) { + // 补充学院信息 + if (studentScore.getCollegeId() == null + && StringUtils.isNotBlank(studentInfo.getCollegeId())) { + try { + studentScore.setCollegeId(Long.valueOf(studentInfo.getCollegeId())); + } catch (NumberFormatException e) { + log.warn("学院ID转换失败,学生ID: {}, 学院ID: {}", studentScoreBo.getStudentId(), + studentInfo.getCollegeId()); + } + } + if (StringUtils.isBlank(studentScore.getCollegeName())) { + studentScore.setCollegeName(studentInfo.getCollegeName()); + } + + // 补充专业信息 + if (studentScore.getMajorId() == null && StringUtils.isNotBlank(studentInfo.getMajorId())) { + try { + studentScore.setMajorId(Long.valueOf(studentInfo.getMajorId())); + } catch (NumberFormatException e) { + log.warn("专业ID转换失败,学生ID: {}, 专业ID: {}", studentScoreBo.getStudentId(), + studentInfo.getMajorId()); + } + } + if (StringUtils.isBlank(studentScore.getMajorName())) { + studentScore.setMajorName(studentInfo.getMajorName()); + } + + // 补充班级信息 + if (studentScore.getClassId() == null && StringUtils.isNotBlank(studentInfo.getClassId())) { + try { + studentScore.setClassId(Long.valueOf(studentInfo.getClassId())); + } catch (NumberFormatException e) { + log.warn("班级ID转换失败,学生ID: {}, 班级ID: {}", studentScoreBo.getStudentId(), + studentInfo.getClassId()); + } + } + if (StringUtils.isBlank(studentScore.getClassName())) { + studentScore.setClassName(studentInfo.getClassName()); + } + + log.info("补充学生信息 - 学生ID: {}, 学院: {}, 专业: {}, 班级: {}", + studentScoreBo.getStudentId(), + studentInfo.getCollegeName(), + studentInfo.getMajorName(), + studentInfo.getClassName()); + } + } catch (Exception e) { + log.warn("查询学生信息失败,学生ID: {}, 错误: {}", studentScoreBo.getStudentId(), e.getMessage()); + } + } + + log.info("studentScore: {}", studentScore); + log.info("studentScoreBo: {}", studentScoreBo); + studentScoreMapper.insert(studentScore); + } + } + + return flag; + } + + /** + * 修改宿舍卫生检查(包含学生评分数据) + * + * @param bo 完整的宿舍卫生检查数据,包含学生评分信息 + * @return 是否修改成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateWithStudentScores(XgDormHygieneCheckCompleteBo bo) { + // 1. 更新主表数据 + XgDormHygieneCheck update = MapstructUtils.convert(bo, XgDormHygieneCheck.class); + validEntityBeforeSave(update); + boolean flag = baseMapper.updateById(update) > 0; + + if (flag && bo.getStudentScores() != null && !bo.getStudentScores().isEmpty()) { + // 2. 逻辑删除原有学生评分记录 + LambdaQueryWrapper deleteWrapper = Wrappers.lambdaQuery(); + deleteWrapper.eq(XgDormHygieneStudentScore::getHygieneCheckId, bo.getId()); + studentScoreMapper.delete(deleteWrapper); + + // 3. 插入新的学生评分记录 + for (XgDormHygieneStudentScoreBo studentScoreBo : bo.getStudentScores()) { + XgDormHygieneStudentScore studentScore = MapstructUtils.convert(studentScoreBo, + XgDormHygieneStudentScore.class); + studentScore.setHygieneCheckId(bo.getId()); + studentScore.setId(null); + // 根据学生ID查询学生完整信息,补充学院、专业、班级信息 + if (studentScoreBo.getStudentId() != null) { + try { + var studentInfo = xgStudentMsgService.queryById(studentScoreBo.getStudentId()); + if (studentInfo != null) { + // 补充学院信息 + if (studentScore.getCollegeId() == null + && StringUtils.isNotBlank(studentInfo.getCollegeId())) { + try { + studentScore.setCollegeId(Long.valueOf(studentInfo.getCollegeId())); + } catch (NumberFormatException e) { + log.warn("学院ID转换失败,学生ID: {}, 学院ID: {}", studentScoreBo.getStudentId(), + studentInfo.getCollegeId()); + } + } + if (StringUtils.isBlank(studentScore.getCollegeName())) { + studentScore.setCollegeName(studentInfo.getCollegeName()); + } + + // 补充专业信息 + if (studentScore.getMajorId() == null && StringUtils.isNotBlank(studentInfo.getMajorId())) { + try { + studentScore.setMajorId(Long.valueOf(studentInfo.getMajorId())); + } catch (NumberFormatException e) { + log.warn("专业ID转换失败,学生ID: {}, 专业ID: {}", studentScoreBo.getStudentId(), + studentInfo.getMajorId()); + } + } + if (StringUtils.isBlank(studentScore.getMajorName())) { + studentScore.setMajorName(studentInfo.getMajorName()); + } + + // 补充班级信息 + if (studentScore.getClassId() == null && StringUtils.isNotBlank(studentInfo.getClassId())) { + try { + studentScore.setClassId(Long.valueOf(studentInfo.getClassId())); + } catch (NumberFormatException e) { + log.warn("班级ID转换失败,学生ID: {}, 班级ID: {}", studentScoreBo.getStudentId(), + studentInfo.getClassId()); + } + } + if (StringUtils.isBlank(studentScore.getClassName())) { + studentScore.setClassName(studentInfo.getClassName()); + } + + log.info("更新时补充学生信息 - 学生ID: {}, 学院: {}, 专业: {}, 班级: {}", + studentScoreBo.getStudentId(), + studentInfo.getCollegeName(), + studentInfo.getMajorName(), + studentInfo.getClassName()); + } + } catch (Exception e) { + log.warn("查询学生信息失败,学生ID: {}, 错误: {}", studentScoreBo.getStudentId(), e.getMessage()); + } + } + + studentScoreMapper.insert(studentScore); + } + } + + return flag; + } + + /** + * 查询宿舍卫生检查导出列表(包含学生卫生情况) + * + * @param bo 查询条件 + * @return 导出数据列表 + */ + @Override + public List queryExportList(XgDormHygieneCheckBo bo) { + // 调用mapper查询导出数据 + List exportList = baseMapper.selectExportList(bo); + + // 转换字典值为文本 + for (DormHygieneCheckExportVo exportVo : exportList) { + // 转换检查类型 + exportVo.setCheckTypeText(convertCheckTypeToText(exportVo.getCheckTypeText())); + + // 转换检查级别 + exportVo.setCheckLevelText(convertCheckLevelToText(exportVo.getCheckLevelText())); + + // 转换卫生等级 + exportVo.setHygieneGradeText(convertHygieneGradeToText(exportVo.getHygieneGradeText())); + + // 转换出勤状态 + exportVo.setAttendanceStatusText(convertAttendanceStatusToText(exportVo.getAttendanceStatusText())); + + // 转换学生评级 + exportVo.setStudentGradeText(convertStudentGradeToText(exportVo.getStudentGradeText())); + + // 转换是否寝室长 + // exportVo.setIsResponsiblePersonText(convertResponsiblePersonToText(exportVo.getIsResponsiblePersonText())); + } + + return exportList; + } + + /** + * 根据学生ID查询该学生宿舍的卫生检查信息 + * + * @param studentId 学生ID + * @return 卫生检查信息列表 + */ + @Override + public List queryByStudentId(Long studentId) { + if (studentId == null) { + log.warn("学生ID为空,无法查询卫生检查信息"); + return List.of(); + } + + // 1. 通过学生ID查询学生评分记录,获取卫生检查ID列表 + LambdaQueryWrapper studentScoreWrapper = Wrappers.lambdaQuery(); + studentScoreWrapper.eq(XgDormHygieneStudentScore::getStudentId, studentId); + studentScoreWrapper.eq(XgDormHygieneStudentScore::getDeleted, 0); // 只查询未删除的记录 + List studentScores = studentScoreMapper.selectList(studentScoreWrapper); + + if (studentScores.isEmpty()) { + log.info("学生ID: {} 没有找到相关的卫生检查记录", studentId); + return List.of(); + } + + // 2. 提取卫生检查ID列表 + List hygieneCheckIds = studentScores.stream() + .map(XgDormHygieneStudentScore::getHygieneCheckId) + .distinct() + .toList(); + + // 3. 根据卫生检查ID列表查询卫生检查信息 + LambdaQueryWrapper hygieneCheckWrapper = Wrappers.lambdaQuery(); + hygieneCheckWrapper.in(XgDormHygieneCheck::getId, hygieneCheckIds); + hygieneCheckWrapper.eq(XgDormHygieneCheck::getDeleted, 0); // 只查询未删除的记录 + hygieneCheckWrapper.orderByDesc(XgDormHygieneCheck::getCreateTime); // 按创建时间倒序排列 + + List result = baseMapper.selectVoList(hygieneCheckWrapper); + + log.info("学生ID: {} 查询到 {} 条卫生检查记录", studentId, result.size()); + return result; + } + + /** + * 转换检查类型代码为文本 + */ + private String convertCheckTypeToText(String checkType) { + if (StringUtils.isEmpty(checkType)) { + return ""; + } + try { + return sysDictDataService.selectDictLabel("xg_dorm_hygiene_check_type", checkType); + } catch (Exception e) { + log.warn("获取字典标签失败,字典类型: xg_dorm_hygiene_check_type, 字典值: {}, 错误: {}", checkType, e.getMessage()); + return checkType; + } + } + + /** + * 转换检查级别代码为文本 + */ + private String convertCheckLevelToText(String checkLevel) { + if (StringUtils.isEmpty(checkLevel)) { + return ""; + } + try { + return sysDictDataService.selectDictLabel("xg_dorm_hygiene_check_level", checkLevel); + } catch (Exception e) { + log.warn("获取字典标签失败,字典类型: xg_dorm_hygiene_check_level, 字典值: {}, 错误: {}", checkLevel, e.getMessage()); + return checkLevel; + } + } + + /** + * 转换卫生等级代码为文本 + */ + private String convertHygieneGradeToText(String hygieneGrade) { + if (StringUtils.isEmpty(hygieneGrade)) { + return ""; + } + try { + return sysDictDataService.selectDictLabel("xg_dorm_hygiene_grade", hygieneGrade); + } catch (Exception e) { + log.warn("获取字典标签失败,字典类型: xg_dorm_hygiene_grade, 字典值: {}, 错误: {}", hygieneGrade, e.getMessage()); + return hygieneGrade; + } + } + + /** + * 转换出勤状态代码为文本 + */ + private String convertAttendanceStatusToText(String attendanceStatus) { + if (StringUtils.isEmpty(attendanceStatus)) { + return ""; + } + try { + return sysDictDataService.selectDictLabel("xg_student_attendance_status", attendanceStatus); + } catch (Exception e) { + log.warn("获取字典标签失败,字典类型: xg_student_attendance_status, 字典值: {}, 错误: {}", attendanceStatus, e.getMessage()); + return attendanceStatus; + } + } + + /** + * 转换学生评级代码为文本 + */ + private String convertStudentGradeToText(String studentGrade) { + if (StringUtils.isEmpty(studentGrade)) { + return ""; + } + try { + return sysDictDataService.selectDictLabel("xg_dorm_hygiene_grade", studentGrade); + } catch (Exception e) { + log.warn("获取字典标签失败,字典类型: xg_dorm_hygiene_grade, 字典值: {}, 错误: {}", studentGrade, e.getMessage()); + return studentGrade; + } + } + + /** + * 转换是否寝室长代码为文本 + */ + private String convertResponsiblePersonToText(String isResponsiblePerson) { + if (StringUtils.isEmpty(isResponsiblePerson)) { + return "否"; + } + return "1".equals(isResponsiblePerson) ? "是" : "否"; + } + +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormHygieneStudentScoreServiceImpl.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormHygieneStudentScoreServiceImpl.java new file mode 100644 index 00000000..dea6878a --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormHygieneStudentScoreServiceImpl.java @@ -0,0 +1,186 @@ +package org.dromara.xg.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.dromara.xg.domain.bo.XgDormHygieneStudentScoreBo; +import org.dromara.xg.domain.vo.XgDormHygieneStudentScoreVo; +import org.dromara.xg.domain.XgDormHygieneStudentScore; +import org.dromara.xg.mapper.XgDormHygieneStudentScoreMapper; +import org.dromara.xg.service.IXgDormHygieneStudentScoreService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 宿舍卫生检查学生评分Service业务层处理 + * + * @author zlwu + * @date 2025-08-16 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class XgDormHygieneStudentScoreServiceImpl implements IXgDormHygieneStudentScoreService { + + private final XgDormHygieneStudentScoreMapper baseMapper; + + /** + * 查询宿舍卫生检查学生评分 + * + * @param id 主键 + * @return 宿舍卫生检查学生评分 + */ + @Override + public XgDormHygieneStudentScoreVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询宿舍卫生检查学生评分列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 宿舍卫生检查学生评分分页列表 + */ + @Override + public TableDataInfo queryPageList(XgDormHygieneStudentScoreBo bo, + PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的宿舍卫生检查学生评分列表 + * + * @param bo 查询条件 + * @return 宿舍卫生检查学生评分列表 + */ + @Override + public List queryList(XgDormHygieneStudentScoreBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(XgDormHygieneStudentScoreBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(XgDormHygieneStudentScore::getId); + lqw.eq(bo.getDeleted() != null, XgDormHygieneStudentScore::getDeleted, bo.getDeleted()); + lqw.eq(bo.getHygieneCheckId() != null, XgDormHygieneStudentScore::getHygieneCheckId, bo.getHygieneCheckId()); + lqw.eq(bo.getStudentId() != null, XgDormHygieneStudentScore::getStudentId, bo.getStudentId()); + lqw.eq(StringUtils.isNotBlank(bo.getStudentNo()), XgDormHygieneStudentScore::getStudentNo, bo.getStudentNo()); + lqw.like(StringUtils.isNotBlank(bo.getStudentName()), XgDormHygieneStudentScore::getStudentName, + bo.getStudentName()); + lqw.eq(bo.getClassId() != null, XgDormHygieneStudentScore::getClassId, bo.getClassId()); + lqw.like(StringUtils.isNotBlank(bo.getClassName()), XgDormHygieneStudentScore::getClassName, bo.getClassName()); + lqw.eq(bo.getMajorId() != null, XgDormHygieneStudentScore::getMajorId, bo.getMajorId()); + lqw.like(StringUtils.isNotBlank(bo.getMajorName()), XgDormHygieneStudentScore::getMajorName, bo.getMajorName()); + lqw.eq(bo.getCollegeId() != null, XgDormHygieneStudentScore::getCollegeId, bo.getCollegeId()); + lqw.like(StringUtils.isNotBlank(bo.getCollegeName()), XgDormHygieneStudentScore::getCollegeName, + bo.getCollegeName()); + lqw.eq(bo.getBedId() != null, XgDormHygieneStudentScore::getBedId, bo.getBedId()); + lqw.like(StringUtils.isNotBlank(bo.getBedCode()), XgDormHygieneStudentScore::getBedCode, bo.getBedCode()); + lqw.eq(bo.getStudentScore() != null, XgDormHygieneStudentScore::getStudentScore, bo.getStudentScore()); + lqw.eq(StringUtils.isNotBlank(bo.getAttendanceStatus()), XgDormHygieneStudentScore::getAttendanceStatus, + bo.getAttendanceStatus()); + lqw.eq(StringUtils.isNotBlank(bo.getStudentGrade()), XgDormHygieneStudentScore::getStudentGrade, + bo.getStudentGrade()); + lqw.eq(bo.getIsResponsiblePerson() != null, XgDormHygieneStudentScore::getIsResponsiblePerson, + bo.getIsResponsiblePerson()); + lqw.like(StringUtils.isNotBlank(bo.getScoreRemarks()), XgDormHygieneStudentScore::getScoreRemarks, + bo.getScoreRemarks()); + lqw.like(StringUtils.isNotBlank(bo.getImprovementNotes()), XgDormHygieneStudentScore::getImprovementNotes, + bo.getImprovementNotes()); + return lqw; + } + + /** + * 新增宿舍卫生检查学生评分 + * + * @param bo 宿舍卫生检查学生评分 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(XgDormHygieneStudentScoreBo bo) { + XgDormHygieneStudentScore add = MapstructUtils.convert(bo, XgDormHygieneStudentScore.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改宿舍卫生检查学生评分 + * + * @param bo 宿舍卫生检查学生评分 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(XgDormHygieneStudentScoreBo bo) { + XgDormHygieneStudentScore update = MapstructUtils.convert(bo, XgDormHygieneStudentScore.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(XgDormHygieneStudentScore entity) { + // TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除宿舍卫生检查学生评分信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + /** + * 根据卫生检查ID查询学生评分列表 + * + * @param hygieneCheckId 卫生检查ID + * @return 学生评分列表 + */ + @Override + public List queryByHygieneCheckId(Long hygieneCheckId) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(XgDormHygieneStudentScore::getHygieneCheckId, hygieneCheckId); + lqw.eq(XgDormHygieneStudentScore::getDeleted, 0); // 只查询未删除的记录 + lqw.orderByAsc(XgDormHygieneStudentScore::getId); + return baseMapper.selectVoList(lqw); + } + + /** + * 根据卫生检查ID删除学生评分记录 + * + * @param hygieneCheckId 卫生检查ID + * @return 是否删除成功 + */ + @Override + public Boolean deleteByHygieneCheckId(Long hygieneCheckId) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(XgDormHygieneStudentScore::getHygieneCheckId, hygieneCheckId); + return baseMapper.delete(lqw) > 0; + } +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormRoomServiceImpl.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormRoomServiceImpl.java index ad751863..dddcfed0 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormRoomServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormRoomServiceImpl.java @@ -13,12 +13,19 @@ import org.springframework.stereotype.Service; import org.dromara.xg.domain.bo.XgDormRoomBo; import org.dromara.xg.domain.vo.XgDormRoomVo; import org.dromara.xg.domain.XgDormRoom; +import org.dromara.xg.domain.XgDormBedStudent; +import org.dromara.xg.domain.XgSysDept; import org.dromara.xg.mapper.XgDormRoomMapper; +import org.dromara.xg.mapper.XgDormBedStudentMapper; +import org.dromara.xg.mapper.XgSysDeptMapper; import org.dromara.xg.service.IXgDormRoomService; +import org.dromara.common.satoken.utils.LoginHelper; import java.util.List; import java.util.Map; import java.util.Collection; +import java.util.Set; +import java.util.stream.Collectors; /** * 宿舍房间信息Service业务层处理 @@ -32,6 +39,8 @@ import java.util.Collection; public class XgDormRoomServiceImpl implements IXgDormRoomService { private final XgDormRoomMapper baseMapper; + private final XgDormBedStudentMapper xgDormBedStudentMapper; + private final XgSysDeptMapper xgSysDeptMapper; /** * 查询宿舍房间信息 @@ -58,6 +67,68 @@ public class XgDormRoomServiceImpl implements IXgDormRoomService { return TableDataInfo.build(result); } + /** + * 分页查询当前用户所在学院的宿舍房间信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 宿舍房间信息分页列表 + */ + @Override + public TableDataInfo queryPageListByCurrentUserCollege(XgDormRoomBo bo, PageQuery pageQuery) { + // 1. 获取当前用户的学院ID + Long currentUserCollegeId = getCurrentUserCollegeId(); + if (currentUserCollegeId == null) { + log.warn("无法获取当前用户的学院信息,返回空结果"); + return TableDataInfo.build(new Page<>()); + } + + // 2. 获取分配给当前学院的房间ID列表 + Set collegeAllocatedRoomIds = getCollegeAllocatedRoomIds(currentUserCollegeId); + if (collegeAllocatedRoomIds.isEmpty()) { + log.info("当前学院 {} 没有分配的房间,返回空结果", currentUserCollegeId); + return TableDataInfo.build(new Page<>()); + } + + // 3. 构建查询条件,只查询分配给当前学院的具体房间,并支持额外的过滤条件 + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + lqw.in(XgDormRoom::getId, collegeAllocatedRoomIds); + + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 分页查询指定班级的宿舍房间信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @param classId 班级ID + * @return 宿舍房间信息分页列表 + */ + @Override + public TableDataInfo queryPageListByClassId(XgDormRoomBo bo, PageQuery pageQuery, Long classId) { + // 1. 参数校验 + if (classId == null) { + log.warn("班级ID不能为空,返回空结果"); + return TableDataInfo.build(new Page<>()); + } + + // 2. 获取分配给指定班级的房间ID列表 + Set classAllocatedRoomIds = getClassAllocatedRoomIds(classId); + if (classAllocatedRoomIds.isEmpty()) { + log.info("班级 {} 没有分配的房间,返回空结果", classId); + return TableDataInfo.build(new Page<>()); + } + + // 3. 构建查询条件,只查询分配给指定班级的具体房间,并支持额外的过滤条件 + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + lqw.in(XgDormRoom::getId, classAllocatedRoomIds); + + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + /** * 查询符合条件的宿舍房间信息列表 * @@ -138,4 +209,105 @@ public class XgDormRoomServiceImpl implements IXgDormRoomService { } return baseMapper.deleteByIds(ids) > 0; } + + /** + * 获取当前用户的二级学院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 getCollegeAllocatedRoomIds(Long collegeId) { + try { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.select(XgDormBedStudent::getRoomId) + .eq(XgDormBedStudent::getCollegeId, collegeId) + .eq(XgDormBedStudent::getStatus, "0") + .eq(XgDormBedStudent::getDeleted, "0") + .isNotNull(XgDormBedStudent::getRoomId) + .groupBy(XgDormBedStudent::getRoomId); + + List roomRecords = xgDormBedStudentMapper.selectList(queryWrapper); + return roomRecords.stream() + .map(XgDormBedStudent::getRoomId) + .collect(Collectors.toSet()); + + } catch (Exception e) { + log.error("获取学院 {} 分配的房间ID列表失败", collegeId, e); + return Set.of(); + } + } + + /** + * 获取分配给指定班级的房间ID列表 + */ + private Set getClassAllocatedRoomIds(Long classId) { + try { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.select(XgDormBedStudent::getRoomId) + .eq(XgDormBedStudent::getClassId, classId) + .eq(XgDormBedStudent::getStatus, "0") + .eq(XgDormBedStudent::getDeleted, "0") + .isNotNull(XgDormBedStudent::getRoomId) + .groupBy(XgDormBedStudent::getRoomId); + + List roomRecords = xgDormBedStudentMapper.selectList(queryWrapper); + return roomRecords.stream() + .map(XgDormBedStudent::getRoomId) + .collect(Collectors.toSet()); + + } catch (Exception e) { + log.error("获取班级 {} 分配的房间ID列表失败", classId, e); + return Set.of(); + } + } } diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormViolationServiceImpl.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormViolationServiceImpl.java new file mode 100644 index 00000000..83c9ad67 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormViolationServiceImpl.java @@ -0,0 +1,290 @@ +package org.dromara.xg.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import cn.hutool.core.util.ObjectUtil; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.dromara.xg.domain.bo.XgDormViolationBo; +import org.dromara.xg.domain.vo.XgDormViolationVo; +import org.dromara.xg.domain.bo.StudentOptionBo; +import org.dromara.xg.domain.vo.StudentOptionVo; +import org.dromara.xg.domain.vo.XgDormViolationImportVo; +import org.dromara.xg.domain.XgDormViolation; +import org.dromara.xg.mapper.XgDormViolationMapper; +import org.dromara.xg.service.IXgDormViolationService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 宿舍违纪记录Service业务层处理 + * + * @author zlwu + * @date 2025-08-16 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class XgDormViolationServiceImpl implements IXgDormViolationService { + + private final XgDormViolationMapper baseMapper; + + /** + * 查询宿舍违纪记录 + * + * @param id 主键 + * @return 宿舍违纪记录 + */ + @Override + public XgDormViolationVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询宿舍违纪记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 宿舍违纪记录分页列表 + */ + @Override + public TableDataInfo queryPageList(XgDormViolationBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的宿舍违纪记录列表 + * + * @param bo 查询条件 + * @return 宿舍违纪记录列表 + */ + @Override + public List queryList(XgDormViolationBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(XgDormViolationBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(XgDormViolation::getId); + lqw.eq(bo.getStudentId() != null, XgDormViolation::getStudentId, bo.getStudentId()); + lqw.like(StringUtils.isNotBlank(bo.getStudentName()), XgDormViolation::getStudentName, bo.getStudentName()); + lqw.like(StringUtils.isNotBlank(bo.getStudentNo()), XgDormViolation::getStudentNo, bo.getStudentNo()); + lqw.eq(bo.getCollegeId() != null, XgDormViolation::getCollegeId, bo.getCollegeId()); + lqw.like(StringUtils.isNotBlank(bo.getCollegeName()), XgDormViolation::getCollegeName, bo.getCollegeName()); + lqw.eq(bo.getMajorId() != null, XgDormViolation::getMajorId, bo.getMajorId()); + lqw.like(StringUtils.isNotBlank(bo.getMajorName()), XgDormViolation::getMajorName, bo.getMajorName()); + lqw.eq(bo.getClassId() != null, XgDormViolation::getClassId, bo.getClassId()); + lqw.like(StringUtils.isNotBlank(bo.getClassName()), XgDormViolation::getClassName, bo.getClassName()); + lqw.eq(bo.getCampusId() != null, XgDormViolation::getCampusId, bo.getCampusId()); + lqw.like(StringUtils.isNotBlank(bo.getCampusName()), XgDormViolation::getCampusName, bo.getCampusName()); + lqw.eq(bo.getAreaId() != null, XgDormViolation::getAreaId, bo.getAreaId()); + lqw.like(StringUtils.isNotBlank(bo.getAreaName()), XgDormViolation::getAreaName, bo.getAreaName()); + lqw.eq(bo.getBuildingId() != null, XgDormViolation::getBuildingId, bo.getBuildingId()); + lqw.like(StringUtils.isNotBlank(bo.getBuildingName()), XgDormViolation::getBuildingName, bo.getBuildingName()); + lqw.eq(bo.getRoomId() != null, XgDormViolation::getRoomId, bo.getRoomId()); + lqw.like(StringUtils.isNotBlank(bo.getRoomName()), XgDormViolation::getRoomName, bo.getRoomName()); + lqw.eq(bo.getBedId() != null, XgDormViolation::getBedId, bo.getBedId()); + lqw.like(StringUtils.isNotBlank(bo.getBedName()), XgDormViolation::getBedName, bo.getBedName()); + lqw.eq(StringUtils.isNotBlank(bo.getViolationType()), XgDormViolation::getViolationType, bo.getViolationType()); + lqw.eq(StringUtils.isNotBlank(bo.getViolationLevel()), XgDormViolation::getViolationLevel, + bo.getViolationLevel()); + lqw.eq(bo.getViolationTime() != null, XgDormViolation::getViolationTime, bo.getViolationTime()); + lqw.like(StringUtils.isNotBlank(bo.getViolationDescription()), XgDormViolation::getViolationDescription, + bo.getViolationDescription()); + lqw.like(StringUtils.isNotBlank(bo.getInvolvedStudents()), XgDormViolation::getInvolvedStudents, + bo.getInvolvedStudents()); + lqw.like(StringUtils.isNotBlank(bo.getAttachmentOssIds()), XgDormViolation::getAttachmentOssIds, + bo.getAttachmentOssIds()); + lqw.eq(StringUtils.isNotBlank(bo.getDeleted()), XgDormViolation::getDeleted, bo.getDeleted()); + return lqw; + } + + /** + * 新增宿舍违纪记录 + * + * @param bo 宿舍违纪记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(XgDormViolationBo bo) { + XgDormViolation add = MapstructUtils.convert(bo, XgDormViolation.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改宿舍违纪记录 + * + * @param bo 宿舍违纪记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(XgDormViolationBo bo) { + XgDormViolation update = MapstructUtils.convert(bo, XgDormViolation.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(XgDormViolation entity) { + // TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除宿舍违纪记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + /** + * 获取学生选择器选项列表(支持按学号、姓名检索) + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 学生选项分页列表 + */ + @Override + public TableDataInfo getStudentOptions(StudentOptionBo bo, PageQuery pageQuery) { + Page result = baseMapper.selectStudentOptions(pageQuery.build(), bo); + return TableDataInfo.build(result); + } + + /** + * 根据学生ID获取学生详细信息 + * + * @param studentId 学生ID + * @return 学生详细信息 + */ + @Override + public StudentOptionVo getStudentById(Long studentId) { + return baseMapper.selectStudentById(studentId); + } + + /** + * 根据学号获取学生详细信息 + * + * @param studentNo 学号 + * @return 学生详细信息 + */ + @Override + public StudentOptionVo getStudentByStudentNo(String studentNo) { + return baseMapper.selectStudentByStudentNo(studentNo); + } + + /** + * 导入宿舍违纪记录数据 + * + * @param importList 导入的宿舍违纪记录列表 + * @param isUpdateSupport 是否支持更新已存在的记录 + * @param operName 操作人员名称 + * @return 导入结果信息 + */ + @Override + public String importDormViolation(List importList, boolean isUpdateSupport, + String operName) { + if (ObjectUtil.isNull(importList) || importList.isEmpty()) { + throw new RuntimeException("导入宿舍违纪记录数据不能为空!"); + } + + int successNum = 0; + int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + + for (XgDormViolationImportVo importVo : importList) { + try { + // 通过学号查询学生信息 + StudentOptionVo student = this.getStudentByStudentNo(importVo.getStudentNo()); + if (student == null) { + failureNum++; + String msg = "
" + failureNum + "、学号 " + importVo.getStudentNo() + " 的学生信息不存在"; + failureMsg.append(msg); + continue; + } + + // 验证学生姓名是否匹配 + // if (!student.getStudentName().equals(importVo.getStudentName())) { + // failureNum++; + // String msg = "
" + failureNum + "、学号 " + importVo.getStudentNo() + " + // 对应的学生姓名不匹配,数据库中为:" + // + student.getStudentName() + ",导入数据为:" + importVo.getStudentName(); + // failureMsg.append(msg); + // continue; + // } + + // 创建宿舍违纪记录 + XgDormViolation violation = new XgDormViolation(); + violation.setStudentId(student.getId()); + violation.setStudentName(student.getName()); + violation.setStudentNo(student.getStudentNo()); + violation.setCollegeId(student.getCollegeId()); + violation.setCollegeName(student.getCollegeName()); + violation.setMajorId(student.getMajorId()); + violation.setMajorName(student.getMajorName()); + violation.setClassId(student.getClassId()); + violation.setClassName(student.getClassName()); + violation.setCampusId(student.getDormInfo().getCampusId()); + violation.setCampusName(student.getDormInfo().getCampusName()); + violation.setAreaId(student.getDormInfo().getAreaId()); + violation.setAreaName(student.getDormInfo().getAreaName()); + violation.setBuildingId(student.getDormInfo().getBuildingId()); + violation.setBuildingName(student.getDormInfo().getBuildingName()); + violation.setRoomId(student.getDormInfo().getRoomId()); + violation.setRoomName(student.getDormInfo().getRoomName()); + violation.setBedId(student.getDormInfo().getBedId()); + violation.setBedName(student.getDormInfo().getBedName()); + violation.setViolationType(importVo.getViolationType()); + violation.setViolationLevel(importVo.getViolationLevel()); + violation.setViolationTime(importVo.getViolationTime()); + violation.setViolationDescription(importVo.getViolationDescription()); + violation.setRemark(importVo.getRemark()); + + // 插入数据 + baseMapper.insert(violation); + successNum++; + successMsg.append("
" + successNum + "、学号 " + importVo.getStudentNo() + " 的宿舍违纪记录导入成功"); + + } catch (Exception e) { + failureNum++; + String msg = "
" + failureNum + "、学号 " + importVo.getStudentNo() + " 导入失败:" + e.getMessage(); + failureMsg.append(msg); + log.error("导入宿舍违纪记录失败", e); + } + } + + if (failureNum > 0) { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new RuntimeException(failureMsg.toString()); + } else { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } +} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgHolidayStayServiceImpl.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgHolidayStayServiceImpl.java new file mode 100644 index 00000000..93f8229e --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgHolidayStayServiceImpl.java @@ -0,0 +1,217 @@ +package org.dromara.xg.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.dromara.xg.domain.bo.XgHolidayStayBo; +import org.dromara.xg.domain.vo.XgHolidayStayVo; +import org.dromara.xg.domain.XgHolidayStay; +import org.dromara.xg.domain.bo.StudentOptionBo; +import org.dromara.xg.domain.vo.StudentOptionVo; +import org.dromara.xg.mapper.XgHolidayStayMapper; +import org.dromara.xg.service.IXgHolidayStayService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 假期留宿管理Service业务层处理 + * + * @author zlwu + * @date 2025-08-15 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class XgHolidayStayServiceImpl implements IXgHolidayStayService { + + private final XgHolidayStayMapper baseMapper; + + /** + * 查询假期留宿管理 + * + * @param id 主键 + * @return 假期留宿管理 + */ + @Override + public XgHolidayStayVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询假期留宿管理列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 假期留宿管理分页列表 + */ + @Override + public TableDataInfo queryPageList(XgHolidayStayBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的假期留宿管理列表 + * + * @param bo 查询条件 + * @return 假期留宿管理列表 + */ + @Override + public List queryList(XgHolidayStayBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(XgHolidayStayBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(XgHolidayStay::getId); + lqw.eq(bo.getDeleted() != null, XgHolidayStay::getDeleted, bo.getDeleted()); + lqw.eq(bo.getStudentId() != null, XgHolidayStay::getStudentId, bo.getStudentId()); + lqw.like(StringUtils.isNotBlank(bo.getStudentName()), XgHolidayStay::getStudentName, bo.getStudentName()); + lqw.like(StringUtils.isNotBlank(bo.getStudentNo()), XgHolidayStay::getStudentNo, bo.getStudentNo()); + lqw.eq(bo.getClassId() != null, XgHolidayStay::getClassId, bo.getClassId()); + lqw.like(StringUtils.isNotBlank(bo.getClassName()), XgHolidayStay::getClassName, bo.getClassName()); + lqw.eq(bo.getMajorId() != null, XgHolidayStay::getMajorId, bo.getMajorId()); + lqw.like(StringUtils.isNotBlank(bo.getMajorName()), XgHolidayStay::getMajorName, bo.getMajorName()); + lqw.eq(bo.getCollegeId() != null, XgHolidayStay::getCollegeId, bo.getCollegeId()); + lqw.like(StringUtils.isNotBlank(bo.getCollegeName()), XgHolidayStay::getCollegeName, bo.getCollegeName()); + lqw.eq(bo.getCampusId() != null, XgHolidayStay::getCampusId, bo.getCampusId()); + lqw.like(StringUtils.isNotBlank(bo.getCampusName()), XgHolidayStay::getCampusName, bo.getCampusName()); + lqw.eq(bo.getAreaId() != null, XgHolidayStay::getAreaId, bo.getAreaId()); + lqw.like(StringUtils.isNotBlank(bo.getAreaName()), XgHolidayStay::getAreaName, bo.getAreaName()); + lqw.eq(bo.getBuildingId() != null, XgHolidayStay::getBuildingId, bo.getBuildingId()); + lqw.like(StringUtils.isNotBlank(bo.getBuildingName()), XgHolidayStay::getBuildingName, bo.getBuildingName()); + lqw.eq(bo.getRoomId() != null, XgHolidayStay::getRoomId, bo.getRoomId()); + lqw.like(StringUtils.isNotBlank(bo.getRoomName()), XgHolidayStay::getRoomName, bo.getRoomName()); + lqw.eq(bo.getBedId() != null, XgHolidayStay::getBedId, bo.getBedId()); + lqw.like(StringUtils.isNotBlank(bo.getBedName()), XgHolidayStay::getBedName, bo.getBedName()); + lqw.eq(StringUtils.isNotBlank(bo.getHolidayType()), XgHolidayStay::getHolidayType, bo.getHolidayType()); + lqw.ge(bo.getApplyStartDate() != null, XgHolidayStay::getApplyStartDate, bo.getApplyStartDate()); + lqw.le(bo.getApplyEndDate() != null, XgHolidayStay::getApplyEndDate, bo.getApplyEndDate()); + lqw.like(StringUtils.isNotBlank(bo.getStayReason()), XgHolidayStay::getStayReason, bo.getStayReason()); + lqw.eq(StringUtils.isNotBlank(bo.getStayReasonType()), XgHolidayStay::getStayReasonType, + bo.getStayReasonType()); + lqw.like(StringUtils.isNotBlank(bo.getStudentPhone()), XgHolidayStay::getStudentPhone, bo.getStudentPhone()); + lqw.like(StringUtils.isNotBlank(bo.getEmergencyContact()), XgHolidayStay::getEmergencyContact, + bo.getEmergencyContact()); + lqw.like(StringUtils.isNotBlank(bo.getEmergencyPhone()), XgHolidayStay::getEmergencyPhone, + bo.getEmergencyPhone()); + lqw.eq(StringUtils.isNotBlank(bo.getParentConsent()), XgHolidayStay::getParentConsent, bo.getParentConsent()); + lqw.like(StringUtils.isNotBlank(bo.getAttachmentOssIds()), XgHolidayStay::getAttachmentOssIds, + bo.getAttachmentOssIds()); + lqw.eq(StringUtils.isNotBlank(bo.getExtField1()), XgHolidayStay::getExtField1, bo.getExtField1()); + lqw.eq(StringUtils.isNotBlank(bo.getExtField2()), XgHolidayStay::getExtField2, bo.getExtField2()); + lqw.eq(StringUtils.isNotBlank(bo.getExtField3()), XgHolidayStay::getExtField3, bo.getExtField3()); + + // 添加状态过滤逻辑 + addStatusFilter(lqw, bo); + + return lqw; + } + + /** + * 添加状态过滤条件 + * + * @param lqw 查询条件构造器 + * @param bo 查询参数对象 + */ + private void addStatusFilter(LambdaQueryWrapper lqw, XgHolidayStayBo bo) { + // 从参数中获取状态过滤条件 + if (bo.getParams() != null) { + String statusType = (String) bo.getParams().get("statusType"); + String currentTime = (String) bo.getParams().get("currentTime"); + + if (StringUtils.isNotBlank(statusType) && StringUtils.isNotBlank(currentTime)) { + if ("valid".equals(statusType)) { + // 有效状态:结束时间 >= 当前时间 AND 结束时间不为空 + lqw.ge(XgHolidayStay::getApplyEndDate, currentTime) + .isNotNull(XgHolidayStay::getApplyEndDate); + log.debug("添加有效状态过滤条件: apply_end_date >= {} AND apply_end_date IS NOT NULL", currentTime); + } else if ("invalid".equals(statusType)) { + // 失效状态:结束时间 < 当前时间 OR 结束时间为空 + lqw.and(wrapper -> wrapper + .lt(XgHolidayStay::getApplyEndDate, currentTime) + .or() + .isNull(XgHolidayStay::getApplyEndDate)); + log.debug("添加失效状态过滤条件: apply_end_date < {} OR apply_end_date IS NULL", currentTime); + } + } + } + } + + /** + * 新增假期留宿管理 + * + * @param bo 假期留宿管理 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(XgHolidayStayBo bo) { + XgHolidayStay add = MapstructUtils.convert(bo, XgHolidayStay.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改假期留宿管理 + * + * @param bo 假期留宿管理 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(XgHolidayStayBo bo) { + XgHolidayStay update = MapstructUtils.convert(bo, XgHolidayStay.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(XgHolidayStay entity) { + // TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除假期留宿管理信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + /** + * 获取学生选择器选项列表(支持按学号、姓名检索) + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 学生选项分页列表 + */ + @Override + public TableDataInfo getStudentOptions(StudentOptionBo bo, PageQuery pageQuery) { + Page result = baseMapper.selectStudentOptions(pageQuery.build(), bo); + return TableDataInfo.build(result); + } +} 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..14be9ddd 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; /** * 查询学生信息 @@ -1159,20 +1162,80 @@ public class XgStudentMsgServiceImpl implements IXgStudentMsgService { } /** - * 获取学生信息树(二级学院下的学院、专业、班级统计信息) + * 获取学生信息树(二级学院下的学院床位分配统计信息) + * 基于college_id判断床位分配情况,只显示学院层级 * * @param secondaryCollegeId 二级学院ID,默认101 * @return 学生信息树 */ @Override public List getStudentTree(Long secondaryCollegeId) { - log.info("开始构建学生信息树,二级学院ID: {}", secondaryCollegeId); + log.info("开始构建学生信息树(基于college_id床位分配统计),二级学院ID: {}", secondaryCollegeId); List result = new ArrayList<>(); try { - // 1. 查询二级学院下的所有学院信息,并统计男女生人数 - List> collegeStats = baseMapper.getCollegeStudentStats(secondaryCollegeId); + // 1. 查询二级学院下的所有学院信息,并统计男女生床位分配情况 + // 使用新的查询方法,基于college_id统计床位分配 + List> collegeStats = baseMapper.getCollegeBedAssignmentStats(secondaryCollegeId); + log.info("查询到 {} 个学院的床位分配统计", collegeStats.size()); + + // 2. 构建学院节点(只显示学院层级) + for (Map collegeItem : collegeStats) { + Object collegeIdObj = collegeItem.get("college_id"); + Long collegeId = collegeIdObj instanceof Long ? (Long) collegeIdObj + : Long.valueOf(collegeIdObj.toString()); + String collegeName = (String) collegeItem.get("college_name"); + + // 获取统计数据 + Integer maleTotal = ((Number) collegeItem.get("male_total")).intValue(); + Integer femaleTotal = ((Number) collegeItem.get("female_total")).intValue(); + Integer maleAssigned = ((Number) collegeItem.get("male_assigned")).intValue(); + Integer femaleAssigned = ((Number) collegeItem.get("female_assigned")).intValue(); + + XgStudentTreeVo collegeNode = new XgStudentTreeVo(); + collegeNode.setId("college-" + collegeId); + collegeNode.setLabel(collegeName); + collegeNode.setType("college"); + collegeNode.setCollegeId(collegeId); + collegeNode.setCollegeName(collegeName); + collegeNode.setMaleTotal(maleTotal); + collegeNode.setFemaleTotal(femaleTotal); + collegeNode.setMaleAssigned(maleAssigned); + collegeNode.setFemaleAssigned(femaleAssigned); + // 设置为null表示不展示子节点 + collegeNode.setChildren(null); + + result.add(collegeNode); + + log.debug("学院 {} 统计 - 男生总数: {}, 女生总数: {}, 男生已分配: {}, 女生已分配: {}", + collegeName, maleTotal, femaleTotal, maleAssigned, femaleAssigned); + } + + log.info("学生信息树构建完成,共 {} 个学院节点", result.size()); + + } catch (Exception e) { + log.error("构建学生信息树失败", e); + throw new RuntimeException("构建学生信息树失败: " + e.getMessage()); + } + + return result; + } + + /** + * 获取学生信息树(二级学院下的学院、专业、班级统计信息)- 按班级床位分配统计 + * + * @param secondaryCollegeId 二级学院ID,默认101 + * @return 学生信息树 + */ + public List getStudentTreeByClass(Long secondaryCollegeId) { + log.info("开始构建学生信息树(按床位分配统计),二级学院ID: {}", secondaryCollegeId); + + List result = new ArrayList<>(); + + try { + // 1. 查询二级学院下的所有学院信息,并统计男女生人数(使用新的按班级床位分配统计方法) + List> collegeStats = baseMapper.getCollegeStudentStatsByClass(secondaryCollegeId); log.info("查询到 {} 个学院", collegeStats.size()); // 按学院分组 @@ -1190,131 +1253,199 @@ public class XgStudentMsgServiceImpl implements IXgStudentMsgService { String collegeKey = collegeEntry.getKey(); List> collegeMajors = collegeEntry.getValue(); - if (!collegeMajors.isEmpty()) { - Map firstItem = collegeMajors.get(0); - Object collegeIdObj = firstItem.get("college_id"); - Long collegeId = collegeIdObj instanceof Long ? (Long) collegeIdObj - : Long.valueOf(collegeIdObj.toString()); - String collegeName = (String) firstItem.get("college_name"); + String[] keyParts = collegeKey.split("_"); + Long collegeId = Long.valueOf(keyParts[0]); + String collegeName = keyParts[1]; - XgStudentTreeVo collegeNode = new XgStudentTreeVo(); - collegeNode.setId("college-" + collegeId); - collegeNode.setLabel(collegeName); - collegeNode.setType("college"); - collegeNode.setCollegeId(collegeId); - collegeNode.setCollegeName(collegeName); - collegeNode.setChildren(new ArrayList<>()); + // 计算学院级别的统计信息 + int collegeMaleTotal = 0; + int collegeFemaleTotal = 0; + int collegeMaleAssigned = 0; + int collegeFemaleAssigned = 0; - // 统计学院人数 - int collegeMaleTotal = 0, collegeFemaleTotal = 0; - int collegeMaleAssigned = 0, collegeFemaleAssigned = 0; + for (Map item : collegeMajors) { + collegeMaleTotal += ((Number) item.get("male_total")).intValue(); + collegeFemaleTotal += ((Number) item.get("female_total")).intValue(); + collegeMaleAssigned += ((Number) item.get("male_assigned")).intValue(); + collegeFemaleAssigned += ((Number) item.get("female_assigned")).intValue(); + } - // 按专业分组 - Map>> majorMap = collegeMajors.stream() - .collect(Collectors.groupingBy(item -> { - Object majorIdObj = item.get("major_id"); - Long majorId = majorIdObj instanceof Long ? (Long) majorIdObj - : Long.valueOf(majorIdObj.toString()); - String majorName = (String) item.get("major_name"); - return majorId + "_" + majorName; - })); + XgStudentTreeVo collegeNode = new XgStudentTreeVo(); + collegeNode.setId(collegeId.toString()); + collegeNode.setLabel(collegeName); + collegeNode.setType("college"); + collegeNode.setCollegeId(collegeId); + collegeNode.setCollegeName(collegeName); + collegeNode.setMaleTotal(collegeMaleTotal); + collegeNode.setFemaleTotal(collegeFemaleTotal); + collegeNode.setMaleAssigned(collegeMaleAssigned); + collegeNode.setFemaleAssigned(collegeFemaleAssigned); + collegeNode.setChildren(new ArrayList<>()); - // 3. 构建专业节点 - for (Map.Entry>> majorEntry : majorMap.entrySet()) { - String majorKey = majorEntry.getKey(); - List> majorClasses = majorEntry.getValue(); - - if (!majorClasses.isEmpty()) { - Map firstMajorItem = majorClasses.get(0); - Object majorIdObj = firstMajorItem.get("major_id"); + // 按专业分组 + Map>> majorMap = collegeMajors.stream() + .collect(Collectors.groupingBy(item -> { + Object majorIdObj = item.get("major_id"); Long majorId = majorIdObj instanceof Long ? (Long) majorIdObj : Long.valueOf(majorIdObj.toString()); - String majorName = (String) firstMajorItem.get("major_name"); + String majorName = (String) item.get("major_name"); + return majorId + "_" + majorName; + })); - XgStudentTreeVo majorNode = new XgStudentTreeVo(); - majorNode.setId("major-" + majorId); - majorNode.setLabel(majorName); - majorNode.setType("major"); - majorNode.setCollegeId(collegeId); - majorNode.setCollegeName(collegeName); - majorNode.setMajorId(majorId); - majorNode.setMajorName(majorName); - majorNode.setChildren(new ArrayList<>()); + // 3. 构建专业节点 + for (Map.Entry>> majorEntry : majorMap.entrySet()) { + String majorKey = majorEntry.getKey(); + List> majorClasses = majorEntry.getValue(); - // 统计专业人数 - int majorMaleTotal = 0, majorFemaleTotal = 0; - int majorMaleAssigned = 0, majorFemaleAssigned = 0; + String[] majorKeyParts = majorKey.split("_"); + Long majorId = Long.valueOf(majorKeyParts[0]); + String majorName = majorKeyParts[1]; - // 4. 构建班级节点 - for (Map classItem : majorClasses) { - Object classIdObj = classItem.get("class_id"); - Long classId = classIdObj instanceof Long ? (Long) classIdObj - : Long.valueOf(classIdObj.toString()); - String className = (String) classItem.get("class_name"); - String grade = (String) classItem.get("grade"); - Integer maleTotal = ((Number) classItem.get("male_total")).intValue(); - Integer femaleTotal = ((Number) classItem.get("female_total")).intValue(); - Integer maleAssigned = ((Number) classItem.get("male_assigned")).intValue(); - Integer femaleAssigned = ((Number) classItem.get("female_assigned")).intValue(); + // 计算专业级别的统计信息 + int majorMaleTotal = 0; + int majorFemaleTotal = 0; + int majorMaleAssigned = 0; + int majorFemaleAssigned = 0; - XgStudentTreeVo classNode = new XgStudentTreeVo(); - classNode.setId("class-" + classId); - classNode.setLabel(className); - classNode.setType("class"); - classNode.setCollegeId(collegeId); - classNode.setCollegeName(collegeName); - classNode.setMajorId(majorId); - classNode.setMajorName(majorName); - classNode.setClassId(classId); - classNode.setClassName(className); - classNode.setGrade(grade); - classNode.setMaleTotal(maleTotal); - classNode.setFemaleTotal(femaleTotal); - classNode.setMaleAssigned(maleAssigned); - classNode.setFemaleAssigned(femaleAssigned); - - majorNode.getChildren().add(classNode); - - // 累计专业人数 - majorMaleTotal += maleTotal; - majorFemaleTotal += femaleTotal; - majorMaleAssigned += maleAssigned; - majorFemaleAssigned += femaleAssigned; - } - - // 设置专业统计 - majorNode.setMaleTotal(majorMaleTotal); - majorNode.setFemaleTotal(majorFemaleTotal); - majorNode.setMaleAssigned(majorMaleAssigned); - majorNode.setFemaleAssigned(majorFemaleAssigned); - - collegeNode.getChildren().add(majorNode); - - // 累计学院人数 - collegeMaleTotal += majorMaleTotal; - collegeFemaleTotal += majorFemaleTotal; - collegeMaleAssigned += majorMaleAssigned; - collegeFemaleAssigned += majorFemaleAssigned; - } + for (Map item : majorClasses) { + majorMaleTotal += ((Number) item.get("male_total")).intValue(); + majorFemaleTotal += ((Number) item.get("female_total")).intValue(); + majorMaleAssigned += ((Number) item.get("male_assigned")).intValue(); + majorFemaleAssigned += ((Number) item.get("female_assigned")).intValue(); } - // 设置学院统计 - collegeNode.setMaleTotal(collegeMaleTotal); - collegeNode.setFemaleTotal(collegeFemaleTotal); - collegeNode.setMaleAssigned(collegeMaleAssigned); - collegeNode.setFemaleAssigned(collegeFemaleAssigned); + XgStudentTreeVo majorNode = new XgStudentTreeVo(); + majorNode.setId(majorId.toString()); + majorNode.setLabel(majorName); + majorNode.setType("major"); + majorNode.setMajorId(majorId); + majorNode.setMajorName(majorName); + majorNode.setMaleTotal(majorMaleTotal); + majorNode.setFemaleTotal(majorFemaleTotal); + majorNode.setMaleAssigned(majorMaleAssigned); + majorNode.setFemaleAssigned(majorFemaleAssigned); + majorNode.setChildren(new ArrayList<>()); - result.add(collegeNode); + // 4. 构建班级节点 + for (Map classData : majorClasses) { + Object classIdObj = classData.get("class_id"); + Long classId = classIdObj instanceof Long ? (Long) classIdObj + : Long.valueOf(classIdObj.toString()); + String className = (String) classData.get("class_name"); + String grade = (String) classData.get("grade"); + + int classMaleTotal = ((Number) classData.get("male_total")).intValue(); + int classFemaleTotal = ((Number) classData.get("female_total")).intValue(); + int classMaleAssigned = ((Number) classData.get("male_assigned")).intValue(); + int classFemaleAssigned = ((Number) classData.get("female_assigned")).intValue(); + + XgStudentTreeVo classNode = new XgStudentTreeVo(); + classNode.setId(classId.toString()); + classNode.setLabel(className); + classNode.setType("class"); + classNode.setGrade(grade); + classNode.setClassId(classId); + classNode.setClassName(className); + classNode.setMaleTotal(classMaleTotal); + classNode.setFemaleTotal(classFemaleTotal); + classNode.setMaleAssigned(classMaleAssigned); + classNode.setFemaleAssigned(classFemaleAssigned); + classNode.setChildren(new ArrayList<>()); + + majorNode.getChildren().add(classNode); + } + + collegeNode.getChildren().add(majorNode); } + + result.add(collegeNode); } - log.info("学生信息树构建完成,共 {} 个学院节点", result.size()); + log.info("成功构建学生信息树,共 {} 个学院", result.size()); } catch (Exception e) { log.error("构建学生信息树失败", e); - throw new RuntimeException("构建学生信息树失败: " + e.getMessage()); } 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. 调用按床位分配统计的方法 + return getStudentTreeByClass(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/backend/ruoyi-modules/ruoyi-xg/src/main/resources/dormViolation_template.xlsx b/backend/ruoyi-modules/ruoyi-xg/src/main/resources/dormViolation_template.xlsx new file mode 100644 index 00000000..7d1bd0e7 Binary files /dev/null and b/backend/ruoyi-modules/ruoyi-xg/src/main/resources/dormViolation_template.xlsx differ diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/dormHygieneCheck/XgDormHygieneCheckMapper.xml b/backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/dormHygieneCheck/XgDormHygieneCheckMapper.xml new file mode 100644 index 00000000..827ed2ae --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/dormHygieneCheck/XgDormHygieneCheckMapper.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/dormHygieneStudentScore/XgDormHygieneStudentScoreMapper.xml b/backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/dormHygieneStudentScore/XgDormHygieneStudentScoreMapper.xml new file mode 100644 index 00000000..a4df44e7 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/dormHygieneStudentScore/XgDormHygieneStudentScoreMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/dormViolation/XgDormViolationMapper.xml b/backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/dormViolation/XgDormViolationMapper.xml new file mode 100644 index 00000000..e81ccb55 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/dormViolation/XgDormViolationMapper.xml @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/holidayStay/XgHolidayStayMapper.xml b/backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/holidayStay/XgHolidayStayMapper.xml new file mode 100644 index 00000000..4cf092d0 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/holidayStay/XgHolidayStayMapper.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/studentMsg/XgStudentMsgMapper.xml b/backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/studentMsg/XgStudentMsgMapper.xml index dae23ebf..1c7de23c 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/studentMsg/XgStudentMsgMapper.xml +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/studentMsg/XgStudentMsgMapper.xml @@ -200,4 +200,100 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ORDER BY c.grade DESC, sm.college_name, sm.major_name, sm.class_name + + + + + + diff --git a/backend/ruoyi-modules/ruoyi-zsjy/pom.xml b/backend/ruoyi-modules/ruoyi-zsjy/pom.xml index 3a3abd1b..aed2e7cc 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/pom.xml +++ b/backend/ruoyi-modules/ruoyi-zsjy/pom.xml @@ -104,6 +104,12 @@ ruoyi-workflow + + + org.dromara + ruoyi-xg + + diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/InternshipInfoController.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/InternshipInfoController.java index 94bfc509..e0638e8b 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/InternshipInfoController.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/InternshipInfoController.java @@ -5,14 +5,20 @@ import org.dromara.common.core.domain.R; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.web.core.BaseController; +import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.zsjy.domain.vo.InternshipInfoVO; import org.dromara.zsjy.domain.bo.InternshipInfoQueryBO; +import org.dromara.zsjy.domain.bo.InternshipInfoAddBO; +import org.dromara.zsjy.domain.bo.InternshipInfoEditBO; import org.dromara.zsjy.service.IInternshipInfoService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.constraints.NotEmpty; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import jakarta.servlet.http.HttpServletResponse; @@ -23,6 +29,7 @@ import jakarta.servlet.http.HttpServletResponse; * @author system * @date 2025-01-03 */ +@Slf4j @Validated @RequiredArgsConstructor @RestController @@ -39,7 +46,18 @@ public class InternshipInfoController extends BaseController { @SaCheckPermission("zsjy:internship:list") @GetMapping("/list") public TableDataInfo list(@Validated InternshipInfoQueryBO queryBO, PageQuery pageQuery) { - return internshipInfoService.queryPageList(queryBO, pageQuery); + log.info("=== Controller层调试 ==="); + log.info("接口被调用: /zsjy/internship/list"); + log.info("查询参数: {}", queryBO); + log.info("分页参数: pageNum={}, pageSize={}", pageQuery.getPageNum(), pageQuery.getPageSize()); + + TableDataInfo result = internshipInfoService.queryPageList(queryBO, pageQuery); + + log.info("Controller返回结果: total={}, rows.size={}", + result.getTotal(), result.getRows() != null ? result.getRows().size() : "null"); + log.info("=== Controller层调试结束 ==="); + + return result; } /** @@ -83,4 +101,128 @@ public class InternshipInfoController extends BaseController { public R getInternshipStatistics(@Validated InternshipInfoQueryBO queryBO) { return R.ok(internshipInfoService.getInternshipStatistics(queryBO)); } + + /** + * 学生获取自己的实习信息(移动端专用) + */ + @Operation(summary = "学生获取实习信息", description = "学生查看自己的实习信息(移动端)") + @GetMapping("/student/info") + public R getStudentInfo() { + log.info("=== 学生获取实习信息 ==="); + log.info("当前用户ID: {}", LoginHelper.getUserId()); + + try { + Long currentUserId = LoginHelper.getUserId(); + if (currentUserId == null) { + log.error("用户ID为空,无法获取实习信息"); + return R.fail("用户未登录"); + } + + InternshipInfoVO result = internshipInfoService.queryById(currentUserId); + + if (result != null) { + log.info("成功获取学生实习信息,学生ID: {}", currentUserId); + return R.ok(result); + } else { + log.info("学生暂无实习信息,学生ID: {}", currentUserId); + return R.fail("暂无实习信息"); + } + } catch (Exception e) { + log.error("获取学生实习信息异常,用户ID: {},错误信息: {}", LoginHelper.getUserId(), e.getMessage(), e); + return R.fail("获取实习信息失败:" + e.getMessage()); + } + } + + /** + * 新增实习信息 + */ + @Operation(summary = "新增实习信息", description = "为学生新增实习信息") + @SaCheckPermission("zsjy:internship:add") + @PostMapping + public R add(@RequestBody @Validated(AddGroup.class) InternshipInfoAddBO addBO) { + log.info("新增实习信息请求,参数:{}", addBO); + try { + Boolean result = internshipInfoService.insertInternshipInfo(addBO); + if (result) { + log.info("新增实习信息成功,学生ID:{}", addBO.getStudentId()); + return R.ok(); + } else { + log.error("新增实习信息失败,学生ID:{}", addBO.getStudentId()); + return R.fail("新增实习信息失败"); + } + } catch (Exception e) { + log.error("新增实习信息异常,学生ID:{},错误信息:{}", addBO.getStudentId(), e.getMessage(), e); + return R.fail("新增实习信息失败:" + e.getMessage()); + } + } + + /** + * 33 + */ + @Operation(summary = "修改实习信息", description = "修改学生实习信息") + @SaCheckPermission("zsjy:internship:edit") + @PutMapping + public R edit(@RequestBody @Validated(EditGroup.class) InternshipInfoEditBO editBO) { + log.info("修改实习信息请求,参数:{}", editBO); + try { + Boolean result = internshipInfoService.updateInternshipInfo(editBO); + if (result) { + log.info("修改实习信息成功,实习信息ID:{}", editBO.getInternshipId()); + return R.ok(); + } else { + log.error("修改实习信息失败,实习信息ID:{}", editBO.getInternshipId()); + return R.fail("修改实习信息失败"); + } + } catch (Exception e) { + log.error("修改实习信息异常,实习信息ID:{},错误信息:{}", editBO.getInternshipId(), e.getMessage(), e); + return R.fail("修改实习信息失败:" + e.getMessage()); + } + } + + /** + * 删除实习信息 + */ + @Operation(summary = "删除实习信息", description = "删除学生实习信息") + @SaCheckPermission("zsjy:internship:remove") + @DeleteMapping("/{internshipIds}") + public R remove(@Parameter(description = "实习信息ID串") + @PathVariable Long[] internshipIds) { + log.info("删除实习信息请求,IDs:{}", (Object) internshipIds); + try { + Boolean result = internshipInfoService.deleteInternshipInfo(internshipIds); + if (result) { + log.info("删除实习信息成功,IDs:{}", (Object) internshipIds); + return R.ok(); + } else { + log.error("删除实习信息失败,IDs:{}", (Object) internshipIds); + return R.fail("删除实习信息失败"); + } + } catch (Exception e) { + log.error("删除实习信息异常,IDs:{},错误信息:{}", internshipIds, e.getMessage(), e); + return R.fail("删除实习信息失败:" + e.getMessage()); + } + } + + /** + * 获取当前学生的实习信息 + */ + @Operation(summary = "获取当前学生实习信息", description = "获取当前登录学生的实习信息,用于月度总结等功能") + @SaCheckPermission("zsjy:internship:query") + @GetMapping("/current") + public R getCurrentStudentInternship() { + try { + log.info("=== 获取当前学生实习信息接口被调用 ==="); + InternshipInfoVO result = internshipInfoService.getCurrentStudentInternship(); + if (result != null) { + log.info("成功获取当前学生实习信息,实习ID:{}", result.getInternshipId()); + return R.ok(result); + } else { + log.info("当前学生没有实习信息"); + return R.ok(); // 返回空数据,前端需要处理这种情况 + } + } catch (Exception e) { + log.error("获取当前学生实习信息失败:{}", e.getMessage(), e); + return R.fail("获取实习信息失败:" + e.getMessage()); + } + } } diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyInternshipDailyController.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyInternshipDailyController.java new file mode 100644 index 00000000..6399c816 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyInternshipDailyController.java @@ -0,0 +1,120 @@ +package org.dromara.zsjy.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.zsjy.domain.bo.CheckInGroup; +import org.dromara.zsjy.domain.bo.ZsjyInternshipDailyAddBo; +import org.dromara.zsjy.domain.bo.ZsjyInternshipDailyQueryBo; +import org.dromara.zsjy.domain.vo.ZsjyInternshipDailyVo; +import org.dromara.zsjy.service.IZsjyInternshipDailyService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * 实习日常管理Controller + * + * @author system + * @date 2025-01-03 + */ +@Slf4j +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/zsjy/internship/daily") +@Tag(name = "实习日常管理", description = "学生实习日常打卡签到管理") +public class ZsjyInternshipDailyController extends BaseController { + + private final IZsjyInternshipDailyService dailyService; + + /** + * 查询实习日常记录列表 + */ + @Operation(summary = "查询实习日常记录列表", description = "班主任查看学生实习日常记录") + @SaCheckPermission("zsjy:internshipdaily:list") + @GetMapping("/list") + public TableDataInfo list(@Validated ZsjyInternshipDailyQueryBo queryBo, PageQuery pageQuery) { + log.info("=== Controller层调试 ==="); + log.info("接口被调用: /zsjy/internship/daily/list"); + log.info("查询参数: {}", queryBo); + log.info("分页参数: {}", pageQuery); + + return dailyService.queryPageList(queryBo, pageQuery); + } + + /** + * 学生签到打卡 + */ + @Operation(summary = "学生签到打卡", description = "学生进行实习日常签到打卡") + @SaCheckPermission("zsjy:internshipdaily:checkin") + @PostMapping("/checkin") + public R checkIn(@Validated(CheckInGroup.class) @RequestBody ZsjyInternshipDailyAddBo addBo) { + log.info("=== Controller层调试 ==="); + log.info("接口被调用: /zsjy/internship/daily/checkin"); + log.info("签到参数: {}", addBo); + + return dailyService.checkIn(addBo); + } + + /** + * 获取学生考勤日历 + */ + @Operation(summary = "获取学生考勤日历", description = "查看指定学生的考勤日历数据") + @SaCheckPermission("zsjy:internshipdaily:calendar") + @GetMapping("/calendar") + public R>> getCalendar( + @Parameter(description = "学生ID", required = true) @RequestParam Long studentId, + @Parameter(description = "年份") @RequestParam(required = false) Integer year, + @Parameter(description = "月份") @RequestParam(required = false) Integer month) { + + log.info("=== Controller层调试 ==="); + log.info("接口被调用: /zsjy/internship/daily/calendar"); + log.info("学生ID: {}, 查询年月: {}-{}", studentId, year, month); + + return dailyService.getCalendarData(studentId, year, month); + } + + /** + * 获取考勤统计数据 + */ + @Operation(summary = "获取考勤统计数据", description = "查看实习生考勤统计分析") + @SaCheckPermission("zsjy:internshipdaily:statistics") + @GetMapping("/statistics") + public R> getStatistics(@Validated ZsjyInternshipDailyQueryBo queryBo) { + log.info("=== Controller层调试 ==="); + log.info("接口被调用: /zsjy/internship/daily/statistics"); + log.info("统计参数: {}", queryBo); + + return dailyService.getStatistics(queryBo); + } + + /** + * 获取学生个人月度考勤统计(移动端专用) + */ + @Operation(summary = "获取学生月度统计", description = "获取指定学生的月度考勤统计数据") + @SaCheckPermission("zsjy:internshipdaily:statistics") + @GetMapping("/monthly-statistics") + public R> getStudentMonthlyStatistics( + @Parameter(description = "学生ID", required = true) @RequestParam Long studentId, + @Parameter(description = "年份", required = true) @RequestParam Integer year, + @Parameter(description = "月份", required = true) @RequestParam Integer month) { + + log.info("=== Controller层调试 ==="); + log.info("接口被调用: /zsjy/internship/daily/monthly-statistics"); + log.info("学生ID: {}, 年月: {}-{}", studentId, year, month); + + return dailyService.getStudentMonthlyStatistics(studentId, year, month); + } + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyInternshipMonthlyController.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyInternshipMonthlyController.java new file mode 100644 index 00000000..996a1112 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyInternshipMonthlyController.java @@ -0,0 +1,238 @@ +package org.dromara.zsjy.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; + +import jakarta.servlet.http.HttpServletResponse; +import org.dromara.zsjy.domain.bo.ZsjyInternshipMonthlyAddBo; +import org.dromara.zsjy.domain.bo.ZsjyInternshipMonthlyQueryBo; +import org.dromara.zsjy.domain.vo.ZsjyInternshipMonthlyVo; +import org.dromara.zsjy.service.IZsjyInternshipMonthlyService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * 学生月度实习总结Controller + * + * @author system + * @date 2025-01-03 + */ +@Slf4j +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/zsjy/internship/monthly") +@Tag(name = "学生月度实习总结", description = "学生月度实习总结填写管理") +public class ZsjyInternshipMonthlyController extends BaseController { + + private final IZsjyInternshipMonthlyService monthlyService; + + /** + * 查询学生月度实习总结列表 + */ + @Operation(summary = "查询学生月度实习总结列表", description = "学生查看自己的月度总结列表") + @SaCheckPermission("zsjy:internshipmonthly:list") + @GetMapping("/list") + public TableDataInfo list(@Validated ZsjyInternshipMonthlyQueryBo queryBo, PageQuery pageQuery) { + log.info("=== Controller层调试 ==="); + log.info("接口被调用: /zsjy/internship/monthly/list"); + log.info("查询参数: {}", queryBo); + log.info("分页参数: {}", pageQuery); + + return monthlyService.queryPageList(queryBo, pageQuery); + } + + /** + * 查询学生月度实习总结详细信息 + */ + @Operation(summary = "查询学生月度实习总结详细信息", description = "根据ID查询月度总结详情") + @SaCheckPermission("zsjy:internshipmonthly:query") + @GetMapping("/{monthlyId}") + public R getInfo(@Parameter(description = "月度总结ID") + @PathVariable Long monthlyId) { + log.info("查询月度总结详情, ID: {}", monthlyId); + return R.ok(monthlyService.queryById(monthlyId)); + } + + /** + * 新增学生月度实习总结 + */ + @Operation(summary = "新增学生月度实习总结", description = "学生创建新的月度总结") + @SaCheckPermission("zsjy:internshipmonthly:add") + @PostMapping + public R add(@Validated(AddGroup.class) @RequestBody ZsjyInternshipMonthlyAddBo addBo) { + log.info("新增月度总结: {}", addBo); + + if (!monthlyService.insertByBo(addBo)) { + return R.fail("新增月度总结失败"); + } + return R.ok(); + } + + /** + * 修改学生月度实习总结 + */ + @Operation(summary = "修改学生月度实习总结", description = "学生编辑自己的月度总结") + @SaCheckPermission("zsjy:internshipmonthly:edit") + @PutMapping + public R edit(@Validated(EditGroup.class) @RequestBody ZsjyInternshipMonthlyAddBo addBo) { + log.info("修改月度总结: {}", addBo); + + if (!monthlyService.updateByBo(addBo)) { + return R.fail("修改月度总结失败"); + } + return R.ok(); + } + + /** + * 删除学生月度实习总结 + */ + @Operation(summary = "删除学生月度实习总结", description = "学生删除自己的草稿状态月度总结") + @SaCheckPermission("zsjy:internshipmonthly:remove") + @DeleteMapping("/{monthlyIds}") + public R remove(@Parameter(description = "月度总结ID串") + @PathVariable Long[] monthlyIds) { + log.info("删除月度总结, IDs: {}", (Object) monthlyIds); + + if (!monthlyService.deleteByIds(List.of(monthlyIds))) { + return R.fail("删除月度总结失败"); + } + return R.ok(); + } + + /** + * 提交月度总结审批 + */ + @Operation(summary = "提交月度总结审批", description = "学生提交月度总结给班主任审批") + @SaCheckPermission("zsjy:internshipmonthly:submit") + @PutMapping("/submit/{monthlyId}") + public R submit(@Parameter(description = "月度总结ID") + @PathVariable Long monthlyId) { + log.info("提交月度总结审批, ID: {}", monthlyId); + + if (!monthlyService.submitForApproval(monthlyId)) { + return R.fail("提交月度总结失败"); + } + return R.ok(); + } + + /** + * 自动保存草稿 + */ + @Operation(summary = "自动保存草稿", description = "学生编辑时自动保存草稿") + @SaCheckPermission("zsjy:internshipmonthly:autosave") + @PostMapping("/autosave") + public R autoSave(@RequestBody ZsjyInternshipMonthlyAddBo addBo) { + log.info("自动保存草稿: {}", addBo); + + if (!monthlyService.autoSaveDraft(addBo)) { + return R.fail("自动保存失败"); + } + return R.ok(); + } + + /** + * 检查月份是否存在总结 + */ + @Operation(summary = "检查月份是否存在总结", description = "检查学生某月是否已有总结记录") + @SaCheckPermission("zsjy:internshipmonthly:check") + @GetMapping("/check/{summaryMonth}") + public R checkMonth(@Parameter(description = "总结月份") + @PathVariable String summaryMonth, + @Parameter(description = "排除的总结ID") + @RequestParam(required = false) Long monthlyId) { + log.info("检查月份总结, 月份: {}, 排除ID: {}", summaryMonth, monthlyId); + + Boolean exists = monthlyService.checkMonthExists(null, summaryMonth, monthlyId); + return R.ok(exists); + } + + /** + * 获取月度总结模板 + */ + @Operation(summary = "获取月度总结模板", description = "获取指定月份的总结模板信息") + @SaCheckPermission("zsjy:internshipmonthly:template") + @GetMapping("/template/{summaryMonth}") + public R getTemplate(@Parameter(description = "总结月份") + @PathVariable String summaryMonth) { + log.info("获取月度总结模板, 月份: {}", summaryMonth); + + ZsjyInternshipMonthlyVo template = monthlyService.getMonthlyTemplate(summaryMonth); + return R.ok(template); + } + + /** + * 查询学生指定实习的月度总结列表 + */ + @Operation(summary = "查询学生指定实习的月度总结列表", description = "查询学生某个实习的所有月度总结") + @SaCheckPermission("zsjy:internshipmonthly:list") + @GetMapping("/internship/{internshipId}") + public R> listByInternship(@Parameter(description = "实习ID") + @PathVariable Long internshipId) { + log.info("查询实习月度总结列表, 实习ID: {}", internshipId); + + List list = monthlyService.queryByStudentAndInternship(null, internshipId); + return R.ok(list); + } + + /** + * 统计学生各状态的总结数量 + */ + @Operation(summary = "统计学生各状态的总结数量", description = "统计学生草稿、待审核、已审核等状态的总结数量") + @SaCheckPermission("zsjy:internshipmonthly:statistics") + @GetMapping("/statistics") + public R> statistics() { + log.info("统计学生月度总结状态"); + + List statistics = monthlyService.countByStatus(null); + return R.ok(statistics); + } + + /** + * 获取学生当前实习信息 + */ + @Operation(summary = "获取学生当前实习信息", description = "获取当前登录学生的进行中实习信息") + @SaCheckPermission("zsjy:internshipmonthly:add") + @GetMapping("/current-internship") + public R> getCurrentInternship() { + log.info("获取学生当前实习信息"); + + Map internshipInfo = monthlyService.getCurrentInternshipInfo(); + if (internshipInfo == null || internshipInfo.isEmpty()) { + return R.fail("您当前没有进行中的实习记录"); + } + + return R.ok(internshipInfo); + } + + /** + * 导出学生月度实习总结 + */ + @Operation(summary = "导出学生月度实习总结", description = "导出学生的月度总结数据") + @SaCheckPermission("zsjy:internshipmonthly:export") + @Log(title = "学生月度实习总结", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(@RequestBody ZsjyInternshipMonthlyQueryBo queryBo, HttpServletResponse response) { + log.info("导出月度总结: {}", queryBo); + + List list = monthlyService.queryList(queryBo); + ExcelUtil.exportExcel(list, "学生月度实习总结", ZsjyInternshipMonthlyVo.class, response); + } + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyInternshipVisitController.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyInternshipVisitController.java new file mode 100644 index 00000000..b5da4fc6 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyInternshipVisitController.java @@ -0,0 +1,131 @@ +package org.dromara.zsjy.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.zsjy.domain.bo.ZsjyInternshipVisitBo; +import org.dromara.zsjy.domain.bo.ZsjyInternshipVisitQueryBo; +import org.dromara.zsjy.domain.vo.ZsjyInternshipVisitVo; +import org.dromara.zsjy.service.IZsjyInternshipVisitService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 班主任走访Controller + * + * @author system + * @date 2025-01-03 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/zsjy/internshipVisit") +@Tag(name = "班主任走访管理", description = "班主任走访管理") +public class ZsjyInternshipVisitController extends BaseController { + + private final IZsjyInternshipVisitService zsjyInternshipVisitService; + + /** + * 查询班主任走访列表 + */ + @Operation(summary = "查询班主任走访列表") + @SaCheckPermission("zsjy:internshipVisit:list") + @GetMapping("/list") + public TableDataInfo list(ZsjyInternshipVisitQueryBo bo, PageQuery pageQuery) { + return zsjyInternshipVisitService.queryPageList(bo, pageQuery); + } + + /** + * 导出班主任走访列表 + */ + @Operation(summary = "导出班主任走访列表") + @SaCheckPermission("zsjy:internshipVisit:export") + @Log(title = "班主任走访", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(ZsjyInternshipVisitQueryBo bo, HttpServletResponse response) { + List list = zsjyInternshipVisitService.queryList(bo); + ExcelUtil.exportExcel(list, "班主任走访", ZsjyInternshipVisitVo.class, response); + } + + /** + * 获取班主任走访详细信息 + */ + @Operation(summary = "获取班主任走访详细信息") + @SaCheckPermission("zsjy:internshipVisit:query") + @GetMapping("/{visitId}") + @Parameters({ + @Parameter(name = "visitId", description = "走访记录ID", required = true) + }) + public R getInfo(@NotNull(message = "走访记录ID不能为空") + @PathVariable Long visitId) { + return R.ok(zsjyInternshipVisitService.queryById(visitId)); + } + + /** + * 新增班主任走访 + */ + @Operation(summary = "新增班主任走访") + @SaCheckPermission("zsjy:internshipVisit:add") + @Log(title = "班主任走访", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody ZsjyInternshipVisitBo bo) { + return toAjax(zsjyInternshipVisitService.insertByBo(bo)); + } + + /** + * 修改班主任走访 + */ + @Operation(summary = "修改班主任走访") + @SaCheckPermission("zsjy:internshipVisit:edit") + @Log(title = "班主任走访", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody ZsjyInternshipVisitBo bo) { + return toAjax(zsjyInternshipVisitService.updateByBo(bo)); + } + + /** + * 删除班主任走访 + */ + @Operation(summary = "删除班主任走访") + @SaCheckPermission("zsjy:internshipVisit:remove") + @Log(title = "班主任走访", businessType = BusinessType.DELETE) + @DeleteMapping("/{visitIds}") + @Parameters({ + @Parameter(name = "visitIds", description = "走访记录ID串", required = true) + }) + public R remove(@NotEmpty(message = "走访记录ID不能为空") + @PathVariable Long[] visitIds) { + return toAjax(zsjyInternshipVisitService.deleteWithValidByIds(List.of(visitIds), true)); + } + + /** + * 获取可选项列表(学生选择) + */ + @Operation(summary = "获取可选项列表") + @SaCheckPermission("zsjy:internshipVisit:list") + @GetMapping("/options") + public R> getOptions() { + List list = zsjyInternshipVisitService.getStudentOptions(); + return R.ok(list); + } +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipDaily.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipDaily.java new file mode 100644 index 00000000..df363b7e --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipDaily.java @@ -0,0 +1,186 @@ +package org.dromara.zsjy.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; +import java.sql.Time; +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.Date; + +/** + * 实习日常管理实体对象 zsjy_internship_daily + * + * @author system + * @date 2025-01-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("zsjy_internship_daily") +public class ZsjyInternshipDaily extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 日常记录ID (对应数据库daily_id字段) + */ + @TableId(value = "daily_id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 租户ID + */ + @TableField("tenant_id") + private String tenantId; + + /** + * 实习ID + */ + @TableField("internship_id") + private Long internshipId; + + /** + * 学生ID + */ + @TableField("student_id") + private Long studentId; + + /** + * 签到日期 + */ + @TableField("check_date") + private LocalDate checkDate; + + /** + * 签到时间 + */ + @TableField("check_in_time") + private LocalTime checkInTime; + + /** + * 签到地点 (对应数据库check_in_location字段) + */ + @TableField("check_in_location") + private String checkInLocation; + + /** + * 签到经度 (对应数据库longitude字段) + */ + @TableField("longitude") + private BigDecimal longitude; + + /** + * 签到纬度 (对应数据库latitude字段) + */ + @TableField("latitude") + private BigDecimal latitude; + + /** + * 工作内容/日总结 + */ + @TableField("daily_summary") + private String dailySummary; + + /** + * 图片附件(JSON格式) + */ + @TableField("images") + private String images; + + /** + * 是否异常(0正常 1异常) + */ + @TableField("is_exception") + private String isException; + + /** + * 异常原因 + */ + @TableField("exception_reason") + private String exceptionReason; + + /** + * 异常处理状态(0待处理 1已处理) + */ + @TableField("exception_status") + private String exceptionStatus; + + /** + * 考勤状态(0=未签到 1=正常 2=迟到 3=早退 4=旷工) + */ + @TableField("status") + private String status; + + /** + * 备注 + */ + @TableField("remark") + private String remark; + + // ==================== 扩展字段(数据库中不存在,通过关联查询获取) ==================== + + /** + * 学生姓名 (通过student_id关联查询获取) + */ + @TableField(exist = false) + private String studentName; + + /** + * 学号 (通过student_id关联查询获取) + */ + @TableField(exist = false) + private String studentNumber; + + /** + * 班级名称 (通过student_id关联查询获取) + */ + @TableField(exist = false) + private String className; + + /** + * 实习单位名称 (通过internship_id关联查询获取) + */ + @TableField(exist = false) + private String companyName; + + /** + * 实习岗位 (通过internship_id关联查询获取) + */ + @TableField(exist = false) + private String position; + + + + /** + * 签到经度 (业务扩展字段,当前只有签退经度) + */ + @TableField(exist = false) + private BigDecimal checkInLongitude; + + /** + * 签到纬度 (业务扩展字段,当前只有签退纬度) + */ + @TableField(exist = false) + private BigDecimal checkInLatitude; + + /** + * 签退时间 (业务扩展字段,当前只有签到时间) + */ + @TableField(exist = false) + private LocalTime checkOutTime; + + /** + * 工作时长(小时) (业务计算字段) + */ + @TableField(exist = false) + private BigDecimal workDuration; + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipInfo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipInfo.java new file mode 100644 index 00000000..523eafe1 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipInfo.java @@ -0,0 +1,170 @@ +package org.dromara.zsjy.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 实习信息实体对象 zsjy_internship_info + * + * @author system + * @date 2025-01-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("zsjy_internship_info") +public class ZsjyInternshipInfo extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 实习信息ID + */ + @TableId(value = "internship_id") + private Long internshipId; + + /** + * 学生ID + */ + private Long studentId; + + /** + * 学号 + */ + private String studentNo; + + /** + * 学生姓名 + */ + private String studentName; + + /** + * 性别 + */ + private String gender; + + /** + * 学院ID + */ + private Long collegeId; + + /** + * 专业ID + */ + private Long majorId; + + /** + * 班级ID + */ + private Long classId; + + /** + * 联系电话 + */ + private String phone; + + /** + * 实习单位名称 + */ + private String companyName; + + /** + * 实习单位地址 + */ + private String companyAddress; + + /** + * 行业类型 + */ + private String companyIndustry; + + /** + * 单位规模 + */ + private String companyScale; + + /** + * 单位联系人 + */ + private String companyContact; + + /** + * 联系人电话 + */ + private String companyPhone; + + /** + * 岗位名称 + */ + private String positionName; + + /** + * 岗位职责 + */ + private String positionDuties; + + /** + * 实习津贴 + */ + private BigDecimal internshipAllowance; + + /** + * 工作时长 + */ + private String workHours; + + /** + * 住宿地址 + */ + private String accommodationAddress; + + /** + * 住宿类型 + */ + private String accommodationType; + + /** + * 住宿费用 + */ + private BigDecimal accommodationCost; + + /** + * 实习开始日期 + */ + private Date startDate; + + /** + * 实习结束日期 + */ + private Date endDate; + + /** + * 实际结束日期 + */ + private Date actualEndDate; + + /** + * 状态(0正常 1停用) + */ + private String status; + + /** + * 实习状态(1进行中 2已完成 3中断) + */ + private String internshipStatus; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic(value = "0", delval = "1") + private Long deleted; +} + \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipMonthly.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipMonthly.java new file mode 100644 index 00000000..4213a17a --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipMonthly.java @@ -0,0 +1,191 @@ +package org.dromara.zsjy.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 学生月度实习总结实体对象 zsjy_internship_monthly + * + * @author system + * @date 2025-01-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("zsjy_internship_monthly") +public class ZsjyInternshipMonthly extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 月度总结ID (对应数据库monthly_id字段) + */ + @TableId(value = "monthly_id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 租户ID + */ + @TableField("tenant_id") + private String tenantId; + + /** + * 实习ID + */ + @TableField("internship_id") + private Long internshipId; + + /** + * 学生ID + */ + @TableField("student_id") + private Long studentId; + + /** + * 总结月份 (格式: 2025-01) + */ + @TableField("summary_month") + private String summaryMonth; + + /** + * 月份序号 (1-12) + */ + @TableField("month_number") + private Integer monthNumber; + + /** + * 工作内容 (必填,500字以内) + */ + @TableField("work_content") + private String workContent; + + /** + * 主要成果 + */ + @TableField("achievements") + private String achievements; + + /** + * 学习收获 + */ + @TableField("learning_gains") + private String learningGains; + + /** + * 遇到的问题 + */ + @TableField("problems_encountered") + private String problemsEncountered; + + /** + * 自我评价 + */ + @TableField("self_evaluation") + private String selfEvaluation; + + /** + * 下月工作计划 + */ + @TableField("next_month_plan") + private String nextMonthPlan; + + /** + * 专业技能自评 (1-10分) + */ + @TableField("professional_skills") + private Integer professionalSkills; + + /** + * 沟通技能自评 (1-10分) + */ + @TableField("communication_skills") + private Integer communicationSkills; + + /** + * 团队协作自评 (1-10分) - 修正字段映射 + */ + @TableField("teamwork_ability") + private Integer teamworkAbility; + + /** + * 学习能力自评 (1-10分) + */ + @TableField("learning_ability") + private Integer learningAbility; + + /** + * 工作态度自评 (1-10分) + */ + @TableField("work_attitude") + private Integer workAttitude; + + /** + * 审核状态 (0:待审核 1:已审核 2:审核拒绝 3:草稿) - 修正字段映射 + */ + @TableField("review_status") + private Integer reviewStatus; + + /** + * 审核人ID - 修正字段映射 + */ + @TableField("review_by") + private Long reviewBy; + + /** + * 审核时间 + */ + @TableField("review_time") + private Date reviewTime; + + /** + * 审核评分 (百分制) + */ + @TableField("review_score") + private BigDecimal reviewScore; + + /** + * 审核意见 - 修正字段映射 + */ + @TableField("review_comment") + private String reviewComment; + + /** + * 提交时间 + */ + @TableField("submit_time") + private Date submitTime; + + /** + * 字数统计 + */ + @TableField("word_count") + private Integer wordCount; + + /** + * 是否逾期 (0:未逾期 1:已逾期) + */ + @TableField("is_overdue") + private Integer isOverdue; + + /** + * 提交截止日期 - 修正字段映射 + */ + @TableField("submit_deadline") + private Date submitDeadline; + + /** + * 附件信息 (JSON格式存储) + */ + @TableField("attachments") + private String attachments; + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipVisit.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipVisit.java new file mode 100644 index 00000000..b09fc6a7 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipVisit.java @@ -0,0 +1,173 @@ +package org.dromara.zsjy.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 班主任走访实体对象 zsjy_internship_visit + * + * @author system + * @date 2025-01-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("zsjy_internship_visit") +public class ZsjyInternshipVisit extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 走访记录ID + */ + @TableId(value = "visit_id", type = IdType.ASSIGN_ID) + private Long visitId; + + /** + * 租户ID + */ + @TableField("tenant_id") + private String tenantId; + + /** + * 实习ID + */ + @TableField("internship_id") + private Long internshipId; + + /** + * 学生ID + */ + @TableField("student_id") + private Long studentId; + + /** + * 企业名称 + */ + @TableField("company_name") + private String companyName; + + /** + * 企业地址 + */ + @TableField("company_address") + private String companyAddress; + + /** + * 走访时间 + */ + @TableField("visit_date") + private Date visitDate; + + /** + * 走访人员 + */ + @TableField("visitor_name") + private String visitorName; + + /** + * 走访人员职务 + */ + @TableField("visitor_position") + private String visitorPosition; + + /** + * 走访目的 + */ + @TableField("visit_purpose") + private String visitPurpose; + + /** + * 企业情况 + */ + @TableField("company_situation") + private String companySituation; + + /** + * 学生表现 + */ + @TableField("student_performance") + private String studentPerformance; + + /** + * 发现问题 + */ + @TableField("problems_found") + private String problemsFound; + + /** + * 改进建议 + */ + @TableField("suggestions") + private String suggestions; + + /** + * 走访总结 + */ + @TableField("visit_summary") + private String visitSummary; + + /** + * 现场照片(JSON格式) + */ + @TableField("visit_images") + private String visitImages; + + /** + * 走访文件附件(JSON格式) + */ + @TableField("visit_files") + private String visitFiles; + + /** + * 走访状态:数据库存储数字编码(0-草稿 1-已提交 2-已审核) + */ + @TableField("status") + private String status; + + /** + * 备注 + */ + @TableField("remark") + private String remark; + + // ==================== 扩展字段(数据库中不存在,通过关联查询获取) ==================== + + /** + * 学生姓名 (通过student_id关联查询获取) + */ + @TableField(exist = false) + private String studentName; + + /** + * 学号 (通过student_id关联查询获取) + */ + @TableField(exist = false) + private String studentNumber; + + /** + * 班级名称 (通过student_id关联查询获取) + */ + @TableField(exist = false) + private String className; + + /** + * 专业名称 (通过student_id关联查询获取) + */ + @TableField(exist = false) + private String majorName; + + /** + * 学院名称 (通过student_id关联查询获取) + */ + @TableField(exist = false) + private String collegeName; +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/CheckInGroup.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/CheckInGroup.java new file mode 100644 index 00000000..023010f5 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/CheckInGroup.java @@ -0,0 +1,11 @@ +package org.dromara.zsjy.domain.bo; + +/** + * 签到验证组 + * 用于签到时的数据验证,不包含签退相关字段的验证 + * + * @author system + * @date 2025-01-03 + */ +public interface CheckInGroup { +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/InternshipInfoAddBO.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/InternshipInfoAddBO.java new file mode 100644 index 00000000..752666cd --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/InternshipInfoAddBO.java @@ -0,0 +1,167 @@ +package org.dromara.zsjy.domain.bo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.dromara.common.core.validate.AddGroup; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Positive; +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 实习信息新增业务对象 InternshipInfoAddBO + * + * @author system + * @date 2025-01-03 + */ +@Data +@Schema(description = "实习信息新增业务对象") +public class InternshipInfoAddBO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 学生ID + */ + @Schema(description = "学生ID") + @NotNull(message = "学生ID不能为空", groups = {AddGroup.class}) + private Long studentId; + + /** + * 实习状态:1-进行中,2-已完成,3-已暂停,4-已终止 + */ + @Schema(description = "实习状态:1-进行中,2-已完成,3-已暂停,4-已终止") + @NotBlank(message = "实习状态不能为空", groups = {AddGroup.class}) + @Pattern(regexp = "^[1-4]$", message = "实习状态只能是1-进行中,2-已完成,3-已暂停,4-已终止", groups = {AddGroup.class}) + private String internshipStatus; + + /** + * 实习单位名称 + */ + @Schema(description = "实习单位名称") + @NotBlank(message = "实习单位名称不能为空", groups = {AddGroup.class}) + private String companyName; + + /** + * 实习单位地址 + */ + @Schema(description = "实习单位地址") + private String companyAddress; + + /** + * 单位联系人 + */ + @Schema(description = "单位联系人") + private String companyContact; + + /** + * 联系人电话 + */ + @Schema(description = "联系人电话") + @Pattern(regexp = "^1[3-9]\\d{9}$", message = "联系人电话格式不正确", groups = {AddGroup.class}) + private String companyPhone; + + /** + * 行业类型 + */ + @Schema(description = "行业类型") + private String companyIndustry; + + /** + * 单位规模 + */ + @Schema(description = "单位规模") + private String companyScale; + + /** + * 实习岗位 + */ + @Schema(description = "实习岗位") + @NotBlank(message = "实习岗位不能为空", groups = {AddGroup.class}) + private String internshipPosition; + + /** + * 实习薪资 + */ + @Schema(description = "实习薪资") + @Positive(message = "实习薪资必须大于0", groups = {AddGroup.class}) + private BigDecimal internshipAllowance; + + /** + * 实习开始时间 + */ + @Schema(description = "实习开始时间") + @JsonFormat(pattern = "yyyy-MM-dd") + @NotNull(message = "实习开始时间不能为空", groups = {AddGroup.class}) + private Date internshipStartDate; + + /** + * 实习结束时间 + */ + @Schema(description = "实习结束时间") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date internshipEndDate; + + /** + * 联系人姓名 + */ + @Schema(description = "联系人姓名") + private String contactPersonName; + + /** + * 联系人电话 + */ + @Schema(description = "联系人电话") + @Pattern(regexp = "^1[3-9]\\d{9}$", message = "联系人电话格式不正确", groups = {AddGroup.class}) + private String contactPersonPhone; + + /** + * 岗位职责 + */ + @Schema(description = "岗位职责") + private String positionDuties; + + /** + * 工作时长 + */ + @Schema(description = "工作时长") + private String workHours; + + /** + * 住宿地址 + */ + @Schema(description = "住宿地址") + private String accommodationAddress; + + /** + * 住宿类型 + */ + @Schema(description = "住宿类型") + private String accommodationType; + + /** + * 住宿费用 + */ + @Schema(description = "住宿费用") + private BigDecimal accommodationCost; + + /** + * 实习协议状态:1-已签署,2-未签署 + */ + @Schema(description = "实习协议状态:1-已签署,2-未签署") + @Pattern(regexp = "^[1-2]$", message = "实习协议状态只能是1-已签署,2-未签署", groups = {AddGroup.class}) + private String agreementStatus; + + /** + * 备注信息 + */ + @Schema(description = "备注信息") + private String remark; +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/InternshipInfoEditBO.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/InternshipInfoEditBO.java new file mode 100644 index 00000000..ca56629d --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/InternshipInfoEditBO.java @@ -0,0 +1,174 @@ +package org.dromara.zsjy.domain.bo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.dromara.common.core.validate.EditGroup; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Positive; +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 实习信息修改业务对象 InternshipInfoEditBO + * + * @author system + * @date 2025-01-03 + */ +@Data +@Schema(description = "实习信息修改业务对象") +public class InternshipInfoEditBO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 实习信息ID + */ + @Schema(description = "实习信息ID") + @NotNull(message = "实习信息ID不能为空", groups = {EditGroup.class}) + private Long internshipId; + + /** + * 学生ID + */ + @Schema(description = "学生ID") + @NotNull(message = "学生ID不能为空", groups = {EditGroup.class}) + private Long studentId; + + /** + * 实习状态:1-进行中,2-已完成,3-已暂停,4-已终止 + */ + @Schema(description = "实习状态:1-进行中,2-已完成,3-已暂停,4-已终止") + @NotBlank(message = "实习状态不能为空", groups = {EditGroup.class}) + @Pattern(regexp = "^[1-4]$", message = "实习状态只能是1-进行中,2-已完成,3-已暂停,4-已终止", groups = {EditGroup.class}) + private String internshipStatus; + + /** + * 实习单位名称 + */ + @Schema(description = "实习单位名称") + @NotBlank(message = "实习单位名称不能为空", groups = {EditGroup.class}) + private String companyName; + + /** + * 实习单位地址 + */ + @Schema(description = "实习单位地址") + private String companyAddress; + + /** + * 单位联系人 + */ + @Schema(description = "单位联系人") + private String companyContact; + + /** + * 联系人电话 + */ + @Schema(description = "联系人电话") + @Pattern(regexp = "^1[3-9]\\d{9}$", message = "联系人电话格式不正确", groups = {EditGroup.class}) + private String companyPhone; + + /** + * 行业类型 + */ + @Schema(description = "行业类型") + private String companyIndustry; + + /** + * 单位规模 + */ + @Schema(description = "单位规模") + private String companyScale; + + /** + * 实习岗位 + */ + @Schema(description = "实习岗位") + @NotBlank(message = "实习岗位不能为空", groups = {EditGroup.class}) + private String internshipPosition; + + /** + * 实习薪资 + */ + @Schema(description = "实习薪资") + @Positive(message = "实习薪资必须大于0", groups = {EditGroup.class}) + private BigDecimal internshipAllowance; + + /** + * 实习开始时间 + */ + @Schema(description = "实习开始时间") + @JsonFormat(pattern = "yyyy-MM-dd") + @NotNull(message = "实习开始时间不能为空", groups = {EditGroup.class}) + private Date internshipStartDate; + + /** + * 实习结束时间 + */ + @Schema(description = "实习结束时间") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date internshipEndDate; + + /** + * 联系人姓名 + */ + @Schema(description = "联系人姓名") + private String contactPersonName; + + /** + * 联系人电话 + */ + @Schema(description = "联系人电话") + @Pattern(regexp = "^1[3-9]\\d{9}$", message = "联系人电话格式不正确", groups = {EditGroup.class}) + private String contactPersonPhone; + + /** + * 岗位职责 + */ + @Schema(description = "岗位职责") + private String positionDuties; + + /** + * 工作时长 + */ + @Schema(description = "工作时长") + private String workHours; + + /** + * 住宿地址 + */ + @Schema(description = "住宿地址") + private String accommodationAddress; + + /** + * 住宿类型 + */ + @Schema(description = "住宿类型") + private String accommodationType; + + /** + * 住宿费用 + */ + @Schema(description = "住宿费用") + private BigDecimal accommodationCost; + + /** + * 实习协议状态:1-已签署,2-未签署 + */ + @Schema(description = "实习协议状态:1-已签署,2-未签署") + @Pattern(regexp = "^[1-2]$", message = "实习协议状态只能是1-已签署,2-未签署", groups = {EditGroup.class}) + private String agreementStatus; + + /** + * 备注信息 + */ + @Schema(description = "备注信息") + private String remark; +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipDailyAddBo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipDailyAddBo.java new file mode 100644 index 00000000..5d344d2a --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipDailyAddBo.java @@ -0,0 +1,241 @@ +package org.dromara.zsjy.domain.bo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import java.io.Serial; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.Date; + +/** + * 实习日常管理新增业务对象 ZsjyInternshipDailyAddBo + * + * @author system + * @date 2025-01-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Schema(description = "实习日常管理新增业务对象") +public class ZsjyInternshipDailyAddBo extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 实习ID + */ + @Schema(description = "实习ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "实习ID不能为空", groups = {AddGroup.class, CheckInGroup.class}) + private Long internshipId; + + /** + * 学生ID + */ + @Schema(description = "学生ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "学生ID不能为空", groups = {AddGroup.class, CheckInGroup.class}) + private Long studentId; + + /** + * 签到日期 + */ + @Schema(description = "签到日期", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "签到日期不能为空", groups = {AddGroup.class, CheckInGroup.class}) + private LocalDate checkDate; + + /** + * 签到时间 + */ + @Schema(description = "签到时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "签到时间不能为空", groups = {AddGroup.class, CheckInGroup.class}) + private LocalTime checkInTime; + + /** + * 签退时间 + */ + @Schema(description = "签退时间") + private LocalTime checkOutTime; + + /** + * 签到地点 (前端传递字段,映射到数据库check_in_location字段) + */ + @Schema(description = "签到地点", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "签到地点不能为空", groups = {CheckInGroup.class}) + @Size(max = 500, message = "签到地点长度不能超过500个字符") + private String checkInLocation; + + /** + * 签到纬度 (前端传递字段,映射到数据库latitude字段) + */ + @Schema(description = "签到纬度", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "签到纬度不能为空", groups = {CheckInGroup.class}) + private BigDecimal latitude; + + /** + * 签到经度 (前端传递字段,映射到数据库longitude字段) + */ + @Schema(description = "签到经度", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "签到经度不能为空", groups = {CheckInGroup.class}) + private BigDecimal longitude; + + /** + * 签退地点 (对应数据库check_in_location字段,实际存储签退地点) + */ + @Schema(description = "签退地点", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "签退地点不能为空", groups = {AddGroup.class}) + @Size(max = 500, message = "签退地点长度不能超过500个字符") + private String checkOutLocation; + + /** + * 签退纬度 (对应数据库latitude字段) + */ + @Schema(description = "签退纬度", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "签退纬度不能为空", groups = {AddGroup.class}) + private BigDecimal checkOutLatitude; + + /** + * 签退经度 (对应数据库longitude字段) + */ + @Schema(description = "签退经度", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "签退经度不能为空", groups = {AddGroup.class}) + private BigDecimal checkOutLongitude; + + /** + * 工作内容 (对应数据库daily_summary字段) + */ + @Schema(description = "工作内容", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "工作内容不能为空", groups = {AddGroup.class}) + @Size(max = 1000, message = "工作内容长度不能超过1000个字符") + private String workContent; + + /** + * 图片附件(JSON格式) + */ + @Schema(description = "图片附件") + private String images; + + /** + * 是否异常(0正常 1异常) + */ + @Schema(description = "是否异常") + private String isException; + + /** + * 异常原因 + */ + @Schema(description = "异常原因") + private String exceptionReason; + + /** + * 考勤状态(0=未签到 1=正常 2=迟到 3=早退 4=旷工) + */ + @Schema(description = "考勤状态") + private String status; + + /** + * 备注 + */ + @Schema(description = "备注") + @Size(max = 500, message = "备注长度不能超过500个字符") + private String remark; + + // ==================== 扩展字段(用于显示,不存储到数据库) ==================== + + /** + * 学生姓名(扩展字段,用于显示) + */ + @Schema(description = "学生姓名", hidden = true) + private String studentName; + + /** + * 学生学号(扩展字段,用于显示) + */ + @Schema(description = "学生学号", hidden = true) + private String studentNumber; + + /** + * 班级名称(扩展字段,用于显示) + */ + @Schema(description = "班级名称", hidden = true) + private String className; + + /** + * 企业名称(扩展字段,用于显示) + */ + @Schema(description = "企业名称", hidden = true) + private String companyName; + + /** + * 实习岗位(扩展字段,用于显示) + */ + @Schema(description = "实习岗位", hidden = true) + private String position; + + // ==================== 兼容移动端字段(前端可能使用的别名) ==================== + + /** + * 日总结(兼容移动端,实际映射为workContent) + */ + @Schema(description = "日总结", hidden = true) + private String dailySummary; + + // ==================== 数据转换方法(处理前端字段映射) ==================== + + /** + * 处理移动端兼容字段映射 + * 将移动端使用的字段名映射到实际的字段 + */ + public void processCompatibleFields() { + // 处理经纬度映射(签到时映射到签退字段存储) + if (this.latitude != null && this.checkOutLatitude == null) { + this.checkOutLatitude = this.latitude; + } + if (this.longitude != null && this.checkOutLongitude == null) { + this.checkOutLongitude = this.longitude; + } + + // 处理工作内容映射 + if (this.dailySummary != null && this.workContent == null) { + this.workContent = this.dailySummary; + } + + // 处理签到位置映射(签到时映射到签退字段存储) + if (this.checkInLocation != null && this.checkOutLocation == null) { + this.checkOutLocation = this.checkInLocation; + } + } + + /** + * 获取实际的签退地点(优先使用checkOutLocation,否则使用checkInLocation) + */ + public String getActualCheckOutLocation() { + return this.checkOutLocation != null ? this.checkOutLocation : this.checkInLocation; + } + + /** + * 获取实际的签退经度(优先使用checkOutLongitude,否则使用longitude) + */ + public BigDecimal getActualCheckOutLongitude() { + return this.checkOutLongitude != null ? this.checkOutLongitude : this.longitude; + } + + /** + * 获取实际的签退纬度(优先使用checkOutLatitude,否则使用latitude) + */ + public BigDecimal getActualCheckOutLatitude() { + return this.checkOutLatitude != null ? this.checkOutLatitude : this.latitude; + } + + /** + * 获取实际的工作内容(优先使用workContent,否则使用dailySummary) + */ + public String getActualWorkContent() { + return this.workContent != null ? this.workContent : this.dailySummary; + } +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipDailyQueryBo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipDailyQueryBo.java new file mode 100644 index 00000000..648015fb --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipDailyQueryBo.java @@ -0,0 +1,91 @@ +package org.dromara.zsjy.domain.bo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 实习日常管理查询业务对象 ZsjyInternshipDailyQueryBo + * + * @author system + * @date 2025-01-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Schema(description = "实习日常管理查询业务对象") +public class ZsjyInternshipDailyQueryBo extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 学生ID + */ + @Schema(description = "学生ID") + private Long studentId; + + /** + * 学号 + */ + @Schema(description = "学号") + private String studentNo; + + /** + * 学生姓名 + */ + @Schema(description = "学生姓名") + private String studentName; + + /** + * 实习ID + */ + @Schema(description = "实习ID") + private Long internshipId; + + /** + * 打卡开始日期 + */ + @Schema(description = "打卡开始日期") + private Date checkDateStart; + + /** + * 打卡结束日期 + */ + @Schema(description = "打卡结束日期") + private Date checkDateEnd; + + /** + * 是否异常(0正常 1异常) + */ + @Schema(description = "是否异常") + private String isException; + + /** + * 异常处理状态(0待处理 1已处理) + */ + @Schema(description = "异常处理状态") + private String exceptionStatus; + + /** + * 状态(0正常 1停用) + */ + @Schema(description = "状态") + private String status; + + /** + * 公司名称 + */ + @Schema(description = "公司名称") + private String companyName; + + /** + * 班主任ID(用于权限控制) + */ + @Schema(description = "班主任ID") + private Long headTeacherId; + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipMonthlyAddBo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipMonthlyAddBo.java new file mode 100644 index 00000000..9cfd7357 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipMonthlyAddBo.java @@ -0,0 +1,147 @@ +package org.dromara.zsjy.domain.bo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import jakarta.validation.constraints.*; +import java.util.Date; + +/** + * 学生月度实习总结新增/编辑对象 ZsjyInternshipMonthlyAddBo + * + * @author system + * @date 2025-01-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Schema(description = "学生月度实习总结新增/编辑对象") +public class ZsjyInternshipMonthlyAddBo extends BaseEntity { + + /** + * 月度总结ID + */ + @Schema(description = "月度总结ID") + @NotNull(message = "月度总结ID不能为空", groups = { EditGroup.class }) + private Long monthlyId; + + /** + * 实习ID + */ + @Schema(description = "实习ID") + @NotNull(message = "实习ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long internshipId; + + /** + * 总结月份 (格式: 2025-01) + */ + @Schema(description = "总结月份") + @NotBlank(message = "总结月份不能为空", groups = { AddGroup.class, EditGroup.class }) + @Pattern(regexp = "^\\d{4}-\\d{2}$", message = "月份格式应为 YYYY-MM", groups = { AddGroup.class, EditGroup.class }) + private String summaryMonth; + + /** + * 工作内容 (必填,500字以内) + */ + @Schema(description = "工作内容") + @NotBlank(message = "工作内容不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(max = 500, message = "工作内容不能超过500字", groups = { AddGroup.class, EditGroup.class }) + private String workContent; + + /** + * 主要成果 + */ + @Schema(description = "主要成果") + @Size(max = 500, message = "主要成果不能超过500字", groups = { AddGroup.class, EditGroup.class }) + private String achievements; + + /** + * 学习收获 + */ + @Schema(description = "学习收获") + @Size(max = 500, message = "学习收获不能超过500字", groups = { AddGroup.class, EditGroup.class }) + private String learningGains; + + /** + * 遇到的问题(包含解决方案,按2-A方案合并) + */ + @Schema(description = "遇到的问题") + @Size(max = 1000, message = "遇到的问题不能超过1000字", groups = { AddGroup.class, EditGroup.class }) + private String problemsEncountered; + + /** + * 自我评价 + */ + @Schema(description = "自我评价") + @Size(max = 500, message = "自我评价不能超过500字", groups = { AddGroup.class, EditGroup.class }) + private String selfEvaluation; + + /** + * 下月工作计划 + */ + @Schema(description = "下月工作计划") + @Size(max = 500, message = "下月工作计划不能超过500字", groups = { AddGroup.class, EditGroup.class }) + private String nextMonthPlan; + + /** + * 专业技能自评 (1-10分) + */ + @Schema(description = "专业技能自评") + @Min(value = 1, message = "专业技能自评最小值为1", groups = { AddGroup.class, EditGroup.class }) + @Max(value = 10, message = "专业技能自评最大值为10", groups = { AddGroup.class, EditGroup.class }) + private Integer professionalSkills; + + /** + * 沟通技能自评 (1-10分) + */ + @Schema(description = "沟通技能自评") + @Min(value = 1, message = "沟通技能自评最小值为1", groups = { AddGroup.class, EditGroup.class }) + @Max(value = 10, message = "沟通技能自评最大值为10", groups = { AddGroup.class, EditGroup.class }) + private Integer communicationSkills; + + /** + * 团队协作自评 (1-10分) - 修正字段映射 + */ + @Schema(description = "团队协作自评") + @Min(value = 1, message = "团队协作自评最小值为1", groups = { AddGroup.class, EditGroup.class }) + @Max(value = 10, message = "团队协作自评最大值为10", groups = { AddGroup.class, EditGroup.class }) + private Integer teamworkAbility; + + /** + * 学习能力自评 (1-10分) + */ + @Schema(description = "学习能力自评") + @Min(value = 1, message = "学习能力自评最小值为1", groups = { AddGroup.class, EditGroup.class }) + @Max(value = 10, message = "学习能力自评最大值为10", groups = { AddGroup.class, EditGroup.class }) + private Integer learningAbility; + + /** + * 工作态度自评 (1-10分) + */ + @Schema(description = "工作态度自评") + @Min(value = 1, message = "工作态度自评最小值为1", groups = { AddGroup.class, EditGroup.class }) + @Max(value = 10, message = "工作态度自评最大值为10", groups = { AddGroup.class, EditGroup.class }) + private Integer workAttitude; + + /** + * 附件信息 (JSON格式存储) + */ + @Schema(description = "附件信息") + private String attachments; + + /** + * 审核状态 (0-待审核 1-已审核 2-审核拒绝 3-草稿) + */ + @Schema(description = "审核状态") + private Integer reviewStatus; + + /** + * 提交时间 + */ + @Schema(description = "提交时间") + private Date submitTime; + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipMonthlyQueryBo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipMonthlyQueryBo.java new file mode 100644 index 00000000..8387f8ac --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipMonthlyQueryBo.java @@ -0,0 +1,61 @@ +package org.dromara.zsjy.domain.bo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 学生月度实习总结查询对象 ZsjyInternshipMonthlyQueryBo + * + * @author system + * @date 2025-01-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Schema(description = "学生月度实习总结查询对象") +public class ZsjyInternshipMonthlyQueryBo extends BaseEntity { + + /** + * 学生ID + */ + @Schema(description = "学生ID") + private Long studentId; + + /** + * 总结月份 (格式: 2025-01) + */ + @Schema(description = "总结月份") + private String summaryMonth; + + /** + * 审核状态 (0:待审核 1:已审核 2:审核拒绝 3:草稿) + */ + @Schema(description = "审核状态") + private Integer status; + + /** + * 实习ID + */ + @Schema(description = "实习ID") + private Long internshipId; + + /** + * 是否逾期 (0:未逾期 1:已逾期) + */ + @Schema(description = "是否逾期") + private Integer isOverdue; + + /** + * 开始月份 + */ + @Schema(description = "开始月份") + private String startMonth; + + /** + * 结束月份 + */ + @Schema(description = "结束月份") + private String endMonth; + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipVisitBo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipVisitBo.java new file mode 100644 index 00000000..32272bb0 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipVisitBo.java @@ -0,0 +1,131 @@ +package org.dromara.zsjy.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.zsjy.domain.ZsjyInternshipVisit; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import java.util.Date; + +/** + * 班主任走访业务对象 ZsjyInternshipVisitBo + * + * @author system + * @date 2025-01-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = ZsjyInternshipVisit.class, reverseConvertGenerate = false) +public class ZsjyInternshipVisitBo extends BaseEntity { + + /** + * 走访记录ID + */ + @NotNull(message = "走访记录ID不能为空", groups = { EditGroup.class }) + private Long visitId; + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 实习ID + */ + @NotNull(message = "实习ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long internshipId; + + /** + * 学生ID + */ + @NotNull(message = "学生ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long studentId; + + /** + * 企业名称 + */ + @NotBlank(message = "企业名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String companyName; + + /** + * 企业地址 + */ + private String companyAddress; + + /** + * 走访时间 + */ + @NotNull(message = "走访时间不能为空", groups = { AddGroup.class, EditGroup.class }) + private Date visitDate; + + /** + * 走访人员 + */ + @NotBlank(message = "走访人员不能为空", groups = { AddGroup.class, EditGroup.class }) + private String visitorName; + + /** + * 走访人员职务 + */ + private String visitorPosition; + + /** + * 走访目的 + */ + @NotBlank(message = "走访目的不能为空", groups = { AddGroup.class, EditGroup.class }) + private String visitPurpose; + + /** + * 企业情况 + */ + private String companySituation; + + /** + * 学生表现 + */ + private String studentPerformance; + + /** + * 发现问题 + */ + private String problemsFound; + + /** + * 改进建议 + */ + private String suggestions; + + /** + * 走访总结 + */ + private String visitSummary; + + /** + * 现场照片(JSON格式) + */ + private String visitImages; + + /** + * 走访文件附件(JSON格式) + */ + private String visitFiles; + + /** + * 走访状态:数据库存储数字编码(0-草稿 1-已提交 2-已审核) + */ + @Pattern(regexp = "^[0-2]$", + message = "走访状态只能是:0(草稿)、1(已提交)、2(已审核)", + groups = { AddGroup.class, EditGroup.class }) + private String status; + + /** + * 备注 + */ + private String remark; +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipVisitQueryBo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipVisitQueryBo.java new file mode 100644 index 00000000..8c656c92 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipVisitQueryBo.java @@ -0,0 +1,86 @@ +package org.dromara.zsjy.domain.bo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 班主任走访查询业务对象 ZsjyInternshipVisitQueryBo + * + * @author system + * @date 2025-01-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Schema(description = "班主任走访查询业务对象") +public class ZsjyInternshipVisitQueryBo extends BaseEntity { + + /** + * 走访记录ID + */ + @Schema(description = "走访记录ID") + private Long visitId; + + /** + * 实习ID + */ + @Schema(description = "实习ID") + private Long internshipId; + + /** + * 学生ID + */ + @Schema(description = "学生ID") + private Long studentId; + + /** + * 学生姓名 + */ + @Schema(description = "学生姓名") + private String studentName; + + /** + * 学号 + */ + @Schema(description = "学号") + private String studentNumber; + + /** + * 班级名称 + */ + @Schema(description = "班级名称") + private String className; + + /** + * 企业名称 + */ + @Schema(description = "企业名称") + private String companyName; + + /** + * 走访人员 + */ + @Schema(description = "走访人员") + private String visitorName; + + /** + * 走访状态:数据库存储数字编码(0-草稿 1-已提交 2-已审核),前端通过字典显示为中文 + */ + @Schema(description = "走访状态") + private String status; + + /** + * 走访开始时间 + */ + @Schema(description = "走访开始时间") + private Date visitStartDate; + + /** + * 走访结束时间 + */ + @Schema(description = "走访结束时间") + private Date visitEndDate; +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyInternshipDailyVo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyInternshipDailyVo.java new file mode 100644 index 00000000..61b1e74e --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyInternshipDailyVo.java @@ -0,0 +1,183 @@ +package org.dromara.zsjy.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalTime; +import java.util.Date; + +/** + * 实习日常管理视图对象 ZsjyInternshipDailyVo + * + * @author system + * @date 2025-01-03 + */ +@Data +@Schema(description = "实习日常管理视图对象") +public class ZsjyInternshipDailyVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 日常记录ID + */ + @Schema(description = "日常记录ID") + private Long dailyId; + + /** + * 实习ID + */ + @Schema(description = "实习ID") + private Long internshipId; + + /** + * 学生ID + */ + @Schema(description = "学生ID") + private Long studentId; + + /** + * 打卡日期 + */ + @Schema(description = "打卡日期") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date checkDate; + + /** + * 签到时间 + */ + @Schema(description = "签到时间") + @JsonFormat(pattern = "HH:mm:ss") + private LocalTime checkInTime; + + /** + * 签退时间 + */ + @Schema(description = "签退时间") + @JsonFormat(pattern = "HH:mm:ss") + private LocalTime checkOutTime; + + /** + * 签到位置 + */ + @Schema(description = "签到位置") + private String checkInLocation; + + /** + * 纬度 + */ + @Schema(description = "纬度") + private BigDecimal latitude; + + /** + * 经度 + */ + @Schema(description = "经度") + private BigDecimal longitude; + + /** + * 日总结 + */ + @Schema(description = "日总结") + private String dailySummary; + + /** + * 图片附件(JSON格式) + */ + @Schema(description = "图片附件") + private String images; + + /** + * 是否异常(0正常 1异常) + */ + @Schema(description = "是否异常") + private String isException; + + /** + * 异常原因 + */ + @Schema(description = "异常原因") + private String exceptionReason; + + /** + * 异常处理状态(0待处理 1已处理) + */ + @Schema(description = "异常处理状态") + private String exceptionStatus; + + /** + * 状态(0正常 1停用) + */ + @Schema(description = "状态") + private String status; + + /** + * 备注 + */ + @Schema(description = "备注") + private String remark; + + // ============ VO类基础字段规范(强制性) ============ + /** + * 创建者 + */ + @Schema(description = "创建者") + private Long createBy; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新者 + */ + @Schema(description = "更新者") + private Long updateBy; + + /** + * 更新时间 + */ + @Schema(description = "更新时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + // ============ 扩展字段(用于页面显示) ============ + /** + * 学生姓名 + */ + @Schema(description = "学生姓名") + private String studentName; + + /** + * 学号 + */ + @Schema(description = "学号") + private String studentNo; + + /** + * 学号(前端兼容字段) + */ + @Schema(description = "学号") + private String studentNumber; + + /** + * 工作时长(小时) + */ + @Schema(description = "工作时长") + private BigDecimal workDuration; + + /** + * 公司名称 + */ + @Schema(description = "公司名称") + private String companyName; + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyInternshipMonthlyVo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyInternshipMonthlyVo.java new file mode 100644 index 00000000..d614ceb8 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyInternshipMonthlyVo.java @@ -0,0 +1,253 @@ +package org.dromara.zsjy.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 学生月度实习总结视图对象 ZsjyInternshipMonthlyVo + * + * @author system + * @date 2025-01-03 + */ +@Data +@Schema(description = "学生月度实习总结视图对象") +public class ZsjyInternshipMonthlyVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 月度总结ID + */ + @Schema(description = "月度总结ID") + private Long monthlyId; + + /** + * 实习ID + */ + @Schema(description = "实习ID") + private Long internshipId; + + /** + * 学生ID + */ + @Schema(description = "学生ID") + private Long studentId; + + /** + * 学生姓名 - 通过关联查询获取 + */ + @Schema(description = "学生姓名") + private String studentName; + + /** + * 学生学号 - 通过关联查询获取 + */ + @Schema(description = "学生学号") + private String studentNumber; + + /** + * 总结月份 (格式: 2025-01) + */ + @Schema(description = "总结月份") + private String summaryMonth; + + /** + * 月份序号 (1-12) + */ + @Schema(description = "月份序号") + private Integer monthNumber; + + /** + * 工作内容 + */ + @Schema(description = "工作内容") + private String workContent; + + /** + * 主要成果 + */ + @Schema(description = "主要成果") + private String achievements; + + /** + * 学习收获 + */ + @Schema(description = "学习收获") + private String learningGains; + + /** + * 遇到的问题(包含解决方案,按2-A方案合并) + */ + @Schema(description = "遇到的问题") + private String problemsEncountered; + + /** + * 自我评价 + */ + @Schema(description = "自我评价") + private String selfEvaluation; + + /** + * 下月工作计划 + */ + @Schema(description = "下月工作计划") + private String nextMonthPlan; + + /** + * 专业技能自评 (1-10分) + */ + @Schema(description = "专业技能自评") + private Integer professionalSkills; + + /** + * 沟通技能自评 (1-10分) + */ + @Schema(description = "沟通技能自评") + private Integer communicationSkills; + + /** + * 团队协作自评 (1-10分) - 修正字段映射 + */ + @Schema(description = "团队协作自评") + private Integer teamworkAbility; + + /** + * 学习能力自评 (1-10分) + */ + @Schema(description = "学习能力自评") + private Integer learningAbility; + + /** + * 工作态度自评 (1-10分) + */ + @Schema(description = "工作态度自评") + private Integer workAttitude; + + /** + * 审核状态 (0:待审核 1:已审核 2:审核拒绝 3:草稿) - 修正字段映射 + */ + @Schema(description = "审核状态") + private Integer reviewStatus; + + /** + * 状态文本描述 + */ + @Schema(description = "状态文本描述") + private String statusText; + + /** + * 审核人ID - 修正字段映射 + */ + @Schema(description = "审核人ID") + private Long reviewBy; + + /** + * 审核人姓名 - 通过关联查询获取 + */ + @Schema(description = "审核人姓名") + private String reviewerName; + + /** + * 审核时间 + */ + @Schema(description = "审核时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date reviewTime; + + /** + * 审核评分 (百分制) + */ + @Schema(description = "审核评分") + private BigDecimal reviewScore; + + /** + * 审核意见 - 修正字段映射 + */ + @Schema(description = "审核意见") + private String reviewComment; + + /** + * 提交时间 + */ + @Schema(description = "提交时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date submitTime; + + /** + * 字数统计 + */ + @Schema(description = "字数统计") + private Integer wordCount; + + /** + * 是否逾期 (0:未逾期 1:已逾期) + */ + @Schema(description = "是否逾期") + private Integer isOverdue; + + /** + * 提交截止日期 - 修正字段映射 + */ + @Schema(description = "提交截止日期") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date submitDeadline; + + /** + * 附件信息 + */ + @Schema(description = "附件信息") + private String attachments; + + /** + * 是否可编辑 (草稿状态才可编辑) + */ + @Schema(description = "是否可编辑") + private Boolean editable; + + /** + * 是否可删除 (草稿状态才可删除) + */ + @Schema(description = "是否可删除") + private Boolean deletable; + + /** + * 是否可提交 (草稿状态才可提交) + */ + @Schema(description = "是否可提交") + private Boolean submittable; + + // VO类基础字段规范 - 必须包含的四个字段 + /** + * 创建者 + */ + @Schema(description = "创建者") + private Long createBy; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新者 + */ + @Schema(description = "更新者") + private Long updateBy; + + /** + * 更新时间 + */ + @Schema(description = "更新时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyInternshipVisitVo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyInternshipVisitVo.java new file mode 100644 index 00000000..546dc88a --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyInternshipVisitVo.java @@ -0,0 +1,201 @@ +package org.dromara.zsjy.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.github.linpeilie.annotations.AutoMapper; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.dromara.zsjy.domain.ZsjyInternshipVisit; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 班主任走访视图对象 ZsjyInternshipVisitVo + * + * @author system + * @date 2025-01-03 + */ +@Data +@AutoMapper(target = ZsjyInternshipVisit.class) +@Schema(description = "班主任走访视图对象") +public class ZsjyInternshipVisitVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 走访记录ID + */ + @Schema(description = "走访记录ID") + private Long visitId; + + /** + * 租户编号 + */ + @Schema(description = "租户编号") + private String tenantId; + + /** + * 实习ID + */ + @Schema(description = "实习ID") + private Long internshipId; + + /** + * 学生ID + */ + @Schema(description = "学生ID") + private Long studentId; + + /** + * 企业名称 + */ + @Schema(description = "企业名称") + private String companyName; + + /** + * 企业地址 + */ + @Schema(description = "企业地址") + private String companyAddress; + + /** + * 走访时间 + */ + @Schema(description = "走访时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date visitDate; + + /** + * 走访人员 + */ + @Schema(description = "走访人员") + private String visitorName; + + /** + * 走访人员职务 + */ + @Schema(description = "走访人员职务") + private String visitorPosition; + + /** + * 走访目的 + */ + @Schema(description = "走访目的") + private String visitPurpose; + + /** + * 企业情况 + */ + @Schema(description = "企业情况") + private String companySituation; + + /** + * 学生表现 + */ + @Schema(description = "学生表现") + private String studentPerformance; + + /** + * 发现问题 + */ + @Schema(description = "发现问题") + private String problemsFound; + + /** + * 改进建议 + */ + @Schema(description = "改进建议") + private String suggestions; + + /** + * 走访总结 + */ + @Schema(description = "走访总结") + private String visitSummary; + + /** + * 现场照片(JSON格式) + */ + @Schema(description = "现场照片") + private String visitImages; + + /** + * 走访文件附件(JSON格式) + */ + @Schema(description = "走访文件附件") + private String visitFiles; + + /** + * 走访状态:数据库存储数字编码(0-草稿 1-已提交 2-已审核),前端通过字典显示为中文 + */ + @Schema(description = "走访状态") + private String status; + + /** + * 备注 + */ + @Schema(description = "备注") + private String remark; + + // ==================== 关联查询字段 ==================== + + /** + * 学生姓名 + */ + @Schema(description = "学生姓名") + private String studentName; + + /** + * 学号 + */ + @Schema(description = "学号") + private String studentNumber; + + /** + * 班级名称 + */ + @Schema(description = "班级名称") + private String className; + + /** + * 专业名称 + */ + @Schema(description = "专业名称") + private String majorName; + + /** + * 学院名称 + */ + @Schema(description = "学院名称") + private String collegeName; + + // ==================== 统一基础字段(强制性,避免MyBatis映射错误) ==================== + + /** + * 创建者 + */ + @Schema(description = "创建者") + private Long createBy; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新者 + */ + @Schema(description = "更新者") + private Long updateBy; + + /** + * 更新时间 + */ + @Schema(description = "更新时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/InternshipInfoMapper.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/InternshipInfoMapper.java index 935e9fca..fa0c12d0 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/InternshipInfoMapper.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/InternshipInfoMapper.java @@ -44,6 +44,14 @@ public interface InternshipInfoMapper { */ List> selectTeacherClasses(@Param("teacherId") Long teacherId); + /** + * 统计班主任管理的班级数量 + * + * @param teacherId 班主任ID + * @return 班级数量 + */ + Long countClassesByTeacherId(@Param("teacherId") Long teacherId); + /** * 查询实习状态统计数据 * diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipDailyMapper.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipDailyMapper.java new file mode 100644 index 00000000..58137544 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipDailyMapper.java @@ -0,0 +1,84 @@ +package org.dromara.zsjy.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.zsjy.domain.ZsjyInternshipDaily; +import org.dromara.zsjy.domain.bo.ZsjyInternshipDailyQueryBo; +import org.dromara.zsjy.domain.vo.ZsjyInternshipDailyVo; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 实习日常管理Mapper接口 + * + * @author system + * @date 2025-01-03 + */ +@Mapper +public interface ZsjyInternshipDailyMapper extends BaseMapper { + + /** + * 查询实习日常记录列表 + * + * @param page 分页参数(可选) + * @param queryBo 查询条件 + * @return 实习日常记录列表 + */ + List selectDailyList(@Param("page") Page page, + @Param("queryBo") ZsjyInternshipDailyQueryBo queryBo); + + /** + * 根据学生ID获取考勤日历数据 + * + * @param studentId 学生ID + * @param startDate 开始日期 + * @param endDate 结束日期 + * @return 考勤日历数据 + */ + List> selectCalendarData(@Param("studentId") Long studentId, + @Param("startDate") Date startDate, + @Param("endDate") Date endDate); + + /** + * 获取考勤统计数据 + * + * @param queryBo 查询条件 + * @return 统计数据 + */ + Map selectStatistics(@Param("queryBo") ZsjyInternshipDailyQueryBo queryBo); + + /** + * 检查学生当日是否已签到 + * + * @param studentId 学生ID + * @param checkDate 打卡日期 + * @return 是否已签到 + */ + boolean existsByStudentAndDate(@Param("studentId") Long studentId, + @Param("checkDate") Date checkDate); + + /** + * 获取学生个人月度考勤统计 + * + * @param studentId 学生ID + * @param year 年份 + * @param month 月份 + * @return 学生月度统计数据 + */ + Map selectStudentMonthlyStatistics(@Param("studentId") Long studentId, + @Param("year") Integer year, + @Param("month") Integer month); + + /** + * 统计班主任管理的班级数量 + * + * @param teacherId 班主任ID + * @return 班级数量 + */ + Long countClassesByTeacherId(@Param("teacherId") Long teacherId); + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipInfoMapper.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipInfoMapper.java new file mode 100644 index 00000000..f04bce61 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipInfoMapper.java @@ -0,0 +1,16 @@ +package org.dromara.zsjy.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.dromara.zsjy.domain.ZsjyInternshipInfo; +import org.apache.ibatis.annotations.Mapper; + +/** + * 实习信息Mapper接口 + * + * @author system + * @date 2025-01-03 + */ +@Mapper +public interface ZsjyInternshipInfoMapper extends BaseMapper { + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipMonthlyMapper.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipMonthlyMapper.java new file mode 100644 index 00000000..21069aef --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipMonthlyMapper.java @@ -0,0 +1,82 @@ +package org.dromara.zsjy.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; +import org.dromara.zsjy.domain.ZsjyInternshipMonthly; +import org.dromara.zsjy.domain.bo.ZsjyInternshipMonthlyQueryBo; +import org.dromara.zsjy.domain.vo.ZsjyInternshipMonthlyVo; + +import java.util.List; + +/** + * 学生月度实习总结Mapper接口 + * + * @author system + * @date 2025-01-03 + */ +public interface ZsjyInternshipMonthlyMapper extends BaseMapper { + + /** + * 查询学生月度实习总结 + * + * @param monthlyId 月度总结ID + * @return 学生月度实习总结 + */ + ZsjyInternshipMonthlyVo selectVoById(Long monthlyId); + + /** + * 查询学生月度实习总结列表 + * + * @param queryBo 查询条件 + * @return 学生月度实习总结集合 + */ + Page selectVoPage(Page page, @Param("bo") ZsjyInternshipMonthlyQueryBo queryBo); + + /** + * 查询学生月度实习总结列表 + * + * @param queryBo 查询条件 + * @return 学生月度实习总结集合 + */ + List selectVoList(@Param("bo") ZsjyInternshipMonthlyQueryBo queryBo); + + /** + * 检查学生某月是否已有总结 + * + * @param studentId 学生ID + * @param summaryMonth 总结月份 + * @param monthlyId 排除的总结ID(用于编辑时检查) + * @return 是否存在 + */ + boolean existsByStudentAndMonth(@Param("studentId") Long studentId, + @Param("summaryMonth") String summaryMonth, + @Param("monthlyId") Long monthlyId); + + /** + * 批量删除学生月度实习总结 + * + * @param monthlyIds 需要删除的数据ID + * @return 影响行数 + */ + int deleteByIds(@Param("monthlyIds") Long[] monthlyIds); + + /** + * 查询学生指定实习的月度总结列表 + * + * @param studentId 学生ID + * @param internshipId 实习ID + * @return 月度总结列表 + */ + List selectByStudentAndInternship(@Param("studentId") Long studentId, + @Param("internshipId") Long internshipId); + + /** + * 统计学生各状态的总结数量 + * + * @param studentId 学生ID + * @return 状态统计结果 + */ + List countByStatus(@Param("studentId") Long studentId); + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipVisitMapper.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipVisitMapper.java new file mode 100644 index 00000000..1475b578 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipVisitMapper.java @@ -0,0 +1,40 @@ +package org.dromara.zsjy.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.zsjy.domain.ZsjyInternshipVisit; +import org.dromara.zsjy.domain.bo.ZsjyInternshipVisitQueryBo; +import org.dromara.zsjy.domain.vo.ZsjyInternshipVisitVo; + +import java.util.List; + +/** + * 班主任走访Mapper接口 + * + * @author system + * @date 2025-01-03 + */ +public interface ZsjyInternshipVisitMapper extends BaseMapperPlus { + + /** + * 根据ID查询班主任走访详情(包含关联学生信息) + * @param visitId 走访记录ID + * @return 走访详情VO + */ + ZsjyInternshipVisitVo selectById(Long visitId); + + /** + * 查询班主任走访列表(包含关联学生信息) + * @param bo 查询条件 + * @return 走访记录列表 + */ + List queryList(ZsjyInternshipVisitQueryBo bo); + + /** + * 分页查询班主任走访列表(包含关联学生信息) + * @param page 分页参数 + * @param bo 查询条件 + * @return 走访记录分页结果 + */ + Page queryPageList(Page page, ZsjyInternshipVisitQueryBo bo); +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IInternshipInfoService.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IInternshipInfoService.java index 60ab45c0..660703d5 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IInternshipInfoService.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IInternshipInfoService.java @@ -4,6 +4,8 @@ import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.zsjy.domain.vo.InternshipInfoVO; import org.dromara.zsjy.domain.bo.InternshipInfoQueryBO; +import org.dromara.zsjy.domain.bo.InternshipInfoAddBO; +import org.dromara.zsjy.domain.bo.InternshipInfoEditBO; import jakarta.servlet.http.HttpServletResponse; import java.util.List; @@ -72,4 +74,35 @@ public interface IInternshipInfoService { * @param queryBO 查询条件(会被设置权限参数) */ void setDataPermission(InternshipInfoQueryBO queryBO); + + /** + * 新增实习信息 + * + * @param addBO 新增参数 + * @return 是否成功 + */ + Boolean insertInternshipInfo(InternshipInfoAddBO addBO); + + /** + * 修改实习信息 + * + * @param editBO 修改参数 + * @return 是否成功 + */ + Boolean updateInternshipInfo(InternshipInfoEditBO editBO); + + /** + * 删除实习信息 + * + * @param internshipIds 实习信息ID数组 + * @return 是否成功 + */ + Boolean deleteInternshipInfo(Long[] internshipIds); + + /** + * 获取当前学生的实习信息 + * + * @return 当前学生的实习信息,如果没有则返回null + */ + InternshipInfoVO getCurrentStudentInternship(); } diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyInternshipDailyService.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyInternshipDailyService.java new file mode 100644 index 00000000..fd52d24b --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyInternshipDailyService.java @@ -0,0 +1,74 @@ +package org.dromara.zsjy.service; + +import org.dromara.common.core.domain.R; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.zsjy.domain.bo.ZsjyInternshipDailyAddBo; +import org.dromara.zsjy.domain.bo.ZsjyInternshipDailyQueryBo; +import org.dromara.zsjy.domain.vo.ZsjyInternshipDailyVo; + +import java.util.List; +import java.util.Map; + +/** + * 实习日常管理Service接口 + * + * @author system + * @date 2025-01-03 + */ +public interface IZsjyInternshipDailyService { + + /** + * 查询实习日常记录分页列表 + * + * @param queryBo 查询条件 + * @param pageQuery 分页参数 + * @return 实习日常记录分页列表 + */ + TableDataInfo queryPageList(ZsjyInternshipDailyQueryBo queryBo, PageQuery pageQuery); + + /** + * 查询实习日常记录列表(不分页) + * + * @param queryBo 查询条件 + * @return 实习日常记录列表 + */ + List queryList(ZsjyInternshipDailyQueryBo queryBo); + + /** + * 学生签到打卡 + * + * @param addBo 签到信息 + * @return 签到结果 + */ + R checkIn(ZsjyInternshipDailyAddBo addBo); + + /** + * 根据学生ID获取考勤日历数据 + * + * @param studentId 学生ID + * @param year 年份 + * @param month 月份 + * @return 考勤日历数据 + */ + R>> getCalendarData(Long studentId, Integer year, Integer month); + + /** + * 获取考勤统计数据 + * + * @param queryBo 查询条件 + * @return 统计数据 + */ + R> getStatistics(ZsjyInternshipDailyQueryBo queryBo); + + /** + * 获取学生个人月度考勤统计 + * + * @param studentId 学生ID + * @param year 年份 + * @param month 月份 + * @return 学生月度统计数据 + */ + R> getStudentMonthlyStatistics(Long studentId, Integer year, Integer month); + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyInternshipMonthlyService.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyInternshipMonthlyService.java new file mode 100644 index 00000000..2e167002 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyInternshipMonthlyService.java @@ -0,0 +1,137 @@ +package org.dromara.zsjy.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.zsjy.domain.bo.ZsjyInternshipMonthlyAddBo; +import org.dromara.zsjy.domain.bo.ZsjyInternshipMonthlyQueryBo; +import org.dromara.zsjy.domain.vo.ZsjyInternshipMonthlyVo; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 学生月度实习总结Service接口 + * + * @author system + * @date 2025-01-03 + */ +public interface IZsjyInternshipMonthlyService { + + /** + * 查询学生月度实习总结 + * + * @param monthlyId 月度总结ID + * @return 学生月度实习总结 + */ + ZsjyInternshipMonthlyVo queryById(Long monthlyId); + + /** + * 查询学生月度实习总结列表 + * + * @param queryBo 查询条件 + * @param pageQuery 分页参数 + * @return 学生月度实习总结分页结果 + */ + TableDataInfo queryPageList(ZsjyInternshipMonthlyQueryBo queryBo, PageQuery pageQuery); + + /** + * 查询学生月度实习总结列表 + * + * @param queryBo 查询条件 + * @return 学生月度实习总结集合 + */ + List queryList(ZsjyInternshipMonthlyQueryBo queryBo); + + /** + * 新增学生月度实习总结 + * + * @param addBo 学生月度实习总结 + * @return 新增结果 + */ + Boolean insertByBo(ZsjyInternshipMonthlyAddBo addBo); + + /** + * 修改学生月度实习总结 + * + * @param addBo 学生月度实习总结 + * @return 修改结果 + */ + Boolean updateByBo(ZsjyInternshipMonthlyAddBo addBo); + + /** + * 校验并批量删除学生月度实习总结信息 + * + * @param ids 需要删除的学生月度实习总结ID + * @return 删除结果 + */ + Boolean deleteByIds(Collection ids); + + /** + * 提交月度总结审批 + * + * @param monthlyId 月度总结ID + * @return 提交结果 + */ + Boolean submitForApproval(Long monthlyId); + + /** + * 自动保存草稿 + * + * @param addBo 学生月度实习总结 + * @return 保存结果 + */ + Boolean autoSaveDraft(ZsjyInternshipMonthlyAddBo addBo); + + /** + * 检查学生某月是否已有总结 + * + * @param studentId 学生ID + * @param summaryMonth 总结月份 + * @param monthlyId 排除的总结ID(用于编辑时检查) + * @return 是否存在 + */ + Boolean checkMonthExists(Long studentId, String summaryMonth, Long monthlyId); + + /** + * 获取月度总结模板信息 + * + * @param summaryMonth 总结月份 + * @return 模板信息 + */ + ZsjyInternshipMonthlyVo getMonthlyTemplate(String summaryMonth); + + /** + * 查询学生指定实习的月度总结列表 + * + * @param studentId 学生ID + * @param internshipId 实习ID + * @return 月度总结列表 + */ + List queryByStudentAndInternship(Long studentId, Long internshipId); + + /** + * 统计学生各状态的总结数量 + * + * @param studentId 学生ID + * @return 状态统计结果 + */ + List countByStatus(Long studentId); + + /** + * 计算总结内容字数 + * + * @param addBo 总结内容 + * @return 字数统计 + */ + Integer calculateWordCount(ZsjyInternshipMonthlyAddBo addBo); + + /** + * 获取学生当前实习信息 + * + * @return 当前实习信息 + */ + Map getCurrentInternshipInfo(); + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyInternshipVisitService.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyInternshipVisitService.java new file mode 100644 index 00000000..f2d624aa --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyInternshipVisitService.java @@ -0,0 +1,54 @@ +package org.dromara.zsjy.service; + +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.zsjy.domain.bo.ZsjyInternshipVisitBo; +import org.dromara.zsjy.domain.bo.ZsjyInternshipVisitQueryBo; +import org.dromara.zsjy.domain.vo.ZsjyInternshipVisitVo; + +import java.util.Collection; +import java.util.List; + +/** + * 班主任走访Service接口 + * + * @author system + * @date 2025-01-03 + */ +public interface IZsjyInternshipVisitService { + + /** + * 查询班主任走访 + */ + ZsjyInternshipVisitVo queryById(Long visitId); + + /** + * 查询班主任走访列表 + */ + TableDataInfo queryPageList(ZsjyInternshipVisitQueryBo bo, PageQuery pageQuery); + + /** + * 查询班主任走访列表 + */ + List queryList(ZsjyInternshipVisitQueryBo bo); + + /** + * 新增班主任走访 + */ + Boolean insertByBo(ZsjyInternshipVisitBo bo); + + /** + * 修改班主任走访 + */ + Boolean updateByBo(ZsjyInternshipVisitBo bo); + + /** + * 校验并批量删除班主任走访信息 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 获取学生选项列表(用于下拉选择) + */ + List getStudentOptions(); +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/InternshipInfoServiceImpl.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/InternshipInfoServiceImpl.java index b06d2e15..eba50d51 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/InternshipInfoServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/InternshipInfoServiceImpl.java @@ -14,11 +14,19 @@ import org.dromara.common.excel.utils.ExcelUtil; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.xg.domain.vo.XgStudentMsgVo; +import org.dromara.xg.service.IXgStudentMsgService; +import org.dromara.common.core.exception.ServiceException; import org.dromara.zsjy.domain.bo.InternshipInfoQueryBO; +import org.dromara.zsjy.domain.bo.InternshipInfoAddBO; +import org.dromara.zsjy.domain.bo.InternshipInfoEditBO; import org.dromara.zsjy.domain.vo.InternshipInfoVO; +import org.dromara.zsjy.domain.ZsjyInternshipInfo; import org.dromara.zsjy.mapper.InternshipInfoMapper; +import org.dromara.zsjy.mapper.ZsjyInternshipInfoMapper; import org.dromara.zsjy.service.IInternshipInfoService; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import jakarta.servlet.http.HttpServletResponse; import java.util.*; @@ -35,23 +43,66 @@ import java.util.*; public class InternshipInfoServiceImpl implements IInternshipInfoService { private final InternshipInfoMapper internshipInfoMapper; + private final ZsjyInternshipInfoMapper zsjyInternshipInfoMapper; + private final IXgStudentMsgService xgStudentMsgService; /** * 查询学生实习信息分页列表(班主任专用) */ @Override public TableDataInfo queryPageList(InternshipInfoQueryBO queryBO, PageQuery pageQuery) { + log.info("=== 后端调试日志 ==="); + log.info("1. 查询参数: {}", queryBO); + log.info("2. 分页参数: pageNum={}, pageSize={}", pageQuery.getPageNum(), pageQuery.getPageSize()); + // 设置数据权限 setDataPermission(queryBO); + log.info("3. 设置数据权限后的查询参数: {}", queryBO); + log.info("3.1. 权限控制参数: classTeacherId={}, counselorId={}", + queryBO.getClassTeacherId(), queryBO.getCounselorId()); // 创建分页对象 Page page = pageQuery.build(); // 查询数据 List list = internshipInfoMapper.selectInternshipInfoList(page, queryBO); + log.info("4. 查询到的数据条数: {}", list != null ? list.size() : "null"); + + // 调试:输出前几条数据的关键字段和映射结果 + if (list != null && !list.isEmpty()) { + InternshipInfoVO firstRecord = list.get(0); + log.info("4.1. 第一条数据示例: studentId={}, studentName={}, className={}", + firstRecord.getStudentId(), firstRecord.getStudentName(), firstRecord.getClassName()); + + // 详细输出映射结果 + log.info("=== MyBatis映射结果详细信息 ==="); + log.info("学生信息 - ID:{}, 学号:{}, 姓名:{}, 性别:{}, 手机:{}", + firstRecord.getStudentId(), firstRecord.getStudentNo(), + firstRecord.getStudentName(), firstRecord.getGender(), firstRecord.getPhone()); + log.info("班级信息 - 班级ID:{}, 班级名称:{}, 年级:{}, 学院:{}, 专业:{}", + firstRecord.getClassId(), firstRecord.getClassName(), + firstRecord.getGrade(), firstRecord.getCollegeName(), firstRecord.getMajorName()); + log.info("实习信息 - 实习ID:{}, 状态:{}, 单位:{}, 岗位:{}, 薪资:{}", + firstRecord.getInternshipId(), firstRecord.getInternshipStatus(), + firstRecord.getCompanyName(), firstRecord.getInternshipPosition(), + firstRecord.getInternshipAllowance()); + log.info("时间信息 - 开始:{}, 结束:{}, 创建时间:{}", + firstRecord.getInternshipStartDate(), firstRecord.getInternshipEndDate(), + firstRecord.getCreateTime()); + log.info("联系信息 - 联系人:{}, 电话:{}, 协议状态:{}", + firstRecord.getContactPersonName(), firstRecord.getContactPersonPhone(), + firstRecord.getAgreementStatus()); + log.info("=== 映射结果输出完成 ==="); + } + log.info("5. page对象信息: total={}, size={}, current={}", page.getTotal(), page.getSize(), page.getCurrent()); // 处理实习状态显示名称 processInternshipStatusName(list); + log.info("6. 处理状态名称后的数据条数: {}", list != null ? list.size() : "null"); + + // 设置查询结果到page对象中 + page.setRecords(list); + log.info("7. 设置records后的page信息: total={}, records.size={}", page.getTotal(), page.getRecords().size()); return TableDataInfo.build(page); } @@ -142,15 +193,39 @@ public class InternshipInfoServiceImpl implements IInternshipInfoService { try { // 获取当前登录用户ID Long currentUserId = LoginHelper.getUserId(); + log.info("当前登录用户ID: {}", currentUserId); if (ObjectUtil.isNotNull(currentUserId)) { - // 设置班主任ID用于数据权限控制 - queryBO.setClassTeacherId(currentUserId); + // 检查该用户是否是班主任 + Long classTeacherCount = internshipInfoMapper.countClassesByTeacherId(currentUserId); + log.info("用户ID {}作为班主任管理的班级数量: {}", currentUserId, classTeacherCount); - log.debug("设置数据权限:班主任ID={}", currentUserId); + if (classTeacherCount > 0) { + // 是班主任,设置权限限制 + queryBO.setClassTeacherId(currentUserId); + log.info("设置数据权限:班主任ID={}", currentUserId); + } else { + // 不是班主任,检查是否是辅导员或管理员 + // 这里可以根据业务需求,设置更灵活的权限控制 + log.info("当前用户不是班主任,不设置班主任权限限制"); + + // 可以在这里添加其他角色的权限判断,比如: + // 1. 检查是否是辅导员 + // 2. 检查是否是管理员 + // 3. 检查是否有相关权限 + + // 暂时不设置权限限制,允许查看所有数据 + queryBO.setClassTeacherId(null); + queryBO.setCounselorId(null); + log.info("清除权限限制,允许查看所有数据"); + } } } catch (Exception e) { - log.warn("获取数据权限失败:{}", e.getMessage()); + log.error("设置数据权限失败:{}", e.getMessage(), e); + // 异常情况下,清除权限限制以避免查询为空 + queryBO.setClassTeacherId(null); + queryBO.setCounselorId(null); + log.warn("异常情况下清除权限限制,允许查看所有数据"); } } @@ -185,4 +260,252 @@ public class InternshipInfoServiceImpl implements IInternshipInfoService { default -> "未知状态"; }; } + + /** + * 新增实习信息 + * + * @param addBO 新增参数 + * @return 是否成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertInternshipInfo(InternshipInfoAddBO addBO) { + log.info("=== 新增实习信息开始 ==="); + log.info("1. 接收到的参数:{}", addBO); + + // 检查该学生是否已经有实习信息 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(ZsjyInternshipInfo::getStudentId, addBO.getStudentId()); + ZsjyInternshipInfo existingInfo = zsjyInternshipInfoMapper.selectOne(queryWrapper); + log.info("2. 检查学生是否已有实习信息,结果:{}", existingInfo != null ? "已存在" : "不存在"); + + if (ObjectUtil.isNotNull(existingInfo)) { + log.warn("学生ID:{} 已存在实习信息,不能重复添加", addBO.getStudentId()); + throw new RuntimeException("该学生已存在实习信息,不能重复添加"); + } + + // 根据学生ID查询学生基础信息 + log.info("3. 查询学生基础信息..."); + XgStudentMsgVo studentInfo = getStudentInfo(addBO.getStudentId()); + log.info("4. 学生信息查询结果:学号={}, 姓名={}, 学院ID={}, 专业ID={}, 班级ID={}, 手机号={}", + studentInfo.getStudentNo(), studentInfo.getName(), + studentInfo.getCollegeId(), studentInfo.getMajorId(), + studentInfo.getClassId(), studentInfo.getPhone()); + + // 构建实习信息实体 + ZsjyInternshipInfo internshipInfo = new ZsjyInternshipInfo(); + log.info("5. 开始字段映射..."); + + // 学生基础信息映射(数据库必填字段) + internshipInfo.setStudentId(addBO.getStudentId()); + internshipInfo.setStudentNo(studentInfo.getStudentNo()); // 从学生信息获取 + internshipInfo.setStudentName(studentInfo.getName()); // 从学生信息获取 + internshipInfo.setGender(studentInfo.getGender()); // 从学生信息获取 + + // 类型转换:String -> Long(处理可能的空值) + internshipInfo.setCollegeId(StringUtils.isNotBlank(studentInfo.getCollegeId()) ? + Long.parseLong(studentInfo.getCollegeId()) : null); // 从学生信息获取 + internshipInfo.setMajorId(StringUtils.isNotBlank(studentInfo.getMajorId()) ? + Long.parseLong(studentInfo.getMajorId()) : null); // 从学生信息获取 + internshipInfo.setClassId(StringUtils.isNotBlank(studentInfo.getClassId()) ? + Long.parseLong(studentInfo.getClassId()) : null); // 从学生信息获取 + + internshipInfo.setPhone(studentInfo.getPhone()); // 从学生信息获取 + + // 实习相关信息映射(来自BO) + internshipInfo.setInternshipStatus(addBO.getInternshipStatus()); + internshipInfo.setCompanyName(addBO.getCompanyName()); + internshipInfo.setCompanyAddress(addBO.getCompanyAddress()); + internshipInfo.setInternshipAllowance(addBO.getInternshipAllowance()); + internshipInfo.setPositionName(addBO.getInternshipPosition()); // internshipPosition -> positionName + internshipInfo.setStartDate(addBO.getInternshipStartDate()); // internshipStartDate -> startDate + internshipInfo.setEndDate(addBO.getInternshipEndDate()); // internshipEndDate -> endDate + + // 公司联系信息映射(优先使用BO中的新字段,兼容旧字段) + internshipInfo.setCompanyContact(StringUtils.isNotBlank(addBO.getCompanyContact()) ? + addBO.getCompanyContact() : addBO.getContactPersonName()); // 优先使用companyContact + internshipInfo.setCompanyPhone(StringUtils.isNotBlank(addBO.getCompanyPhone()) ? + addBO.getCompanyPhone() : addBO.getContactPersonPhone()); // 优先使用companyPhone + + // 新增字段映射(从BO中获取真实值) + internshipInfo.setCompanyIndustry(addBO.getCompanyIndustry()); // 行业类型 + internshipInfo.setCompanyScale(addBO.getCompanyScale()); // 单位规模 + internshipInfo.setPositionDuties(addBO.getPositionDuties()); // 岗位职责 + internshipInfo.setWorkHours(addBO.getWorkHours()); // 工作时长 + internshipInfo.setAccommodationAddress(addBO.getAccommodationAddress()); // 住宿地址 + internshipInfo.setAccommodationType(addBO.getAccommodationType()); // 住宿类型 + internshipInfo.setAccommodationCost(addBO.getAccommodationCost()); // 住宿费用 + + internshipInfo.setStatus(addBO.getAgreementStatus()); // agreementStatus -> status + // actualEndDate保持null,因为实习还未结束 + + log.info("6. 映射后的实体对象:"); + log.info(" - studentId: {}", internshipInfo.getStudentId()); + log.info(" - studentNo: {}", internshipInfo.getStudentNo()); + log.info(" - studentName: {}", internshipInfo.getStudentName()); + log.info(" - collegeId: {}", internshipInfo.getCollegeId()); + log.info(" - majorId: {}", internshipInfo.getMajorId()); + log.info(" - classId: {}", internshipInfo.getClassId()); + log.info(" - phone: {}", internshipInfo.getPhone()); + log.info(" - internshipStatus: {}", internshipInfo.getInternshipStatus()); + log.info(" - companyName: {}", internshipInfo.getCompanyName()); + log.info(" - companyAddress: {}", internshipInfo.getCompanyAddress()); + log.info(" - positionName: {}", internshipInfo.getPositionName()); + log.info(" - internshipAllowance: {}", internshipInfo.getInternshipAllowance()); + log.info(" - startDate: {}", internshipInfo.getStartDate()); + log.info(" - endDate: {}", internshipInfo.getEndDate()); + log.info(" - companyContact: {}", internshipInfo.getCompanyContact()); + log.info(" - companyPhone: {}", internshipInfo.getCompanyPhone()); + log.info(" - status: {}", internshipInfo.getStatus()); + + // 插入数据 + log.info("7. 开始执行数据库插入操作..."); + int result = zsjyInternshipInfoMapper.insert(internshipInfo); + log.info("8. 数据库插入结果:{} (1表示成功,0表示失败)", result); + log.info("9. 插入后的主键ID:{}", internshipInfo.getInternshipId()); + + boolean success = result > 0; + log.info("=== 新增实习信息结束,结果:{} ===", success ? "成功" : "失败"); + + return success; + } + + /** + * 修改实习信息 + * + * @param editBO 修改参数 + * @return 是否成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateInternshipInfo(InternshipInfoEditBO editBO) { + log.info("修改实习信息,参数:{}", editBO); + + // 检查实习信息是否存在 + ZsjyInternshipInfo existingInfo = zsjyInternshipInfoMapper.selectById(editBO.getInternshipId()); + if (ObjectUtil.isNull(existingInfo)) { + log.warn("实习信息ID:{} 不存在", editBO.getInternshipId()); + throw new RuntimeException("实习信息不存在"); + } + + // 构建更新实体 + ZsjyInternshipInfo internshipInfo = new ZsjyInternshipInfo(); + BeanUtil.copyProperties(editBO, internshipInfo); + + // 映射字段名称差异 + internshipInfo.setPositionName(editBO.getInternshipPosition()); + internshipInfo.setStartDate(editBO.getInternshipStartDate()); + internshipInfo.setEndDate(editBO.getInternshipEndDate()); + + // 公司联系信息映射(优先使用BO中的新字段,兼容旧字段) + internshipInfo.setCompanyContact(StringUtils.isNotBlank(editBO.getCompanyContact()) ? + editBO.getCompanyContact() : editBO.getContactPersonName()); // 优先使用companyContact + internshipInfo.setCompanyPhone(StringUtils.isNotBlank(editBO.getCompanyPhone()) ? + editBO.getCompanyPhone() : editBO.getContactPersonPhone()); // 优先使用companyPhone + + // 新增字段映射(从BO中获取真实值) + internshipInfo.setCompanyIndustry(editBO.getCompanyIndustry()); // 行业类型 + internshipInfo.setCompanyScale(editBO.getCompanyScale()); // 单位规模 + internshipInfo.setPositionDuties(editBO.getPositionDuties()); // 岗位职责 + internshipInfo.setWorkHours(editBO.getWorkHours()); // 工作时长 + internshipInfo.setAccommodationAddress(editBO.getAccommodationAddress()); // 住宿地址 + internshipInfo.setAccommodationType(editBO.getAccommodationType()); // 住宿类型 + internshipInfo.setAccommodationCost(editBO.getAccommodationCost()); // 住宿费用 + + internshipInfo.setStatus(editBO.getAgreementStatus()); + + // 更新数据 + int result = zsjyInternshipInfoMapper.updateById(internshipInfo); + log.info("修改实习信息结果:{}", result > 0 ? "成功" : "失败"); + + return result > 0; + } + + /** + * 删除实习信息 + * + * @param internshipIds 实习信息ID数组 + * @return 是否成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteInternshipInfo(Long[] internshipIds) { + log.info("删除实习信息,IDs:{}", Arrays.toString(internshipIds)); + + if (ObjectUtil.isEmpty(internshipIds)) { + log.warn("删除实习信息参数为空"); + throw new RuntimeException("删除参数不能为空"); + } + + // 批量删除(逻辑删除) + int result = zsjyInternshipInfoMapper.deleteBatchIds(Arrays.asList(internshipIds)); + log.info("删除实习信息结果:删除了 {} 条记录", result); + + return result > 0; + } + + /** + * 根据学生ID查询学生基础信息 + * + * @param studentId 学生ID + * @return 学生信息 + */ + private XgStudentMsgVo getStudentInfo(Long studentId) { + log.info("根据学生ID查询学生信息:{}", studentId); + XgStudentMsgVo studentInfo = xgStudentMsgService.queryByUserId(studentId); + if (studentInfo == null) { + log.error("未找到学生信息,学生ID:{}", studentId); + throw new ServiceException("未找到学生信息,学生ID:" + studentId); + } + return studentInfo; + } + + /** + * 获取当前学生的实习信息 + * + * @return 当前学生的实习信息,如果没有则返回null + */ + @Override + public InternshipInfoVO getCurrentStudentInternship() { + log.info("开始获取当前学生的实习信息"); + + // 获取当前登录用户ID + Long currentUserId = LoginHelper.getUserId(); + log.info("当前登录用户ID:{}", currentUserId); + + if (currentUserId == null) { + log.error("当前用户未登录"); + throw new ServiceException("用户未登录"); + } + + // 根据用户ID查询实习信息 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(ZsjyInternshipInfo::getStudentId, currentUserId) + .eq(ZsjyInternshipInfo::getDeleted, 0) + .orderByDesc(ZsjyInternshipInfo::getCreateTime) + .last("LIMIT 1"); // 只取最新的一条记录 + + ZsjyInternshipInfo internshipInfo = zsjyInternshipInfoMapper.selectOne(wrapper); + log.info("查询到的实习信息:{}", internshipInfo != null ? internshipInfo.getInternshipId() : "无"); + + if (internshipInfo == null) { + log.info("当前学生没有实习信息"); + return null; + } + + // 转换为VO + InternshipInfoVO vo = BeanUtil.toBean(internshipInfo, InternshipInfoVO.class); + + // 查询学生信息并设置到VO中 + XgStudentMsgVo studentInfo = getStudentInfo(currentUserId); + vo.setStudentName(studentInfo.getName()); + vo.setStudentNo(studentInfo.getStudentNo()); + vo.setClassName(studentInfo.getClassName()); + vo.setMajorName(studentInfo.getMajorName()); + vo.setCollegeName(studentInfo.getCollegeName()); + vo.setGrade(studentInfo.getGrade()); + + log.info("成功获取当前学生实习信息,实习ID:{}", vo.getInternshipId()); + return vo; + } } diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyInternshipDailyServiceImpl.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyInternshipDailyServiceImpl.java new file mode 100644 index 00000000..817b237c --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyInternshipDailyServiceImpl.java @@ -0,0 +1,309 @@ +package org.dromara.zsjy.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.zsjy.domain.ZsjyInternshipDaily; +import org.dromara.zsjy.domain.bo.ZsjyInternshipDailyAddBo; +import org.dromara.zsjy.domain.bo.ZsjyInternshipDailyQueryBo; +import org.dromara.zsjy.domain.vo.ZsjyInternshipDailyVo; +import org.dromara.zsjy.mapper.ZsjyInternshipDailyMapper; +import org.dromara.zsjy.service.IZsjyInternshipDailyService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.sql.Time; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.YearMonth; +import java.time.ZoneId; +import java.util.*; + +/** + * 实习日常管理Service业务层处理 + * + * @author system + * @date 2025-01-03 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class ZsjyInternshipDailyServiceImpl implements IZsjyInternshipDailyService { + + private final ZsjyInternshipDailyMapper baseMapper; + + /** + * 查询实习日常记录分页列表 + */ + @Override + public TableDataInfo queryPageList(ZsjyInternshipDailyQueryBo queryBo, PageQuery pageQuery) { + log.info("=== 实习日常查询后端调试日志 ==="); + log.info("1. 查询参数: {}", queryBo); + log.info("2. 分页参数: pageNum={}, pageSize={}", pageQuery.getPageNum(), pageQuery.getPageSize()); + + // 设置数据权限 + setDataPermission(queryBo); + log.info("3. 设置数据权限后的查询参数: {}", queryBo); + log.info("3.1. 权限控制参数: headTeacherId={}", queryBo.getHeadTeacherId()); + + Page page = pageQuery.build(); + List list = baseMapper.selectDailyList(page, queryBo); + log.info("4. 查询到的数据条数: {}", list != null ? list.size() : "null"); + + return TableDataInfo.build(list); + } + + /** + * 查询实习日常记录列表(不分页) + */ + @Override + public List queryList(ZsjyInternshipDailyQueryBo queryBo) { + // 设置数据权限 + setDataPermission(queryBo); + + return baseMapper.selectDailyList(null, queryBo); + } + + /** + * 学生签到打卡 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public R checkIn(ZsjyInternshipDailyAddBo addBo) { + try { + // 1. 验证学生当日是否已签到 - 将LocalDate转换为Date + Date checkDateAsDate = Date.from(addBo.getCheckDate().atStartOfDay(ZoneId.systemDefault()).toInstant()); + boolean exists = baseMapper.existsByStudentAndDate(addBo.getStudentId(), checkDateAsDate); + if (exists) { + return R.fail("今日已签到,请勿重复签到"); + } + + // 2. 处理兼容字段映射(移动端可能使用的别名映射到正确的字段) + if (addBo.getLatitude() != null && addBo.getCheckOutLatitude() == null) { + addBo.setCheckOutLatitude(addBo.getLatitude()); + } + if (addBo.getLongitude() != null && addBo.getCheckOutLongitude() == null) { + addBo.setCheckOutLongitude(addBo.getLongitude()); + } + if (addBo.getDailySummary() != null && addBo.getWorkContent() == null) { + addBo.setWorkContent(addBo.getDailySummary()); + } + if (addBo.getCheckInLocation() != null && addBo.getCheckOutLocation() == null) { + addBo.setCheckOutLocation(addBo.getCheckInLocation()); + } + + // 3. 构建实体对象 - 手动创建避免MapStruct转换器错误 + ZsjyInternshipDaily entity = new ZsjyInternshipDaily(); + + // 核心必填字段(对应数据库字段) + entity.setInternshipId(addBo.getInternshipId()); + entity.setStudentId(addBo.getStudentId()); + entity.setCheckDate(addBo.getCheckDate()); + entity.setCheckInTime(addBo.getCheckInTime()); + entity.setCheckInLocation(addBo.getCheckOutLocation()); // 对应数据库check_in_location字段 + entity.setLatitude(addBo.getCheckOutLatitude()); // 对应数据库latitude字段 + entity.setLongitude(addBo.getCheckOutLongitude()); // 对应数据库longitude字段 + entity.setDailySummary(addBo.getWorkContent()); // 对应数据库daily_summary字段 + entity.setImages(addBo.getImages()); + entity.setIsException(addBo.getIsException()); + entity.setExceptionReason(addBo.getExceptionReason()); + entity.setStatus(addBo.getStatus()); + entity.setRemark(addBo.getRemark()); + + // 扩展字段(不存储到数据库,仅用于显示) + entity.setStudentName(addBo.getStudentName()); + entity.setStudentNumber(addBo.getStudentNumber()); + entity.setClassName(addBo.getClassName()); + entity.setCompanyName(addBo.getCompanyName()); + entity.setPosition(addBo.getPosition()); + + // 继承BaseEntity的字段会自动填充(create_time, create_by等) + + // 4. 设置签到时间为当前时间(如果没有传入) + if (ObjectUtil.isNull(entity.getCheckInTime())) { + entity.setCheckInTime(LocalTime.now()); + } + + // 5. 设置默认状态 + if (ObjectUtil.isNull(entity.getStatus())) { + entity.setStatus("1"); // 正常状态 + } + + // 6. 设置默认异常状态 + if (ObjectUtil.isNull(entity.getIsException())) { + entity.setIsException("0"); // 正常 + } + + // 7. 保存签到记录 + boolean result = baseMapper.insert(entity) > 0; + if (!result) { + return R.fail("签到失败,请稍后重试"); + } + + log.info("学生[{}]于{}成功签到", addBo.getStudentId(), addBo.getCheckDate()); + return R.ok("签到成功"); + + } catch (Exception e) { + log.error("学生签到失败: {}", e.getMessage(), e); + return R.fail("签到失败: " + e.getMessage()); + } + } + + /** + * 根据学生ID获取考勤日历数据 + */ + @Override + public R>> getCalendarData(Long studentId, Integer year, Integer month) { + try { + // 1. 参数校验 + if (ObjectUtil.isNull(studentId)) { + return R.fail("学生ID不能为空"); + } + if (ObjectUtil.isNull(year) || ObjectUtil.isNull(month)) { + return R.fail("年份和月份不能为空"); + } + + // 2. 计算日期范围 + YearMonth yearMonth = YearMonth.of(year, month); + Date startDate = DateUtil.beginOfMonth(DateUtil.parse(yearMonth + "-01")); + Date endDate = DateUtil.endOfMonth(startDate); + + // 3. 查询考勤数据 + List> calendarData = baseMapper.selectCalendarData(studentId, startDate, endDate); + + return R.ok(calendarData); + + } catch (Exception e) { + log.error("获取考勤日历数据失败: {}", e.getMessage(), e); + return R.fail("获取考勤日历数据失败: " + e.getMessage()); + } + } + + /** + * 获取考勤统计数据 + */ + @Override + public R> getStatistics(ZsjyInternshipDailyQueryBo queryBo) { + try { + // 1. 查询统计数据 + Map statistics = baseMapper.selectStatistics(queryBo); + + // 2. 补充统计信息 + if (ObjectUtil.isNull(statistics)) { + statistics = new HashMap<>(); + statistics.put("totalStudents", 0); + statistics.put("todayCheckedIn", 0); + statistics.put("todayLate", 0); + statistics.put("attendanceRate", "0%"); + } + + return R.ok(statistics); + + } catch (Exception e) { + log.error("获取考勤统计数据失败: {}", e.getMessage(), e); + return R.fail("获取考勤统计数据失败: " + e.getMessage()); + } + } + + /** + * 获取学生个人月度考勤统计 + */ + @Override + public R> getStudentMonthlyStatistics(Long studentId, Integer year, Integer month) { + try { + // 1. 参数校验 + if (ObjectUtil.isNull(studentId)) { + return R.fail("学生ID不能为空"); + } + if (ObjectUtil.isNull(year) || ObjectUtil.isNull(month)) { + return R.fail("年月参数不能为空"); + } + if (year < 2020 || year > 2030) { + return R.fail("年份参数不合法"); + } + if (month < 1 || month > 12) { + return R.fail("月份参数不合法"); + } + + // 2. 查询学生月度统计数据 + Map statistics = baseMapper.selectStudentMonthlyStatistics(studentId, year, month); + + // 3. 补充默认值 + if (ObjectUtil.isNull(statistics)) { + statistics = new HashMap<>(); + statistics.put("totalDays", 0); + statistics.put("checkedDays", 0); + statistics.put("missedDays", 0); + statistics.put("exceptionDays", 0); + } + + // 4. 确保数据类型正确 + statistics.put("totalDays", ObjectUtil.defaultIfNull(statistics.get("totalDays"), 0)); + statistics.put("checkedDays", ObjectUtil.defaultIfNull(statistics.get("checkedDays"), 0)); + statistics.put("missedDays", ObjectUtil.defaultIfNull(statistics.get("missedDays"), 0)); + statistics.put("exceptionDays", ObjectUtil.defaultIfNull(statistics.get("exceptionDays"), 0)); + + log.info("学生{}的{}-{}月度统计: {}", studentId, year, month, statistics); + return R.ok(statistics); + + } catch (Exception e) { + log.error("获取学生月度统计数据失败: studentId={}, year={}, month={}, error={}", + studentId, year, month, e.getMessage(), e); + return R.fail("获取学生月度统计数据失败: " + e.getMessage()); + } + } + + /** + * 设置数据权限(班主任只能查看自己班级的学生数据) + */ + private void setDataPermission(ZsjyInternshipDailyQueryBo queryBo) { + try { + // 获取当前登录用户ID + Long currentUserId = LoginHelper.getUserId(); + log.info("=== 权限控制调试 ==="); + log.info("当前登录用户ID: {}", currentUserId); + + if (ObjectUtil.isNotNull(currentUserId)) { + // 检查该用户是否是班主任 + Long classTeacherCount = baseMapper.countClassesByTeacherId(currentUserId); + log.info("用户ID {}作为班主任管理的班级数量: {}", currentUserId, classTeacherCount); + + if (classTeacherCount > 0) { + // 是班主任,设置权限限制 + queryBo.setHeadTeacherId(currentUserId); + log.info("✅ 权限判断:用户是班主任,设置headTeacherId={}", currentUserId); + } else { + // 不是班主任,设置不存在的班主任ID,使其无法查看任何数据 + log.info("❌ 权限判断:用户不是班主任,设置权限限制"); + + // 设置一个不存在的班主任ID(-1),这样SQL查询结果为空 + queryBo.setHeadTeacherId(-1L); + log.info("🔒 设置数据权限:非班主任用户无法查看数据,headTeacherId=-1"); + } + + // 输出最终的权限设置 + log.info("🎯 最终权限设置:queryBo.headTeacherId = {}", queryBo.getHeadTeacherId()); + } else { + log.warn("⚠️ 未获取到当前用户ID,设置权限限制"); + queryBo.setHeadTeacherId(-1L); + } + } catch (Exception e) { + log.error("❌ 设置数据权限失败:{}", e.getMessage(), e); + // 异常情况下,设置权限限制以确保数据安全 + queryBo.setHeadTeacherId(-1L); + log.warn("🔒 异常情况下设置权限限制,headTeacherId=-1"); + } + } + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyInternshipMonthlyServiceImpl.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyInternshipMonthlyServiceImpl.java new file mode 100644 index 00000000..0ebbbada --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyInternshipMonthlyServiceImpl.java @@ -0,0 +1,465 @@ +package org.dromara.zsjy.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.zsjy.domain.ZsjyInternshipMonthly; +import org.dromara.zsjy.domain.bo.ZsjyInternshipMonthlyAddBo; +import org.dromara.zsjy.domain.bo.ZsjyInternshipMonthlyQueryBo; +import org.dromara.zsjy.domain.vo.ZsjyInternshipMonthlyVo; +import org.dromara.zsjy.mapper.ZsjyInternshipMonthlyMapper; +import org.dromara.zsjy.mapper.ZsjyInternshipInfoMapper; +import org.dromara.zsjy.domain.ZsjyInternshipInfo; +import org.dromara.zsjy.service.IZsjyInternshipMonthlyService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.YearMonth; +import java.util.*; +import java.time.ZoneId; + +/** + * 学生月度实习总结Service业务层处理 + * + * @author system + * @date 2025-01-03 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class ZsjyInternshipMonthlyServiceImpl implements IZsjyInternshipMonthlyService { + + private final ZsjyInternshipMonthlyMapper baseMapper; + private final ZsjyInternshipInfoMapper internshipInfoMapper; + + /** + * 查询学生月度实习总结 + */ + @Override + public ZsjyInternshipMonthlyVo queryById(Long monthlyId) { + ZsjyInternshipMonthlyVo vo = baseMapper.selectVoById(monthlyId); + if (ObjectUtil.isNull(vo)) { + throw new ServiceException("月度总结不存在"); + } + return vo; + } + + /** + * 查询学生月度实习总结分页列表 + */ + @Override + public TableDataInfo queryPageList(ZsjyInternshipMonthlyQueryBo queryBo, PageQuery pageQuery) { + // 如果没有指定学生ID,则使用当前登录用户ID(学生端) + if (ObjectUtil.isNull(queryBo.getStudentId())) { + queryBo.setStudentId(LoginHelper.getUserId()); + } + + Page result = baseMapper.selectVoPage(pageQuery.build(), queryBo); + return TableDataInfo.build(result); + } + + /** + * 查询学生月度实习总结列表 + */ + @Override + public List queryList(ZsjyInternshipMonthlyQueryBo queryBo) { + // 如果没有指定学生ID,则使用当前登录用户ID(学生端) + if (ObjectUtil.isNull(queryBo.getStudentId())) { + queryBo.setStudentId(LoginHelper.getUserId()); + } + return baseMapper.selectVoList(queryBo); + } + + /** + * 新增学生月度实习总结 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(ZsjyInternshipMonthlyAddBo addBo) { + Long studentId = LoginHelper.getUserId(); + + // 如果前端没有传递实习ID,则自动获取当前学生的实习ID + if (ObjectUtil.isNull(addBo.getInternshipId())) { + ZsjyInternshipInfo internshipInfo = internshipInfoMapper.selectOne( + new LambdaQueryWrapper() + .eq(ZsjyInternshipInfo::getStudentId, studentId) + .eq(ZsjyInternshipInfo::getStatus, "1") // 查询进行中状态的实习 + .eq(ZsjyInternshipInfo::getDeleted, 0) + .orderByDesc(ZsjyInternshipInfo::getCreateTime) + .last("LIMIT 1") + ); + + if (ObjectUtil.isNull(internshipInfo)) { + throw new ServiceException("您当前没有进行中的实习记录,无法创建月度总结"); + } + + addBo.setInternshipId(internshipInfo.getInternshipId()); + log.info("自动获取到学生实习ID:{}", internshipInfo.getInternshipId()); + } + + // 检查该学生该月份是否已有总结 + if (checkMonthExists(studentId, addBo.getSummaryMonth(), null)) { + throw new ServiceException("该月份已存在总结记录,请勿重复创建"); + } + + // 手动转换BO到实体,解决字段映射问题 + ZsjyInternshipMonthly add = new ZsjyInternshipMonthly(); + add.setId(addBo.getMonthlyId()); // monthlyId -> id + add.setInternshipId(addBo.getInternshipId()); + add.setSummaryMonth(addBo.getSummaryMonth()); + add.setWorkContent(addBo.getWorkContent()); + add.setAchievements(addBo.getAchievements()); + add.setLearningGains(addBo.getLearningGains()); + add.setProblemsEncountered(addBo.getProblemsEncountered()); + add.setSelfEvaluation(addBo.getSelfEvaluation()); + add.setNextMonthPlan(addBo.getNextMonthPlan()); + add.setProfessionalSkills(addBo.getProfessionalSkills()); + add.setCommunicationSkills(addBo.getCommunicationSkills()); + add.setTeamworkAbility(addBo.getTeamworkAbility()); + add.setLearningAbility(addBo.getLearningAbility()); + add.setWorkAttitude(addBo.getWorkAttitude()); + add.setAttachments(addBo.getAttachments()); + + add.setStudentId(studentId); + add.setTenantId("000000"); // 默认租户 + + // 处理审核状态和提交时间 + if (addBo.getReviewStatus() != null) { + add.setReviewStatus(addBo.getReviewStatus()); + // 如果状态为待审核(0),设置提交时间 + if (addBo.getReviewStatus() == 0) { + add.setSubmitTime(addBo.getSubmitTime() != null ? addBo.getSubmitTime() : new Date()); + } + } else { + add.setReviewStatus(3); // 默认为草稿状态 + } + + // 解析月份序号 + if (StringUtils.isNotBlank(addBo.getSummaryMonth())) { + String[] parts = addBo.getSummaryMonth().split("-"); + if (parts.length == 2) { + add.setMonthNumber(Integer.parseInt(parts[1])); + } + } + + // 计算字数 + add.setWordCount(calculateWordCount(addBo)); + + // 设置默认截止日期(下月5号) + try { + YearMonth ym = YearMonth.parse(addBo.getSummaryMonth()); + add.setSubmitDeadline(Date.from(ym.plusMonths(1).atDay(5).atStartOfDay(ZoneId.systemDefault()).toInstant())); + } catch (Exception e) { + log.warn("解析月份失败: {}", addBo.getSummaryMonth()); + } + + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + addBo.setMonthlyId(add.getId()); + } + return flag; + } + + /** + * 修改学生月度实习总结 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(ZsjyInternshipMonthlyAddBo addBo) { + Long studentId = LoginHelper.getUserId(); + + // 查询原记录,验证权限和状态 + ZsjyInternshipMonthly original = baseMapper.selectById(addBo.getMonthlyId()); + if (ObjectUtil.isNull(original)) { + throw new ServiceException("月度总结不存在"); + } + + // 权限检查:只能修改自己的总结 + if (!Objects.equals(original.getStudentId(), studentId)) { + throw new ServiceException("您只能修改自己的月度总结"); + } + + // 状态检查:只有草稿状态才能修改 + if (!Objects.equals(original.getReviewStatus(), 3)) { + throw new ServiceException("只有草稿状态的总结才能修改"); + } + + // 如果前端没有传递实习ID,则使用原记录的实习ID + if (ObjectUtil.isNull(addBo.getInternshipId())) { + addBo.setInternshipId(original.getInternshipId()); + log.info("使用原记录的实习ID:{}", original.getInternshipId()); + } + + // 检查月份是否被其他记录占用 + if (!Objects.equals(original.getSummaryMonth(), addBo.getSummaryMonth())) { + if (checkMonthExists(studentId, addBo.getSummaryMonth(), addBo.getMonthlyId())) { + throw new ServiceException("该月份已存在其他总结记录"); + } + } + + // 手动转换BO到实体,解决字段映射问题 + ZsjyInternshipMonthly update = new ZsjyInternshipMonthly(); + update.setId(addBo.getMonthlyId()); // monthlyId -> id + update.setInternshipId(addBo.getInternshipId()); + update.setSummaryMonth(addBo.getSummaryMonth()); + update.setWorkContent(addBo.getWorkContent()); + update.setAchievements(addBo.getAchievements()); + update.setLearningGains(addBo.getLearningGains()); + update.setProblemsEncountered(addBo.getProblemsEncountered()); + update.setSelfEvaluation(addBo.getSelfEvaluation()); + update.setNextMonthPlan(addBo.getNextMonthPlan()); + update.setProfessionalSkills(addBo.getProfessionalSkills()); + update.setCommunicationSkills(addBo.getCommunicationSkills()); + update.setTeamworkAbility(addBo.getTeamworkAbility()); + update.setLearningAbility(addBo.getLearningAbility()); + update.setWorkAttitude(addBo.getWorkAttitude()); + update.setAttachments(addBo.getAttachments()); + + // 处理审核状态和提交时间 + if (addBo.getReviewStatus() != null) { + update.setReviewStatus(addBo.getReviewStatus()); + // 如果状态从草稿(3)变为待审核(0),设置提交时间 + if (addBo.getReviewStatus() == 0 && original.getReviewStatus() == 3) { + update.setSubmitTime(addBo.getSubmitTime() != null ? addBo.getSubmitTime() : new Date()); + } + // 如果状态变回草稿(3),清空提交时间 + else if (addBo.getReviewStatus() == 3) { + update.setSubmitTime(null); + } + } + + // 解析月份序号 + if (StringUtils.isNotBlank(addBo.getSummaryMonth())) { + String[] parts = addBo.getSummaryMonth().split("-"); + if (parts.length == 2) { + update.setMonthNumber(Integer.parseInt(parts[1])); + } + } + + // 重新计算字数 + update.setWordCount(calculateWordCount(addBo)); + + return baseMapper.updateById(update) > 0; + } + + /** + * 批量删除学生月度实习总结 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteByIds(Collection ids) { + Long studentId = LoginHelper.getUserId(); + + // 验证权限:只能删除自己的草稿状态总结 + for (Long id : ids) { + ZsjyInternshipMonthly monthly = baseMapper.selectById(id); + if (ObjectUtil.isNull(monthly)) { + continue; + } + + if (!Objects.equals(monthly.getStudentId(), studentId)) { + throw new ServiceException("您只能删除自己的月度总结"); + } + + if (!Objects.equals(monthly.getReviewStatus(), 3)) { + throw new ServiceException("只有草稿状态的总结才能删除"); + } + } + + return baseMapper.deleteByIds(ids.toArray(new Long[0])) > 0; + } + + /** + * 提交月度总结审批 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean submitForApproval(Long monthlyId) { + Long studentId = LoginHelper.getUserId(); + + ZsjyInternshipMonthly monthly = baseMapper.selectById(monthlyId); + if (ObjectUtil.isNull(monthly)) { + throw new ServiceException("月度总结不存在"); + } + + // 权限检查 + if (!Objects.equals(monthly.getStudentId(), studentId)) { + throw new ServiceException("您只能提交自己的月度总结"); + } + + // 状态检查 + if (!Objects.equals(monthly.getReviewStatus(), 3)) { + throw new ServiceException("只有草稿状态的总结才能提交"); + } + + // 必填字段检查 + if (StringUtils.isBlank(monthly.getWorkContent())) { + throw new ServiceException("工作内容不能为空"); + } + + // 更新状态为待审核 + ZsjyInternshipMonthly update = new ZsjyInternshipMonthly(); + update.setId(monthlyId); + update.setReviewStatus(0); // 待审核 + update.setSubmitTime(new Date()); + + return baseMapper.updateById(update) > 0; + } + + /** + * 自动保存草稿 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean autoSaveDraft(ZsjyInternshipMonthlyAddBo addBo) { + try { + if (ObjectUtil.isNull(addBo.getMonthlyId())) { + // 新增草稿 + return insertByBo(addBo); + } else { + // 更新草稿 + return updateByBo(addBo); + } + } catch (Exception e) { + log.warn("自动保存草稿失败: {}", e.getMessage()); + return false; + } + } + + /** + * 检查学生某月是否已有总结 + */ + @Override + public Boolean checkMonthExists(Long studentId, String summaryMonth, Long monthlyId) { + return baseMapper.existsByStudentAndMonth(studentId, summaryMonth, monthlyId); + } + + /** + * 获取月度总结模板信息 + */ + @Override + public ZsjyInternshipMonthlyVo getMonthlyTemplate(String summaryMonth) { + ZsjyInternshipMonthlyVo template = new ZsjyInternshipMonthlyVo(); + template.setSummaryMonth(summaryMonth); + + // 解析月份序号 + if (StringUtils.isNotBlank(summaryMonth)) { + String[] parts = summaryMonth.split("-"); + if (parts.length == 2) { + template.setMonthNumber(Integer.parseInt(parts[1])); + } + } + + // 设置默认截止日期(下月5号) + try { + YearMonth ym = YearMonth.parse(summaryMonth); + template.setSubmitDeadline(Date.from(ym.plusMonths(1).atDay(5).atStartOfDay(ZoneId.systemDefault()).toInstant())); + } catch (Exception e) { + log.warn("解析月份失败: {}", summaryMonth); + } + + template.setReviewStatus(3); // 草稿 + template.setStatusText("草稿"); + template.setEditable(true); + template.setDeletable(true); + template.setSubmittable(true); + + return template; + } + + /** + * 查询学生指定实习的月度总结列表 + */ + @Override + public List queryByStudentAndInternship(Long studentId, Long internshipId) { + if (ObjectUtil.isNull(studentId)) { + studentId = LoginHelper.getUserId(); + } + return baseMapper.selectByStudentAndInternship(studentId, internshipId); + } + + /** + * 统计学生各状态的总结数量 + */ + @Override + public List countByStatus(Long studentId) { + if (ObjectUtil.isNull(studentId)) { + studentId = LoginHelper.getUserId(); + } + return baseMapper.countByStatus(studentId); + } + + /** + * 计算总结内容字数 + */ + @Override + public Integer calculateWordCount(ZsjyInternshipMonthlyAddBo addBo) { + int wordCount = 0; + + if (StringUtils.isNotBlank(addBo.getWorkContent())) { + wordCount += addBo.getWorkContent().length(); + } + if (StringUtils.isNotBlank(addBo.getAchievements())) { + wordCount += addBo.getAchievements().length(); + } + if (StringUtils.isNotBlank(addBo.getLearningGains())) { + wordCount += addBo.getLearningGains().length(); + } + if (StringUtils.isNotBlank(addBo.getProblemsEncountered())) { + wordCount += addBo.getProblemsEncountered().length(); + } + if (StringUtils.isNotBlank(addBo.getSelfEvaluation())) { + wordCount += addBo.getSelfEvaluation().length(); + } + if (StringUtils.isNotBlank(addBo.getNextMonthPlan())) { + wordCount += addBo.getNextMonthPlan().length(); + } + + return wordCount; + } + + /** + * 获取学生当前实习信息 + */ + @Override + public Map getCurrentInternshipInfo() { + Long currentUserId = LoginHelper.getUserId(); + log.info("获取学生当前实习信息, 用户ID: {}", currentUserId); + + // 查询学生当前进行中的实习记录(与insertByBo方法保持一致) + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(ZsjyInternshipInfo::getStudentId, currentUserId) + .eq(ZsjyInternshipInfo::getStatus, "1") // 查询进行中状态的实习 + .eq(ZsjyInternshipInfo::getDeleted, 0) + .orderByDesc(ZsjyInternshipInfo::getCreateTime) + .last("LIMIT 1"); + + ZsjyInternshipInfo internshipInfo = internshipInfoMapper.selectOne(wrapper); + + if (internshipInfo == null) { + log.warn("学生{}没有找到进行中的实习记录", currentUserId); + return new HashMap<>(); + } + + Map result = new HashMap<>(); + result.put("internshipId", internshipInfo.getInternshipId()); + result.put("companyName", internshipInfo.getCompanyName()); + result.put("position", internshipInfo.getPositionName()); + result.put("startDate", internshipInfo.getStartDate()); + result.put("endDate", internshipInfo.getEndDate()); + + log.info("找到学生实习信息: {}", result); + return result; + } + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyInternshipVisitServiceImpl.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyInternshipVisitServiceImpl.java new file mode 100644 index 00000000..40fbfc66 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyInternshipVisitServiceImpl.java @@ -0,0 +1,116 @@ +package org.dromara.zsjy.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.zsjy.domain.ZsjyInternshipVisit; +import org.dromara.zsjy.domain.bo.ZsjyInternshipVisitBo; +import org.dromara.zsjy.domain.bo.ZsjyInternshipVisitQueryBo; +import org.dromara.zsjy.domain.vo.ZsjyInternshipVisitVo; +import org.dromara.zsjy.mapper.ZsjyInternshipVisitMapper; +import org.dromara.zsjy.service.IZsjyInternshipVisitService; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; + +/** + * 班主任走访Service业务层处理 + * + * @author system + * @date 2025-01-03 + */ +@RequiredArgsConstructor +@Service +public class ZsjyInternshipVisitServiceImpl implements IZsjyInternshipVisitService { + + private final ZsjyInternshipVisitMapper baseMapper; + + /** + * 查询班主任走访 + */ + @Override + public ZsjyInternshipVisitVo queryById(Long visitId){ + return baseMapper.selectById(visitId); + } + + /** + * 查询班主任走访列表 + */ + @Override + public TableDataInfo queryPageList(ZsjyInternshipVisitQueryBo bo, PageQuery pageQuery) { + // 使用自定义分页查询方法获取包含学生信息的完整数据 + Page result = baseMapper.queryPageList(pageQuery.build(), bo); + return TableDataInfo.build(result); + } + + /** + * 查询班主任走访列表 + */ + @Override + public List queryList(ZsjyInternshipVisitQueryBo bo) { + // 使用自定义查询方法获取包含学生信息的完整数据 + return baseMapper.queryList(bo); + } + + /** + * 新增班主任走访 + */ + @Override + public Boolean insertByBo(ZsjyInternshipVisitBo bo) { + ZsjyInternshipVisit add = MapstructUtils.convert(bo, ZsjyInternshipVisit.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setVisitId(add.getVisitId()); + } + return flag; + } + + /** + * 修改班主任走访 + */ + @Override + public Boolean updateByBo(ZsjyInternshipVisitBo bo) { + ZsjyInternshipVisit update = MapstructUtils.convert(bo, ZsjyInternshipVisit.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(ZsjyInternshipVisit entity){ + // TODO 做一些数据校验,如唯一约束 + } + + /** + * 批量删除班主任走访 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + // TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + /** + * 获取学生选项列表(用于下拉选择) + */ + @Override + public List getStudentOptions() { + // TODO 实现获取有实习信息的学生列表 + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.select(ZsjyInternshipVisit::getStudentId, ZsjyInternshipVisit::getInternshipId); + lqw.groupBy(ZsjyInternshipVisit::getStudentId); + return baseMapper.selectVoList(lqw); + } +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/InternshipInfoMapper.xml b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/InternshipInfoMapper.xml index a99087ae..7a42d5f0 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/InternshipInfoMapper.xml +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/InternshipInfoMapper.xml @@ -51,8 +51,11 @@ s.class_teacher_name, c.class_teacher_id, c.counselor_id, - i.internship_id as internship_id, - i.internship_status, + i.internship_id, + CASE + WHEN i.internship_status IS NULL OR i.internship_status = '' THEN 'null' + ELSE i.internship_status + END as internship_status, i.company_name, i.company_address, i.position_name as internship_position, @@ -119,22 +122,22 @@ AND i.company_address LIKE CONCAT('%', #{queryBO.companyAddress}, '%') - AND i.internship_position LIKE CONCAT('%', #{queryBO.internshipPosition}, '%') + AND i.position_name LIKE CONCAT('%', #{queryBO.internshipPosition}, '%') - AND i.agreement_status = #{queryBO.agreementStatus} + AND i.status = #{queryBO.agreementStatus} - AND i.internship_start_date >= #{queryBO.internshipStartDateBegin} + AND i.start_date >= #{queryBO.internshipStartDateBegin} - AND i.internship_start_date <= #{queryBO.internshipStartDateEnd} + AND i.start_date <= #{queryBO.internshipStartDateEnd} - AND i.internship_end_date >= #{queryBO.internshipEndDateBegin} + AND i.end_date >= #{queryBO.internshipEndDateBegin} - AND i.internship_end_date <= #{queryBO.internshipEndDateEnd} + AND i.end_date <= #{queryBO.internshipEndDateEnd} @@ -194,4 +197,13 @@ + + + diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipDailyMapper.xml b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipDailyMapper.xml new file mode 100644 index 00000000..6e1e5012 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipDailyMapper.xml @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SELECT + d.daily_id, + d.internship_id, + d.student_id, + d.check_date, + d.check_in_time, + d.check_out_time, + d.check_in_location, + d.latitude, + d.longitude, + d.daily_summary, + d.images, + d.is_exception, + d.exception_reason, + d.exception_status, + d.status, + d.remark, + d.create_by, + d.create_time, + d.update_by, + d.update_time, + -- 关联查询扩展字段 + i.student_name, + i.student_no, + i.company_name, + -- 计算工作时长(小时) + CASE + WHEN d.check_in_time IS NOT NULL AND d.check_out_time IS NOT NULL THEN + ROUND(TIMESTAMPDIFF(MINUTE, d.check_in_time, d.check_out_time) / 60.0, 2) + ELSE NULL + END as work_duration + FROM zsjy_internship_daily d + LEFT JOIN zsjy_internship_info i ON d.internship_id = i.internship_id + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipMonthlyMapper.xml b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipMonthlyMapper.xml new file mode 100644 index 00000000..be52f67b --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipMonthlyMapper.xml @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SELECT + m.monthly_id, + m.internship_id, + m.student_id, + u.nick_name as student_name, + ui.student_no as student_number, + m.summary_month, + m.month_number, + m.work_content, + m.achievements, + m.learning_gains, + m.problems_encountered, + m.self_evaluation, + m.next_month_plan, + m.professional_skills, + m.communication_skills, + m.teamwork_ability, + m.learning_ability, + m.work_attitude, + m.review_status, + CASE m.review_status + WHEN 0 THEN '待审核' + WHEN 1 THEN '已审核' + WHEN 2 THEN '审核拒绝' + WHEN 3 THEN '草稿' + ELSE '未知状态' + END as status_text, + m.review_by, + r.nick_name as reviewer_name, + m.review_time, + m.review_score, + m.review_comment as review_comments, + m.submit_time, + m.word_count, + m.is_overdue, + m.submit_deadline, + m.attachments, + CASE WHEN m.review_status = 3 THEN 1 ELSE 0 END as editable, + CASE WHEN m.review_status = 3 THEN 1 ELSE 0 END as deletable, + CASE WHEN m.review_status = 3 THEN 1 ELSE 0 END as submittable, + m.create_by, + m.create_time, + m.update_by, + m.update_time + FROM zsjy_internship_monthly m + LEFT JOIN sys_user u ON u.user_id = m.student_id + LEFT JOIN zsjy_internship_info ui ON ui.internship_id = m.internship_id AND ui.student_id = m.student_id + LEFT JOIN sys_user r ON r.user_id = m.review_by + WHERE m.deleted = 0 + + + + + + + + + + + + UPDATE zsjy_internship_monthly SET deleted = 1 + WHERE monthly_id IN + + #{monthlyId} + + AND review_status = 3 + + + + + + + diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipVisitMapper.xml b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipVisitMapper.xml new file mode 100644 index 00000000..2e2e4270 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipVisitMapper.xml @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select + v.visit_id, + v.tenant_id, + v.internship_id, + v.student_id, + v.company_name, + v.company_address, + v.visit_date, + v.visitor_name, + v.visitor_position, + v.visit_purpose, + v.company_situation, + v.student_performance, + v.problems_found, + v.suggestions, + v.visit_summary, + v.visit_images, + v.visit_files, + v.status, + v.remark, + v.create_by, + v.create_time, + v.update_by, + v.update_time, + -- 关联查询学生信息 + s.name as student_name, + s.student_no as student_number, + s.class_name as class_name, + s.major_name as major_name, + s.college_name as college_name + from zsjy_internship_visit v + left join zsjy_internship_info ii on v.internship_id = ii.internship_id and ii.tenant_id = '000000' + left join xg_student_msg s on v.student_id = s.id and s.tenant_id = '000000' and s.deleted = 0 + + + + + + + + + diff --git a/backend/script/sql/CHANGELOG-学生数据导入脚本.md b/backend/script/sql/CHANGELOG-学生数据导入脚本.md deleted file mode 100644 index f79c0517..00000000 --- a/backend/script/sql/CHANGELOG-学生数据导入脚本.md +++ /dev/null @@ -1,130 +0,0 @@ -# 学生数据导入脚本更改日志 - -## 📋 版本历史 - -### V1.1.0 - 2025-01-14 16:15:00 - -#### 🚀 新增功能 -- **自动配置获取**: 从项目配置文件自动读取数据库连接信息 -- **简化用户交互**: 移除繁琐的数据库参数输入流程 -- **安全密码显示**: 密码部分隐藏显示(hn0***KLD格式) - -#### 🔧 改进优化 -- **减少操作步骤**: 从原来的6个输入步骤简化为1个确认步骤 -- **降低出错概率**: 避免手动输入导致的连接参数错误 -- **提升使用体验**: 一键确认即可完成数据导入 - -#### 📊 技术变更 -- **配置来源**: `application-dev.yml` → 脚本硬编码配置 -- **连接参数**: 自动设置为项目开发环境配置 -- **执行方式**: 简化MySQL命令执行逻辑 - -#### 🗂️ 文件变更 -``` -backend/script/sql/ -├── import-student-data.sh (修改 - 主要脚本逻辑) -├── README-学生数据生成.md (修改 - 使用说明更新) -├── test-import.sh (新增 - 测试演示脚本) -└── CHANGELOG-学生数据导入脚本.md (新增 - 本文件) -``` - -#### 🔗 数据库连接配置 -```bash -DB_HOST="47.99.164.66" -DB_PORT="3306" -DB_NAME="hzdyjs_xg" -DB_USER="root" -DB_PASSWORD="hn02le.34lkdLKD" -``` - -#### 💡 使用方式对比 - -**V1.0.0 (旧版本)**: -```bash -./import-student-data.sh -# 数据库主机 (默认: localhost): 47.99.164.66 -# 数据库端口 (默认: 3306): 3306 -# 数据库名称 (默认: hzdyjs_xg): hzdyjs_xg -# 数据库用户名 (默认: root): root -# 数据库密码: hn02le.34lkdLKD -# 确认导入数据?(y/N): y -``` - -**V1.1.0 (新版本)**: -```bash -./import-student-data.sh -# 确认导入数据?(y/N): y -``` - -#### ✅ 测试验证 -- 文件完整性检查通过 -- 权限设置正确 (-rwxr-xr-x) -- 功能演示脚本运行正常 -- 用户体验显著提升 - ---- - -### V1.0.0 - 2025-01-14 15:30:00 - -#### 🎯 初始功能 -- **数据导入**: 支持150条学生信息和3个班级的完整导入 -- **交互式配置**: 用户手动输入数据库连接参数 -- **错误处理**: 完整的错误检查和友好提示 -- **结果验证**: 详细的导入结果展示 - -#### 📊 初始特性 -- 支持MySQL数据库连接 -- 彩色输出界面 -- 完整的错误处理机制 -- 详细的成功结果展示 - -#### 🗂️ 初始文件 -``` -backend/script/sql/ -├── generate_student_data.py (数据生成脚本) -├── complete-student-data.sql (完整数据SQL文件) -├── import-student-data.sh (数据导入脚本) -└── README-学生数据生成.md (使用说明文档) -``` - ---- - -## 🎯 未来规划 - -### V1.2.0 (计划中) -- [ ] 支持多环境配置切换(dev/test/prod) -- [ ] 添加数据备份功能 -- [ ] 支持增量数据导入 -- [ ] 添加导入进度显示 - -### V1.3.0 (计划中) -- [ ] 支持配置文件自动解析 -- [ ] 添加数据验证功能 -- [ ] 支持回滚操作 -- [ ] 添加日志记录功能 - ---- - -## 📞 技术支持 - -### 使用问题 -- 确保MySQL客户端已安装 -- 确保网络连接到数据库服务器 -- 确保数据库用户权限正确 - -### 错误排查 -1. **连接失败**: 检查网络和数据库服务状态 -2. **权限不足**: 确认用户具有数据库写入权限 -3. **表不存在**: 确保数据库表结构已创建 -4. **数据冲突**: 检查主键和唯一约束冲突 - -### 联系方式 -- 项目文档: `docs/开发文档/API/` -- 变更记录: `docs/变更管理/` -- 测试文档: `docs/测试文档/` - ---- - -**维护团队**: Cursor AI 辅助开发 -**最后更新**: 2025-01-14 -**文档版本**: 1.0 \ No newline at end of file diff --git a/backend/script/sql/add-head-teacher-role.sql b/backend/script/sql/add-head-teacher-role.sql deleted file mode 100644 index 8531393f..00000000 --- a/backend/script/sql/add-head-teacher-role.sql +++ /dev/null @@ -1,48 +0,0 @@ --- 添加班主任角色 -INSERT INTO sys_role (role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark, tenant_id) -VALUES (1947639680473493505, '班主任', 'head_teacher', 3, '1', 1, 1, '0', '0', 1, NOW(), 1, NOW(), '班主任角色', '000000'); - --- 为班主任角色添加菜单权限 -INSERT INTO sys_role_menu (role_id, menu_id, tenant_id) -SELECT 1947639680473493505, menu_id, '000000' FROM sys_menu WHERE perms LIKE 'xg:%' AND status = '0' AND del_flag = '0'; - --- 为智控学院张三用户分配班主任角色 -INSERT INTO sys_user_role (user_id, role_id, tenant_id) -VALUES (2001, 1947639680473493505, '000000'); - --- 为信息学院李四用户分配班主任角色 -INSERT INTO sys_user_role (user_id, role_id, tenant_id) -VALUES (2002, 1947639680473493505, '000000'); - --- 为机电学院王五用户分配班主任角色 -INSERT INTO sys_user_role (user_id, role_id, tenant_id) -VALUES (2003, 1947639680473493505, '000000'); - --- 为经管学院赵六用户分配班主任角色 -INSERT INTO sys_user_role (user_id, role_id, tenant_id) -VALUES (2004, 1947639680473493505, '000000'); - --- 添加班主任角色相关的权限 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark, tenant_id) -VALUES (1947639680473493506, '班主任管理', 2000, 5, 'headteacher', 'xg-xx/headteacher/index', NULL, 1, 0, 'C', '0', '0', 'xg:headteacher:list', 'user', 1, NOW(), NULL, NULL, '班主任管理菜单', '000000'); - --- 添加班主任管理按钮权限 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark, tenant_id) -VALUES (1947639680473493507, '班主任查询', 1947639680473493506, 1, '', '', NULL, 1, 0, 'F', '0', '0', 'xg:headteacher:query', '#', 1, NOW(), NULL, NULL, '', '000000'); - -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark, tenant_id) -VALUES (1947639680473493508, '班主任新增', 1947639680473493506, 2, '', '', NULL, 1, 0, 'F', '0', '0', 'xg:headteacher:add', '#', 1, NOW(), NULL, NULL, '', '000000'); - -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark, tenant_id) -VALUES (1947639680473493509, '班主任修改', 1947639680473493506, 3, '', '', NULL, 1, 0, 'F', '0', '0', 'xg:headteacher:edit', '#', 1, NOW(), NULL, NULL, '', '000000'); - -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark, tenant_id) -VALUES (1947639680473493510, '班主任删除', 1947639680473493506, 4, '', '', NULL, 1, 0, 'F', '0', '0', 'xg:headteacher:remove', '#', 1, NOW(), NULL, NULL, '', '000000'); - --- 为班主任角色添加班主任管理权限 -INSERT INTO sys_role_menu (role_id, menu_id, tenant_id) -VALUES (1947639680473493505, 1947639680473493506, '000000'), - (1947639680473493505, 1947639680473493507, '000000'), - (1947639680473493505, 1947639680473493508, '000000'), - (1947639680473493505, 1947639680473493509, '000000'), - (1947639680473493505, 1947639680473493510, '000000'); diff --git a/backend/script/sql/class-basic-data.sql b/backend/script/sql/class-basic-data.sql deleted file mode 100644 index 3feabde9..00000000 --- a/backend/script/sql/class-basic-data.sql +++ /dev/null @@ -1,115 +0,0 @@ --- ========================================== --- 班级信息管理基础数据脚本 --- 版本:V1.0.0 --- 功能:创建3个班级基础数据 --- 作者:Cursor AI 辅助 --- 时间:2025-01-21 --- 说明:班级信息管理功能的基础数据准备 --- ========================================== - -USE hzdyjs_xg; - --- 设置字符集 -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS = 0; - --- ============================================== --- 1. 创建3个新班级 --- ============================================== - --- 计算机科学与技术2024级1班 -INSERT INTO `xg_class_info` ( - `id`, `tenant_id`, `class_code`, `class_name`, `class_type`, `grade`, `enrollment_year`, `graduation_year`, - `college_code`, `college_name`, `major_name`, `major_code`, `training_type`, `degree_type`, - `counselor_id`, `counselor_name`, `class_teacher_id`, `class_teacher_name`, - `planned_capacity`, `current_student_count`, `male_count`, `female_count`, `active_count`, - `contact_phone`, `qq_group`, `email`, `class_status`, `is_graduation`, - `create_by`, `create_time`, `update_by`, `update_time` -) VALUES ( - 1100001, '000000', 'CS2024001', '计算机科学与技术2024级1班', 'regular', '2024', 2024, 2028, - 'CS001', '计算机科学与技术学院', '计算机科学与技术', 'CS001', '全日制', '本科', - 10001, '李明华', 10002, '王建国', - 50, 0, 0, 0, 0, - '0571-88000001', '888888001', 'cs2024001@hzdyjs.edu.cn', '1', 0, - 1, NOW(), 1, NOW() -) ON DUPLICATE KEY UPDATE - class_name = VALUES(class_name), - college_name = VALUES(college_name), - major_name = VALUES(major_name), - update_time = NOW(); - --- 电子信息工程2024级1班 -INSERT INTO `xg_class_info` ( - `id`, `tenant_id`, `class_code`, `class_name`, `class_type`, `grade`, `enrollment_year`, `graduation_year`, - `college_code`, `college_name`, `major_name`, `major_code`, `training_type`, `degree_type`, - `counselor_id`, `counselor_name`, `class_teacher_id`, `class_teacher_name`, - `planned_capacity`, `current_student_count`, `male_count`, `female_count`, `active_count`, - `contact_phone`, `qq_group`, `email`, `class_status`, `is_graduation`, - `create_by`, `create_time`, `update_by`, `update_time` -) VALUES ( - 1100002, '000000', 'EE2024001', '电子信息工程2024级1班', 'regular', '2024', 2024, 2028, - 'EE001', '电子信息工程学院', '电子信息工程', 'EE001', '全日制', '本科', - 10003, '张丽萍', 10004, '刘德华', - 50, 0, 0, 0, 0, - '0571-88000002', '888888002', 'ee2024001@hzdyjs.edu.cn', '1', 0, - 1, NOW(), 1, NOW() -) ON DUPLICATE KEY UPDATE - class_name = VALUES(class_name), - college_name = VALUES(college_name), - major_name = VALUES(major_name), - update_time = NOW(); - --- 工商管理2024级1班 -INSERT INTO `xg_class_info` ( - `id`, `tenant_id`, `class_code`, `class_name`, `class_type`, `grade`, `enrollment_year`, `graduation_year`, - `college_code`, `college_name`, `major_name`, `major_code`, `training_type`, `degree_type`, - `counselor_id`, `counselor_name`, `class_teacher_id`, `class_teacher_name`, - `planned_capacity`, `current_student_count`, `male_count`, `female_count`, `active_count`, - `contact_phone`, `qq_group`, `email`, `class_status`, `is_graduation`, - `create_by`, `create_time`, `update_by`, `update_time` -) VALUES ( - 1100003, '000000', 'BM2024001', '工商管理2024级1班', 'regular', '2024', 2024, 2028, - 'BM001', '经济管理学院', '工商管理', 'BM001', '全日制', '本科', - 10005, '陈建华', 10006, '赵文强', - 50, 0, 0, 0, 0, - '0571-88000003', '888888003', 'bm2024001@hzdyjs.edu.cn', '1', 0, - 1, NOW(), 1, NOW() -) ON DUPLICATE KEY UPDATE - class_name = VALUES(class_name), - college_name = VALUES(college_name), - major_name = VALUES(major_name), - update_time = NOW(); - --- ============================================== --- 2. 验证数据创建结果 --- ============================================== - --- 验证班级信息 -SELECT '=== 班级信息验证 ===' as info; -SELECT - id, - class_code, - class_name, - college_name, - major_name, - current_student_count, - male_count, - female_count, - class_status -FROM xg_class_info -WHERE id IN (1100001, 1100002, 1100003); - --- 验证数据字典 -SELECT '=== 数据字典验证 ===' as info; -SELECT dict_type, dict_label, dict_value -FROM sys_dict_data -WHERE dict_type IN ('xg_class_type', 'xg_class_status') -ORDER BY dict_type, dict_sort; - --- 恢复外键检查 -SET FOREIGN_KEY_CHECKS = 1; - -SELECT '🎉 班级信息管理数据库准备完成!' as result; -SELECT '📊 总计:3个班级信息' as summary; -SELECT '✅ 数据字典:班级类型、班级状态字典已配置' as dict_status; -SELECT '✅ 数据表:xg_class_info、xg_class_student表结构完整' as table_status; diff --git a/backend/script/sql/create-missing-tables.sql b/backend/script/sql/create-missing-tables.sql deleted file mode 100644 index 732f7594..00000000 --- a/backend/script/sql/create-missing-tables.sql +++ /dev/null @@ -1,150 +0,0 @@ --- ========================================== --- 创建信息采集批次缺失的关联表 --- 版本:V1.0.0 --- 创建时间:2025-01-17 --- ========================================== - -USE hzdyjs_xg; - --- 1. 创建批次学生关联表 -CREATE TABLE IF NOT EXISTS xg_collection_batch_student ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - batch_id BIGINT NOT NULL COMMENT '批次ID', - student_id BIGINT NOT NULL COMMENT '学生ID', - student_code VARCHAR(50) NOT NULL COMMENT '学号', - student_name VARCHAR(100) NOT NULL COMMENT '学生姓名', - college_id BIGINT COMMENT '学院ID', - college_name VARCHAR(100) COMMENT '学院名称', - major_id BIGINT COMMENT '专业ID', - major_name VARCHAR(100) COMMENT '专业名称', - class_id BIGINT COMMENT '班级ID', - class_name VARCHAR(100) COMMENT '班级名称', - grade VARCHAR(50) COMMENT '届别', - can_edit TINYINT DEFAULT 1 COMMENT '是否可编辑', - has_submitted TINYINT DEFAULT 0 COMMENT '是否已提交', - submit_time DATETIME COMMENT '提交时间', - last_edit_time DATETIME COMMENT '最后编辑时间', - edit_count INT DEFAULT 0 COMMENT '编辑次数', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - create_by BIGINT COMMENT '创建者', - update_by BIGINT COMMENT '更新者', - deleted TINYINT DEFAULT 0 COMMENT '删除标志', - tenant_id VARCHAR(20) DEFAULT '000000' COMMENT '租户ID', - INDEX idx_batch_id (batch_id), - INDEX idx_student_id (student_id), - INDEX idx_batch_student (batch_id, student_id), - INDEX idx_college_id (college_id), - INDEX idx_grade (grade), - INDEX idx_create_time (create_time), - INDEX idx_has_submitted (has_submitted), - INDEX idx_tenant_id (tenant_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='信息采集批次学生关联表'; - --- 2. 创建批次操作日志表 -CREATE TABLE IF NOT EXISTS xg_collection_batch_log ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - batch_id BIGINT NOT NULL COMMENT '批次ID', - student_id BIGINT COMMENT '学生ID', - operation_type VARCHAR(50) NOT NULL COMMENT '操作类型(CREATE,UPDATE,SUBMIT,ADMIN_MODIFY,BATCH_PUBLISH,BATCH_CLOSE)', - operation_desc TEXT COMMENT '操作描述', - old_data TEXT COMMENT '修改前数据(JSON格式)', - new_data TEXT COMMENT '修改后数据(JSON格式)', - operator_id BIGINT COMMENT '操作人ID', - operator_name VARCHAR(100) COMMENT '操作人姓名', - operator_type VARCHAR(50) COMMENT '操作人类型(STUDENT,ADMIN,SYSTEM)', - ip_address VARCHAR(50) COMMENT 'IP地址', - user_agent TEXT COMMENT '用户代理', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - tenant_id VARCHAR(20) DEFAULT '000000' COMMENT '租户ID', - INDEX idx_batch_id (batch_id), - INDEX idx_student_id (student_id), - INDEX idx_operation_type (operation_type), - INDEX idx_create_time (create_time), - INDEX idx_operator_id (operator_id), - INDEX idx_tenant_id (tenant_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='信息采集批次操作日志表'; - --- 3. 创建批次权限配置表 -CREATE TABLE IF NOT EXISTS xg_collection_batch_permission ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - batch_id BIGINT NOT NULL COMMENT '批次ID', - permission_type VARCHAR(50) NOT NULL COMMENT '权限类型(EDIT,VIEW,SUBMIT,ADMIN)', - role_id BIGINT COMMENT '角色ID', - user_id BIGINT COMMENT '用户ID', - college_id BIGINT COMMENT '学院ID', - is_granted TINYINT DEFAULT 1 COMMENT '是否授权', - grant_time DATETIME COMMENT '授权时间', - expire_time DATETIME COMMENT '过期时间', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - create_by BIGINT COMMENT '创建者', - update_by BIGINT COMMENT '更新者', - deleted TINYINT DEFAULT 0 COMMENT '删除标志', - tenant_id VARCHAR(20) DEFAULT '000000' COMMENT '租户ID', - INDEX idx_batch_id (batch_id), - INDEX idx_permission_type (permission_type), - INDEX idx_role_id (role_id), - INDEX idx_user_id (user_id), - INDEX idx_college_id (college_id), - INDEX idx_tenant_id (tenant_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='信息采集批次权限配置表'; - --- 4. 创建批次统计表 -CREATE TABLE IF NOT EXISTS xg_collection_batch_statistics ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - batch_id BIGINT NOT NULL COMMENT '批次ID', - total_students INT DEFAULT 0 COMMENT '总学生数', - submitted_students INT DEFAULT 0 COMMENT '已提交学生数', - pending_students INT DEFAULT 0 COMMENT '待提交学生数', - completion_rate DECIMAL(5,2) DEFAULT 0.00 COMMENT '完成率', - college_id BIGINT COMMENT '学院ID(按学院统计时使用)', - college_name VARCHAR(100) COMMENT '学院名称', - major_id BIGINT COMMENT '专业ID(按专业统计时使用)', - major_name VARCHAR(100) COMMENT '专业名称', - class_id BIGINT COMMENT '班级ID(按班级统计时使用)', - class_name VARCHAR(100) COMMENT '班级名称', - grade VARCHAR(50) COMMENT '届别', - statistics_type VARCHAR(50) DEFAULT 'TOTAL' COMMENT '统计类型(TOTAL,COLLEGE,MAJOR,CLASS,GRADE)', - last_updated DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - tenant_id VARCHAR(20) DEFAULT '000000' COMMENT '租户ID', - INDEX idx_batch_id (batch_id), - INDEX idx_college_id (college_id), - INDEX idx_major_id (major_id), - INDEX idx_class_id (class_id), - INDEX idx_grade (grade), - INDEX idx_statistics_type (statistics_type), - INDEX idx_tenant_id (tenant_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='信息采集批次统计表'; - --- 5. 创建批次定时任务表 -CREATE TABLE IF NOT EXISTS xg_batch_scheduled_task ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - batch_id BIGINT NOT NULL COMMENT '批次ID', - task_type VARCHAR(50) NOT NULL COMMENT '任务类型(REMIND_START,REMIND_END,AUTO_CLOSE)', - task_time DATETIME NOT NULL COMMENT '执行时间', - task_status VARCHAR(20) DEFAULT 'PENDING' COMMENT '任务状态(PENDING,EXECUTED,FAILED)', - execute_time DATETIME COMMENT '实际执行时间', - error_message TEXT COMMENT '错误信息', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - tenant_id VARCHAR(20) DEFAULT '000000' COMMENT '租户ID', - INDEX idx_batch_id (batch_id), - INDEX idx_task_type (task_type), - INDEX idx_task_time (task_time), - INDEX idx_task_status (task_status), - INDEX idx_tenant_id (tenant_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='信息采集批次定时任务表'; - --- 6. 插入一些测试数据 -INSERT INTO xg_collection_batch (id, tenant_id, batch_code, batch_name, batch_type, academic_year, semester, batch_status, start_time, end_time, description, create_by, create_time, update_by, update_time, deleted) VALUES -(1745001000000000001, '000000', 'BATCH_2024_1', '2024-2025学年第一学期信息采集', 'annual', '2024-2025', '1', '2', '2024-09-01 00:00:00', '2024-09-30 23:59:59', '2024-2025学年第一学期学生信息采集工作', 1, NOW(), 1, NOW(), 0), -(1745001000000000002, '000000', 'BATCH_2024_2', '2024-2025学年第二学期信息采集', 'annual', '2024-2025', '2', '1', '2025-02-15 00:00:00', '2025-03-15 23:59:59', '2024-2025学年第二学期学生信息采集工作', 1, NOW(), 1, NOW(), 0) -ON DUPLICATE KEY UPDATE - batch_name = VALUES(batch_name), - description = VALUES(description), - update_time = NOW(); - -SELECT 'Database tables created successfully!' as message; diff --git a/backend/script/sql/cw-budget-approval-menu-fixed-path.sql b/backend/script/sql/cw-budget-approval-menu-fixed-path.sql deleted file mode 100644 index 3ccafba4..00000000 --- a/backend/script/sql/cw-budget-approval-menu-fixed-path.sql +++ /dev/null @@ -1,77 +0,0 @@ --- ========================================== --- 财务项目预算审批系统 - 菜单配置脚本(修复路径版) --- 版本: 1.1 --- 创建时间: 2025-08-01 --- 修复内容: 删除budgetApproval中间路径,使用简化的路径结构 --- 模块前缀: cw_ (财务模块) --- 菜单ID范围: 5000-5999 --- ========================================== - --- 1. 一级菜单:财务管理 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5000, '财务管理', 0, 10, 'finance', NULL, '', 1, 0, 'M', '0', '0', '', 'money', 103, 1, now(), NULL, NULL, '财务管理根菜单', '000000'); - --- 2. 二级菜单:项目预算审批 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5100, '项目预算审批', 5000, 1, 'budget-approval', NULL, '', 1, 0, 'M', '0', '0', '', 'file-text', 103, 1, now(), NULL, NULL, '项目预算审批管理', '000000'); - --- 3. 三级菜单:预算申报管理(修复路径) -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5110, '预算申报管理', 5100, 1, 'application', 'cw/application/index', '', 1, 0, 'C', '0', '0', 'cw:application:list', 'form', 103, 1, now(), NULL, NULL, '预算申报管理页面', '000000'); - --- 预算申报管理 - 功能按钮 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5111, '预算申报查询', 5110, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:query', '#', 103, 1, now(), NULL, NULL, '预算申报查询权限', '000000'), -(5112, '预算申报详情', 5110, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:detail', '#', 103, 1, now(), NULL, NULL, '预算申报详情权限', '000000'), -(5113, '预算申报新增', 5110, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:add', '#', 103, 1, now(), NULL, NULL, '预算申报新增权限', '000000'), -(5114, '预算申报修改', 5110, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:edit', '#', 103, 1, now(), NULL, NULL, '预算申报修改权限', '000000'), -(5115, '预算申报删除', 5110, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:remove', '#', 103, 1, now(), NULL, NULL, '预算申报删除权限', '000000'), -(5116, '预算申报导出', 5110, 6, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:export', '#', 103, 1, now(), NULL, NULL, '预算申报导出权限', '000000'), -(5117, '预算申报提交', 5110, 7, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:submit', '#', 103, 1, now(), NULL, NULL, '预算申报提交审批权限', '000000'); - --- 4. 三级菜单:审批人配置(修复路径) -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5120, '审批人配置', 5100, 2, 'approver-config', 'cw/approverConfig/index', '', 1, 0, 'C', '0', '0', 'cw:approverConfig:list', 'peoples', 103, 1, now(), NULL, NULL, '审批人配置页面', '000000'); - --- 审批人配置 - 功能按钮 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5121, '审批人查询', 5120, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:approverConfig:query', '#', 103, 1, now(), NULL, NULL, '审批人配置查询权限', '000000'), -(5122, '审批人详情', 5120, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:approverConfig:detail', '#', 103, 1, now(), NULL, NULL, '审批人配置详情权限', '000000'), -(5123, '审批人新增', 5120, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:approverConfig:add', '#', 103, 1, now(), NULL, NULL, '审批人配置新增权限', '000000'), -(5124, '审批人修改', 5120, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:approverConfig:edit', '#', 103, 1, now(), NULL, NULL, '审批人配置修改权限', '000000'), -(5125, '审批人删除', 5120, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:approverConfig:remove', '#', 103, 1, now(), NULL, NULL, '审批人配置删除权限', '000000'); - --- 5. 三级菜单:审批记录查询(修复路径) -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5130, '审批记录查询', 5100, 3, 'approval-record', 'cw/approvalRecord/index', '', 1, 0, 'C', '0', '0', 'cw:approvalRecord:list', 'documentation', 103, 1, now(), NULL, NULL, '审批记录查询页面', '000000'); - --- 审批记录查询 - 功能按钮 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5131, '审批记录查询', 5130, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:approvalRecord:query', '#', 103, 1, now(), NULL, NULL, '审批记录查询权限', '000000'), -(5132, '审批记录详情', 5130, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:approvalRecord:detail', '#', 103, 1, now(), NULL, NULL, '审批记录详情权限', '000000'); - --- 6. 三级菜单:支出明细管理(修复路径) -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5140, '支出明细管理', 5100, 4, 'expenditure-detail', 'cw/expenditureDetail/index', '', 1, 0, 'C', '0', '0', 'cw:expenditureDetail:list', 'money', 103, 1, now(), NULL, NULL, '支出明细管理页面', '000000'); - --- 支出明细管理 - 功能按钮 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5141, '支出明细查询', 5140, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:expenditureDetail:query', '#', 103, 1, now(), NULL, NULL, '支出明细查询权限', '000000'), -(5142, '支出明细详情', 5140, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:expenditureDetail:detail', '#', 103, 1, now(), NULL, NULL, '支出明细详情权限', '000000'), -(5143, '支出明细新增', 5140, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:expenditureDetail:add', '#', 103, 1, now(), NULL, NULL, '支出明细新增权限', '000000'), -(5144, '支出明细修改', 5140, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:expenditureDetail:edit', '#', 103, 1, now(), NULL, NULL, '支出明细修改权限', '000000'), -(5145, '支出明细删除', 5140, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:expenditureDetail:remove', '#', 103, 1, now(), NULL, NULL, '支出明细删除权限', '000000'); - --- 7. 三级菜单:绩效指标管理(修复路径) -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5150, '绩效指标管理', 5100, 5, 'performance-indicator', 'cw/performanceIndicator/index', '', 1, 0, 'C', '0', '0', 'cw:performanceIndicator:list', 'chart', 103, 1, now(), NULL, NULL, '绩效指标管理页面', '000000'); - --- 绩效指标管理 - 功能按钮 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5151, '绩效指标查询', 5150, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:performanceIndicator:query', '#', 103, 1, now(), NULL, NULL, '绩效指标查询权限', '000000'), -(5152, '绩效指标详情', 5150, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:performanceIndicator:detail', '#', 103, 1, now(), NULL, NULL, '绩效指标详情权限', '000000'), -(5153, '绩效指标新增', 5150, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:performanceIndicator:add', '#', 103, 1, now(), NULL, NULL, '绩效指标新增权限', '000000'), -(5154, '绩效指标修改', 5150, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:performanceIndicator:edit', '#', 103, 1, now(), NULL, NULL, '绩效指标修改权限', '000000'), -(5155, '绩效指标删除', 5150, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:performanceIndicator:remove', '#', 103, 1, now(), NULL, NULL, '绩效指标删除权限', '000000'); - -SELECT '财务项目预算审批系统菜单配置完成(修复路径版)!' AS result; diff --git a/backend/script/sql/cw-budget-approval-menu-fixed.sql b/backend/script/sql/cw-budget-approval-menu-fixed.sql deleted file mode 100644 index 894c68a6..00000000 --- a/backend/script/sql/cw-budget-approval-menu-fixed.sql +++ /dev/null @@ -1,99 +0,0 @@ --- ========================================== --- 财务项目预算审批系统 - 菜单配置脚本(修正版) --- 版本: 1.1 --- 创建时间: 2025-01-26 --- 模块前缀: cw_ (财务模块) --- 菜单ID范围: 5000-5999 --- ========================================== - --- 1. 一级菜单:财务管理 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES -(5000, '财务管理', 0, 10, 'finance', NULL, '', 1, 0, 'M', '0', '0', '', 'money', 103, 1, now(), NULL, NULL, '财务管理根菜单'); - --- 2. 二级菜单:项目预算审批 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES -(5100, '项目预算审批', 5000, 1, 'budget-approval', NULL, '', 1, 0, 'M', '0', '0', '', 'file-text', 103, 1, now(), NULL, NULL, '项目预算审批管理'); - --- 3. 三级菜单:预算申报管理 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES -(5110, '预算申报管理', 5100, 1, 'application', 'cw/budgetApproval/application/index', '', 1, 0, 'C', '0', '0', 'cw:application:list', 'form', 103, 1, now(), NULL, NULL, '预算申报管理页面'); - --- 预算申报管理 - 功能按钮 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES -(5111, '预算申报查询', 5110, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:query', '#', 103, 1, now(), NULL, NULL, '预算申报查询权限'), -(5112, '预算申报详情', 5110, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:detail', '#', 103, 1, now(), NULL, NULL, '预算申报详情权限'), -(5113, '预算申报新增', 5110, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:add', '#', 103, 1, now(), NULL, NULL, '预算申报新增权限'), -(5114, '预算申报修改', 5110, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:edit', '#', 103, 1, now(), NULL, NULL, '预算申报修改权限'), -(5115, '预算申报删除', 5110, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:remove', '#', 103, 1, now(), NULL, NULL, '预算申报删除权限'), -(5116, '预算申报导出', 5110, 6, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:export', '#', 103, 1, now(), NULL, NULL, '预算申报导出权限'), -(5117, '预算申报提交', 5110, 7, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:submit', '#', 103, 1, now(), NULL, NULL, '预算申报提交审批权限'); - --- 4. 三级菜单:审批人配置 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES -(5120, '审批人配置', 5100, 2, 'approver-config', 'cw/budgetApproval/approverConfig/index', '', 1, 0, 'C', '0', '0', 'cw:approverConfig:list', 'peoples', 103, 1, now(), NULL, NULL, '审批人配置页面'); - --- 审批人配置 - 功能按钮 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES -(5121, '审批人查询', 5120, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:approverConfig:query', '#', 103, 1, now(), NULL, NULL, '审批人配置查询权限'), -(5122, '审批人详情', 5120, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:approverConfig:detail', '#', 103, 1, now(), NULL, NULL, '审批人配置详情权限'), -(5123, '审批人新增', 5120, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:approverConfig:add', '#', 103, 1, now(), NULL, NULL, '审批人配置新增权限'), -(5124, '审批人修改', 5120, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:approverConfig:edit', '#', 103, 1, now(), NULL, NULL, '审批人配置修改权限'), -(5125, '审批人删除', 5120, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:approverConfig:remove', '#', 103, 1, now(), NULL, NULL, '审批人配置删除权限'); - --- 5. 三级菜单:审批记录查询 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES -(5130, '审批记录查询', 5100, 3, 'approval-record', 'cw/budgetApproval/approvalRecord/index', '', 1, 0, 'C', '0', '0', 'cw:approvalRecord:list', 'documentation', 103, 1, now(), NULL, NULL, '审批记录查询页面'); - --- 审批记录查询 - 功能按钮 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES -(5131, '审批记录查询', 5130, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:approvalRecord:query', '#', 103, 1, now(), NULL, NULL, '审批记录查询权限'), -(5132, '审批记录详情', 5130, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:approvalRecord:detail', '#', 103, 1, now(), NULL, NULL, '审批记录详情权限'), -(5133, '审批操作', 5130, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:approvalRecord:approve', '#', 103, 1, now(), NULL, NULL, '审批操作权限'), -(5134, '退回操作', 5130, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:approvalRecord:return', '#', 103, 1, now(), NULL, NULL, '退回操作权限'), -(5135, '审批记录导出', 5130, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:approvalRecord:export', '#', 103, 1, now(), NULL, NULL, '审批记录导出权限'); - --- 6. 三级菜单:支出明细管理 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES -(5140, '支出明细管理', 5100, 4, 'expenditure-detail', 'cw/budgetApproval/expenditureDetail/index', '', 1, 0, 'C', '0', '0', 'cw:expenditureDetail:list', 'money', 103, 1, now(), NULL, NULL, '支出明细管理页面'); - --- 支出明细管理 - 功能按钮 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES -(5141, '支出明细查询', 5140, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:expenditureDetail:query', '#', 103, 1, now(), NULL, NULL, '支出明细查询权限'), -(5142, '支出明细详情', 5140, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:expenditureDetail:detail', '#', 103, 1, now(), NULL, NULL, '支出明细详情权限'), -(5143, '支出明细新增', 5140, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:expenditureDetail:add', '#', 103, 1, now(), NULL, NULL, '支出明细新增权限'), -(5144, '支出明细修改', 5140, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:expenditureDetail:edit', '#', 103, 1, now(), NULL, NULL, '支出明细修改权限'), -(5145, '支出明细删除', 5140, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:expenditureDetail:remove', '#', 103, 1, now(), NULL, NULL, '支出明细删除权限'); - --- 7. 三级菜单:绩效指标管理 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES -(5150, '绩效指标管理', 5100, 5, 'performance-indicator', 'cw/budgetApproval/performanceIndicator/index', '', 1, 0, 'C', '0', '0', 'cw:performanceIndicator:list', 'chart', 103, 1, now(), NULL, NULL, '绩效指标管理页面'); - --- 绩效指标管理 - 功能按钮 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES -(5151, '绩效指标查询', 5150, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:performanceIndicator:query', '#', 103, 1, now(), NULL, NULL, '绩效指标查询权限'), -(5152, '绩效指标详情', 5150, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:performanceIndicator:detail', '#', 103, 1, now(), NULL, NULL, '绩效指标详情权限'), -(5153, '绩效指标新增', 5150, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:performanceIndicator:add', '#', 103, 1, now(), NULL, NULL, '绩效指标新增权限'), -(5154, '绩效指标修改', 5150, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:performanceIndicator:edit', '#', 103, 1, now(), NULL, NULL, '绩效指标修改权限'), -(5155, '绩效指标删除', 5150, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:performanceIndicator:remove', '#', 103, 1, now(), NULL, NULL, '绩效指标删除权限'); - --- 8. 三级菜单:附件管理 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES -(5160, '附件管理', 5100, 6, 'attachment', 'cw/budgetApproval/attachment/index', '', 1, 0, 'C', '0', '0', 'cw:attachment:list', 'upload', 103, 1, now(), NULL, NULL, '附件管理页面'); - --- 附件管理 - 功能按钮 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES -(5161, '附件查询', 5160, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:attachment:query', '#', 103, 1, now(), NULL, NULL, '附件查询权限'), -(5162, '附件详情', 5160, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:attachment:detail', '#', 103, 1, now(), NULL, NULL, '附件详情权限'), -(5163, '附件上传', 5160, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:attachment:upload', '#', 103, 1, now(), NULL, NULL, '附件上传权限'), -(5164, '附件下载', 5160, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:attachment:download', '#', 103, 1, now(), NULL, NULL, '附件下载权限'), -(5165, '附件删除', 5160, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:attachment:remove', '#', 103, 1, now(), NULL, NULL, '附件删除权限'); - --- 菜单创建完成,显示创建结果 -SELECT '财务项目预算审批系统菜单配置完成!' AS result; - --- 验证菜单创建情况 -SELECT '=== 菜单创建验证 ===' as verification; -SELECT menu_id, menu_name, menu_type, parent_id, order_num, perms -FROM sys_menu -WHERE menu_id BETWEEN 5000 AND 5999 -ORDER BY menu_id; diff --git a/backend/script/sql/cw-budget-approval-menu.sql b/backend/script/sql/cw-budget-approval-menu.sql deleted file mode 100644 index d67c2715..00000000 --- a/backend/script/sql/cw-budget-approval-menu.sql +++ /dev/null @@ -1,92 +0,0 @@ --- ========================================== --- 财务项目预算审批系统 - 菜单配置脚本 --- 版本: 1.0 --- 创建时间: 2025-01-26 --- 模块前缀: cw_ (财务模块) --- 菜单ID范围: 5000-5999 --- ========================================== - --- 1. 一级菜单:财务管理 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5000, '财务管理', 0, 10, 'finance', NULL, '', 1, 0, 'M', '0', '0', '', 'money', 103, 1, now(), NULL, NULL, '财务管理根菜单', '000000'); - --- 2. 二级菜单:项目预算审批 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5100, '项目预算审批', 5000, 1, 'budget-approval', NULL, '', 1, 0, 'M', '0', '0', '', 'file-text', 103, 1, now(), NULL, NULL, '项目预算审批管理', '000000'); - --- 3. 三级菜单:预算申报管理 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5110, '预算申报管理', 5100, 1, 'application', 'cw/budgetApproval/application/index', '', 1, 0, 'C', '0', '0', 'cw:application:list', 'form', 103, 1, now(), NULL, NULL, '预算申报管理页面', '000000'); - --- 预算申报管理 - 功能按钮 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5111, '预算申报查询', 5110, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:query', '#', 103, 1, now(), NULL, NULL, '预算申报查询权限', '000000'), -(5112, '预算申报详情', 5110, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:detail', '#', 103, 1, now(), NULL, NULL, '预算申报详情权限', '000000'), -(5113, '预算申报新增', 5110, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:add', '#', 103, 1, now(), NULL, NULL, '预算申报新增权限', '000000'), -(5114, '预算申报修改', 5110, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:edit', '#', 103, 1, now(), NULL, NULL, '预算申报修改权限', '000000'), -(5115, '预算申报删除', 5110, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:remove', '#', 103, 1, now(), NULL, NULL, '预算申报删除权限', '000000'), -(5116, '预算申报导出', 5110, 6, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:export', '#', 103, 1, now(), NULL, NULL, '预算申报导出权限', '000000'), -(5117, '预算申报提交', 5110, 7, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:submit', '#', 103, 1, now(), NULL, NULL, '预算申报提交审批权限', '000000'); - --- 4. 三级菜单:审批人配置 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5120, '审批人配置', 5100, 2, 'approver-config', 'cw/budgetApproval/approverConfig/index', '', 1, 0, 'C', '0', '0', 'cw:approverConfig:list', 'peoples', 103, 1, now(), NULL, NULL, '审批人配置页面', '000000'); - --- 审批人配置 - 功能按钮 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5121, '审批人查询', 5120, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:approverConfig:query', '#', 103, 1, now(), NULL, NULL, '审批人配置查询权限', '000000'), -(5122, '审批人详情', 5120, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:approverConfig:detail', '#', 103, 1, now(), NULL, NULL, '审批人配置详情权限', '000000'), -(5123, '审批人新增', 5120, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:approverConfig:add', '#', 103, 1, now(), NULL, NULL, '审批人配置新增权限', '000000'), -(5124, '审批人修改', 5120, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:approverConfig:edit', '#', 103, 1, now(), NULL, NULL, '审批人配置修改权限', '000000'), -(5125, '审批人删除', 5120, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:approverConfig:remove', '#', 103, 1, now(), NULL, NULL, '审批人配置删除权限', '000000'); - --- 5. 三级菜单:审批记录查询 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5130, '审批记录查询', 5100, 3, 'approval-record', 'cw/budgetApproval/approvalRecord/index', '', 1, 0, 'C', '0', '0', 'cw:approvalRecord:list', 'documentation', 103, 1, now(), NULL, NULL, '审批记录查询页面', '000000'); - --- 审批记录查询 - 功能按钮 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5131, '审批记录查询', 5130, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:approvalRecord:query', '#', 103, 1, now(), NULL, NULL, '审批记录查询权限', '000000'), -(5132, '审批记录详情', 5130, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:approvalRecord:detail', '#', 103, 1, now(), NULL, NULL, '审批记录详情权限', '000000'), -(5133, '审批操作', 5130, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:approvalRecord:approve', '#', 103, 1, now(), NULL, NULL, '审批操作权限', '000000'), -(5134, '退回操作', 5130, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:approvalRecord:return', '#', 103, 1, now(), NULL, NULL, '退回操作权限', '000000'), -(5135, '审批记录导出', 5130, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:approvalRecord:export', '#', 103, 1, now(), NULL, NULL, '审批记录导出权限', '000000'); - --- 6. 三级菜单:支出明细管理 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5140, '支出明细管理', 5100, 4, 'expenditure-detail', 'cw/budgetApproval/expenditureDetail/index', '', 1, 0, 'C', '0', '0', 'cw:expenditureDetail:list', 'money', 103, 1, now(), NULL, NULL, '支出明细管理页面', '000000'); - --- 支出明细管理 - 功能按钮 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5141, '支出明细查询', 5140, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:expenditureDetail:query', '#', 103, 1, now(), NULL, NULL, '支出明细查询权限', '000000'), -(5142, '支出明细详情', 5140, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:expenditureDetail:detail', '#', 103, 1, now(), NULL, NULL, '支出明细详情权限', '000000'), -(5143, '支出明细新增', 5140, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:expenditureDetail:add', '#', 103, 1, now(), NULL, NULL, '支出明细新增权限', '000000'), -(5144, '支出明细修改', 5140, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:expenditureDetail:edit', '#', 103, 1, now(), NULL, NULL, '支出明细修改权限', '000000'), -(5145, '支出明细删除', 5140, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:expenditureDetail:remove', '#', 103, 1, now(), NULL, NULL, '支出明细删除权限', '000000'); - --- 7. 三级菜单:绩效指标管理 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5150, '绩效指标管理', 5100, 5, 'performance-indicator', 'cw/budgetApproval/performanceIndicator/index', '', 1, 0, 'C', '0', '0', 'cw:performanceIndicator:list', 'chart', 103, 1, now(), NULL, NULL, '绩效指标管理页面', '000000'); - --- 绩效指标管理 - 功能按钮 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5151, '绩效指标查询', 5150, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:performanceIndicator:query', '#', 103, 1, now(), NULL, NULL, '绩效指标查询权限', '000000'), -(5152, '绩效指标详情', 5150, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:performanceIndicator:detail', '#', 103, 1, now(), NULL, NULL, '绩效指标详情权限', '000000'), -(5153, '绩效指标新增', 5150, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:performanceIndicator:add', '#', 103, 1, now(), NULL, NULL, '绩效指标新增权限', '000000'), -(5154, '绩效指标修改', 5150, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:performanceIndicator:edit', '#', 103, 1, now(), NULL, NULL, '绩效指标修改权限', '000000'), -(5155, '绩效指标删除', 5150, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:performanceIndicator:remove', '#', 103, 1, now(), NULL, NULL, '绩效指标删除权限', '000000'); - --- 8. 三级菜单:附件管理 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5160, '附件管理', 5100, 6, 'attachment', 'cw/budgetApproval/attachment/index', '', 1, 0, 'C', '0', '0', 'cw:attachment:list', 'upload', 103, 1, now(), NULL, NULL, '附件管理页面', '000000'); - --- 附件管理 - 功能按钮 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES -(5161, '附件查询', 5160, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:attachment:query', '#', 103, 1, now(), NULL, NULL, '附件查询权限', '000000'), -(5162, '附件详情', 5160, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:attachment:detail', '#', 103, 1, now(), NULL, NULL, '附件详情权限', '000000'), -(5163, '附件上传', 5160, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:attachment:upload', '#', 103, 1, now(), NULL, NULL, '附件上传权限', '000000'), -(5164, '附件下载', 5160, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:attachment:download', '#', 103, 1, now(), NULL, NULL, '附件下载权限', '000000'), -(5165, '附件删除', 5160, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:attachment:remove', '#', 103, 1, now(), NULL, NULL, '附件删除权限', '000000'); - --- 菜单创建完成,显示创建结果 -SELECT '财务项目预算审批系统菜单配置完成!' AS result; diff --git a/backend/script/sql/fix-college-vs-collage-fields.sql b/backend/script/sql/fix-college-vs-collage-fields.sql deleted file mode 100644 index 8b59a8f3..00000000 --- a/backend/script/sql/fix-college-vs-collage-fields.sql +++ /dev/null @@ -1,100 +0,0 @@ --- 修复学院字段拼写问题 --- 版本: 1.0.0 --- 创建日期: 2025-07-13 --- 描述: 检查并修复学院字段的拼写问题(college vs collage) - --- 第一步:检查字段情况 -SELECT - COUNT(*) as college_name_count, - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_NAME = 'zsjy_enrollment_info' - AND COLUMN_NAME = 'collage_name' - AND TABLE_SCHEMA = DATABASE()) as collage_name_count, - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_NAME = 'zsjy_enrollment_info' - AND COLUMN_NAME = 'college_id' - AND TABLE_SCHEMA = DATABASE()) as college_id_count, - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_NAME = 'zsjy_enrollment_info' - AND COLUMN_NAME = 'collage_id' - AND TABLE_SCHEMA = DATABASE()) as collage_id_count -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_NAME = 'zsjy_enrollment_info' -AND COLUMN_NAME = 'college_name' -AND TABLE_SCHEMA = DATABASE(); - --- 第二步:如果存在collage_name而不存在college_name,则重命名字段 -SET @rename_collage_name_sql = ( - SELECT IF( - EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_NAME = 'zsjy_enrollment_info' - AND COLUMN_NAME = 'collage_name' - AND TABLE_SCHEMA = DATABASE()) - AND - NOT EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_NAME = 'zsjy_enrollment_info' - AND COLUMN_NAME = 'college_name' - AND TABLE_SCHEMA = DATABASE()), - 'ALTER TABLE zsjy_enrollment_info CHANGE COLUMN collage_name college_name varchar(100) COMMENT \'学院名称\'', - 'SELECT \'无需修改collage_name字段\' AS message' - ) -); - -PREPARE stmt FROM @rename_collage_name_sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 第三步:如果存在collage_id而不存在college_id,则重命名字段 -SET @rename_collage_id_sql = ( - SELECT IF( - EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_NAME = 'zsjy_enrollment_info' - AND COLUMN_NAME = 'collage_id' - AND TABLE_SCHEMA = DATABASE()) - AND - NOT EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_NAME = 'zsjy_enrollment_info' - AND COLUMN_NAME = 'college_id' - AND TABLE_SCHEMA = DATABASE()), - 'ALTER TABLE zsjy_enrollment_info CHANGE COLUMN collage_id college_id varchar(20) COMMENT \'学院代码\'', - 'SELECT \'无需修改collage_id字段\' AS message' - ) -); - -PREPARE stmt FROM @rename_collage_id_sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 第四步:如果不存在college_name,则添加该字段 -SET @add_college_name_sql = ( - SELECT IF( - NOT EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_NAME = 'zsjy_enrollment_info' - AND COLUMN_NAME = 'college_name' - AND TABLE_SCHEMA = DATABASE()), - 'ALTER TABLE zsjy_enrollment_info ADD COLUMN college_name varchar(100) COMMENT \'学院名称\'', - 'SELECT \'college_name字段已存在\' AS message' - ) -); - -PREPARE stmt FROM @add_college_name_sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 第五步:如果不存在college_id,则添加该字段 -SET @add_college_id_sql = ( - SELECT IF( - NOT EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_NAME = 'zsjy_enrollment_info' - AND COLUMN_NAME = 'college_id' - AND TABLE_SCHEMA = DATABASE()), - 'ALTER TABLE zsjy_enrollment_info ADD COLUMN college_id varchar(20) COMMENT \'学院代码\'', - 'SELECT \'college_id字段已存在\' AS message' - ) -); - -PREPARE stmt FROM @add_college_id_sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - -SELECT '学院字段拼写修复完成!' AS result; diff --git a/backend/script/sql/fix-enrollment-college-fields.sql b/backend/script/sql/fix-enrollment-college-fields.sql deleted file mode 100644 index b58b7935..00000000 --- a/backend/script/sql/fix-enrollment-college-fields.sql +++ /dev/null @@ -1,78 +0,0 @@ --- 修复招生报名信息表中的学院相关字段 --- 版本: 1.0.0 --- 创建日期: 2025-07-25 --- 描述: 检查并添加学院名称和学院代码字段 - --- 检查college_name字段是否存在,如果不存在则添加 -SET @check_college_name = ( - SELECT COUNT(*) - FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_NAME = 'zsjy_enrollment_info' - AND COLUMN_NAME = 'college_name' - AND TABLE_SCHEMA = DATABASE() -); - -SET @alter_college_name = IF(@check_college_name = 0, - 'ALTER TABLE zsjy_enrollment_info ADD COLUMN college_name varchar(100) COMMENT \'学院名称\'', - 'SELECT \'college_name字段已存在,无需添加\' AS message'); - -PREPARE stmt FROM @alter_college_name; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 检查college_id字段是否存在,如果不存在则添加 -SET @check_college_id = ( - SELECT COUNT(*) - FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_NAME = 'zsjy_enrollment_info' - AND COLUMN_NAME = 'college_id' - AND TABLE_SCHEMA = DATABASE() -); - -SET @alter_college_id = IF(@check_college_id = 0, - 'ALTER TABLE zsjy_enrollment_info ADD COLUMN college_id varchar(20) COMMENT \'学院代码\'', - 'SELECT \'college_id字段已存在,无需添加\' AS message'); - -PREPARE stmt FROM @alter_college_id; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 检查数据字典是否存在 -SET @dict_exists = ( - SELECT COUNT(*) - FROM sys_dict_type - WHERE dict_type = 'zsjy_college' -); - --- 如果不存在则添加字典类型 -SET @insert_dict_type = IF(@dict_exists = 0, - 'INSERT INTO sys_dict_type(dict_name, dict_type, status, create_by, create_time, update_by, update_time, remark) VALUES (\'学院列表\', \'zsjy_college\', \'0\', \'1\', NOW(), \'1\', NOW(), \'招生系统学院列表\')', - 'SELECT \'zsjy_college字典类型已存在,无需添加\' AS message'); - -PREPARE stmt FROM @insert_dict_type; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 检查字典数据是否存在 -SET @dict_data_exists = ( - SELECT COUNT(*) - FROM sys_dict_data - WHERE dict_type = 'zsjy_college' -); - --- 如果不存在则添加示例字典数据 -SET @insert_dict_data = IF(@dict_data_exists = 0, - 'INSERT INTO sys_dict_data(dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, update_by, update_time, remark) - VALUES - (1, \'机电工程学院\', \'JDGC\', \'zsjy_college\', null, \'default\', \'Y\', \'0\', \'1\', NOW(), \'1\', NOW(), null), - (2, \'信息工程学院\', \'XXGC\', \'zsjy_college\', null, \'default\', \'N\', \'0\', \'1\', NOW(), \'1\', NOW(), null), - (3, \'艺术设计学院\', \'YSSJ\', \'zsjy_college\', null, \'default\', \'N\', \'0\', \'1\', NOW(), \'1\', NOW(), null), - (4, \'经济管理学院\', \'JJGL\', \'zsjy_college\', null, \'default\', \'N\', \'0\', \'1\', NOW(), \'1\', NOW(), null), - (5, \'医药健康学院\', \'YYJK\', \'zsjy_college\', null, \'default\', \'N\', \'0\', \'1\', NOW(), \'1\', NOW(), null)', - 'SELECT \'zsjy_college字典数据已存在,无需添加\' AS message'); - -PREPARE stmt FROM @insert_dict_data; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - -SELECT '招生报名信息表字段修复完成!' AS result; diff --git a/backend/script/sql/fix-enrollment-dict-data.sql b/backend/script/sql/fix-enrollment-dict-data.sql deleted file mode 100644 index 7a4fd88b..00000000 --- a/backend/script/sql/fix-enrollment-dict-data.sql +++ /dev/null @@ -1,56 +0,0 @@ --- ======================================== --- 修复招生就业模块字典数据脚本 --- 文件:fix-enrollment-dict-data.sql --- 创建时间:2025-07-03 --- 说明:修复性别显示和校服尺码问题 --- ======================================== - --- ---------------------------- --- 1、更新字典标签值 --- ---------------------------- - --- 修复性别字典数据 -UPDATE sys_dict_data -SET dict_label = '男', remark = '男性' -WHERE dict_type = 'zsjy_gender' AND dict_value = 'M'; - -UPDATE sys_dict_data -SET dict_label = '女', remark = '女性' -WHERE dict_type = 'zsjy_gender' AND dict_value = 'F'; - --- 确保校服尺码字典数据存在并可用 --- 如果不存在则插入 -INSERT INTO sys_dict_data (dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) -SELECT 480, '000000', 1, 'XS', 'XS', 'zsjy_uniform_size', '', 'default', 'N', '0', 1, NOW(), '加小号' -FROM dual -WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'zsjy_uniform_size' AND dict_value = 'XS'); - -INSERT INTO sys_dict_data (dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) -SELECT 481, '000000', 2, 'S', 'S', 'zsjy_uniform_size', '', 'default', 'N', '0', 1, NOW(), '小号' -FROM dual -WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'zsjy_uniform_size' AND dict_value = 'S'); - -INSERT INTO sys_dict_data (dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) -SELECT 482, '000000', 3, 'M', 'M', 'zsjy_uniform_size', '', 'default', 'Y', '0', 1, NOW(), '中号' -FROM dual -WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'zsjy_uniform_size' AND dict_value = 'M'); - -INSERT INTO sys_dict_data (dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) -SELECT 483, '000000', 4, 'L', 'L', 'zsjy_uniform_size', '', 'default', 'N', '0', 1, NOW(), '大号' -FROM dual -WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'zsjy_uniform_size' AND dict_value = 'L'); - -INSERT INTO sys_dict_data (dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) -SELECT 484, '000000', 5, 'XL', 'XL', 'zsjy_uniform_size', '', 'default', 'N', '0', 1, NOW(), '加大号' -FROM dual -WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'zsjy_uniform_size' AND dict_value = 'XL'); - -INSERT INTO sys_dict_data (dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) -SELECT 485, '000000', 6, 'XXL', 'XXL', 'zsjy_uniform_size', '', 'default', 'N', '0', 1, NOW(), '加加大号' -FROM dual -WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'zsjy_uniform_size' AND dict_value = 'XXL'); - -INSERT INTO sys_dict_data (dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) -SELECT 486, '000000', 7, 'XXXL', 'XXXL', 'zsjy_uniform_size', '', 'default', 'N', '0', 1, NOW(), '加加加大号' -FROM dual -WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'zsjy_uniform_size' AND dict_value = 'XXXL'); diff --git a/backend/script/sql/fix-zsjy-graduate-info-create-dept-field.sql b/backend/script/sql/fix-zsjy-graduate-info-create-dept-field.sql deleted file mode 100644 index 10b75674..00000000 --- a/backend/script/sql/fix-zsjy-graduate-info-create-dept-field.sql +++ /dev/null @@ -1,12 +0,0 @@ --- 修复毕业生信息表缺失的create_dept字段 --- 执行时间: 2025-01-26 24:25:00 --- 问题: MyBatis-Plus插入数据时报错 Unknown column 'create_dept' in 'field list' --- 原因: 数据库表缺少BaseEntity中的create_dept字段 - --- 添加create_dept字段 -ALTER TABLE zsjy_graduate_info -ADD COLUMN create_dept bigint(20) DEFAULT NULL COMMENT '创建部门' -AFTER remark; - --- 验证字段添加成功 --- DESCRIBE zsjy_graduate_info; \ No newline at end of file diff --git a/backend/script/sql/fix_all_graduate_dicts.sql b/backend/script/sql/fix_all_graduate_dicts.sql deleted file mode 100644 index 32b82ffe..00000000 --- a/backend/script/sql/fix_all_graduate_dicts.sql +++ /dev/null @@ -1,85 +0,0 @@ --- ================================================== --- 修复所有毕业生相关字典数据 - 解决下拉框为空问题 --- 创建日期: 2025-01-26 --- 说明: 修复所有使用过大dict_code值的毕业生相关字典数据 --- ================================================== - --- 1. 查看所有需要修复的字典类型 -SELECT '=== 需要修复的字典类型统计 ===' as info; -SELECT dict_type, COUNT(*) as count, MIN(dict_code) as min_code, MAX(dict_code) as max_code -FROM sys_dict_data -WHERE dict_type IN ( - 'zsjy_graduate_employment_status', - 'zsjy_graduate_company_type', - 'zsjy_graduate_salary_range', - 'zsjy_graduate_audit_status' -) -GROUP BY dict_type; - --- 2. 修复毕业生就业状态字典 -DELETE FROM sys_dict_data WHERE dict_type = 'zsjy_graduate_employment_status'; -INSERT INTO sys_dict_data ( - dict_code, tenant_id, dict_sort, dict_label, dict_value, - dict_type, css_class, list_class, is_default, - create_dept, create_by, create_time, update_by, update_time, remark -) VALUES -(220, '000000', 1, '待就业', '1', 'zsjy_graduate_employment_status', NULL, 'info', 'N', NULL, 103, NOW(), 103, NOW(), '未确定就业状态'), -(221, '000000', 2, '已签约', '2', 'zsjy_graduate_employment_status', NULL, 'success', 'N', NULL, 103, NOW(), 103, NOW(), '已签署就业协议'), -(222, '000000', 3, '升学', '3', 'zsjy_graduate_employment_status', NULL, 'primary', 'N', NULL, 103, NOW(), 103, NOW(), '继续深造学习'), -(223, '000000', 4, '出国', '4', 'zsjy_graduate_employment_status', NULL, 'warning', 'N', NULL, 103, NOW(), 103, NOW(), '出国留学或工作'), -(224, '000000', 5, '自主创业', '5', 'zsjy_graduate_employment_status', NULL, 'danger', 'N', NULL, 103, NOW(), 103, NOW(), '自主创业'), -(225, '000000', 6, '灵活就业', '6', 'zsjy_graduate_employment_status', NULL, 'info', 'N', NULL, 103, NOW(), 103, NOW(), '灵活就业'), -(226, '000000', 7, '暂不就业', '7', 'zsjy_graduate_employment_status', NULL, 'info', 'N', NULL, 103, NOW(), 103, NOW(), '暂不就业'), -(227, '000000', 8, '其他', '8', 'zsjy_graduate_employment_status', NULL, 'info', 'N', NULL, 103, NOW(), 103, NOW(), '其他情况'); - --- 3. 修复就业单位性质字典 -DELETE FROM sys_dict_data WHERE dict_type = 'zsjy_graduate_company_type'; -INSERT INTO sys_dict_data ( - dict_code, tenant_id, dict_sort, dict_label, dict_value, - dict_type, css_class, list_class, is_default, - create_dept, create_by, create_time, update_by, update_time, remark -) VALUES -(230, '000000', 1, '国有企业', '1', 'zsjy_graduate_company_type', NULL, 'primary', 'N', NULL, 103, NOW(), 103, NOW(), '国有企业'), -(231, '000000', 2, '私营企业', '2', 'zsjy_graduate_company_type', NULL, 'success', 'N', NULL, 103, NOW(), 103, NOW(), '私营企业'), -(232, '000000', 3, '外资企业', '3', 'zsjy_graduate_company_type', NULL, 'warning', 'N', NULL, 103, NOW(), 103, NOW(), '外资企业'), -(233, '000000', 4, '事业单位', '4', 'zsjy_graduate_company_type', NULL, 'info', 'N', NULL, 103, NOW(), 103, NOW(), '事业单位'), -(234, '000000', 5, '政府机关', '5', 'zsjy_graduate_company_type', NULL, 'danger', 'N', NULL, 103, NOW(), 103, NOW(), '政府机关'); - --- 4. 修复月薪范围字典 -DELETE FROM sys_dict_data WHERE dict_type = 'zsjy_graduate_salary_range'; -INSERT INTO sys_dict_data ( - dict_code, tenant_id, dict_sort, dict_label, dict_value, - dict_type, css_class, list_class, is_default, - create_dept, create_by, create_time, update_by, update_time, remark -) VALUES -(240, '000000', 1, '3000以下', '1', 'zsjy_graduate_salary_range', NULL, 'info', 'N', NULL, 103, NOW(), 103, NOW(), '月薪3000元以下'), -(241, '000000', 2, '3000-5000', '2', 'zsjy_graduate_salary_range', NULL, 'primary', 'N', NULL, 103, NOW(), 103, NOW(), '月薪3000-5000元'), -(242, '000000', 3, '5000-8000', '3', 'zsjy_graduate_salary_range', NULL, 'success', 'N', NULL, 103, NOW(), 103, NOW(), '月薪5000-8000元'), -(243, '000000', 4, '8000-12000', '4', 'zsjy_graduate_salary_range', NULL, 'warning', 'N', NULL, 103, NOW(), 103, NOW(), '月薪8000-12000元'), -(244, '000000', 5, '12000以上', '5', 'zsjy_graduate_salary_range', NULL, 'danger', 'N', NULL, 103, NOW(), 103, NOW(), '月薪12000元以上'); - --- 5. 修复毕业生信息审核状态字典 -DELETE FROM sys_dict_data WHERE dict_type = 'zsjy_graduate_audit_status'; -INSERT INTO sys_dict_data ( - dict_code, tenant_id, dict_sort, dict_label, dict_value, - dict_type, css_class, list_class, is_default, - create_dept, create_by, create_time, update_by, update_time, remark -) VALUES -(250, '000000', 1, '待审核', '0', 'zsjy_graduate_audit_status', NULL, 'info', 'Y', NULL, 103, NOW(), 103, NOW(), '待审核状态'), -(251, '000000', 2, '审核通过', '1', 'zsjy_graduate_audit_status', NULL, 'success', 'N', NULL, 103, NOW(), 103, NOW(), '审核通过'), -(252, '000000', 3, '审核拒绝', '2', 'zsjy_graduate_audit_status', NULL, 'danger', 'N', NULL, 103, NOW(), 103, NOW(), '审核拒绝'); - --- 6. 验证修复后的数据 -SELECT '=== 修复后的字典数据验证 ===' as info; - -SELECT '--- 毕业生就业状态 ---' as info; -SELECT dict_code, dict_label, dict_value FROM sys_dict_data WHERE dict_type = 'zsjy_graduate_employment_status' ORDER BY dict_sort; - -SELECT '--- 就业单位性质 ---' as info; -SELECT dict_code, dict_label, dict_value FROM sys_dict_data WHERE dict_type = 'zsjy_graduate_company_type' ORDER BY dict_sort; - -SELECT '--- 月薪范围 ---' as info; -SELECT dict_code, dict_label, dict_value FROM sys_dict_data WHERE dict_type = 'zsjy_graduate_salary_range' ORDER BY dict_sort; - -SELECT '--- 审核状态 ---' as info; -SELECT dict_code, dict_label, dict_value FROM sys_dict_data WHERE dict_type = 'zsjy_graduate_audit_status' ORDER BY dict_sort; diff --git a/backend/script/sql/fix_education_level_dict.sql b/backend/script/sql/fix_education_level_dict.sql deleted file mode 100644 index 2d9228dc..00000000 --- a/backend/script/sql/fix_education_level_dict.sql +++ /dev/null @@ -1,39 +0,0 @@ --- ================================================== --- 修复学历层次字典数据 - 下拉框为空问题解决 --- 创建日期: 2025-01-26 --- 说明: 重新创建学历层次字典数据,使用正常范围的dict_code值 --- ================================================== - --- 1. 查看修复前的数据 -SELECT '=== 修复前的学历层次字典 ===' as info; -SELECT dict_code, dict_label, dict_value, dict_sort -FROM sys_dict_data -WHERE dict_type = 'zsjy_education_level' -ORDER BY dict_sort; - --- 2. 删除现有的有问题的字典数据 -DELETE FROM sys_dict_data WHERE dict_type = 'zsjy_education_level'; - --- 3. 重新插入学历层次字典数据,使用正常的dict_code值 -INSERT INTO sys_dict_data ( - dict_code, tenant_id, dict_sort, dict_label, dict_value, - dict_type, css_class, list_class, is_default, - create_dept, create_by, create_time, update_by, update_time, remark -) VALUES -(214, '000000', 1, '专科', '1', 'zsjy_education_level', NULL, 'primary', 'N', NULL, 103, NOW(), 103, NOW(), '专科学历'), -(215, '000000', 2, '本科', '2', 'zsjy_education_level', NULL, 'success', 'N', NULL, 103, NOW(), 103, NOW(), '本科学历'), -(216, '000000', 3, '硕士', '3', 'zsjy_education_level', NULL, 'info', 'N', NULL, 103, NOW(), 103, NOW(), '硕士研究生学历'), -(217, '000000', 4, '博士', '4', 'zsjy_education_level', NULL, 'warning', 'N', NULL, 103, NOW(), 103, NOW(), '博士研究生学历'); - --- 4. 验证修复后的数据 -SELECT '=== 修复后的学历层次字典 ===' as info; -SELECT dict_code, dict_label, dict_value, dict_sort -FROM sys_dict_data -WHERE dict_type = 'zsjy_education_level' -ORDER BY dict_sort; - --- 5. 验证字典类型是否存在 -SELECT '=== 验证字典类型配置 ===' as info; -SELECT dict_type, dict_name -FROM sys_dict_type -WHERE dict_type = 'zsjy_education_level'; diff --git a/backend/script/sql/fix_graduate_menu_structure.sql b/backend/script/sql/fix_graduate_menu_structure.sql deleted file mode 100644 index 6dfdfd40..00000000 --- a/backend/script/sql/fix_graduate_menu_structure.sql +++ /dev/null @@ -1,84 +0,0 @@ --- ================================================== --- 修复毕业生菜单结构 - 404错误解决方案 --- 创建日期: 2025-01-26 --- 说明: 将错误的C类型嵌套改为正确的M类型目录+C类型页面结构 --- ================================================== - --- 1. 查看修复前的结构 -SELECT '=== 修复前的菜单结构 ===' as info; -SELECT menu_id, parent_id, menu_name, path, component, menu_type, visible -FROM sys_menu -WHERE menu_id BETWEEN 8800 AND 8804 -ORDER BY menu_id; - --- 2. 删除现有的错误权限关联 -DELETE FROM sys_role_menu WHERE menu_id BETWEEN 8801 AND 8816; - --- 3. 重新创建毕业生管理目录菜单(M类型) -INSERT INTO sys_menu ( - menu_id, menu_name, parent_id, order_num, path, component, - query_param, is_frame, is_cache, menu_type, visible, status, - perms, icon, create_by, create_time, update_by, update_time, remark -) VALUES ( - 8800, '毕业生管理', 8000, 3, 'graduate', NULL, - NULL, 1, 0, 'M', '0', '0', - NULL, 'education', 103, NOW(), 103, NOW(), '毕业生信息管理目录' -); - --- 4. 修改现有菜单的parent_id和path,改为正确的目录结构 -UPDATE sys_menu SET - parent_id = 8800, - path = 'info', - order_num = 1 -WHERE menu_id = 8801; - -UPDATE sys_menu SET - parent_id = 8800, - path = 'info/add', - order_num = 2, - visible = '1' -WHERE menu_id = 8802; - -UPDATE sys_menu SET - parent_id = 8800, - path = 'info/edit/:id(\\d+)', - order_num = 3, - visible = '1' -WHERE menu_id = 8803; - -UPDATE sys_menu SET - parent_id = 8800, - path = 'statistics', - order_num = 4 -WHERE menu_id = 8804; - --- 5. 重新分配权限给超级管理员 -INSERT INTO sys_role_menu (role_id, menu_id) VALUES -(1, 8800), -- 毕业生管理目录菜单 -(1, 8801), -- 毕业生信息子菜单 -(1, 8802), -- 毕业生信息新增页面 -(1, 8803), -- 毕业生信息编辑页面 -(1, 8804), -- 毕业生统计分析页面 -(1, 8810), -- 毕业生信息查询权限 -(1, 8811), -- 毕业生信息新增权限 -(1, 8812), -- 毕业生信息修改权限 -(1, 8813), -- 毕业生信息删除权限 -(1, 8814), -- 毕业生信息导出权限 -(1, 8815), -- 毕业生信息审核权限 -(1, 8816); -- 毕业生统计分析权限 - --- 6. 验证修复后的结构 -SELECT '=== 修复后的菜单结构 ===' as info; -SELECT menu_id, parent_id, menu_name, path, component, menu_type, visible -FROM sys_menu -WHERE menu_id BETWEEN 8800 AND 8804 -ORDER BY menu_id; - --- 7. 验证权限分配结果 -SELECT '=== 超级管理员权限分配验证 ===' as info; -SELECT rm.role_id, r.role_name, rm.menu_id, m.menu_name, m.path -FROM sys_role_menu rm -JOIN sys_role r ON rm.role_id = r.role_id -JOIN sys_menu m ON rm.menu_id = m.menu_id -WHERE rm.role_id = 1 AND rm.menu_id BETWEEN 8800 AND 8816 -ORDER BY rm.menu_id; diff --git a/backend/script/sql/import-student-data.sh b/backend/script/sql/import-student-data.sh deleted file mode 100755 index a040ea32..00000000 --- a/backend/script/sql/import-student-data.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/bin/bash - -# ========================================== -# 学生数据导入脚本 -# 版本:V1.1.0 -# 功能:快速导入150条学生信息数据 -# 数据库:从项目配置文件自动获取连接信息 -# 作者:Cursor AI 辅助 -# 时间:2025-01-14 (V1.1.0 更新) -# ========================================== - -# 颜色定义 -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# 脚本信息 -echo -e "${BLUE}===========================================${NC}" -echo -e "${BLUE}🎓 学生信息数据导入脚本${NC}" -echo -e "${BLUE}===========================================${NC}" - -# 检查SQL文件是否存在 -if [ ! -f "complete-student-data.sql" ]; then - echo -e "${RED}❌ 错误:找不到 complete-student-data.sql 文件${NC}" - echo -e "${YELLOW}💡 请确保文件存在于当前目录${NC}" - exit 1 -fi - -echo -e "${GREEN}✅ 找到数据文件:complete-student-data.sql${NC}" - -# 从项目配置文件获取数据库连接参数 -echo -e "${BLUE}📋 从项目配置获取数据库连接信息...${NC}" - -# 数据库连接参数(从 application-dev.yml 配置文件获取) -DB_HOST="47.99.164.66" -DB_PORT="3306" -DB_NAME="hzdyjs_xg" -DB_USER="root" -DB_PASSWORD="hn02le.34lkdLKD" - -# 显示连接信息 -echo -e "${BLUE}===========================================${NC}" -echo -e "${YELLOW}📊 数据库连接信息:${NC}" -echo -e "主机: ${DB_HOST}" -echo -e "端口: ${DB_PORT}" -echo -e "数据库: ${DB_NAME}" -echo -e "用户: ${DB_USER}" -echo -e "密码: ${DB_PASSWORD:0:3}***${DB_PASSWORD: -3}" -echo -e "${BLUE}===========================================${NC}" - -# 简化确认流程 -read -p "确认导入数据?(y/N): " CONFIRM -if [ "$CONFIRM" != "y" ] && [ "$CONFIRM" != "Y" ]; then - echo -e "${YELLOW}❌ 操作已取消${NC}" - exit 0 -fi - -echo -e "${BLUE}🚀 开始导入数据...${NC}" - -# 执行SQL文件 -mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASSWORD" "$DB_NAME" < complete-student-data.sql - -# 检查执行结果 -if [ $? -eq 0 ]; then - echo -e "${GREEN}===========================================${NC}" - echo -e "${GREEN}🎉 数据导入成功!${NC}" - echo -e "${GREEN}===========================================${NC}" - echo -e "${GREEN}📊 导入内容:${NC}" - echo -e " - 3个班级信息" - echo -e " - 150条学生信息" - echo -e " - 150条班级学生关联关系" - echo -e "${GREEN}===========================================${NC}" - echo -e "${YELLOW}👥 班级分布:${NC}" - echo -e " - 计算机科学与技术2024级1班:50人(男28,女22)" - echo -e " - 电子信息工程2024级1班:50人(男32,女18)" - echo -e " - 工商管理2024级1班:50人(男20,女30)" - echo -e "${GREEN}===========================================${NC}" -else - echo -e "${RED}===========================================${NC}" - echo -e "${RED}❌ 数据导入失败!${NC}" - echo -e "${RED}===========================================${NC}" - echo -e "${YELLOW}💡 可能的原因:${NC}" - echo -e " - 数据库连接失败" - echo -e " - 数据表不存在" - echo -e " - 权限不足" - echo -e " - 数据冲突" - echo -e "${YELLOW}🔧 建议检查:${NC}" - echo -e " - 确认数据库连接信息" - echo -e " - 确认数据表已创建" - echo -e " - 确认用户权限" - exit 1 -fi - -echo -e "${BLUE}🎓 学生信息数据导入完成!${NC}" diff --git a/backend/script/sql/insert-zsjy-major-dict-data.sql b/backend/script/sql/insert-zsjy-major-dict-data.sql deleted file mode 100644 index 33050718..00000000 --- a/backend/script/sql/insert-zsjy-major-dict-data.sql +++ /dev/null @@ -1,27 +0,0 @@ --- 插入专业字典数据 -INSERT INTO sys_dict_data VALUES (400, '000000', 1, '电气自动化设备安装与维修', 'DQZDHSB', 'zsjy_major', '', 'primary', 'N', 103, 1, sysdate(), NULL, NULL, '电气自动化设备安装与维修专业'); -INSERT INTO sys_dict_data VALUES (401, '000000', 2, '工业机器人应用与维护', 'GYJQR', 'zsjy_major', '', 'success', 'N', 103, 1, sysdate(), NULL, NULL, '工业机器人应用与维护专业'); -INSERT INTO sys_dict_data VALUES (402, '000000', 3, '无人机应用技术', 'WRJYY', 'zsjy_major', '', 'warning', 'N', 103, 1, sysdate(), NULL, NULL, '无人机应用技术专业'); -INSERT INTO sys_dict_data VALUES (403, '000000', 4, '工业互联网与大数据应用', 'GYHLW', 'zsjy_major', '', 'info', 'N', 103, 1, sysdate(), NULL, NULL, '工业互联网与大数据应用专业'); -INSERT INTO sys_dict_data VALUES (404, '000000', 5, '数字化设计与制造', 'SZHSJ', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '数字化设计与制造专业'); -INSERT INTO sys_dict_data VALUES (405, '000000', 6, '智能制作技术应用', 'ZNZZ', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '智能制作技术应用专业'); -INSERT INTO sys_dict_data VALUES (406, '000000', 7, '汽车维修', 'QCWX', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '汽车维修专业'); -INSERT INTO sys_dict_data VALUES (407, '000000', 8, '产品检测与质量控制', 'CPJC', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '产品检测与质量控制专业'); -INSERT INTO sys_dict_data VALUES (408, '000000', 9, '中药', 'ZY', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '中药专业'); -INSERT INTO sys_dict_data VALUES (409, '000000', 10, '药物制剂(预备技师)', 'YWZJ_YB', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '药物制剂(预备技师)专业'); -INSERT INTO sys_dict_data VALUES (410, '000000', 11, '药物制剂(高级工)', 'YWZJ_GJ', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '药物制剂(高级工)专业'); -INSERT INTO sys_dict_data VALUES (411, '000000', 12, '药品营销', 'YPYX', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '药品营销专业'); -INSERT INTO sys_dict_data VALUES (412, '000000', 13, '健康服务与管理', 'JKFW', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '健康服务与管理专业'); -INSERT INTO sys_dict_data VALUES (413, '000000', 14, '园林技术(预备技师)', 'YLJS_YB', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '园林技术(预备技师)专业'); -INSERT INTO sys_dict_data VALUES (414, '000000', 15, '园林技术(高级工)', 'YLJS_GJ', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '园林技术(高级工)专业'); -INSERT INTO sys_dict_data VALUES (415, '000000', 16, '园林技术(园林景观与工程施工)', 'YLJS_JG', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '园林技术(园林景观与工程施工)专业'); -INSERT INTO sys_dict_data VALUES (416, '000000', 17, '环境艺术设计', 'HJYS', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '环境艺术设计专业'); -INSERT INTO sys_dict_data VALUES (417, '000000', 18, '酒店管理(预备技师)', 'JDGL_YB', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '酒店管理(预备技师)专业'); -INSERT INTO sys_dict_data VALUES (418, '000000', 19, '酒店管理(高级工)', 'JDGL_GJ', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '酒店管理(高级工)专业'); -INSERT INTO sys_dict_data VALUES (419, '000000', 20, '烹饪(中式烹调)', 'PR_ZS', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '烹饪(中式烹调)专业'); -INSERT INTO sys_dict_data VALUES (420, '000000', 21, '烹饪(中西式面点)', 'PR_MD', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '烹饪(中西式面点)专业'); -INSERT INTO sys_dict_data VALUES (421, '000000', 22, '电子商务', 'DZSW', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '电子商务专业'); -INSERT INTO sys_dict_data VALUES (422, '000000', 23, '数字媒体技术应用', 'SZMT', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '数字媒体技术应用专业'); -INSERT INTO sys_dict_data VALUES (423, '000000', 24, '人工智能技术应用', 'RGZN', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '人工智能技术应用专业'); -INSERT INTO sys_dict_data VALUES (424, '000000', 25, '网络与信息安全', 'WLXX', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '网络与信息安全专业'); -INSERT INTO sys_dict_data VALUES (425, '000000', 26, '美术设计与制作', 'MSSJ', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '美术设计与制作专业'); \ No newline at end of file diff --git a/backend/script/sql/scholarship-review-menu-permissions.sql b/backend/script/sql/scholarship-review-menu-permissions.sql deleted file mode 100644 index 8db3e800..00000000 --- a/backend/script/sql/scholarship-review-menu-permissions.sql +++ /dev/null @@ -1,75 +0,0 @@ --- 奖学金审核管理菜单和权限配置 --- 专注于审核功能,不包含申请提交功能 --- Date: 2025-01-27 --- Author: system - --- 使用正确的数据库 -USE hzdyjs_xg; - --- 设置会话字符集 -SET NAMES utf8mb4; -SET CHARACTER_SET_CLIENT = utf8mb4; -SET CHARACTER_SET_CONNECTION = utf8mb4; -SET CHARACTER_SET_RESULTS = utf8mb4; - --- 删除可能存在的旧菜单(从子级开始删除) -DELETE FROM sys_menu WHERE menu_id >= 2100 AND menu_id <= 2120; - --- 1. 奖学金审核管理目录 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES -(2100, '奖学金审核管理', 1, 5, 'scholarshipReview', NULL, 1, 0, 'M', '0', '0', NULL, 'form', 1, NOW(), 1, NOW(), '奖学金审核管理目录'); - --- 2. 我的审核任务菜单 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES -(2101, '我的审核任务', 2100, 1, 'myTasks', 'jcbt/scholarship/review/myTasks', 1, 0, 'C', '0', '0', 'jcbt:review:list', 'form', 1, NOW(), 1, NOW(), '我的审核任务菜单'); - --- 3. 审核历史菜单 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES -(2102, '审核历史', 2100, 2, 'history', 'jcbt/scholarship/review/history', 1, 0, 'C', '0', '0', 'jcbt:review:history', 'time', 1, NOW(), 1, NOW(), '审核历史记录菜单'); - --- 4. 审核记录查询权限 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES -(2103, '审核记录查询', 2101, 1, '', '', 1, 0, 'F', '0', '0', 'jcbt:review:query', '#', 1, NOW(), 1, NOW(), '审核记录查询权限'); - --- 5. 审核操作权限(通过/拒绝/退回) -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES -(2104, '审核操作', 2101, 2, '', '', 1, 0, 'F', '0', '0', 'jcbt:review:approve', '#', 1, NOW(), 1, NOW(), '审核通过/拒绝/退回操作权限'); - --- 6. 批量审核权限 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES -(2105, '批量审核', 2101, 3, '', '', 1, 0, 'F', '0', '0', 'jcbt:review:batchApprove', '#', 1, NOW(), 1, NOW(), '批量审核操作权限'); - --- 7. 审核转移权限 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES -(2106, '审核转移', 2101, 4, '', '', 1, 0, 'F', '0', '0', 'jcbt:review:transfer', '#', 1, NOW(), 1, NOW(), '审核任务转移权限'); - --- 8. 审核撤回权限 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES -(2107, '审核撤回', 2101, 5, '', '', 1, 0, 'F', '0', '0', 'jcbt:review:revoke', '#', 1, NOW(), 1, NOW(), '审核结果撤回权限'); - --- 9. 待审核列表权限 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES -(2108, '待审核列表', 2101, 6, '', '', 1, 0, 'F', '0', '0', 'jcbt:review:pending', '#', 1, NOW(), 1, NOW(), '查看待审核列表权限'); - --- 10. 审核详情权限 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES -(2109, '审核详情', 2101, 7, '', '', 1, 0, 'F', '0', '0', 'jcbt:review:detail', '#', 1, NOW(), 1, NOW(), '查看审核详情权限'); - --- 11. 审核统计权限 -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES -(2110, '审核统计', 2101, 8, '', '', 1, 0, 'F', '0', '0', 'jcbt:review:statistics', '#', 1, NOW(), 1, NOW(), '审核数据统计权限'); - --- 12. 导出权限(审核记录导出) -INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES -(2111, '审核记录导出', 2101, 9, '', '', 1, 0, 'F', '0', '0', 'jcbt:review:export', '#', 1, NOW(), 1, NOW(), '审核记录导出权限'); - --- 验证插入结果 -SELECT COUNT(*) as '插入菜单数量' FROM sys_menu WHERE menu_id >= 2100 AND menu_id <= 2111; - --- 显示插入的菜单 -SELECT menu_id, menu_name, parent_id, menu_type, perms, remark -FROM sys_menu -WHERE menu_id >= 2100 AND menu_id <= 2111 -ORDER BY menu_id; - -COMMIT; diff --git a/backend/script/sql/test-import.sh b/backend/script/sql/test-import.sh deleted file mode 100755 index 08953de0..00000000 --- a/backend/script/sql/test-import.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash - -# ========================================== -# 学生数据导入测试脚本 -# 版本:V1.0.0 -# 功能:演示自动化导入流程 -# 作者:Cursor AI 辅助 -# 时间:2025-01-14 -# ========================================== - -echo "🧪 学生数据导入脚本测试演示" -echo "==========================================" -echo "" - -echo "📋 测试流程:" -echo "1. 检查文件是否存在" -echo "2. 显示数据库连接信息" -echo "3. 模拟导入确认" -echo "4. 显示预期结果" -echo "" - -# 检查文件 -echo "🔍 检查必要文件..." -if [ -f "complete-student-data.sql" ]; then - echo "✅ complete-student-data.sql 文件存在" - FILE_SIZE=$(wc -l < complete-student-data.sql) - echo " 文件大小: ${FILE_SIZE} 行" -else - echo "❌ complete-student-data.sql 文件不存在" - echo "💡 请先运行: python3 generate_student_data.py" - exit 1 -fi - -if [ -f "import-student-data.sh" ]; then - echo "✅ import-student-data.sh 脚本存在" - echo " 权限: $(ls -l import-student-data.sh | cut -d' ' -f1)" -else - echo "❌ import-student-data.sh 脚本不存在" - exit 1 -fi - -echo "" - -# 显示连接信息 -echo "🔗 数据库连接信息(从配置文件获取):" -echo " 主机: 47.99.164.66:3306" -echo " 数据库: hzdyjs_xg" -echo " 用户: root" -echo " 密码: hn0***KLD (已隐藏)" -echo "" - -# 显示导入内容 -echo "📊 将要导入的数据:" -echo " - 3个班级信息" -echo " - 150条学生信息" -echo " - 150条班级学生关联关系" -echo "" - -echo "👥 班级分布:" -echo " - 计算机科学与技术2024级1班:50人(男28,女22)" -echo " - 电子信息工程2024级1班:50人(男32,女18)" -echo " - 工商管理2024级1班:50人(男20,女30)" -echo "" - -echo "🚀 使用方法:" -echo " cd backend/script/sql" -echo " ./import-student-data.sh" -echo " # 输入 y 确认导入" -echo "" - -echo "✨ 改进效果:" -echo " - 无需手动输入5个数据库参数" -echo " - 自动从配置文件获取连接信息" -echo " - 一键确认即可完成导入" -echo " - 减少操作错误和时间成本" -echo "" - -echo "🎉 测试演示完成!" -echo "==========================================" diff --git a/backend/script/sql/xg-dict-data.sql b/backend/script/sql/xg-dict-data.sql deleted file mode 100644 index 23091414..00000000 --- a/backend/script/sql/xg-dict-data.sql +++ /dev/null @@ -1,35 +0,0 @@ --- ======================================== --- 学工模块字典数据初始化脚本 --- 文件:xg-dict-data.sql --- 创建时间:2025-01-14 --- 说明:添加班级信息管理所需的字典数据 --- ======================================== - --- ---------------------------- --- 1、添加字典类型 --- ---------------------------- - --- 班级类型字典 -INSERT INTO sys_dict_type VALUES (100, '000000', '班级类型', 'xg_class_type', 103, 1, sysdate(), NULL, NULL, '班级类型列表'); - --- 班级状态字典 -INSERT INTO sys_dict_type VALUES (101, '000000', '班级状态', 'xg_class_status', 103, 1, sysdate(), NULL, NULL, '班级状态列表'); - --- ---------------------------- --- 2、添加字典数据 --- ---------------------------- - --- 班级类型字典数据 -INSERT INTO sys_dict_data VALUES (200, '000000', 1, '普通班级', 'regular', 'xg_class_type', '', 'default', 'Y', 103, 1, sysdate(), NULL, NULL, '普通班级'); -INSERT INTO sys_dict_data VALUES (201, '000000', 2, '荣誉班级', 'honor', 'xg_class_type', '', 'warning', 'N', 103, 1, sysdate(), NULL, NULL, '荣誉班级'); -INSERT INTO sys_dict_data VALUES (202, '000000', 3, '实验班级', 'experimental', 'xg_class_type', '', 'primary', 'N', 103, 1, sysdate(), NULL, NULL, '实验班级'); -INSERT INTO sys_dict_data VALUES (203, '000000', 4, '特殊班级', 'special', 'xg_class_type', '', 'danger', 'N', 103, 1, sysdate(), NULL, NULL, '特殊班级'); - --- 班级状态字典数据 -INSERT INTO sys_dict_data VALUES (210, '000000', 1, '正常', '1', 'xg_class_status', '', 'success', 'Y', 103, 1, sysdate(), NULL, NULL, '正常状态'); -INSERT INTO sys_dict_data VALUES (211, '000000', 2, '暂停', '2', 'xg_class_status', '', 'warning', 'N', 103, 1, sysdate(), NULL, NULL, '暂停招生'); -INSERT INTO sys_dict_data VALUES (212, '000000', 3, '毕业', '3', 'xg_class_status', '', 'info', 'N', 103, 1, sysdate(), NULL, NULL, '已毕业'); -INSERT INTO sys_dict_data VALUES (213, '000000', 4, '解散', '4', 'xg_class_status', '', 'danger', 'N', 103, 1, sysdate(), NULL, NULL, '已解散'); - --- 提交 -COMMIT; diff --git a/backend/script/sql/xg_dorm_manager.sql b/backend/script/sql/xg_dorm_manager.sql deleted file mode 100644 index a8f35995..00000000 --- a/backend/script/sql/xg_dorm_manager.sql +++ /dev/null @@ -1,29 +0,0 @@ --- ---------------------------- --- 宿舍管理员表 --- ---------------------------- -drop table if exists xg_dorm_manager; -create table xg_dorm_manager ( - id bigint(20) not null comment '主键', - name varchar(100) not null comment '管理员姓名', - phone varchar(11) not null comment '手机号码', - sex int(1) default 0 comment '性别(0男 1女 2未知)', - manager_type int(1) default 0 comment '管理类型(0全校 1校区 2宿舍区 3宿舍楼)', - scope varchar(100) comment '管理范围(校区/宿舍区/宿舍楼)', - user_id bigint(20) comment '关联用户ID', - remark varchar(500) comment '备注', - create_by bigint(20) comment '创建者', - create_time datetime comment '创建时间', - update_by bigint(20) comment '更新者', - update_time datetime comment '更新时间', - deleted char(1) default '0' comment '删除标志(0代表存在 2代表删除)', - tenant_id varchar(20) default '000000' comment '租户编号', - primary key (id) -) engine=innodb comment = '宿舍管理员信息表'; - --- ---------------------------- --- 初始数据 --- ---------------------------- -INSERT INTO xg_dorm_manager(id, name, phone, sex, manager_type, scope, user_id, remark, create_by, create_time, update_by, update_time, deleted, tenant_id) -VALUES (1, '张三', '13800000001', 0, 0, null, 2, '全校主管', 1, sysdate(), 1, sysdate(), '0', '000000'); -INSERT INTO xg_dorm_manager(id, name, phone, sex, manager_type, scope, user_id, remark, create_by, create_time, update_by, update_time, deleted, tenant_id) -VALUES (2, '李四', '13800000002', 1, 0, null, 3, '全校副主管', 1, sysdate(), 1, sysdate(), '0', '000000'); diff --git a/backend/script/sql/zsjy-enrollment-dict-data.sql b/backend/script/sql/zsjy-enrollment-dict-data.sql deleted file mode 100644 index aec6d32c..00000000 --- a/backend/script/sql/zsjy-enrollment-dict-data.sql +++ /dev/null @@ -1,75 +0,0 @@ --- ======================================== --- 招生就业模块字典数据初始化脚本 --- 文件:zsjy-enrollment-dict-data.sql --- 创建时间:2025-01-20 --- 说明:添加招生报名管理所需的字典数据 --- ======================================== - --- ---------------------------- --- 1、添加字典类型 --- ---------------------------- - --- 招生性别字典(使用M/F格式,与数据库字段一致) -INSERT INTO sys_dict_type VALUES (299, '000000', '招生性别', 'zsjy_gender', 103, 1, sysdate(), NULL, NULL, '招生性别列表'); - --- 招生专业字典 -INSERT INTO sys_dict_type VALUES (300, '000000', '招生专业', 'zsjy_major', 103, 1, sysdate(), NULL, NULL, '招生专业列表'); - --- 报名状态字典 -INSERT INTO sys_dict_type VALUES (301, '000000', '报名状态', 'zsjy_enrollment_status', 103, 1, sysdate(), NULL, NULL, '报名状态列表'); - --- 校服尺码字典 -INSERT INTO sys_dict_type VALUES (302, '000000', '校服尺码', 'zsjy_uniform_size', 103, 1, sysdate(), NULL, NULL, '校服尺码列表'); - --- ---------------------------- --- 2、添加字典数据 --- ---------------------------- - --- 招生性别字典数据(使用M/F格式) -INSERT INTO sys_dict_data VALUES (390, '000000', 1, '男', 'M', 'zsjy_gender', '', 'primary', 'Y', 103, 1, sysdate(), NULL, NULL, '男性'); -INSERT INTO sys_dict_data VALUES (391, '000000', 2, '女', 'F', 'zsjy_gender', '', 'danger', 'N', 103, 1, sysdate(), NULL, NULL, '女性'); - --- 招生专业字典数据(基于真实专业名称) -INSERT INTO sys_dict_data VALUES (400, '000000', 1, '电气自动化设备安装与维修', 'DQZDHSB', 'zsjy_major', '', 'primary', 'N', 103, 1, sysdate(), NULL, NULL, '电气自动化设备安装与维修专业'); -INSERT INTO sys_dict_data VALUES (401, '000000', 2, '工业机器人应用与维护', 'GYJQR', 'zsjy_major', '', 'success', 'N', 103, 1, sysdate(), NULL, NULL, '工业机器人应用与维护专业'); -INSERT INTO sys_dict_data VALUES (402, '000000', 3, '无人机应用技术', 'WRJYY', 'zsjy_major', '', 'warning', 'N', 103, 1, sysdate(), NULL, NULL, '无人机应用技术专业'); -INSERT INTO sys_dict_data VALUES (403, '000000', 4, '工业互联网与大数据应用', 'GYHLW', 'zsjy_major', '', 'info', 'N', 103, 1, sysdate(), NULL, NULL, '工业互联网与大数据应用专业'); -INSERT INTO sys_dict_data VALUES (404, '000000', 5, '数字化设计与制造', 'SZHSJ', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '数字化设计与制造专业'); -INSERT INTO sys_dict_data VALUES (405, '000000', 6, '智能制作技术应用', 'ZNZZ', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '智能制作技术应用专业'); -INSERT INTO sys_dict_data VALUES (406, '000000', 7, '汽车维修', 'QCWX', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '汽车维修专业'); -INSERT INTO sys_dict_data VALUES (407, '000000', 8, '产品检测与质量控制', 'CPJC', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '产品检测与质量控制专业'); -INSERT INTO sys_dict_data VALUES (408, '000000', 9, '中药', 'ZY', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '中药专业'); -INSERT INTO sys_dict_data VALUES (409, '000000', 10, '药物制剂(预备技师)', 'YWZJ_YB', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '药物制剂(预备技师)专业'); -INSERT INTO sys_dict_data VALUES (410, '000000', 11, '药物制剂(高级工)', 'YWZJ_GJ', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '药物制剂(高级工)专业'); -INSERT INTO sys_dict_data VALUES (411, '000000', 12, '药品营销', 'YPYX', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '药品营销专业'); -INSERT INTO sys_dict_data VALUES (412, '000000', 13, '健康服务与管理', 'JKFW', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '健康服务与管理专业'); -INSERT INTO sys_dict_data VALUES (413, '000000', 14, '园林技术(预备技师)', 'YLJS_YB', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '园林技术(预备技师)专业'); -INSERT INTO sys_dict_data VALUES (414, '000000', 15, '园林技术(高级工)', 'YLJS_GJ', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '园林技术(高级工)专业'); -INSERT INTO sys_dict_data VALUES (415, '000000', 16, '园林技术(园林景观与工程施工)', 'YLJS_JG', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '园林技术(园林景观与工程施工)专业'); -INSERT INTO sys_dict_data VALUES (416, '000000', 17, '环境艺术设计', 'HJYS', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '环境艺术设计专业'); -INSERT INTO sys_dict_data VALUES (417, '000000', 18, '酒店管理(预备技师)', 'JDGL_YB', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '酒店管理(预备技师)专业'); -INSERT INTO sys_dict_data VALUES (418, '000000', 19, '酒店管理(高级工)', 'JDGL_GJ', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '酒店管理(高级工)专业'); -INSERT INTO sys_dict_data VALUES (419, '000000', 20, '烹饪(中式烹调)', 'PR_ZS', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '烹饪(中式烹调)专业'); -INSERT INTO sys_dict_data VALUES (420, '000000', 21, '烹饪(中西式面点)', 'PR_MD', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '烹饪(中西式面点)专业'); -INSERT INTO sys_dict_data VALUES (421, '000000', 22, '电子商务', 'DZSW', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '电子商务专业'); -INSERT INTO sys_dict_data VALUES (422, '000000', 23, '数字媒体技术应用', 'SZMT', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '数字媒体技术应用专业'); -INSERT INTO sys_dict_data VALUES (423, '000000', 24, '人工智能技术应用', 'RGZN', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '人工智能技术应用专业'); -INSERT INTO sys_dict_data VALUES (424, '000000', 25, '网络与信息安全', 'WLXX', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '网络与信息安全专业'); -INSERT INTO sys_dict_data VALUES (425, '000000', 26, '美术设计与制作', 'MSSJ', 'zsjy_major', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '美术设计与制作专业'); - --- 报名状态字典数据 -INSERT INTO sys_dict_data VALUES (450, '000000', 1, '待审核', '0', 'zsjy_enrollment_status', '', 'info', 'Y', 103, 1, sysdate(), NULL, NULL, '报名信息待审核'); -INSERT INTO sys_dict_data VALUES (451, '000000', 2, '审核中', '1', 'zsjy_enrollment_status', '', 'warning', 'N', 103, 1, sysdate(), NULL, NULL, '报名信息审核中'); -INSERT INTO sys_dict_data VALUES (452, '000000', 3, '审核通过', '2', 'zsjy_enrollment_status', '', 'success', 'N', 103, 1, sysdate(), NULL, NULL, '报名信息审核通过'); -INSERT INTO sys_dict_data VALUES (453, '000000', 4, '待确认', '3', 'zsjy_enrollment_status', '', 'primary', 'N', 103, 1, sysdate(), NULL, NULL, '录取待确认'); -INSERT INTO sys_dict_data VALUES (454, '000000', 5, '录取完成', '4', 'zsjy_enrollment_status', '', 'success', 'N', 103, 1, sysdate(), NULL, NULL, '录取完成'); -INSERT INTO sys_dict_data VALUES (459, '000000', 9, '审核未通过', '9', 'zsjy_enrollment_status', '', 'danger', 'N', 103, 1, sysdate(), NULL, NULL, '报名信息审核未通过'); - --- 校服尺码字典数据 -INSERT INTO sys_dict_data VALUES (480, '000000', 1, 'XS', 'XS', 'zsjy_uniform_size', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '加小号'); -INSERT INTO sys_dict_data VALUES (481, '000000', 2, 'S', 'S', 'zsjy_uniform_size', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '小号'); -INSERT INTO sys_dict_data VALUES (482, '000000', 3, 'M', 'M', 'zsjy_uniform_size', '', 'default', 'Y', 103, 1, sysdate(), NULL, NULL, '中号'); -INSERT INTO sys_dict_data VALUES (483, '000000', 4, 'L', 'L', 'zsjy_uniform_size', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '大号'); -INSERT INTO sys_dict_data VALUES (484, '000000', 5, 'XL', 'XL', 'zsjy_uniform_size', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '加大号'); -INSERT INTO sys_dict_data VALUES (485, '000000', 6, 'XXL', 'XXL', 'zsjy_uniform_size', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '加加大号'); -INSERT INTO sys_dict_data VALUES (486, '000000', 7, 'XXXL', 'XXXL', 'zsjy_uniform_size', '', 'default', 'N', 103, 1, sysdate(), NULL, NULL, '加加加大号'); \ No newline at end of file diff --git a/backend/script/sql/zsjy_graduate_dict_data.sql b/backend/script/sql/zsjy_graduate_dict_data.sql deleted file mode 100644 index a516be2e..00000000 --- a/backend/script/sql/zsjy_graduate_dict_data.sql +++ /dev/null @@ -1,61 +0,0 @@ --- 毕业生信息管理字典数据插入脚本 - --- 1. 创建学历层次字典类型 -INSERT INTO sys_dict_type (dict_id, tenant_id, dict_name, dict_type, create_by, create_time, remark) -VALUES (2000001, '000000', '学历层次', 'zsjy_education_level', 1, NOW(), '学历层次列表'); - --- 2. 创建就业状态字典类型 -INSERT INTO sys_dict_type (dict_id, tenant_id, dict_name, dict_type, create_by, create_time, remark) -VALUES (2000002, '000000', '毕业生就业状态', 'zsjy_graduate_employment_status', 1, NOW(), '毕业生就业状态'); - --- 3. 创建单位性质字典类型 -INSERT INTO sys_dict_type (dict_id, tenant_id, dict_name, dict_type, create_by, create_time, remark) -VALUES (2000003, '000000', '就业单位性质', 'zsjy_graduate_company_type', 1, NOW(), '就业单位性质'); - --- 4. 创建月薪范围字典类型 -INSERT INTO sys_dict_type (dict_id, tenant_id, dict_name, dict_type, create_by, create_time, remark) -VALUES (2000004, '000000', '月薪范围', 'zsjy_graduate_salary_range', 1, NOW(), '月薪范围'); - --- 5. 创建审核状态字典类型 -INSERT INTO sys_dict_type (dict_id, tenant_id, dict_name, dict_type, create_by, create_time, remark) -VALUES (2000005, '000000', '毕业生信息审核状态', 'zsjy_graduate_audit_status', 1, NOW(), '审核状态'); - --- 插入学历层次字典数据 -INSERT INTO sys_dict_data (dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark) VALUES -(2000001, '000000', 1, '专科', '1', 'zsjy_education_level', '', '', 'N', 1, NOW(), '专科'), -(2000002, '000000', 2, '本科', '2', 'zsjy_education_level', '', '', 'Y', 1, NOW(), '本科'), -(2000003, '000000', 3, '硕士', '3', 'zsjy_education_level', '', '', 'N', 1, NOW(), '硕士'), -(2000004, '000000', 4, '博士', '4', 'zsjy_education_level', '', '', 'N', 1, NOW(), '博士'); - --- 插入就业状态字典数据 -INSERT INTO sys_dict_data (dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark) VALUES -(2000011, '000000', 1, '已就业(劳动合同)', '1', 'zsjy_graduate_employment_status', '', 'success', 'N', 1, NOW(), '签订劳动合同'), -(2000012, '000000', 2, '已就业(就业协议)', '2', 'zsjy_graduate_employment_status', '', 'success', 'N', 1, NOW(), '签订就业协议'), -(2000013, '000000', 3, '自主创业', '3', 'zsjy_graduate_employment_status', '', 'warning', 'N', 1, NOW(), '自主创业'), -(2000014, '000000', 4, '升学(国内)', '4', 'zsjy_graduate_employment_status', '', 'info', 'N', 1, NOW(), '国内升学'), -(2000015, '000000', 5, '升学(国外)', '5', 'zsjy_graduate_employment_status', '', 'info', 'N', 1, NOW(), '国外升学'), -(2000016, '000000', 6, '应征入伍', '6', 'zsjy_graduate_employment_status', '', 'primary', 'N', 1, NOW(), '应征入伍'), -(2000017, '000000', 7, '待就业', '7', 'zsjy_graduate_employment_status', '', 'danger', 'Y', 1, NOW(), '待就业'), -(2000018, '000000', 8, '暂不就业', '8', 'zsjy_graduate_employment_status', '', 'default', 'N', 1, NOW(), '暂不就业'); - --- 插入单位性质字典数据 -INSERT INTO sys_dict_data (dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark) VALUES -(2000021, '000000', 1, '国有企业', '1', 'zsjy_graduate_company_type', '', '', 'N', 1, NOW(), '国有企业'), -(2000022, '000000', 2, '私营企业', '2', 'zsjy_graduate_company_type', '', '', 'Y', 1, NOW(), '私营企业'), -(2000023, '000000', 3, '外资企业', '3', 'zsjy_graduate_company_type', '', '', 'N', 1, NOW(), '外资企业'), -(2000024, '000000', 4, '机关事业单位', '4', 'zsjy_graduate_company_type', '', '', 'N', 1, NOW(), '机关事业单位'), -(2000025, '000000', 5, '其他', '5', 'zsjy_graduate_company_type', '', '', 'N', 1, NOW(), '其他'); - --- 插入月薪范围字典数据 -INSERT INTO sys_dict_data (dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark) VALUES -(2000031, '000000', 1, '3000以下', '1', 'zsjy_graduate_salary_range', '', '', 'N', 1, NOW(), '3000以下'), -(2000032, '000000', 2, '3000-5000', '2', 'zsjy_graduate_salary_range', '', '', 'Y', 1, NOW(), '3000-5000'), -(2000033, '000000', 3, '5000-8000', '3', 'zsjy_graduate_salary_range', '', '', 'N', 1, NOW(), '5000-8000'), -(2000034, '000000', 4, '8000-12000', '4', 'zsjy_graduate_salary_range', '', '', 'N', 1, NOW(), '8000-12000'), -(2000035, '000000', 5, '12000以上', '5', 'zsjy_graduate_salary_range', '', '', 'N', 1, NOW(), '12000以上'); - --- 插入审核状态字典数据 -INSERT INTO sys_dict_data (dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark) VALUES -(2000041, '000000', 1, '待审核', '0', 'zsjy_graduate_audit_status', '', 'warning', 'Y', 1, NOW(), '待审核'), -(2000042, '000000', 2, '已审核', '1', 'zsjy_graduate_audit_status', '', 'success', 'N', 1, NOW(), '已审核'), -(2000043, '000000', 3, '审核拒绝', '2', 'zsjy_graduate_audit_status', '', 'danger', 'N', 1, NOW(), '审核拒绝'); diff --git a/backend/script/sql/zsjy_graduate_info.sql b/backend/script/sql/zsjy_graduate_info.sql deleted file mode 100644 index c3906f15..00000000 --- a/backend/script/sql/zsjy_graduate_info.sql +++ /dev/null @@ -1,129 +0,0 @@ --- 毕业生信息管理表 -DROP TABLE IF EXISTS `zsjy_graduate_info`; -CREATE TABLE `zsjy_graduate_info` -( - `id` bigint(20) NOT NULL COMMENT '主键ID', - `student_no` varchar(50) NOT NULL COMMENT '学号', - `name` varchar(30) NOT NULL COMMENT '姓名', - `gender` char(1) NOT NULL DEFAULT '0' COMMENT '性别 (0男 1女)', - `id_card` varchar(18) NOT NULL COMMENT '身份证号', - `nation` varchar(50) DEFAULT NULL COMMENT '民族', - `political_status` varchar(50) DEFAULT NULL COMMENT '政治面貌', - `birth_date` date DEFAULT NULL COMMENT '出生日期', - `enrollment_year` int(4) NOT NULL COMMENT '入学年份', - `graduation_year` int(4) NOT NULL COMMENT '毕业年份', - `college_id` bigint(20) DEFAULT NULL COMMENT '学院ID', - `college_name` varchar(100) DEFAULT NULL COMMENT '学院名称', - `major_id` bigint(20) DEFAULT NULL COMMENT '专业ID', - `major_name` varchar(100) DEFAULT NULL COMMENT '专业名称', - `class_id` bigint(20) DEFAULT NULL COMMENT '班级ID', - `class_name` varchar(100) DEFAULT NULL COMMENT '班级名称', - `education_level` char(1) NOT NULL DEFAULT '2' COMMENT '学历层次 (1专科 2本科 3硕士 4博士)', - `training_mode` varchar(50) DEFAULT NULL COMMENT '培养方式', - `phone_number` varchar(11) NOT NULL COMMENT '手机号码', - `email` varchar(100) DEFAULT NULL COMMENT '邮箱地址', - `qq_number` varchar(20) DEFAULT NULL COMMENT 'QQ号码', - `wechat_number` varchar(50) DEFAULT NULL COMMENT '微信号码', - `province` varchar(50) DEFAULT NULL COMMENT '省份', - `city` varchar(50) DEFAULT NULL COMMENT '城市', - `district` varchar(50) DEFAULT NULL COMMENT '区县', - `detail_address` varchar(255) DEFAULT NULL COMMENT '详细地址', - `postal_code` varchar(6) DEFAULT NULL COMMENT '邮政编码', - `employment_status` char(1) NOT NULL DEFAULT '7' COMMENT '就业状态 (1已就业-劳动合同 2已就业-就业协议 3自主创业 4升学-国内 5升学-国外 6应征入伍 7待就业 8暂不就业)', - `employment_company` varchar(200) DEFAULT NULL COMMENT '就业单位名称', - `company_type` char(1) DEFAULT NULL COMMENT '单位性质 (1国有企业 2私营企业 3外资企业 4机关事业单位 5其他)', - `job_position` varchar(100) DEFAULT NULL COMMENT '工作职位', - `work_province` varchar(50) DEFAULT NULL COMMENT '工作省份', - `work_city` varchar(50) DEFAULT NULL COMMENT '工作城市', - `salary_range` char(1) DEFAULT NULL COMMENT '月薪范围 (1-3000以下 2-3000-5000 3-5000-8000 4-8000-12000 5-12000以上)', - `further_school` varchar(200) DEFAULT NULL COMMENT '升学院校', - `further_major` varchar(100) DEFAULT NULL COMMENT '升学专业', - `skills` text DEFAULT NULL COMMENT '特长技能', - `awards` text DEFAULT NULL COMMENT '获奖情况', - `job_intention` text DEFAULT NULL COMMENT '工作意向', - `audit_status` char(1) NOT NULL DEFAULT '0' COMMENT '审核状态 (0待审核 1已审核 2审核拒绝)', - `audit_time` datetime DEFAULT NULL COMMENT '审核时间', - `audit_by` bigint(20) DEFAULT NULL COMMENT '审核人', - `audit_remark` varchar(500) DEFAULT NULL COMMENT '审核意见', - `status` char(1) NOT NULL DEFAULT '0' COMMENT '状态 (0正常 1停用)', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户ID', - `create_by` bigint(20) DEFAULT NULL COMMENT '创建者', - `create_time` datetime DEFAULT NULL COMMENT '创建时间', - `update_by` bigint(20) DEFAULT NULL COMMENT '更新者', - `update_time` datetime DEFAULT NULL COMMENT '更新时间', - `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_student_no` (`student_no`) COMMENT '学号唯一', - UNIQUE KEY `uk_id_card` (`id_card`) COMMENT '身份证号唯一', - KEY `idx_name` (`name`) COMMENT '姓名索引', - KEY `idx_college_major` (`college_id`, `major_id`) COMMENT '学院专业索引', - KEY `idx_graduation_year` (`graduation_year`) COMMENT '毕业年份索引', - KEY `idx_employment_status` (`employment_status`) COMMENT '就业状态索引', - KEY `idx_audit_status` (`audit_status`) COMMENT '审核状态索引' -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4 COMMENT = '毕业生信息表'; - --- 插入字典数据 --- 性别 -INSERT INTO `sys_dict_type` (`dict_id`, `tenant_id`, `dict_name`, `dict_type`, `create_by`, `create_time`, `remark`) VALUES -(1001, '000000', '性别', 'sys_user_sex', 1, NOW(), '性别列表'); - -INSERT INTO `sys_dict_data` (`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_by`, `create_time`, `remark`) VALUES -(1001, '000000', 1, '男', '0', 'sys_user_sex', '', '', 'Y', 1, NOW(), '男性'), -(1002, '000000', 2, '女', '1', 'sys_user_sex', '', '', 'N', 1, NOW(), '女性'); - --- 学历层次 -INSERT INTO `sys_dict_type` (`dict_id`, `tenant_id`, `dict_name`, `dict_type`, `create_by`, `create_time`, `remark`) VALUES -(1002, '000000', '学历层次', 'zsjy_education_level', 1, NOW(), '学历层次列表'); - -INSERT INTO `sys_dict_data` (`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_by`, `create_time`, `remark`) VALUES -(1003, '000000', 1, '专科', '1', 'zsjy_education_level', '', '', 'N', 1, NOW(), '专科'), -(1004, '000000', 2, '本科', '2', 'zsjy_education_level', '', '', 'Y', 1, NOW(), '本科'), -(1005, '000000', 3, '硕士', '3', 'zsjy_education_level', '', '', 'N', 1, NOW(), '硕士'), -(1006, '000000', 4, '博士', '4', 'zsjy_education_level', '', '', 'N', 1, NOW(), '博士'); - --- 就业状态 -INSERT INTO `sys_dict_type` (`dict_id`, `tenant_id`, `dict_name`, `dict_type`, `create_by`, `create_time`, `remark`) VALUES -(1003, '000000', '就业状态', 'zsjy_employment_status', 1, NOW(), '毕业生就业状态'); - -INSERT INTO `sys_dict_data` (`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_by`, `create_time`, `remark`) VALUES -(1007, '000000', 1, '已就业(劳动合同)', '1', 'zsjy_employment_status', '', 'success', 'N', 1, NOW(), '签订劳动合同'), -(1008, '000000', 2, '已就业(就业协议)', '2', 'zsjy_employment_status', '', 'success', 'N', 1, NOW(), '签订就业协议'), -(1009, '000000', 3, '自主创业', '3', 'zsjy_employment_status', '', 'warning', 'N', 1, NOW(), '自主创业'), -(1010, '000000', 4, '升学(国内)', '4', 'zsjy_employment_status', '', 'info', 'N', 1, NOW(), '国内升学'), -(1011, '000000', 5, '升学(国外)', '5', 'zsjy_employment_status', '', 'info', 'N', 1, NOW(), '国外升学'), -(1012, '000000', 6, '应征入伍', '6', 'zsjy_employment_status', '', 'primary', 'N', 1, NOW(), '应征入伍'), -(1013, '000000', 7, '待就业', '7', 'zsjy_employment_status', '', 'danger', 'Y', 1, NOW(), '待就业'), -(1014, '000000', 8, '暂不就业', '8', 'zsjy_employment_status', '', 'default', 'N', 1, NOW(), '暂不就业'); - --- 单位性质 -INSERT INTO `sys_dict_type` (`dict_id`, `tenant_id`, `dict_name`, `dict_type`, `create_by`, `create_time`, `remark`) VALUES -(1004, '000000', '单位性质', 'zsjy_company_type', 1, NOW(), '就业单位性质'); - -INSERT INTO `sys_dict_data` (`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_by`, `create_time`, `remark`) VALUES -(1015, '000000', 1, '国有企业', '1', 'zsjy_company_type', '', '', 'N', 1, NOW(), '国有企业'), -(1016, '000000', 2, '私营企业', '2', 'zsjy_company_type', '', '', 'Y', 1, NOW(), '私营企业'), -(1017, '000000', 3, '外资企业', '3', 'zsjy_company_type', '', '', 'N', 1, NOW(), '外资企业'), -(1018, '000000', 4, '机关事业单位', '4', 'zsjy_company_type', '', '', 'N', 1, NOW(), '机关事业单位'), -(1019, '000000', 5, '其他', '5', 'zsjy_company_type', '', '', 'N', 1, NOW(), '其他'); - --- 月薪范围 -INSERT INTO `sys_dict_type` (`dict_id`, `tenant_id`, `dict_name`, `dict_type`, `create_by`, `create_time`, `remark`) VALUES -(1005, '000000', '月薪范围', 'zsjy_salary_range', 1, NOW(), '月薪范围'); - -INSERT INTO `sys_dict_data` (`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_by`, `create_time`, `remark`) VALUES -(1020, '000000', 1, '3000以下', '1', 'zsjy_salary_range', '', '', 'N', 1, NOW(), '3000以下'), -(1021, '000000', 2, '3000-5000', '2', 'zsjy_salary_range', '', '', 'Y', 1, NOW(), '3000-5000'), -(1022, '000000', 3, '5000-8000', '3', 'zsjy_salary_range', '', '', 'N', 1, NOW(), '5000-8000'), -(1023, '000000', 4, '8000-12000', '4', 'zsjy_salary_range', '', '', 'N', 1, NOW(), '8000-12000'), -(1024, '000000', 5, '12000以上', '5', 'zsjy_salary_range', '', '', 'N', 1, NOW(), '12000以上'); - --- 审核状态 -INSERT INTO `sys_dict_type` (`dict_id`, `tenant_id`, `dict_name`, `dict_type`, `create_by`, `create_time`, `remark`) VALUES -(1006, '000000', '审核状态', 'zsjy_audit_status', 1, NOW(), '审核状态'); - -INSERT INTO `sys_dict_data` (`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_by`, `create_time`, `remark`) VALUES -(1025, '000000', 1, '待审核', '0', 'zsjy_audit_status', '', 'warning', 'Y', 1, NOW(), '待审核'), -(1026, '000000', 2, '已审核', '1', 'zsjy_audit_status', '', 'success', 'N', 1, NOW(), '已审核'), -(1027, '000000', 3, '审核拒绝', '2', 'zsjy_audit_status', '', 'danger', 'N', 1, NOW(), '审核拒绝'); diff --git a/backend/script/sql/zsjy_graduate_info_menu.sql b/backend/script/sql/zsjy_graduate_info_menu.sql deleted file mode 100644 index 3c570364..00000000 --- a/backend/script/sql/zsjy_graduate_info_menu.sql +++ /dev/null @@ -1,60 +0,0 @@ --- ================================================== --- 招生就业模块 - 毕业生信息管理菜单配置 --- 创建日期: 2025-01-26 --- 说明: 毕业生信息管理相关菜单和权限配置 --- ================================================== - --- 1. 毕业生管理父菜单 -INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) -VALUES (8800, '毕业生管理', 8000, 5, 'graduate', null, null, 1, 0, 'M', '0', '0', '', 'user', 103, 1, NOW(), null, null, '毕业生管理菜单'); - --- 2. 毕业生信息子菜单 -INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) -VALUES (8801, '毕业生信息', 8800, 1, 'graduateInfo', 'zsjy/graduateInfo/index', null, 1, 0, 'C', '0', '0', 'zsjy:graduate:list', 'peoples', 103, 1, NOW(), null, null, '毕业生信息管理菜单'); - --- 3. 毕业生信息新增页面菜单(隐藏菜单) -INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) -VALUES (8802, '毕业生信息新增', 8801, 1, 'graduateInfo/add', 'zsjy/graduateInfo/form', null, 1, 1, 'C', '1', '0', 'zsjy:graduate:add', '#', 103, 1, NOW(), null, null, '毕业生信息新增页面'); - --- 4. 毕业生信息编辑页面菜单(隐藏菜单) -INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) -VALUES (8803, '毕业生信息编辑', 8801, 2, 'graduateInfo/edit/:id(\\d+)', 'zsjy/graduateInfo/form', null, 1, 1, 'C', '1', '0', 'zsjy:graduate:edit', '#', 103, 1, NOW(), null, null, '毕业生信息编辑页面'); - --- 5. 毕业生统计分析页面菜单(隐藏菜单) -INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) -VALUES (8804, '毕业生统计分析', 8801, 3, 'graduateInfo/statistics', 'zsjy/graduateInfo/statistics', null, 1, 1, 'C', '1', '0', 'zsjy:graduate:statistics', '#', 103, 1, NOW(), null, null, '毕业生统计分析页面'); - --- 6. 毕业生信息按钮权限 --- 6.1 查询权限 -INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) -VALUES (8810, '毕业生信息查询', 8801, 1, '', '', null, 1, 0, 'F', '0', '0', 'zsjy:graduate:query', '#', 103, 1, NOW(), null, null, ''); - --- 6.2 新增权限 -INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) -VALUES (8811, '毕业生信息新增', 8801, 2, '', '', null, 1, 0, 'F', '0', '0', 'zsjy:graduate:add', '#', 103, 1, NOW(), null, null, ''); - --- 6.3 修改权限 -INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) -VALUES (8812, '毕业生信息修改', 8801, 3, '', '', null, 1, 0, 'F', '0', '0', 'zsjy:graduate:edit', '#', 103, 1, NOW(), null, null, ''); - --- 6.4 删除权限 -INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) -VALUES (8813, '毕业生信息删除', 8801, 4, '', '', null, 1, 0, 'F', '0', '0', 'zsjy:graduate:remove', '#', 103, 1, NOW(), null, null, ''); - --- 6.5 导出权限 -INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) -VALUES (8814, '毕业生信息导出', 8801, 5, '', '', null, 1, 0, 'F', '0', '0', 'zsjy:graduate:export', '#', 103, 1, NOW(), null, null, ''); - --- 6.6 审核权限 -INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) -VALUES (8815, '毕业生信息审核', 8801, 6, '', '', null, 1, 0, 'F', '0', '0', 'zsjy:graduate:audit', '#', 103, 1, NOW(), null, null, ''); - --- 6.7 统计分析权限 -INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) -VALUES (8816, '毕业生统计分析', 8801, 7, '', '', null, 1, 0, 'F', '0', '0', 'zsjy:graduate:statistics', '#', 103, 1, NOW(), null, null, ''); - --- 查询验证菜单创建结果 -SELECT menu_id, menu_name, parent_id, order_num, path, component, menu_type, visible, perms, icon -FROM sys_menu -WHERE menu_id >= 8800 AND menu_id <= 8820 -ORDER BY menu_id; diff --git a/backend/sql/add_create_dept.sql b/backend/sql/add_create_dept.sql deleted file mode 100644 index 71ddce64..00000000 --- a/backend/sql/add_create_dept.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE jcbt_scholarship_type ADD COLUMN create_dept BIGINT(20) COMMENT '创建部门' AFTER is_system; diff --git a/backend/sql/add_create_dept_field.sql b/backend/sql/add_create_dept_field.sql deleted file mode 100644 index 18067ae6..00000000 --- a/backend/sql/add_create_dept_field.sql +++ /dev/null @@ -1,6 +0,0 @@ --- 为 jcbt_scholarship_application 表添加 create_dept 字段 --- 解决 MyBatis-Plus 插入数据时的字段缺失问题 - -ALTER TABLE jcbt_scholarship_application -ADD COLUMN create_dept bigint(20) DEFAULT NULL COMMENT '创建部门' -AFTER submit_time; diff --git a/backend/sql/add_family_economy_summary_field.sql b/backend/sql/add_family_economy_summary_field.sql deleted file mode 100644 index a38c575b..00000000 --- a/backend/sql/add_family_economy_summary_field.sql +++ /dev/null @@ -1,41 +0,0 @@ --- ============================================================================ --- 贫困生认定申请表 - 添加缺失的family_economy_summary字段 --- 创建时间: 2025-01-27 --- 描述: 修复 jcbt_poverty_application 表缺失的 family_economy_summary 字段 --- ============================================================================ - --- 检查并添加 family_economy_summary 字段(如果不存在) -SET @sql = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'family_economy_summary'); - -SET @sql = IF(@sql = 0, - 'ALTER TABLE jcbt_poverty_application ADD COLUMN family_economy_summary text COMMENT ''家庭经济情况摘要'' AFTER assistance_type_names', - 'SELECT ''family_economy_summary字段已存在'' as result'); - -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 验证字段是否添加成功 -SELECT - CASE - WHEN (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'family_economy_summary') > 0 - THEN 'SUCCESS: family_economy_summary字段已成功添加到数据库表中' - ELSE 'ERROR: family_economy_summary字段添加失败' - END as verification_result; - --- 显示表结构确认 -DESCRIBE jcbt_poverty_application; - --- ============================================================================ --- 使用说明: --- 1. 连接到对应的数据库 --- 2. 执行本脚本: source /path/to/add_family_economy_summary_field.sql --- 3. 检查输出结果,确认字段已正确添加 --- 4. 重新启动后端服务 --- ============================================================================ diff --git a/backend/sql/add_missing_fields_to_scholarship_type.sql b/backend/sql/add_missing_fields_to_scholarship_type.sql deleted file mode 100644 index bd0f2f46..00000000 --- a/backend/sql/add_missing_fields_to_scholarship_type.sql +++ /dev/null @@ -1,28 +0,0 @@ --- ===================================================== --- 奖学金类型表 - 添加缺失字段 --- 创建时间: 2025-08-04 --- 说明: 添加申请条件、评定标准、备注三个字段 --- ===================================================== - --- 添加申请条件字段 -ALTER TABLE jcbt_scholarship_type -ADD COLUMN IF NOT EXISTS application_conditions TEXT COMMENT '申请条件' -AFTER application_guide; - --- 添加评定标准字段 -ALTER TABLE jcbt_scholarship_type -ADD COLUMN IF NOT EXISTS evaluation_criteria TEXT COMMENT '评定标准' -AFTER application_conditions; - --- 添加备注字段 -ALTER TABLE jcbt_scholarship_type -ADD COLUMN IF NOT EXISTS remark VARCHAR(500) COMMENT '备注' -AFTER evaluation_criteria; - --- 验证字段添加 -SELECT COLUMN_NAME, DATA_TYPE, COLUMN_COMMENT -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'jcbt_scholarship_type' - AND COLUMN_NAME IN ('application_conditions', 'evaluation_criteria', 'remark') -ORDER BY ORDINAL_POSITION; diff --git a/backend/sql/database_fix_assistance_types.sql b/backend/sql/database_fix_assistance_types.sql deleted file mode 100644 index 6422d817..00000000 --- a/backend/sql/database_fix_assistance_types.sql +++ /dev/null @@ -1,21 +0,0 @@ --- 修复 jcbt_poverty_application 表中 assistance_types 字段的问题 --- 问题: Field 'assistance_types' doesn't have a default value --- 解决方案: 设置字段允许NULL或者添加默认值 - -USE hzdyjs_xg; - --- 修改 assistance_types 字段,允许NULL值 -ALTER TABLE jcbt_poverty_application -MODIFY COLUMN assistance_types varchar(200) COMMENT '申请资助类型' DEFAULT NULL; - --- 检查修改结果 -SELECT - COLUMN_NAME as '字段名', - DATA_TYPE as '数据类型', - IS_NULLABLE as '允许NULL', - COLUMN_DEFAULT as '默认值', - COLUMN_COMMENT as '注释' -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' -AND TABLE_NAME = 'jcbt_poverty_application' -AND COLUMN_NAME = 'assistance_types'; diff --git a/backend/sql/database_fix_jcbt_poverty_application.sql b/backend/sql/database_fix_jcbt_poverty_application.sql deleted file mode 100644 index 621cc994..00000000 --- a/backend/sql/database_fix_jcbt_poverty_application.sql +++ /dev/null @@ -1,189 +0,0 @@ --- ============================================================================ --- 贫困生认定申请表字段修复脚本 --- 创建时间: 2025-01-03 --- 描述: 修复 jcbt_poverty_application 表缺失的字段 --- ============================================================================ - --- 检查并添加缺失的字段到 jcbt_poverty_application 表 - --- 1. 检查表是否存在,如不存在则创建完整表 -CREATE TABLE IF NOT EXISTS `jcbt_poverty_application` ( - `id` bigint(20) NOT NULL COMMENT '主键ID', - `application_no` varchar(50) DEFAULT NULL COMMENT '申请编号', - `academic_year` varchar(20) DEFAULT NULL COMMENT '学年', - `term` int(11) DEFAULT NULL COMMENT '学期', - `student_id` bigint(20) DEFAULT NULL COMMENT '学生ID', - `student_no` varchar(50) DEFAULT NULL COMMENT '学号', - `student_name` varchar(100) DEFAULT NULL COMMENT '学生姓名', - `gender` varchar(10) DEFAULT NULL COMMENT '性别', - `college_id` bigint(20) DEFAULT NULL COMMENT '学院ID', - `college_name` varchar(100) DEFAULT NULL COMMENT '学院名称', - `major_id` bigint(20) DEFAULT NULL COMMENT '专业ID', - `major_name` varchar(100) DEFAULT NULL COMMENT '专业名称', - `class_id` bigint(20) DEFAULT NULL COMMENT '班级ID', - `class_name` varchar(100) DEFAULT NULL COMMENT '班级名称', - `recognition_group` varchar(50) DEFAULT NULL COMMENT '认定群体(特殊群体/其他群体)', - `recognition_type_id` bigint(20) DEFAULT NULL COMMENT '认定类型ID', - `recognition_type_code` varchar(50) DEFAULT NULL COMMENT '认定类型编码', - `recognition_type_name` varchar(100) DEFAULT NULL COMMENT '认定类型名称', - `assistance_type_ids` varchar(500) DEFAULT NULL COMMENT '申请自助类型ID数组(多选)', - `assistance_type_names` varchar(500) DEFAULT NULL COMMENT '申请自助类型名称(多个用逗号分隔)', - `family_economy_summary` text COMMENT '家庭经济情况摘要', - `attach_application_form_url` varchar(500) DEFAULT NULL COMMENT '申请表附件URL', - `attach_application_form_id` bigint(20) DEFAULT NULL COMMENT '申请表附件ID', - `attach_survey_form_url` varchar(500) DEFAULT NULL COMMENT '调查表附件URL', - `attach_survey_form_id` bigint(20) DEFAULT NULL COMMENT '调查表附件ID', - `status` varchar(20) DEFAULT 'DRAFT' COMMENT '申请状态(DRAFT-草稿,SUBMITTED-已提交,REVIEWING-审核中,APPROVED-已通过,REJECTED-已拒绝,WITHDRAWN-已撤回)', - `submitter_id` bigint(20) DEFAULT NULL COMMENT '提交人ID', - `submitter_name` varchar(100) DEFAULT NULL COMMENT '提交人姓名', - `submit_time` datetime DEFAULT NULL COMMENT '提交时间', - `auditor_id` bigint(20) DEFAULT NULL COMMENT '审核人ID', - `auditor_name` varchar(100) DEFAULT NULL COMMENT '审核人姓名', - `audit_time` datetime DEFAULT NULL COMMENT '审核时间', - `audit_opinion` text COMMENT '审核意见', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `deleted` bigint(20) DEFAULT '0' COMMENT '逻辑删除标记(0-正常,非0-已删除)', - `tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号', - `create_by` bigint(20) DEFAULT NULL COMMENT '创建者', - `create_time` datetime DEFAULT NULL COMMENT '创建时间', - `update_by` bigint(20) DEFAULT NULL COMMENT '更新者', - `update_time` datetime DEFAULT NULL COMMENT '更新时间', - PRIMARY KEY (`id`), - KEY `idx_student_no` (`student_no`), - KEY `idx_status` (`status`), - KEY `idx_academic_year` (`academic_year`), - KEY `idx_college_id` (`college_id`), - KEY `idx_tenant_deleted` (`tenant_id`, `deleted`), - KEY `idx_create_time` (`create_time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='贫困生认定申请表'; - --- 2. 如果表已存在但缺少字段,则添加缺失字段 --- 添加 term 字段(如果不存在) -SET @sql = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'term'); - -SET @sql = IF(@sql = 0, - 'ALTER TABLE jcbt_poverty_application ADD COLUMN term int(11) DEFAULT NULL COMMENT ''学期'' AFTER academic_year', - 'SELECT ''term字段已存在'' as result'); - -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 添加 attach_application_form_id 字段(如果不存在) -SET @sql = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'attach_application_form_id'); - -SET @sql = IF(@sql = 0, - 'ALTER TABLE jcbt_poverty_application ADD COLUMN attach_application_form_id bigint(20) DEFAULT NULL COMMENT ''申请表附件ID'' AFTER attach_application_form_url', - 'SELECT ''attach_application_form_id字段已存在'' as result'); - -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 添加 submit_time 字段(如果不存在) -SET @sql = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'submit_time'); - -SET @sql = IF(@sql = 0, - 'ALTER TABLE jcbt_poverty_application ADD COLUMN submit_time datetime DEFAULT NULL COMMENT ''提交时间'' AFTER submitter_name', - 'SELECT ''submit_time字段已存在'' as result'); - -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 添加 auditor_id 字段(如果不存在) -SET @sql = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'auditor_id'); - -SET @sql = IF(@sql = 0, - 'ALTER TABLE jcbt_poverty_application ADD COLUMN auditor_id bigint(20) DEFAULT NULL COMMENT ''审核人ID'' AFTER submit_time', - 'SELECT ''auditor_id字段已存在'' as result'); - -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 添加 auditor_name 字段(如果不存在) -SET @sql = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'auditor_name'); - -SET @sql = IF(@sql = 0, - 'ALTER TABLE jcbt_poverty_application ADD COLUMN auditor_name varchar(100) DEFAULT NULL COMMENT ''审核人姓名'' AFTER auditor_id', - 'SELECT ''auditor_name字段已存在'' as result'); - -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 添加 audit_time 字段(如果不存在) -SET @sql = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'audit_time'); - -SET @sql = IF(@sql = 0, - 'ALTER TABLE jcbt_poverty_application ADD COLUMN audit_time datetime DEFAULT NULL COMMENT ''审核时间'' AFTER auditor_name', - 'SELECT ''audit_time字段已存在'' as result'); - -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 添加 audit_opinion 字段(如果不存在) -SET @sql = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'audit_opinion'); - -SET @sql = IF(@sql = 0, - 'ALTER TABLE jcbt_poverty_application ADD COLUMN audit_opinion text COMMENT ''审核意见'' AFTER audit_time', - 'SELECT ''audit_opinion字段已存在'' as result'); - -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 3. 验证表结构 -SELECT 'Table structure verification:' as message; -DESCRIBE jcbt_poverty_application; - --- 4. 检查缺失的字段 -SELECT - 'Missing fields check:' as message, - CASE - WHEN (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'term') = 0 - THEN 'term字段缺失' - ELSE 'term字段存在' - END as term_status, - CASE - WHEN (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'attach_application_form_id') = 0 - THEN 'attach_application_form_id字段缺失' - ELSE 'attach_application_form_id字段存在' - END as attach_application_form_id_status; - --- ============================================================================ --- 使用说明: --- 1. 连接到对应的数据库 --- 2. 执行本脚本 --- 3. 检查输出结果,确认所有字段都已正确添加 --- 4. 重新启动后端服务 --- ============================================================================ diff --git a/backend/sql/database_fix_jcbt_poverty_application_complete.sql b/backend/sql/database_fix_jcbt_poverty_application_complete.sql deleted file mode 100644 index 7a954c7d..00000000 --- a/backend/sql/database_fix_jcbt_poverty_application_complete.sql +++ /dev/null @@ -1,98 +0,0 @@ --- ================================================================ --- 贫困生认定申请表完整修复脚本 --- 文件: database_fix_jcbt_poverty_application_complete.sql --- 功能: 修复 teacher_id 字段默认值问题,添加 family_economy_summary 字段 --- ================================================================ - -USE hzdyjs_xg; - --- 1. 检查并修复 teacher_id 字段 -SELECT '开始检查 teacher_id 字段...' AS message; - --- 检查字段是否存在以及是否允许NULL -SELECT - COLUMN_NAME, - IS_NULLABLE, - COLUMN_DEFAULT, - DATA_TYPE -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'teacher_id'; - --- 修改 teacher_id 字段为允许NULL(如果字段存在且不允许NULL) -SET @sql = ''; -SELECT COUNT(*) INTO @count -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'teacher_id' - AND IS_NULLABLE = 'NO'; - -SELECT IF(@count > 0, - CONCAT('ALTER TABLE jcbt_poverty_application MODIFY COLUMN teacher_id bigint(20) NULL COMMENT ''班主任教师ID'';'), - 'SELECT ''teacher_id 字段已允许NULL或不存在'' AS result;' -) INTO @sql; - -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - -SELECT '✅ teacher_id 字段修复完成' AS message; - --- 2. 检查并添加 family_economy_summary 字段 -SELECT '开始检查 family_economy_summary 字段...' AS message; - -SET @sql = ''; -SELECT COUNT(*) INTO @count -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'family_economy_summary'; - -SELECT IF(@count = 0, - 'ALTER TABLE jcbt_poverty_application ADD COLUMN family_economy_summary text COMMENT ''家庭经济情况摘要'' AFTER assistance_type_names;', - 'SELECT ''family_economy_summary 字段已存在'' AS result;' -) INTO @sql; - -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - -SELECT '✅ family_economy_summary 字段检查完成' AS message; - --- 3. 验证修复结果 -SELECT '=== 修复结果验证 ===' AS message; - --- 显示相关字段信息 -SELECT - COLUMN_NAME AS '字段名', - DATA_TYPE AS '数据类型', - IS_NULLABLE AS '允许NULL', - COLUMN_DEFAULT AS '默认值', - COLUMN_COMMENT AS '注释' -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME IN ('teacher_id', 'family_economy_summary') -ORDER BY ORDINAL_POSITION; - --- 4. 显示完整表结构(关键字段) -SELECT '=== 表结构确认 ===' AS message; - -SELECT - COLUMN_NAME AS '字段名', - DATA_TYPE AS '数据类型', - IS_NULLABLE AS '允许NULL', - COLUMN_COMMENT AS '注释' -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME IN ( - 'id', 'student_id', 'class_id', 'teacher_id', - 'family_economy_summary', 'status', 'create_by', 'create_time' - ) -ORDER BY ORDINAL_POSITION; - -SELECT '🎉 所有修复操作完成!' AS message; -SELECT '📝 请重启后端服务以生效!' AS note; diff --git a/backend/sql/database_fix_teacher_name.sql b/backend/sql/database_fix_teacher_name.sql deleted file mode 100644 index 863d6e1f..00000000 --- a/backend/sql/database_fix_teacher_name.sql +++ /dev/null @@ -1,131 +0,0 @@ --- ================================================================ --- 贫困生认定申请表 teacher_name 字段修复脚本 --- 文件: database_fix_teacher_name.sql --- 功能: 添加缺失的 teacher_name 字段,并确保允许NULL --- ================================================================ - -USE hzdyjs_xg; - --- 1. 检查 teacher_name 字段是否存在 -SELECT '开始检查 teacher_name 字段...' AS message; - -SELECT - COLUMN_NAME, - IS_NULLABLE, - COLUMN_DEFAULT, - DATA_TYPE -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'teacher_name'; - --- 2. 添加 teacher_name 字段(如果不存在) -SET @sql = ''; -SELECT COUNT(*) INTO @count -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'teacher_name'; - -SELECT IF(@count = 0, - 'ALTER TABLE jcbt_poverty_application ADD COLUMN teacher_name varchar(100) DEFAULT NULL COMMENT ''班主任教师姓名'' AFTER teacher_id;', - 'SELECT ''teacher_name 字段已存在,检查是否需要修改属性'' AS result;' -) INTO @sql; - -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 3. 修改 teacher_name 字段为允许 NULL(如果字段存在且不允许NULL) -SET @sql = ''; -SELECT COUNT(*) INTO @count -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'teacher_name' - AND IS_NULLABLE = 'NO'; - -SELECT IF(@count > 0, - 'ALTER TABLE jcbt_poverty_application MODIFY COLUMN teacher_name varchar(100) DEFAULT NULL COMMENT ''班主任教师姓名'';', - 'SELECT ''teacher_name 字段已允许NULL'' AS result;' -) INTO @sql; - -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - -SELECT '✅ teacher_name 字段修复完成' AS message; - --- 4. 添加 teacher_id 字段(如果不存在) -SET @sql = ''; -SELECT COUNT(*) INTO @count -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'teacher_id'; - -SELECT IF(@count = 0, - 'ALTER TABLE jcbt_poverty_application ADD COLUMN teacher_id bigint(20) DEFAULT NULL COMMENT ''班主任教师ID'' AFTER class_name;', - 'SELECT ''teacher_id 字段已存在'' AS result;' -) INTO @sql; - -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 5. 确保 teacher_id 字段允许 NULL -SELECT '检查 teacher_id 字段...' AS message; - -SET @sql = ''; -SELECT COUNT(*) INTO @count -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'teacher_id' - AND IS_NULLABLE = 'NO'; - -SELECT IF(@count > 0, - 'ALTER TABLE jcbt_poverty_application MODIFY COLUMN teacher_id bigint(20) DEFAULT NULL COMMENT ''班主任教师ID'';', - 'SELECT ''teacher_id 字段已允许NULL或不存在'' AS result;' -) INTO @sql; - -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - -SELECT '✅ teacher_id 字段修复完成' AS message; - --- 6. 验证修复结果 -SELECT '=== 修复结果验证 ===' AS message; - -SELECT - COLUMN_NAME AS '字段名', - DATA_TYPE AS '数据类型', - IS_NULLABLE AS '允许NULL', - COLUMN_DEFAULT AS '默认值', - COLUMN_COMMENT AS '注释' -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME IN ('teacher_id', 'teacher_name') -ORDER BY ORDINAL_POSITION; - --- 7. 显示完整的字段结构确认 -SELECT '=== 相关字段结构确认 ===' AS message; - -SELECT - COLUMN_NAME AS '字段名', - DATA_TYPE AS '数据类型', - IS_NULLABLE AS '允许NULL', - COLUMN_DEFAULT AS '默认值', - COLUMN_COMMENT AS '注释' -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME IN ( - 'class_id', 'class_name', 'teacher_id', 'teacher_name', 'recognition_group' - ) -ORDER BY ORDINAL_POSITION; - -SELECT '🎉 teacher_name 和 teacher_id 字段修复完成!' AS message; -SELECT '📝 请重启后端服务以生效!' AS note; diff --git a/backend/sql/database_fix_teacher_name_field.sql b/backend/sql/database_fix_teacher_name_field.sql deleted file mode 100644 index 69db6e9a..00000000 --- a/backend/sql/database_fix_teacher_name_field.sql +++ /dev/null @@ -1,75 +0,0 @@ --- ================================================================ --- 贫困生认定申请表teacher_name字段修复脚本 --- 文件: database_fix_teacher_name_field.sql --- 功能: 添加缺失的teacher_name字段 --- ================================================================ - -USE hzdyjs_xg; - --- 1. 检查并添加 teacher_name 字段 -SELECT '开始检查 teacher_name 字段...' AS message; - --- 检查字段是否存在 -SELECT - COLUMN_NAME, - IS_NULLABLE, - COLUMN_DEFAULT, - DATA_TYPE -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'teacher_name'; - --- 添加 teacher_name 字段(如果不存在) -SET @sql = ''; -SELECT COUNT(*) INTO @count -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME = 'teacher_name'; - -SELECT IF(@count = 0, - 'ALTER TABLE jcbt_poverty_application ADD COLUMN teacher_name varchar(100) NULL COMMENT ''班主任教师姓名'' AFTER teacher_id;', - 'SELECT ''teacher_name 字段已存在'' AS result;' -) INTO @sql; - -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - -SELECT '✅ teacher_name 字段检查完成' AS message; - --- 2. 验证修复结果 -SELECT '=== 修复结果验证 ===' AS message; - --- 显示teacher相关字段信息 -SELECT - COLUMN_NAME AS '字段名', - DATA_TYPE AS '数据类型', - IS_NULLABLE AS '允许NULL', - COLUMN_DEFAULT AS '默认值', - COLUMN_COMMENT AS '注释' -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME LIKE '%teacher%' -ORDER BY ORDINAL_POSITION; - --- 3. 显示完整表结构确认 -SELECT '=== 班级相关字段确认 ===' AS message; - -SELECT - COLUMN_NAME AS '字段名', - DATA_TYPE AS '数据类型', - IS_NULLABLE AS '允许NULL', - COLUMN_COMMENT AS '注释' -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME IN ( - 'class_id', 'class_name', 'teacher_id', 'teacher_name' - ) -ORDER BY ORDINAL_POSITION; - -SELECT '🎉 teacher_name字段修复操作完成!' AS message; -SELECT '📝 请重启后端服务以生效!' AS note; diff --git a/backend/sql/debug_compatibility_type.sql b/backend/sql/debug_compatibility_type.sql deleted file mode 100644 index 86b790d5..00000000 --- a/backend/sql/debug_compatibility_type.sql +++ /dev/null @@ -1,30 +0,0 @@ --- 调试compatibility_type字段问题 --- 检查表结构 -DESCRIBE jcbt_scholarship_type; - --- 检查字段是否存在 -SHOW COLUMNS FROM jcbt_scholarship_type LIKE 'compatibility_type'; - --- 查看具体数据 -SELECT id, type_name, compatibility_type, - CASE - WHEN compatibility_type IS NULL THEN 'NULL值' - WHEN compatibility_type = '' THEN '空字符串' - ELSE CONCAT('有值:', compatibility_type) - END as value_status -FROM jcbt_scholarship_type -WHERE deleted = 0 -LIMIT 10; - --- 统计各种值的分布 -SELECT - compatibility_type, - COUNT(*) as count, - CASE - WHEN compatibility_type IS NULL THEN 'NULL值' - WHEN compatibility_type = '' THEN '空字符串' - ELSE '有效值' - END as value_type -FROM jcbt_scholarship_type -WHERE deleted = 0 -GROUP BY compatibility_type; diff --git a/backend/sql/debug_quota_delete_issue.sql b/backend/sql/debug_quota_delete_issue.sql deleted file mode 100644 index d866ddd1..00000000 --- a/backend/sql/debug_quota_delete_issue.sql +++ /dev/null @@ -1,134 +0,0 @@ --- ======================================== --- 奖学金名额分配删除问题诊断脚本 --- 创建时间: 2025-01-28 --- 说明: 用于排查无法删除的名额分配记录 --- ======================================== - -USE hzdyjs_xg; - --- 1. 查看所有名额分配记录的状态 -SELECT - id, - batch_id, - scholarship_type_id, - allocation_level, - target_dept_id, - total_quota, - used_quota, - reserved_quota, - available_quota, - is_adjustable, - is_locked, - status, - deleted, - create_time, - update_time -FROM jcbt_scholarship_quota -WHERE deleted = 0 -ORDER BY create_time DESC; - --- 2. 查找可能有问题的记录(已使用名额大于0或已锁定) -SELECT - '已使用名额大于0' as issue_type, - id, - batch_id, - scholarship_type_id, - total_quota, - used_quota, - reserved_quota, - available_quota, - is_locked, - status -FROM jcbt_scholarship_quota -WHERE deleted = 0 AND used_quota > 0 - -UNION ALL - -SELECT - '已被锁定' as issue_type, - id, - batch_id, - scholarship_type_id, - total_quota, - used_quota, - reserved_quota, - available_quota, - is_locked, - status -FROM jcbt_scholarship_quota -WHERE deleted = 0 AND is_locked = 1 - -UNION ALL - -SELECT - '状态异常' as issue_type, - id, - batch_id, - scholarship_type_id, - total_quota, - used_quota, - reserved_quota, - available_quota, - is_locked, - status -FROM jcbt_scholarship_quota -WHERE deleted = 0 AND status != 'ACTIVE'; - --- 3. 检查是否存在外键约束或关联数据 --- 检查是否有申请记录引用了这些名额 -SELECT - q.id as quota_id, - q.batch_id, - q.scholarship_type_id, - q.used_quota, - q.is_locked, - COUNT(DISTINCT a.id) as application_count -FROM jcbt_scholarship_quota q -LEFT JOIN jcbt_scholarship_application a ON a.batch_id = q.batch_id - AND a.scholarship_type_id = q.scholarship_type_id -WHERE q.deleted = 0 -GROUP BY q.id, q.batch_id, q.scholarship_type_id, q.used_quota, q.is_locked -HAVING application_count > 0 OR q.used_quota > 0 OR q.is_locked = 1; - --- 4. 检查是否有下级名额分配 -SELECT - parent.id as parent_quota_id, - parent.allocation_level as parent_level, - parent.used_quota as parent_used_quota, - parent.is_locked as parent_is_locked, - COUNT(child.id) as child_count -FROM jcbt_scholarship_quota parent -LEFT JOIN jcbt_scholarship_quota child ON child.parent_quota_id = parent.id AND child.deleted = 0 -WHERE parent.deleted = 0 -GROUP BY parent.id, parent.allocation_level, parent.used_quota, parent.is_locked -HAVING child_count > 0; - --- 5. 详细查看有问题的记录(根据实际情况调整ID) --- 请替换下面的ID为实际无法删除的记录ID --- SELECT * FROM jcbt_scholarship_quota WHERE id = 替换为实际ID; - --- 6. 查看相关批次信息 -SELECT - b.id as batch_id, - b.batch_name, - b.batch_code, - b.status as batch_status, - b.start_time, - b.end_time, - COUNT(q.id) as quota_count -FROM jcbt_scholarship_batch b -LEFT JOIN jcbt_scholarship_quota q ON q.batch_id = b.id AND q.deleted = 0 -WHERE b.deleted = 0 -GROUP BY b.id, b.batch_name, b.batch_code, b.status, b.start_time, b.end_time; - --- 7. 查看奖学金类型信息 -SELECT - st.id as type_id, - st.type_name, - st.type_code, - st.status as type_status, - COUNT(q.id) as quota_count -FROM jcbt_scholarship_type st -LEFT JOIN jcbt_scholarship_quota q ON q.scholarship_type_id = st.id AND q.deleted = 0 -WHERE st.deleted = 0 -GROUP BY st.id, st.type_name, st.type_code, st.status; diff --git a/backend/sql/diagnose_quota_display_issue.sql b/backend/sql/diagnose_quota_display_issue.sql deleted file mode 100644 index ea988bd8..00000000 --- a/backend/sql/diagnose_quota_display_issue.sql +++ /dev/null @@ -1,249 +0,0 @@ --- =================================================================== --- 奖学金名额显示问题诊断脚本 --- 创建时间: 2025-01-27 --- 目的: 检查班级名额分配和奖学金类型的数据完整性 --- =================================================================== - --- 1. 检查当前班主任和班级信息 -SELECT - '=== 1. 班主任和班级信息检查 ===' as info; - -SELECT - u.user_id, - u.user_name, - u.nick_name, - c.class_id, - c.class_name, - c.grade_level, - c.dept_id, - d.dept_name -FROM sys_user u -LEFT JOIN jcbt_class c ON u.user_id = c.head_teacher_id -LEFT JOIN sys_dept d ON c.dept_id = d.dept_id -WHERE u.user_type = '03' -- 班主任 -AND c.class_id IS NOT NULL -ORDER BY c.class_id; - --- 2. 检查奖学金批次信息 -SELECT - '=== 2. 奖学金批次信息检查 ===' as info; - -SELECT - batch_id, - batch_name, - batch_status, - start_date, - end_date, - create_time -FROM jcbt_scholarship_batch -WHERE batch_status = '1' -- 启用状态 -ORDER BY create_time DESC; - --- 3. 检查奖学金类型信息 -SELECT - '=== 3. 奖学金类型信息检查 ===' as info; - -SELECT - st.type_id, - st.type_name, - st.type_level, - st.type_status, - bt.batch_id, - b.batch_name -FROM jcbt_scholarship_type st -LEFT JOIN jcbt_batch_type bt ON st.type_id = bt.type_id -LEFT JOIN jcbt_scholarship_batch b ON bt.batch_id = b.batch_id -WHERE st.type_status = '1' -- 启用状态 -ORDER BY st.type_level, st.type_name; - --- 4. 检查班级名额分配情况 -SELECT - '=== 4. 班级名额分配情况检查 ===' as info; - -SELECT - qa.allocation_id, - qa.batch_id, - b.batch_name, - qa.type_id, - st.type_name, - qa.class_id, - c.class_name, - qa.allocated_quota, - qa.allocation_basis, - qa.create_time -FROM jcbt_quota_allocation qa -LEFT JOIN jcbt_scholarship_batch b ON qa.batch_id = b.batch_id -LEFT JOIN jcbt_scholarship_type st ON qa.type_id = st.type_id -LEFT JOIN jcbt_class c ON qa.class_id = c.class_id -ORDER BY qa.batch_id, qa.class_id, qa.type_id; - --- 5. 检查已使用名额情况 -SELECT - '=== 5. 已使用名额情况检查 ===' as info; - -SELECT - sa.batch_id, - b.batch_name, - sa.scholarship_type_id, - st.type_name, - sa.applicant_class_id, - c.class_name, - COUNT(*) as used_quota, - GROUP_CONCAT(sa.application_status) as status_list -FROM jcbt_scholarship_application sa -LEFT JOIN jcbt_scholarship_batch b ON sa.batch_id = b.batch_id -LEFT JOIN jcbt_scholarship_type st ON sa.scholarship_type_id = st.type_id -LEFT JOIN jcbt_class c ON sa.applicant_class_id = c.class_id -WHERE sa.application_status IN ('submitted', 'approved') -- 有效申请 -GROUP BY sa.batch_id, sa.scholarship_type_id, sa.applicant_class_id -ORDER BY sa.batch_id, sa.applicant_class_id, sa.scholarship_type_id; - --- 6. 检查名额使用情况汇总 -SELECT - '=== 6. 名额使用情况汇总 ===' as info; - -SELECT - qa.batch_id, - b.batch_name, - qa.class_id, - c.class_name, - qa.type_id, - st.type_name, - qa.allocated_quota, - COALESCE(used.used_count, 0) as used_quota, - (qa.allocated_quota - COALESCE(used.used_count, 0)) as available_quota, - CASE - WHEN qa.allocated_quota = 0 THEN 'unlimited' - WHEN (qa.allocated_quota - COALESCE(used.used_count, 0)) > 0 THEN 'available' - ELSE 'full' - END as quota_status -FROM jcbt_quota_allocation qa -LEFT JOIN jcbt_scholarship_batch b ON qa.batch_id = b.batch_id -LEFT JOIN jcbt_scholarship_type st ON qa.type_id = st.type_id -LEFT JOIN jcbt_class c ON qa.class_id = c.class_id -LEFT JOIN ( - SELECT - batch_id, - scholarship_type_id, - applicant_class_id, - COUNT(*) as used_count - FROM jcbt_scholarship_application - WHERE application_status IN ('submitted', 'approved') - GROUP BY batch_id, scholarship_type_id, applicant_class_id -) used ON qa.batch_id = used.batch_id - AND qa.type_id = used.scholarship_type_id - AND qa.class_id = used.applicant_class_id -ORDER BY qa.batch_id, qa.class_id, qa.type_id; - --- 7. 检查特定班级的详细情况(以班级ID=1为例) -SELECT - '=== 7. 特定班级详细情况检查 (class_id=1) ===' as info; - -SELECT - '班级基本信息:' as section, - c.class_id, - c.class_name, - c.grade_level, - u.user_name as head_teacher_name, - u.nick_name as head_teacher_nick -FROM jcbt_class c -LEFT JOIN sys_user u ON c.head_teacher_id = u.user_id -WHERE c.class_id = 1; - -SELECT - '该班级的名额分配:' as section, - qa.type_id, - st.type_name, - qa.allocated_quota, - qa.allocation_basis -FROM jcbt_quota_allocation qa -LEFT JOIN jcbt_scholarship_type st ON qa.type_id = st.type_id -WHERE qa.class_id = 1 -ORDER BY qa.type_id; - --- 8. 检查数据完整性问题 -SELECT - '=== 8. 数据完整性问题检查 ===' as info; - --- 检查缺少名额分配的奖学金类型 -SELECT - '缺少名额分配的奖学金类型:' as issue_type, - st.type_id, - st.type_name, - bt.batch_id, - b.batch_name -FROM jcbt_scholarship_type st -INNER JOIN jcbt_batch_type bt ON st.type_id = bt.type_id -INNER JOIN jcbt_scholarship_batch b ON bt.batch_id = b.batch_id -LEFT JOIN jcbt_quota_allocation qa ON st.type_id = qa.type_id AND bt.batch_id = qa.batch_id -WHERE st.type_status = '1' -AND b.batch_status = '1' -AND qa.allocation_id IS NULL; - --- 检查有名额分配但奖学金类型不存在的情况 -SELECT - '名额分配指向不存在的奖学金类型:' as issue_type, - qa.allocation_id, - qa.type_id, - qa.batch_id, - qa.class_id -FROM jcbt_quota_allocation qa -LEFT JOIN jcbt_scholarship_type st ON qa.type_id = st.type_id -WHERE st.type_id IS NULL; - --- 9. 模拟API查询结果 -SELECT - '=== 9. 模拟API查询结果 (batch_id=1, class_id=1) ===' as info; - -SELECT - st.type_id, - st.type_name, - st.type_level, - st.type_description, - CASE - WHEN qa.allocated_quota IS NULL THEN 0 - ELSE qa.allocated_quota - END as total_quota, - COALESCE(used.used_count, 0) as used_quota, - CASE - WHEN qa.allocated_quota IS NULL THEN 0 - WHEN qa.allocated_quota = 0 THEN 999999 - ELSE (qa.allocated_quota - COALESCE(used.used_count, 0)) - END as available_quota, - CASE - WHEN qa.allocated_quota IS NULL THEN FALSE - WHEN qa.allocated_quota = 0 THEN FALSE - ELSE TRUE - END as has_quota, - CASE - WHEN qa.allocated_quota IS NULL THEN 'unlimited' - WHEN qa.allocated_quota = 0 THEN 'unlimited' - WHEN (qa.allocated_quota - COALESCE(used.used_count, 0)) > 0 THEN 'available' - ELSE 'full' - END as quota_status -FROM jcbt_scholarship_type st -INNER JOIN jcbt_batch_type bt ON st.type_id = bt.type_id -LEFT JOIN jcbt_quota_allocation qa ON st.type_id = qa.type_id - AND bt.batch_id = qa.batch_id - AND qa.class_id = 1 -LEFT JOIN ( - SELECT - scholarship_type_id, - applicant_class_id, - COUNT(*) as used_count - FROM jcbt_scholarship_application - WHERE batch_id = 1 - AND applicant_class_id = 1 - AND application_status IN ('submitted', 'approved') - GROUP BY scholarship_type_id, applicant_class_id -) used ON st.type_id = used.scholarship_type_id -WHERE bt.batch_id = 1 -AND st.type_status = '1' -ORDER BY - CASE quota_status - WHEN 'available' THEN 1 - WHEN 'unlimited' THEN 2 - WHEN 'full' THEN 3 - END, - st.type_level, - st.type_name; diff --git a/backend/sql/diagnose_quota_display_issue_correct.sql b/backend/sql/diagnose_quota_display_issue_correct.sql deleted file mode 100644 index e7928d46..00000000 --- a/backend/sql/diagnose_quota_display_issue_correct.sql +++ /dev/null @@ -1,245 +0,0 @@ --- =================================================================== --- 奖学金名额显示问题诊断脚本 (修正版) --- 创建时间: 2025-01-27 --- 目的: 检查班级名额分配和奖学金类型的数据完整性 --- =================================================================== - --- 1. 检查当前班主任和班级信息 -SELECT - '=== 1. 班主任和班级信息检查 ===' as info; - -SELECT - u.user_id, - u.user_name, - u.nick_name, - c.class_id, - c.class_name, - c.grade, - c.dept_id -FROM sys_user u -LEFT JOIN xg_class_info c ON u.user_id = c.head_teacher_id -WHERE u.user_type = '03' -- 班主任 -AND c.class_id IS NOT NULL -ORDER BY c.class_id; - --- 2. 检查奖学金批次信息 -SELECT - '=== 2. 奖学金批次信息检查 ===' as info; - -SELECT - batch_id, - batch_name, - batch_status, - start_date, - end_date, - create_time -FROM jcbt_scholarship_batch -WHERE batch_status = '1' -- 启用状态 -ORDER BY create_time DESC; - --- 3. 检查奖学金类型信息 -SELECT - '=== 3. 奖学金类型信息检查 ===' as info; - -SELECT - st.type_id, - st.type_name, - st.type_level, - st.type_status, - bt.batch_id, - b.batch_name -FROM jcbt_scholarship_type st -LEFT JOIN jcbt_scholarship_batch_type bt ON st.type_id = bt.type_id -LEFT JOIN jcbt_scholarship_batch b ON bt.batch_id = b.batch_id -WHERE st.type_status = '1' -- 启用状态 -ORDER BY st.type_level, st.type_name; - --- 4. 检查班级名额分配情况 -SELECT - '=== 4. 班级名额分配情况检查 ===' as info; - -SELECT - cq.quota_id, - cq.batch_id, - b.batch_name, - cq.type_id, - st.type_name, - cq.class_id, - c.class_name, - cq.allocated_quota, - cq.create_time -FROM jcbt_scholarship_class_quota cq -LEFT JOIN jcbt_scholarship_batch b ON cq.batch_id = b.batch_id -LEFT JOIN jcbt_scholarship_type st ON cq.type_id = st.type_id -LEFT JOIN xg_class_info c ON cq.class_id = c.class_id -ORDER BY cq.batch_id, cq.class_id, cq.type_id; - --- 5. 检查已使用名额情况 -SELECT - '=== 5. 已使用名额情况检查 ===' as info; - -SELECT - sa.batch_id, - b.batch_name, - sa.scholarship_type_id, - st.type_name, - sa.applicant_class_id, - c.class_name, - COUNT(*) as used_quota, - GROUP_CONCAT(sa.application_status) as status_list -FROM jcbt_scholarship_application sa -LEFT JOIN jcbt_scholarship_batch b ON sa.batch_id = b.batch_id -LEFT JOIN jcbt_scholarship_type st ON sa.scholarship_type_id = st.type_id -LEFT JOIN xg_class_info c ON sa.applicant_class_id = c.class_id -WHERE sa.application_status IN ('submitted', 'approved') -- 有效申请 -GROUP BY sa.batch_id, sa.scholarship_type_id, sa.applicant_class_id -ORDER BY sa.batch_id, sa.applicant_class_id, sa.scholarship_type_id; - --- 6. 检查名额使用情况汇总 -SELECT - '=== 6. 名额使用情况汇总 ===' as info; - -SELECT - cq.batch_id, - b.batch_name, - cq.class_id, - c.class_name, - cq.type_id, - st.type_name, - cq.allocated_quota, - COALESCE(used.used_count, 0) as used_quota, - (cq.allocated_quota - COALESCE(used.used_count, 0)) as available_quota, - CASE - WHEN cq.allocated_quota = 0 THEN 'unlimited' - WHEN (cq.allocated_quota - COALESCE(used.used_count, 0)) > 0 THEN 'available' - ELSE 'full' - END as quota_status -FROM jcbt_scholarship_class_quota cq -LEFT JOIN jcbt_scholarship_batch b ON cq.batch_id = b.batch_id -LEFT JOIN jcbt_scholarship_type st ON cq.type_id = st.type_id -LEFT JOIN xg_class_info c ON cq.class_id = c.class_id -LEFT JOIN ( - SELECT - batch_id, - scholarship_type_id, - applicant_class_id, - COUNT(*) as used_count - FROM jcbt_scholarship_application - WHERE application_status IN ('submitted', 'approved') - GROUP BY batch_id, scholarship_type_id, applicant_class_id -) used ON cq.batch_id = used.batch_id - AND cq.type_id = used.scholarship_type_id - AND cq.class_id = used.applicant_class_id -ORDER BY cq.batch_id, cq.class_id, cq.type_id; - --- 7. 检查特定班级的详细情况(以班级ID=1为例) -SELECT - '=== 7. 特定班级详细情况检查 (class_id=1) ===' as info; - -SELECT - '班级基本信息:' as section, - c.class_id, - c.class_name, - c.grade, - u.user_name as head_teacher_name, - u.nick_name as head_teacher_nick -FROM xg_class_info c -LEFT JOIN sys_user u ON c.head_teacher_id = u.user_id -WHERE c.class_id = 1; - -SELECT - '该班级的名额分配:' as section, - cq.type_id, - st.type_name, - cq.allocated_quota -FROM jcbt_scholarship_class_quota cq -LEFT JOIN jcbt_scholarship_type st ON cq.type_id = st.type_id -WHERE cq.class_id = 1 -ORDER BY cq.type_id; - --- 8. 检查数据完整性问题 -SELECT - '=== 8. 数据完整性问题检查 ===' as info; - --- 检查缺少名额分配的奖学金类型 -SELECT - '缺少名额分配的奖学金类型:' as issue_type, - st.type_id, - st.type_name, - bt.batch_id, - b.batch_name -FROM jcbt_scholarship_type st -INNER JOIN jcbt_scholarship_batch_type bt ON st.type_id = bt.type_id -INNER JOIN jcbt_scholarship_batch b ON bt.batch_id = b.batch_id -LEFT JOIN jcbt_scholarship_class_quota cq ON st.type_id = cq.type_id AND bt.batch_id = cq.batch_id -WHERE st.type_status = '1' -AND b.batch_status = '1' -AND cq.quota_id IS NULL; - --- 检查有名额分配但奖学金类型不存在的情况 -SELECT - '名额分配指向不存在的奖学金类型:' as issue_type, - cq.quota_id, - cq.type_id, - cq.batch_id, - cq.class_id -FROM jcbt_scholarship_class_quota cq -LEFT JOIN jcbt_scholarship_type st ON cq.type_id = st.type_id -WHERE st.type_id IS NULL; - --- 9. 模拟API查询结果 -SELECT - '=== 9. 模拟API查询结果 (batch_id=1, class_id=1) ===' as info; - -SELECT - st.type_id, - st.type_name, - st.type_level, - st.type_description, - CASE - WHEN cq.allocated_quota IS NULL THEN 0 - ELSE cq.allocated_quota - END as total_quota, - COALESCE(used.used_count, 0) as used_quota, - CASE - WHEN cq.allocated_quota IS NULL THEN 0 - WHEN cq.allocated_quota = 0 THEN 999999 - ELSE (cq.allocated_quota - COALESCE(used.used_count, 0)) - END as available_quota, - CASE - WHEN cq.allocated_quota IS NULL THEN FALSE - WHEN cq.allocated_quota = 0 THEN FALSE - ELSE TRUE - END as has_quota, - CASE - WHEN cq.allocated_quota IS NULL THEN 'unlimited' - WHEN cq.allocated_quota = 0 THEN 'unlimited' - WHEN (cq.allocated_quota - COALESCE(used.used_count, 0)) > 0 THEN 'available' - ELSE 'full' - END as quota_status -FROM jcbt_scholarship_type st -INNER JOIN jcbt_scholarship_batch_type bt ON st.type_id = bt.type_id -LEFT JOIN jcbt_scholarship_class_quota cq ON st.type_id = cq.type_id - AND bt.batch_id = cq.batch_id - AND cq.class_id = 1 -LEFT JOIN ( - SELECT - scholarship_type_id, - applicant_class_id, - COUNT(*) as used_count - FROM jcbt_scholarship_application - WHERE batch_id = 1 - AND applicant_class_id = 1 - AND application_status IN ('submitted', 'approved') - GROUP BY scholarship_type_id, applicant_class_id -) used ON st.type_id = used.scholarship_type_id -WHERE bt.batch_id = 1 -AND st.type_status = '1' -ORDER BY - CASE quota_status - WHEN 'available' THEN 1 - WHEN 'unlimited' THEN 2 - WHEN 'full' THEN 3 - END, - st.type_level, - st.type_name; diff --git a/backend/sql/diagnose_quota_display_issue_corrected.sql b/backend/sql/diagnose_quota_display_issue_corrected.sql deleted file mode 100644 index 777f0868..00000000 --- a/backend/sql/diagnose_quota_display_issue_corrected.sql +++ /dev/null @@ -1,353 +0,0 @@ --- =================================================================== --- 奖学金名额显示问题诊断脚本 (完全修正版) --- 创建时间: 2025-01-27 --- 目的: 检查班级名额分配和奖学金类型的数据完整性 --- 表名和字段名完全修正 --- =================================================================== - --- 1. 检查当前班主任和班级信息 -SELECT - '=== 1. 班主任和班级信息检查 ===' as info; - -SELECT - u.user_id, - u.user_name, - u.nick_name, - c.id as class_id, - c.class_name, - c.grade, - c.college_id, - c.major_id -FROM sys_user u -LEFT JOIN xg_class_msg c ON u.user_id = c.class_teacher_id -WHERE u.user_type = '03' -- 班主任 -AND c.id IS NOT NULL -ORDER BY c.id; - --- 2. 检查奖学金批次信息 -SELECT - '=== 2. 奖学金批次信息检查 ===' as info; - -SELECT - id as batch_id, - batch_name, - status as batch_status, - start_time, - end_time, - create_time -FROM jcbt_scholarship_batch -WHERE status = 'ACTIVE' -- 启用状态 -ORDER BY create_time DESC; - --- 3. 检查奖学金类型信息 -SELECT - '=== 3. 奖学金类型信息检查 ===' as info; - -SELECT - st.id as type_id, - st.type_name, - st.level_type, - st.status as type_status, - bt.batch_id, - b.batch_name -FROM jcbt_scholarship_type st -LEFT JOIN jcbt_scholarship_batch_type bt ON st.id = bt.scholarship_type_id -LEFT JOIN jcbt_scholarship_batch b ON bt.batch_id = b.id -WHERE st.status = 1 -- 启用状态 -ORDER BY st.level_type, st.type_name; - --- 4. 检查班级名额分配情况 -SELECT - '=== 4. 班级名额分配情况检查 ===' as info; - -SELECT - cq.id as quota_id, - cq.batch_id, - b.batch_name, - cq.scholarship_type_id, - st.type_name, - cq.class_id, - c.class_name, - cq.total_quota, - cq.used_quota, - cq.available_quota, - cq.allocation_method, - cq.status, - cq.create_time -FROM jcbt_scholarship_class_quota cq -LEFT JOIN jcbt_scholarship_batch b ON cq.batch_id = b.id -LEFT JOIN jcbt_scholarship_type st ON cq.scholarship_type_id = st.id -LEFT JOIN xg_class_msg c ON cq.class_id = c.id -ORDER BY cq.batch_id, cq.class_id, cq.scholarship_type_id; - --- 5. 检查已使用名额情况 -SELECT - '=== 5. 已使用名额情况检查 ===' as info; - -SELECT - sa.batch_id, - b.batch_name, - sa.scholarship_type_id, - st.type_name, - sa.applicant_class_id, - c.class_name, - COUNT(*) as actual_used_quota, - GROUP_CONCAT(sa.application_status) as status_list -FROM jcbt_scholarship_application sa -LEFT JOIN jcbt_scholarship_batch b ON sa.batch_id = b.id -LEFT JOIN jcbt_scholarship_type st ON sa.scholarship_type_id = st.id -LEFT JOIN xg_class_msg c ON sa.applicant_class_id = c.id -WHERE sa.application_status IN ('submitted', 'approved') -- 有效申请 -GROUP BY sa.batch_id, sa.scholarship_type_id, sa.applicant_class_id -ORDER BY sa.batch_id, sa.applicant_class_id, sa.scholarship_type_id; - --- 6. 检查名额使用情况汇总与一致性 -SELECT - '=== 6. 名额使用情况汇总与一致性检查 ===' as info; - -SELECT - cq.batch_id, - b.batch_name, - cq.class_id, - c.class_name, - cq.scholarship_type_id, - st.type_name, - cq.total_quota, - cq.used_quota as recorded_used_quota, - COALESCE(actual_used.used_count, 0) as actual_used_quota, - cq.available_quota as recorded_available_quota, - (cq.total_quota - COALESCE(actual_used.used_count, 0)) as calculated_available_quota, - CASE - WHEN cq.used_quota != COALESCE(actual_used.used_count, 0) THEN 'INCONSISTENT' - WHEN cq.available_quota != (cq.total_quota - COALESCE(actual_used.used_count, 0)) THEN 'INCONSISTENT' - ELSE 'CONSISTENT' - END as consistency_status, - CASE - WHEN cq.total_quota = 0 THEN 'unlimited' - WHEN (cq.total_quota - COALESCE(actual_used.used_count, 0)) > 0 THEN 'available' - ELSE 'full' - END as quota_status -FROM jcbt_scholarship_class_quota cq -LEFT JOIN jcbt_scholarship_batch b ON cq.batch_id = b.id -LEFT JOIN jcbt_scholarship_type st ON cq.scholarship_type_id = st.id -LEFT JOIN xg_class_msg c ON cq.class_id = c.id -LEFT JOIN ( - SELECT - batch_id, - scholarship_type_id, - applicant_class_id, - COUNT(*) as used_count - FROM jcbt_scholarship_application - WHERE application_status IN ('submitted', 'approved') - GROUP BY batch_id, scholarship_type_id, applicant_class_id -) actual_used ON cq.batch_id = actual_used.batch_id - AND cq.scholarship_type_id = actual_used.scholarship_type_id - AND cq.class_id = actual_used.applicant_class_id -ORDER BY cq.batch_id, cq.class_id, cq.scholarship_type_id; - --- 7. 检查特定班级的详细情况(以班级ID=1为例) -SELECT - '=== 7. 特定班级详细情况检查 (class_id=1) ===' as info; - -SELECT - '班级基本信息:' as section, - c.id as class_id, - c.class_name, - c.grade, - c.class_teacher_id, - u.user_name as class_teacher_name, - u.nick_name as class_teacher_nick -FROM xg_class_msg c -LEFT JOIN sys_user u ON c.class_teacher_id = u.user_id -WHERE c.id = 1; - -SELECT - '该班级的名额分配:' as section, - cq.scholarship_type_id, - st.type_name, - cq.total_quota, - cq.used_quota, - cq.available_quota, - cq.allocation_method, - cq.status -FROM jcbt_scholarship_class_quota cq -LEFT JOIN jcbt_scholarship_type st ON cq.scholarship_type_id = st.id -WHERE cq.class_id = 1 -ORDER BY cq.scholarship_type_id; - --- 8. 检查数据完整性问题 -SELECT - '=== 8. 数据完整性问题检查 ===' as info; - --- 检查缺少名额分配的奖学金类型 -SELECT - '缺少名额分配的奖学金类型:' as issue_type, - st.id as type_id, - st.type_name, - bt.batch_id, - b.batch_name, - COUNT(DISTINCT c.id) as total_classes_without_quota -FROM jcbt_scholarship_type st -INNER JOIN jcbt_scholarship_batch_type bt ON st.id = bt.scholarship_type_id -INNER JOIN jcbt_scholarship_batch b ON bt.batch_id = b.id -CROSS JOIN xg_class_msg c -LEFT JOIN jcbt_scholarship_class_quota cq ON st.id = cq.scholarship_type_id - AND bt.batch_id = cq.batch_id - AND c.id = cq.class_id -WHERE st.status = 1 -AND b.status = 'ACTIVE' -AND c.class_status = '1' -AND cq.id IS NULL -GROUP BY st.id, st.type_name, bt.batch_id, b.batch_name; - --- 检查有名额分配但奖学金类型不存在的情况 -SELECT - '名额分配指向不存在的奖学金类型:' as issue_type, - cq.id as quota_id, - cq.scholarship_type_id, - cq.batch_id, - cq.class_id -FROM jcbt_scholarship_class_quota cq -LEFT JOIN jcbt_scholarship_type st ON cq.scholarship_type_id = st.id -WHERE st.id IS NULL; - --- 检查名额分配指向不存在的班级 -SELECT - '名额分配指向不存在的班级:' as issue_type, - cq.id as quota_id, - cq.class_id, - cq.class_name as recorded_class_name -FROM jcbt_scholarship_class_quota cq -LEFT JOIN xg_class_msg c ON cq.class_id = c.id -WHERE c.id IS NULL; - --- 9. 模拟API查询结果 -SELECT - '=== 9. 模拟API查询结果 (batch_id=1, class_id=1) ===' as info; - -SELECT - st.id as type_id, - st.type_name, - st.level_type, - st.description, - CASE - WHEN cq.total_quota IS NULL THEN 0 - ELSE cq.total_quota - END as total_quota, - CASE - WHEN cq.used_quota IS NULL THEN 0 - ELSE cq.used_quota - END as used_quota, - CASE - WHEN cq.available_quota IS NULL THEN 0 - WHEN cq.total_quota = 0 THEN 999999 - ELSE cq.available_quota - END as available_quota, - CASE - WHEN cq.total_quota IS NULL THEN FALSE - WHEN cq.total_quota = 0 THEN FALSE - ELSE TRUE - END as has_quota, - CASE - WHEN cq.total_quota IS NULL THEN 'no_allocation' - WHEN cq.total_quota = 0 THEN 'unlimited' - WHEN cq.available_quota > 0 THEN 'available' - ELSE 'full' - END as quota_status, - cq.allocation_method, - cq.status as allocation_status -FROM jcbt_scholarship_type st -INNER JOIN jcbt_scholarship_batch_type bt ON st.id = bt.scholarship_type_id -LEFT JOIN jcbt_scholarship_class_quota cq ON st.id = cq.scholarship_type_id - AND bt.batch_id = cq.batch_id - AND cq.class_id = 1 -WHERE bt.batch_id = 1 -AND st.status = 1 -ORDER BY - CASE quota_status - WHEN 'available' THEN 1 - WHEN 'unlimited' THEN 2 - WHEN 'full' THEN 3 - WHEN 'no_allocation' THEN 4 - END, - st.level_type, - st.type_name; - --- 10. 额外检查:验证数据同步问题 -SELECT - '=== 10. 数据同步问题检查 ===' as info; - --- 检查used_quota和available_quota字段是否需要更新 -SELECT - 'need_quota_update' as check_type, - cq.id, - cq.batch_id, - cq.class_id, - cq.scholarship_type_id, - cq.total_quota, - cq.used_quota as recorded_used, - COALESCE(actual.actual_used, 0) as actual_used, - cq.available_quota as recorded_available, - (cq.total_quota - COALESCE(actual.actual_used, 0)) as calculated_available -FROM jcbt_scholarship_class_quota cq -LEFT JOIN ( - SELECT - batch_id, - scholarship_type_id, - applicant_class_id, - COUNT(*) as actual_used - FROM jcbt_scholarship_application - WHERE application_status IN ('submitted', 'approved') - GROUP BY batch_id, scholarship_type_id, applicant_class_id -) actual ON cq.batch_id = actual.batch_id - AND cq.scholarship_type_id = actual.scholarship_type_id - AND cq.class_id = actual.applicant_class_id -WHERE cq.used_quota != COALESCE(actual.actual_used, 0) - OR cq.available_quota != (cq.total_quota - COALESCE(actual.actual_used, 0)); - --- 11. 快速检查当前数据状态 -SELECT - '=== 11. 快速数据状态检查 ===' as info; - -SELECT - '总批次数' as item, - COUNT(*) as count -FROM jcbt_scholarship_batch; - -SELECT - '启用批次数' as item, - COUNT(*) as count -FROM jcbt_scholarship_batch -WHERE status = 'ACTIVE'; - -SELECT - '总奖学金类型数' as item, - COUNT(*) as count -FROM jcbt_scholarship_type; - -SELECT - '启用奖学金类型数' as item, - COUNT(*) as count -FROM jcbt_scholarship_type -WHERE status = 1; - -SELECT - '总班级数' as item, - COUNT(*) as count -FROM xg_class_msg; - -SELECT - '启用班级数' as item, - COUNT(*) as count -FROM xg_class_msg -WHERE class_status = '1'; - -SELECT - '总名额分配记录数' as item, - COUNT(*) as count -FROM jcbt_scholarship_class_quota; - -SELECT - '总申请记录数' as item, - COUNT(*) as count -FROM jcbt_scholarship_application; diff --git a/backend/sql/diagnose_quota_display_issue_final.sql b/backend/sql/diagnose_quota_display_issue_final.sql deleted file mode 100644 index 74c9d840..00000000 --- a/backend/sql/diagnose_quota_display_issue_final.sql +++ /dev/null @@ -1,306 +0,0 @@ --- =================================================================== --- 奖学金名额显示问题诊断脚本 (最终修正版) --- 创建时间: 2025-01-27 --- 目的: 检查班级名额分配和奖学金类型的数据完整性 --- 表名修正: xg_class_msg, jcbt_scholarship_class_quota --- =================================================================== - --- 1. 检查当前班主任和班级信息 -SELECT - '=== 1. 班主任和班级信息检查 ===' as info; - -SELECT - u.user_id, - u.user_name, - u.nick_name, - c.id as class_id, - c.class_name, - c.grade, - c.college_id, - c.major_id -FROM sys_user u -LEFT JOIN xg_class_msg c ON u.user_id = c.class_teacher_id -WHERE u.user_type = '03' -- 班主任 -AND c.id IS NOT NULL -ORDER BY c.id; - --- 2. 检查奖学金批次信息 -SELECT - '=== 2. 奖学金批次信息检查 ===' as info; - -SELECT - id as batch_id, - batch_name, - status as batch_status, - start_time, - end_time, - create_time -FROM jcbt_scholarship_batch -WHERE status = 'ACTIVE' -- 启用状态 -ORDER BY create_time DESC; - --- 3. 检查奖学金类型信息 -SELECT - '=== 3. 奖学金类型信息检查 ===' as info; - -SELECT - st.id as type_id, - st.type_name, - st.level_type, - st.status as type_status, - bt.batch_id, - b.batch_name -FROM jcbt_scholarship_type st -LEFT JOIN jcbt_scholarship_batch_type bt ON st.id = bt.scholarship_type_id -LEFT JOIN jcbt_scholarship_batch b ON bt.batch_id = b.id -WHERE st.status = 1 -- 启用状态 -ORDER BY st.level_type, st.type_name; - --- 4. 检查班级名额分配情况 -SELECT - '=== 4. 班级名额分配情况检查 ===' as info; - -SELECT - cq.id as quota_id, - cq.batch_id, - b.batch_name, - cq.scholarship_type_id, - st.type_name, - cq.class_id, - c.class_name, - cq.total_quota, - cq.used_quota, - cq.available_quota, - cq.allocation_method, - cq.status, - cq.create_time -FROM jcbt_scholarship_class_quota cq -LEFT JOIN jcbt_scholarship_batch b ON cq.batch_id = b.id -LEFT JOIN jcbt_scholarship_type st ON cq.scholarship_type_id = st.id -LEFT JOIN xg_class_msg c ON cq.class_id = c.id -ORDER BY cq.batch_id, cq.class_id, cq.scholarship_type_id; - --- 5. 检查已使用名额情况 -SELECT - '=== 5. 已使用名额情况检查 ===' as info; - -SELECT - sa.batch_id, - b.batch_name, - sa.scholarship_type_id, - st.type_name, - sa.applicant_class_id, - c.class_name, - COUNT(*) as actual_used_quota, - GROUP_CONCAT(sa.application_status) as status_list -FROM jcbt_scholarship_application sa -LEFT JOIN jcbt_scholarship_batch b ON sa.batch_id = b.batch_id -LEFT JOIN jcbt_scholarship_type st ON sa.scholarship_type_id = st.type_id -LEFT JOIN xg_class_msg c ON sa.applicant_class_id = c.id -WHERE sa.application_status IN ('submitted', 'approved') -- 有效申请 -GROUP BY sa.batch_id, sa.scholarship_type_id, sa.applicant_class_id -ORDER BY sa.batch_id, sa.applicant_class_id, sa.scholarship_type_id; - --- 6. 检查名额使用情况汇总与一致性 -SELECT - '=== 6. 名额使用情况汇总与一致性检查 ===' as info; - -SELECT - cq.batch_id, - b.batch_name, - cq.class_id, - c.class_name, - cq.scholarship_type_id, - st.type_name, - cq.total_quota, - cq.used_quota as recorded_used_quota, - COALESCE(actual_used.used_count, 0) as actual_used_quota, - cq.available_quota as recorded_available_quota, - (cq.total_quota - COALESCE(actual_used.used_count, 0)) as calculated_available_quota, - CASE - WHEN cq.used_quota != COALESCE(actual_used.used_count, 0) THEN 'INCONSISTENT' - WHEN cq.available_quota != (cq.total_quota - COALESCE(actual_used.used_count, 0)) THEN 'INCONSISTENT' - ELSE 'CONSISTENT' - END as consistency_status, - CASE - WHEN cq.total_quota = 0 THEN 'unlimited' - WHEN (cq.total_quota - COALESCE(actual_used.used_count, 0)) > 0 THEN 'available' - ELSE 'full' - END as quota_status -FROM jcbt_scholarship_class_quota cq -LEFT JOIN jcbt_scholarship_batch b ON cq.batch_id = b.batch_id -LEFT JOIN jcbt_scholarship_type st ON cq.scholarship_type_id = st.type_id -LEFT JOIN xg_class_msg c ON cq.class_id = c.id -LEFT JOIN ( - SELECT - batch_id, - scholarship_type_id, - applicant_class_id, - COUNT(*) as used_count - FROM jcbt_scholarship_application - WHERE application_status IN ('submitted', 'approved') - GROUP BY batch_id, scholarship_type_id, applicant_class_id -) actual_used ON cq.batch_id = actual_used.batch_id - AND cq.scholarship_type_id = actual_used.scholarship_type_id - AND cq.class_id = actual_used.applicant_class_id -ORDER BY cq.batch_id, cq.class_id, cq.scholarship_type_id; - --- 7. 检查特定班级的详细情况(以班级ID=1为例) -SELECT - '=== 7. 特定班级详细情况检查 (class_id=1) ===' as info; - -SELECT - '班级基本信息:' as section, - c.id as class_id, - c.class_name, - c.grade, - c.class_teacher_id, - u.user_name as class_teacher_name, - u.nick_name as class_teacher_nick -FROM xg_class_msg c -LEFT JOIN sys_user u ON c.class_teacher_id = u.user_id -WHERE c.id = 1; - -SELECT - '该班级的名额分配:' as section, - cq.scholarship_type_id, - st.type_name, - cq.total_quota, - cq.used_quota, - cq.available_quota, - cq.allocation_method, - cq.status -FROM jcbt_scholarship_class_quota cq -LEFT JOIN jcbt_scholarship_type st ON cq.scholarship_type_id = st.type_id -WHERE cq.class_id = 1 -ORDER BY cq.scholarship_type_id; - --- 8. 检查数据完整性问题 -SELECT - '=== 8. 数据完整性问题检查 ===' as info; - --- 检查缺少名额分配的奖学金类型 -SELECT - '缺少名额分配的奖学金类型:' as issue_type, - st.type_id, - st.type_name, - bt.batch_id, - b.batch_name, - COUNT(DISTINCT c.id) as total_classes_without_quota -FROM jcbt_scholarship_type st -INNER JOIN jcbt_scholarship_batch_type bt ON st.type_id = bt.type_id -INNER JOIN jcbt_scholarship_batch b ON bt.batch_id = b.batch_id -CROSS JOIN xg_class_msg c -LEFT JOIN jcbt_scholarship_class_quota cq ON st.type_id = cq.scholarship_type_id - AND bt.batch_id = cq.batch_id - AND c.id = cq.class_id -WHERE st.type_status = '1' -AND b.batch_status = '1' -AND c.class_status = '1' -AND cq.id IS NULL -GROUP BY st.type_id, st.type_name, bt.batch_id, b.batch_name; - --- 检查有名额分配但奖学金类型不存在的情况 -SELECT - '名额分配指向不存在的奖学金类型:' as issue_type, - cq.id as quota_id, - cq.scholarship_type_id, - cq.batch_id, - cq.class_id -FROM jcbt_scholarship_class_quota cq -LEFT JOIN jcbt_scholarship_type st ON cq.scholarship_type_id = st.type_id -WHERE st.type_id IS NULL; - --- 检查名额分配指向不存在的班级 -SELECT - '名额分配指向不存在的班级:' as issue_type, - cq.id as quota_id, - cq.class_id, - cq.class_name as recorded_class_name -FROM jcbt_scholarship_class_quota cq -LEFT JOIN xg_class_msg c ON cq.class_id = c.id -WHERE c.id IS NULL; - --- 9. 模拟API查询结果 -SELECT - '=== 9. 模拟API查询结果 (batch_id=1, class_id=1) ===' as info; - -SELECT - st.type_id, - st.type_name, - st.type_level, - st.type_description, - CASE - WHEN cq.total_quota IS NULL THEN 0 - ELSE cq.total_quota - END as total_quota, - CASE - WHEN cq.used_quota IS NULL THEN 0 - ELSE cq.used_quota - END as used_quota, - CASE - WHEN cq.available_quota IS NULL THEN 0 - WHEN cq.total_quota = 0 THEN 999999 - ELSE cq.available_quota - END as available_quota, - CASE - WHEN cq.total_quota IS NULL THEN FALSE - WHEN cq.total_quota = 0 THEN FALSE - ELSE TRUE - END as has_quota, - CASE - WHEN cq.total_quota IS NULL THEN 'no_allocation' - WHEN cq.total_quota = 0 THEN 'unlimited' - WHEN cq.available_quota > 0 THEN 'available' - ELSE 'full' - END as quota_status, - cq.allocation_method, - cq.status as allocation_status -FROM jcbt_scholarship_type st -INNER JOIN jcbt_scholarship_batch_type bt ON st.type_id = bt.type_id -LEFT JOIN jcbt_scholarship_class_quota cq ON st.type_id = cq.scholarship_type_id - AND bt.batch_id = cq.batch_id - AND cq.class_id = 1 -WHERE bt.batch_id = 1 -AND st.type_status = '1' -ORDER BY - CASE quota_status - WHEN 'available' THEN 1 - WHEN 'unlimited' THEN 2 - WHEN 'full' THEN 3 - WHEN 'no_allocation' THEN 4 - END, - st.type_level, - st.type_name; - --- 10. 额外检查:验证数据同步问题 -SELECT - '=== 10. 数据同步问题检查 ===' as info; - --- 检查used_quota和available_quota字段是否需要更新 -SELECT - 'need_quota_update' as check_type, - cq.id, - cq.batch_id, - cq.class_id, - cq.scholarship_type_id, - cq.total_quota, - cq.used_quota as recorded_used, - COALESCE(actual.actual_used, 0) as actual_used, - cq.available_quota as recorded_available, - (cq.total_quota - COALESCE(actual.actual_used, 0)) as calculated_available -FROM jcbt_scholarship_class_quota cq -LEFT JOIN ( - SELECT - batch_id, - scholarship_type_id, - applicant_class_id, - COUNT(*) as actual_used - FROM jcbt_scholarship_application - WHERE application_status IN ('submitted', 'approved') - GROUP BY batch_id, scholarship_type_id, applicant_class_id -) actual ON cq.batch_id = actual.batch_id - AND cq.scholarship_type_id = actual.scholarship_type_id - AND cq.class_id = actual.applicant_class_id -WHERE cq.used_quota != COALESCE(actual.actual_used, 0) - OR cq.available_quota != (cq.total_quota - COALESCE(actual.actual_used, 0)); diff --git a/backend/sql/fix_audit_field_names.sql b/backend/sql/fix_audit_field_names.sql deleted file mode 100644 index 4235a22e..00000000 --- a/backend/sql/fix_audit_field_names.sql +++ /dev/null @@ -1,45 +0,0 @@ --- 修复 jcbt_poverty_application 表中审核字段名称不一致问题 --- 问题: 数据库字段为 auditor_time/auditor_opinion,但实体类期望 audit_time/audit_opinion --- 解决方案: 重命名数据库字段,保持与实体类一致 - -USE hzdyjs_xg; - --- 检查当前字段状态 -SELECT '=== 修复前字段检查 ===' as message; -SELECT - COLUMN_NAME as '字段名', - DATA_TYPE as '数据类型', - IS_NULLABLE as '允许NULL', - COLUMN_DEFAULT as '默认值', - COLUMN_COMMENT as '注释' -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' -AND TABLE_NAME = 'jcbt_poverty_application' -AND COLUMN_NAME IN ('auditor_time', 'auditor_opinion', 'audit_time', 'audit_opinion'); - --- 方案1: 重命名字段(推荐) -SELECT '=== 开始重命名字段 ===' as message; - --- 重命名 auditor_time 为 audit_time -ALTER TABLE jcbt_poverty_application -CHANGE COLUMN auditor_time audit_time datetime DEFAULT NULL COMMENT '审核时间'; - --- 重命名 auditor_opinion 为 audit_opinion -ALTER TABLE jcbt_poverty_application -CHANGE COLUMN auditor_opinion audit_opinion text COMMENT '审核意见'; - --- 验证修复结果 -SELECT '=== 修复后字段检查 ===' as message; -SELECT - COLUMN_NAME as '字段名', - DATA_TYPE as '数据类型', - IS_NULLABLE as '允许NULL', - COLUMN_DEFAULT as '默认值', - COLUMN_COMMENT as '注释' -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' -AND TABLE_NAME = 'jcbt_poverty_application' -AND COLUMN_NAME IN ('auditor_time', 'auditor_opinion', 'audit_time', 'audit_opinion') -ORDER BY COLUMN_NAME; - -SELECT '✅ 字段重命名完成!现在数据库字段与实体类保持一致!' as result; diff --git a/backend/sql/fix_compatibility_create_dept.sql b/backend/sql/fix_compatibility_create_dept.sql deleted file mode 100644 index b025492b..00000000 --- a/backend/sql/fix_compatibility_create_dept.sql +++ /dev/null @@ -1,36 +0,0 @@ --- ===================================================== --- 奖学金兼得关系表 - create_dept字段修复脚本 --- 版本: v1.0 --- 创建时间: 2025-01-27 --- 问题描述: 实体类继承BaseEntity包含create_dept字段,但数据库表缺少该字段 --- 解决方案: 为jcbt_scholarship_compatibility表添加create_dept字段 --- ===================================================== - -USE `ruoyi-vue-pro`; - --- 为奖学金兼得关系表添加create_dept字段 -ALTER TABLE `jcbt_scholarship_compatibility` -ADD COLUMN IF NOT EXISTS `create_dept` BIGINT(20) COMMENT '创建部门' -AFTER `description`; - --- 验证字段添加结果 -SELECT - COLUMN_NAME, - DATA_TYPE, - IS_NULLABLE, - COLUMN_DEFAULT, - COLUMN_COMMENT -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'ruoyi-vue-pro' - AND TABLE_NAME = 'jcbt_scholarship_compatibility' - AND COLUMN_NAME = 'create_dept'; - --- 显示表结构确认修复结果 -DESCRIBE `jcbt_scholarship_compatibility`; - --- ===================================================== --- 修复说明: --- 1. 该字段是BaseEntity的标准字段,用于记录创建部门 --- 2. 字段类型为BIGINT(20),允许NULL,与其他表保持一致 --- 3. 字段位置放在description字段之后,符合标准字段顺序 --- ===================================================== diff --git a/backend/sql/fix_compatibility_type_field.sql b/backend/sql/fix_compatibility_type_field.sql deleted file mode 100644 index 22275451..00000000 --- a/backend/sql/fix_compatibility_type_field.sql +++ /dev/null @@ -1,56 +0,0 @@ --- ===================================================== --- 奖学金管理模块 - 兼得类型字段修复脚本 --- 版本: v1.0 --- 创建时间: 2025-08-04 --- 说明: 为jcbt_scholarship_type表添加缺失的compatibility_type字段 --- ===================================================== - --- 设置字符集 -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS = 0; - --- ===================================================== --- 1. 为奖学金类型表添加兼得类型字段 --- ===================================================== - --- 检查字段是否存在,如果不存在则添加 -ALTER TABLE `jcbt_scholarship_type` -ADD COLUMN IF NOT EXISTS `compatibility_type` VARCHAR(20) DEFAULT 'COMPATIBLE' COMMENT '兼得类型:EXCLUSIVE-完全互斥,COMPATIBLE-完全兼容,PRIORITY-优先级关系,CUSTOM-自定义配置' -AFTER `application_guide`; - --- ===================================================== --- 2. 添加兼得类型字段索引 --- ===================================================== - --- 为新字段添加索引以提高查询性能 -ALTER TABLE `jcbt_scholarship_type` -ADD INDEX IF NOT EXISTS `idx_compatibility_type` (`compatibility_type`) COMMENT '兼得类型索引'; - --- ===================================================== --- 3. 更新现有数据(可选) --- ===================================================== - --- 将现有数据的兼得类型设置为默认值(兼容) --- 如果需要不同的默认值,可以修改这里的UPDATE语句 -UPDATE `jcbt_scholarship_type` -SET `compatibility_type` = 'COMPATIBLE' -WHERE `compatibility_type` IS NULL AND `deleted` = 0; - --- ===================================================== --- 4. 验证字段添加结果 --- ===================================================== - --- 查询表结构确认字段已添加 --- DESCRIBE `jcbt_scholarship_type`; - --- 查询数据确认字段值正确 --- SELECT id, type_name, compatibility_type FROM `jcbt_scholarship_type` WHERE deleted = 0 LIMIT 5; - --- ===================================================== --- 脚本执行完成 --- ===================================================== - -SET FOREIGN_KEY_CHECKS = 1; - --- 执行完成提示 -SELECT '兼得类型字段修复完成!' AS status; diff --git a/backend/sql/fix_jcbt_scholarship_application_create_dept.sql b/backend/sql/fix_jcbt_scholarship_application_create_dept.sql deleted file mode 100644 index b4ff35ab..00000000 --- a/backend/sql/fix_jcbt_scholarship_application_create_dept.sql +++ /dev/null @@ -1,33 +0,0 @@ --- 修复奖学金申请表缺失的create_dept字段 --- 问题: MyBatis-Plus插入数据时报错 Unknown column 'create_dept' in 'field list' --- 原因: 数据库表缺少BaseEntity中的create_dept字段 --- 解决方案: 为jcbt_scholarship_application表添加create_dept字段 - --- 检查字段是否存在,不存在则添加 -SET @sql = ''; -SELECT COUNT(*) INTO @col_exists -FROM information_schema.columns -WHERE table_schema = 'hzdyjs_xg' - AND table_name = 'jcbt_scholarship_application' - AND column_name = 'create_dept'; - --- 如果字段不存在,则添加 -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE jcbt_scholarship_application ADD COLUMN create_dept bigint(20) DEFAULT NULL COMMENT ''创建部门'' AFTER submit_time;', - 'SELECT ''字段 create_dept 已存在,跳过添加'' AS message;'); - -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 验证字段是否添加成功 -SELECT - COLUMN_NAME as '字段名', - DATA_TYPE as '数据类型', - IS_NULLABLE as '允许空值', - COLUMN_DEFAULT as '默认值', - COLUMN_COMMENT as '注释' -FROM information_schema.columns -WHERE table_schema = 'hzdyjs_xg' - AND table_name = 'jcbt_scholarship_application' - AND column_name = 'create_dept'; diff --git a/backend/sql/fix_poverty_application_status.sql b/backend/sql/fix_poverty_application_status.sql deleted file mode 100644 index 362ffa5a..00000000 --- a/backend/sql/fix_poverty_application_status.sql +++ /dev/null @@ -1,27 +0,0 @@ --- 修复助学金申请表状态问题 --- 将状态为SUBMITTED的记录改为WAITING,以便正常进行审核 - --- 查看当前状态分布(执行前检查) -SELECT status, COUNT(*) as count -FROM jcbt_poverty_application -GROUP BY status; - --- 修复SUBMITTED状态为WAITING -UPDATE jcbt_poverty_application -SET status = 'WAITING', - update_time = NOW(), - update_by = 103, - remark = CONCAT(IFNULL(remark, ''), ' [系统修复:状态从SUBMITTED更新为WAITING以支持审核]') -WHERE status = 'SUBMITTED'; - --- 查看修复后的状态分布 -SELECT status, COUNT(*) as count -FROM jcbt_poverty_application -GROUP BY status; - --- 显示被修复的记录详情 -SELECT id, application_no, student_name, status, submit_time, update_time -FROM jcbt_poverty_application -WHERE status = 'WAITING' - AND remark LIKE '%系统修复:状态从SUBMITTED更新为WAITING%' -ORDER BY update_time DESC; diff --git a/backend/sql/fix_quota_unique_constraint.sql b/backend/sql/fix_quota_unique_constraint.sql deleted file mode 100644 index e518b286..00000000 --- a/backend/sql/fix_quota_unique_constraint.sql +++ /dev/null @@ -1,101 +0,0 @@ --- ======================================== --- 修复奖学金名额分配唯一约束问题 --- 创建时间: 2025-01-28 --- 问题描述: 唯一约束包含deleted字段导致逻辑删除时出现约束冲突 --- ======================================== - -USE hzdyjs_xg; - --- 1. 查看当前的唯一约束问题数据 -SELECT - batch_id, - scholarship_type_id, - target_dept_id, - deleted, - COUNT(*) as count_records, - GROUP_CONCAT(id) as record_ids, - GROUP_CONCAT(status) as statuses -FROM jcbt_scholarship_quota -GROUP BY batch_id, scholarship_type_id, target_dept_id, deleted -HAVING COUNT(*) > 1 -ORDER BY batch_id, scholarship_type_id, target_dept_id; - --- 2. 查看可能存在冲突的记录(同一组合下有删除和未删除的记录) -SELECT - q1.id as active_id, - q1.batch_id, - q1.scholarship_type_id, - q1.target_dept_id, - q1.deleted as active_deleted, - q1.status as active_status, - q2.id as deleted_id, - q2.deleted as deleted_deleted, - q2.status as deleted_status -FROM jcbt_scholarship_quota q1 -INNER JOIN jcbt_scholarship_quota q2 ON - q1.batch_id = q2.batch_id - AND q1.scholarship_type_id = q2.scholarship_type_id - AND q1.target_dept_id = q2.target_dept_id - AND q1.deleted = 0 - AND q2.deleted = 1 -ORDER BY q1.batch_id, q1.scholarship_type_id, q1.target_dept_id; - --- 3. 删除旧的唯一约束(包含deleted字段的约束) -ALTER TABLE jcbt_scholarship_quota DROP INDEX uk_batch_type_dept; - --- 4. 先清理可能的重复数据,然后重新添加不包含deleted字段的唯一约束 --- 这样可以避免删除时的约束冲突,同时保证业务数据的唯一性 - --- 清理重复的已删除记录(保留最新的一条) -DELETE q1 FROM jcbt_scholarship_quota q1 -INNER JOIN jcbt_scholarship_quota q2 -WHERE q1.batch_id = q2.batch_id - AND q1.scholarship_type_id = q2.scholarship_type_id - AND q1.target_dept_id = q2.target_dept_id - AND q1.deleted = 1 - AND q2.deleted = 1 - AND q1.id < q2.id; - --- 如果仍然存在同一业务组合的active和deleted记录,物理删除deleted记录 -DELETE FROM jcbt_scholarship_quota -WHERE deleted = 1 -AND EXISTS ( - SELECT 1 FROM jcbt_scholarship_quota q2 - WHERE q2.batch_id = jcbt_scholarship_quota.batch_id - AND q2.scholarship_type_id = jcbt_scholarship_quota.scholarship_type_id - AND q2.target_dept_id = jcbt_scholarship_quota.target_dept_id - AND q2.deleted = 0 -); - --- 重新添加不包含deleted字段的唯一约束 -ALTER TABLE jcbt_scholarship_quota -ADD CONSTRAINT uk_batch_type_dept_business -UNIQUE (batch_id, scholarship_type_id, target_dept_id); - --- 5. 验证修复结果 -SELECT - '修复后检查' as check_type, - batch_id, - scholarship_type_id, - target_dept_id, - COUNT(*) as count_records, - GROUP_CONCAT(CONCAT('id:', id, ',deleted:', deleted)) as record_details -FROM jcbt_scholarship_quota -WHERE deleted = 0 -GROUP BY batch_id, scholarship_type_id, target_dept_id -HAVING COUNT(*) > 1 -UNION ALL -SELECT - '已删除记录检查' as check_type, - batch_id, - scholarship_type_id, - target_dept_id, - COUNT(*) as count_records, - GROUP_CONCAT(CONCAT('id:', id, ',deleted:', deleted)) as record_details -FROM jcbt_scholarship_quota -WHERE deleted = 1 -GROUP BY batch_id, scholarship_type_id, target_dept_id -HAVING COUNT(*) > 1; - --- 6. 显示当前表的约束信息 -SHOW INDEX FROM jcbt_scholarship_quota WHERE Key_name LIKE '%batch%' OR Key_name LIKE '%type%' OR Key_name LIKE '%dept%'; diff --git a/backend/sql/fix_work_study_position_create_dept.sql b/backend/sql/fix_work_study_position_create_dept.sql deleted file mode 100644 index 648b19ed..00000000 --- a/backend/sql/fix_work_study_position_create_dept.sql +++ /dev/null @@ -1,20 +0,0 @@ --- 修复勤工俭学岗位表缺少 create_dept 字段的问题 --- 创建日期: 2025-01-03 --- 说明: 添加 create_dept 字段以保持与 BaseEntity 的一致性 - --- 添加 create_dept 字段 -ALTER TABLE `jcbt_work_study_position` -ADD COLUMN `create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门' -AFTER `tenant_id`; - --- 更新现有数据的 create_dept 字段(设置为默认值 NULL) -UPDATE `jcbt_work_study_position` -SET `create_dept` = NULL -WHERE `create_dept` IS NULL; - --- 验证字段是否添加成功 -SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_COMMENT -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'jcbt_work_study_position' - AND COLUMN_NAME = 'create_dept'; diff --git a/backend/sql/insert_test_data_simple.sql b/backend/sql/insert_test_data_simple.sql deleted file mode 100644 index cd317339..00000000 --- a/backend/sql/insert_test_data_simple.sql +++ /dev/null @@ -1,71 +0,0 @@ --- ===================================================== --- 奖学金名额分配 - 简单测试数据插入脚本 --- 创建时间: 2025-01-27 --- 说明: 插入基础测试数据 --- ===================================================== - --- 设置数据库 -USE hzdyjs_xg; - --- ===================================================== --- 1. 插入奖学金批次数据 --- ===================================================== -INSERT IGNORE INTO jcbt_scholarship_batch ( - id, batch_code, batch_name, academic_year, semester, batch_type, - start_time, end_time, apply_start_time, apply_end_time, - review_start_time, review_end_time, status, description, - create_by, create_time, update_by, update_time, tenant_id, deleted -) VALUES -(1890000000000000001, 'BATCH_2024_1', '2024学年第一学期奖学金评定', '2024-2025', 1, 'REGULAR', - '2024-09-01 00:00:00', '2024-12-31 23:59:59', '2024-10-01 00:00:00', '2024-10-31 23:59:59', - '2024-11-01 00:00:00', '2024-11-30 23:59:59', 'ACTIVE', '2024学年第一学期常规奖学金评定批次', - 1, NOW(), 1, NOW(), 1, 0); - --- ===================================================== --- 2. 插入奖学金类型数据 --- ===================================================== -INSERT IGNORE INTO jcbt_scholarship_type ( - id, type_code, type_name, type_category, level_type, award_amount, - description, application_guide, application_conditions, evaluation_criteria, - compatibility_type, sort_order, status, is_system, - create_by, create_time, update_by, update_time, tenant_id, deleted -) VALUES -(1890000000000000101, 'GJZXJ', '国家奖学金', 'NATIONAL', 'FIRST_CLASS', 8000.00, - '国家奖学金是为了激励普通本科高校、高等职业学校和高等专科学校学生勤奋学习、努力进取。', - '学生本人提出申请,按学校规定程序推荐上报。', '品学兼优,学习成绩排名在评选范围内位于前10%', - '学习成绩、综合测评、道德品质等综合评定', 'EXCLUSIVE', 1, 1, 1, - 1, NOW(), 1, NOW(), 1, 0); - --- ===================================================== --- 3. 插入名额分配数据 --- ===================================================== -INSERT IGNORE INTO jcbt_scholarship_quota ( - id, batch_id, scholarship_type_id, allocation_level, target_dept_id, parent_quota_id, - total_quota, used_quota, reserved_quota, available_quota, - allocation_rule, allocation_basis, allocation_method, is_adjustable, is_locked, status, - remark, create_by, create_time, update_by, update_time, tenant_id, deleted -) VALUES -(1890000000000000201, 1890000000000000001, 1890000000000000101, 'UNIVERSITY', NULL, NULL, - 50, 0, 5, 45, '{"type":"university","rule":"total_allocation"}', '根据学校总体规模和历年获奖情况分配', - 'MANUAL', 1, 0, 'ACTIVE', '全校国家奖学金总名额', 1, NOW(), 1, NOW(), 1, 0); - --- ===================================================== --- 4. 验证数据 --- ===================================================== -SELECT '插入结果验证' as message; -SELECT COUNT(*) as batch_count FROM jcbt_scholarship_batch WHERE deleted = 0; -SELECT COUNT(*) as type_count FROM jcbt_scholarship_type WHERE deleted = 0; -SELECT COUNT(*) as quota_count FROM jcbt_scholarship_quota WHERE deleted = 0; - --- 验证关联查询 -SELECT - q.id, - b.batch_name, - st.type_name as scholarship_type_name, - q.allocation_level, - q.total_quota, - q.status -FROM jcbt_scholarship_quota q -LEFT JOIN jcbt_scholarship_batch b ON q.batch_id = b.id -LEFT JOIN jcbt_scholarship_type st ON q.scholarship_type_id = st.id -WHERE q.deleted = 0; diff --git a/backend/sql/jcbt_poverty_application_menu.sql b/backend/sql/jcbt_poverty_application_menu.sql deleted file mode 100644 index 13f37d49..00000000 --- a/backend/sql/jcbt_poverty_application_menu.sql +++ /dev/null @@ -1,119 +0,0 @@ --- ======================================== --- 贫困生认定申请管理模块 - 菜单权限配置 --- 创建时间: 2025-01-26 --- 说明: 配置贫困生认定申请管理功能的菜单和权限 --- ======================================== - -USE hzdyjs_xg; - --- 设置变量 -SET @parent_menu_id = 2015; -- 奖惩补贴管理父菜单ID -SET @menu_order = 4; -- 菜单排序(在奖学金申请管理之后) - --- ===================================================== --- 1. 删除已存在的菜单(开发环境重新创建) --- ===================================================== -DELETE FROM sys_menu WHERE menu_name = '助学金申请管理' AND parent_id = @parent_menu_id; - --- ===================================================== --- 2. 创建助学金申请管理菜单 --- ===================================================== -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('助学金申请管理', @parent_menu_id, @menu_order, 'povertyApplication', 'jcbt/poverty/Application/index', '', 1, 0, 'C', '0', '0', 'jcbt:povertyApplication:list', 'money', 1, NOW(), 1, NOW(), '贫困生认定申请管理菜单'); - --- 获取刚插入的菜单ID -SET @poverty_menu_id = LAST_INSERT_ID(); - --- ===================================================== --- 3. 创建基础管理功能按钮权限(6个) --- ===================================================== - --- 3.1 查询权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('申请查询', @poverty_menu_id, 1, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:povertyApplication:query', '#', 1, NOW(), 1, NOW(), ''); - --- 3.2 新增权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('申请新增', @poverty_menu_id, 2, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:povertyApplication:add', '#', 1, NOW(), 1, NOW(), ''); - --- 3.3 修改权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('申请修改', @poverty_menu_id, 3, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:povertyApplication:edit', '#', 1, NOW(), 1, NOW(), ''); - --- 3.4 删除权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('申请删除', @poverty_menu_id, 4, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:povertyApplication:remove', '#', 1, NOW(), 1, NOW(), ''); - --- 3.5 详情权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('申请详情', @poverty_menu_id, 5, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:povertyApplication:detail', '#', 1, NOW(), 1, NOW(), ''); - --- 3.6 导出权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('申请导出', @poverty_menu_id, 6, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:povertyApplication:export', '#', 1, NOW(), 1, NOW(), ''); - --- ===================================================== --- 4. 创建业务功能权限(4个) --- ===================================================== - --- 4.1 我的申请权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('我的申请', @poverty_menu_id, 7, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:povertyApplication:myList', '#', 1, NOW(), 1, NOW(), ''); - --- 4.2 待审核申请权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('待审核申请', @poverty_menu_id, 8, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:povertyApplication:pendingList', '#', 1, NOW(), 1, NOW(), ''); - --- 4.3 申请资格检查权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('资格检查', @poverty_menu_id, 9, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:povertyApplication:check', '#', 1, NOW(), 1, NOW(), ''); - --- 4.4 申请审核权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('申请审核', @poverty_menu_id, 10, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:povertyApplication:approve', '#', 1, NOW(), 1, NOW(), ''); - --- ===================================================== --- 5. 创建辅助功能权限(6个) --- ===================================================== - --- 5.1 提交申请权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('提交申请', @poverty_menu_id, 11, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:povertyApplication:submit', '#', 1, NOW(), 1, NOW(), ''); - --- 5.2 撤回申请权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('撤回申请', @poverty_menu_id, 12, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:povertyApplication:cancel', '#', 1, NOW(), 1, NOW(), ''); - --- 5.3 驳回申请权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('驳回申请', @poverty_menu_id, 13, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:povertyApplication:reject', '#', 1, NOW(), 1, NOW(), ''); - --- 5.4 草稿保存权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('保存草稿', @poverty_menu_id, 14, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:povertyApplication:draft', '#', 1, NOW(), 1, NOW(), ''); - --- 5.5 重复检查权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('重复检查', @poverty_menu_id, 15, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:povertyApplication:duplicate', '#', 1, NOW(), 1, NOW(), ''); - --- 5.6 申请统计权限 ⚠️ 这是关键的统计权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('申请统计', @poverty_menu_id, 16, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:povertyApplication:statistics', '#', 1, NOW(), 1, NOW(), ''); - --- ===================================================== --- 6. 分配权限给超级管理员角色 --- ===================================================== - --- 获取超级管理员角色ID (通常是1) -SET @admin_role_id = 1; - --- 分配所有权限给超级管理员 -INSERT INTO sys_role_menu (role_id, menu_id) -SELECT @admin_role_id, menu_id FROM sys_menu WHERE parent_id = @poverty_menu_id OR menu_id = @poverty_menu_id; - --- ===================================================== --- 7. 输出确认信息 --- ===================================================== -SELECT '贫困生认定申请管理菜单权限配置完成!' as message; -SELECT CONCAT('主菜单ID: ', @poverty_menu_id) as menu_info; -SELECT COUNT(*) as total_permissions FROM sys_menu WHERE parent_id = @poverty_menu_id OR menu_id = @poverty_menu_id; diff --git a/backend/sql/jcbt_poverty_application_table.sql b/backend/sql/jcbt_poverty_application_table.sql deleted file mode 100644 index 7ad7acca..00000000 --- a/backend/sql/jcbt_poverty_application_table.sql +++ /dev/null @@ -1,311 +0,0 @@ --- ============================================================================ --- 贫困生认定申请管理 - 数据库表创建脚本 --- 创建时间: 2024-12-19 --- 描述: 创建贫困生认定申请相关的数据库表、菜单权限和测试数据 --- ============================================================================ - --- ============================================================================ --- 1. 创建贫困生认定申请表 --- ============================================================================ -CREATE TABLE IF NOT EXISTS `jcbt_poverty_application` ( - `id` bigint(20) NOT NULL COMMENT '主键ID', - `application_no` varchar(50) DEFAULT NULL COMMENT '申请编号', - `academic_year` varchar(20) DEFAULT NULL COMMENT '学年', - `term` int(11) DEFAULT NULL COMMENT '学期', - `student_id` bigint(20) DEFAULT NULL COMMENT '学生ID', - `student_no` varchar(50) DEFAULT NULL COMMENT '学号', - `student_name` varchar(100) DEFAULT NULL COMMENT '学生姓名', - `gender` varchar(10) DEFAULT NULL COMMENT '性别', - `college_id` bigint(20) DEFAULT NULL COMMENT '学院ID', - `college_name` varchar(100) DEFAULT NULL COMMENT '学院名称', - `major_id` bigint(20) DEFAULT NULL COMMENT '专业ID', - `major_name` varchar(100) DEFAULT NULL COMMENT '专业名称', - `class_id` bigint(20) DEFAULT NULL COMMENT '班级ID', - `class_name` varchar(100) DEFAULT NULL COMMENT '班级名称', - `recognition_group` varchar(50) DEFAULT NULL COMMENT '认定群体(特殊群体/其他群体)', - `recognition_type_id` bigint(20) DEFAULT NULL COMMENT '认定类型ID', - `recognition_type_code` varchar(50) DEFAULT NULL COMMENT '认定类型编码', - `recognition_type_name` varchar(100) DEFAULT NULL COMMENT '认定类型名称', - `assistance_type_ids` varchar(500) DEFAULT NULL COMMENT '申请自助类型ID数组(多选)', - `assistance_type_names` varchar(500) DEFAULT NULL COMMENT '申请自助类型名称(多个用逗号分隔)', - `family_economy_summary` text COMMENT '家庭经济情况摘要', - `attach_application_form_url` varchar(500) DEFAULT NULL COMMENT '申请表附件URL', - `attach_application_form_id` bigint(20) DEFAULT NULL COMMENT '申请表附件ID', - `attach_survey_form_url` varchar(500) DEFAULT NULL COMMENT '调查表附件URL', - `attach_survey_form_id` bigint(20) DEFAULT NULL COMMENT '调查表附件ID', - `status` varchar(20) DEFAULT 'DRAFT' COMMENT '申请状态(DRAFT-草稿,SUBMITTED-已提交,REVIEWING-审核中,APPROVED-已通过,REJECTED-已拒绝,WITHDRAWN-已撤回)', - `submitter_id` bigint(20) DEFAULT NULL COMMENT '提交人ID', - `submitter_name` varchar(100) DEFAULT NULL COMMENT '提交人姓名', - `submit_time` datetime DEFAULT NULL COMMENT '提交时间', - `auditor_id` bigint(20) DEFAULT NULL COMMENT '审核人ID', - `auditor_name` varchar(100) DEFAULT NULL COMMENT '审核人姓名', - `audit_time` datetime DEFAULT NULL COMMENT '审核时间', - `audit_opinion` text COMMENT '审核意见', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `deleted` bigint(20) DEFAULT '0' COMMENT '逻辑删除标记(0-正常,非0-已删除)', - `tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号', - `create_by` bigint(20) DEFAULT NULL COMMENT '创建者', - `create_time` datetime DEFAULT NULL COMMENT '创建时间', - `update_by` bigint(20) DEFAULT NULL COMMENT '更新者', - `update_time` datetime DEFAULT NULL COMMENT '更新时间', - PRIMARY KEY (`id`), - KEY `idx_student_no` (`student_no`), - KEY `idx_status` (`status`), - KEY `idx_academic_year` (`academic_year`), - KEY `idx_college_id` (`college_id`), - KEY `idx_tenant_deleted` (`tenant_id`, `deleted`), - KEY `idx_create_time` (`create_time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='贫困生认定申请表'; - --- ============================================================================ --- 2. 插入菜单权限配置 --- ============================================================================ - --- 2.1 插入主菜单(贫困生认定申请管理) -INSERT INTO sys_menu VALUES( - '1954043650013946881', - '贫困生认定申请管理', - '1851080776692494337', - '4', - 'jcbt:poverty:application:list', - 'poverty/Application/index', - '', - '', - '1', - '0', - 'C', - '0', - '0', - 'jcbt:poverty:application:list', - 'education', - '103', - '1', - NOW(), - NULL, - NULL, - '' -); - --- 2.2 插入按钮权限 --- 查询按钮 -INSERT INTO sys_menu VALUES( - '1954043651013946881', - '贫困生认定申请查询', - '1954043650013946881', - '1', - 'jcbt:poverty:application:query', - '', - '', - '', - '1', - '0', - 'F', - '0', - '0', - 'jcbt:poverty:application:query', - '#', - '103', - '1', - NOW(), - NULL, - NULL, - '' -); - --- 新增按钮 -INSERT INTO sys_menu VALUES( - '1954043652013946881', - '贫困生认定申请新增', - '1954043650013946881', - '2', - 'jcbt:poverty:application:add', - '', - '', - '', - '1', - '0', - 'F', - '0', - '0', - 'jcbt:poverty:application:add', - '#', - '103', - '1', - NOW(), - NULL, - NULL, - '' -); - --- 修改按钮 -INSERT INTO sys_menu VALUES( - '1954043653013946881', - '贫困生认定申请修改', - '1954043650013946881', - '3', - 'jcbt:poverty:application:edit', - '', - '', - '', - '1', - '0', - 'F', - '0', - '0', - 'jcbt:poverty:application:edit', - '#', - '103', - '1', - NOW(), - NULL, - NULL, - '' -); - --- 删除按钮 -INSERT INTO sys_menu VALUES( - '1954043654013946881', - '贫困生认定申请删除', - '1954043650013946881', - '4', - 'jcbt:poverty:application:remove', - '', - '', - '', - '1', - '0', - 'F', - '0', - '0', - 'jcbt:poverty:application:remove', - '#', - '103', - '1', - NOW(), - NULL, - NULL, - '' -); - --- 导出按钮 -INSERT INTO sys_menu VALUES( - '1954043655013946881', - '贫困生认定申请导出', - '1954043650013946881', - '5', - 'jcbt:poverty:application:export', - '', - '', - '', - '1', - '0', - 'F', - '0', - '0', - 'jcbt:poverty:application:export', - '#', - '103', - '1', - NOW(), - NULL, - NULL, - '' -); - --- 审核按钮 -INSERT INTO sys_menu VALUES( - '1954043656013946881', - '贫困生认定申请审核', - '1954043650013946881', - '6', - 'jcbt:poverty:application:audit', - '', - '', - '', - '1', - '0', - 'F', - '0', - '0', - 'jcbt:poverty:application:audit', - '#', - '103', - '1', - NOW(), - NULL, - NULL, - '' -); - --- ============================================================================ --- 3. 插入字典数据 --- ============================================================================ - --- 3.1 申请状态字典 -INSERT INTO sys_dict_type VALUES( - (SELECT IFNULL(MAX(dict_id), 0) + 1 FROM sys_dict_type t), - '贫困生申请状态', - 'jcbt_poverty_application_status', - '0', - '103', - '1', - NOW(), - NULL, - NULL, - '贫困生认定申请状态选项' -); - --- 获取刚插入的字典类型ID -SET @dict_type_id = (SELECT dict_id FROM sys_dict_type WHERE dict_type = 'jcbt_poverty_application_status'); - --- 插入状态字典数据 -INSERT INTO sys_dict_data VALUES(@dict_type_id * 10 + 1, 1, '草稿', 'DRAFT', 'jcbt_poverty_application_status', '', 'info', 'N', '0', '103', '1', NOW(), NULL, NULL, '申请草稿状态'); -INSERT INTO sys_dict_data VALUES(@dict_type_id * 10 + 2, 2, '已提交', 'SUBMITTED', 'jcbt_poverty_application_status', '', 'primary', 'N', '0', '103', '1', NOW(), NULL, NULL, '申请已提交状态'); -INSERT INTO sys_dict_data VALUES(@dict_type_id * 10 + 3, 3, '审核中', 'REVIEWING', 'jcbt_poverty_application_status', '', 'warning', 'N', '0', '103', '1', NOW(), NULL, NULL, '申请审核中状态'); -INSERT INTO sys_dict_data VALUES(@dict_type_id * 10 + 4, 4, '已通过', 'APPROVED', 'jcbt_poverty_application_status', '', 'success', 'N', '0', '103', '1', NOW(), NULL, NULL, '申请已通过状态'); -INSERT INTO sys_dict_data VALUES(@dict_type_id * 10 + 5, 5, '已拒绝', 'REJECTED', 'jcbt_poverty_application_status', '', 'danger', 'N', '0', '103', '1', NOW(), NULL, NULL, '申请已拒绝状态'); -INSERT INTO sys_dict_data VALUES(@dict_type_id * 10 + 6, 6, '已撤回', 'WITHDRAWN', 'jcbt_poverty_application_status', '', 'info', 'N', '0', '103', '1', NOW(), NULL, NULL, '申请已撤回状态'); - --- ============================================================================ --- 4. 插入测试数据 --- ============================================================================ - -INSERT INTO `jcbt_poverty_application` VALUES -(1954048400013946881, 'PK2024001', '2024-2025', 1, 1001, '202401001', '张三', '男', 101, '计算机学院', 10101, '计算机科学与技术', 1010101, '计软2401班', '其他群体', 1, 'TYPE001', '一般困难', '1,2', '学费减免,生活补助', '家庭经济困难,父母务农收入微薄', '', NULL, '', NULL, 'SUBMITTED', 1001, '张三', '2024-12-01 10:00:00', NULL, NULL, NULL, NULL, '因家庭经济困难申请贫困生认定', 0, '000000', 103, '2024-12-01 09:30:00', 103, '2024-12-01 10:00:00'), -(1954048401013946881, 'PK2024002', '2024-2025', 1, 1002, '202401002', '李四', '女', 102, '外国语学院', 10201, '英语', 1020101, '英语2401班', '特殊群体', 2, 'TYPE002', '特别困难', '1,3', '学费减免,勤工助学', '单亲家庭,母亲重病', '', NULL, '', NULL, 'REVIEWING', 1002, '李四', '2024-12-02 14:00:00', 201, '王老师', NULL, NULL, '单亲家庭申请特别困难认定', 0, '000000', 103, '2024-12-02 13:30:00', 201, '2024-12-02 14:00:00'), -(1954048402013946881, 'PK2024003', '2024-2025', 1, 1003, '202401003', '王五', '男', 101, '计算机学院', 10102, '软件工程', 1010201, '软工2401班', '其他群体', 1, 'TYPE001', '一般困难', '2', '生活补助', '家庭收入较低', '', NULL, '', NULL, 'APPROVED', 1003, '王五', '2024-11-28 16:00:00', 201, '王老师', '2024-11-30 10:00:00', '符合认定条件,予以通过', '已通过认定审核', 0, '000000', 103, '2024-11-28 15:30:00', 201, '2024-11-30 10:00:00'), -(1954048403013946881, 'PK2024004', '2024-2025', 1, 1004, '202401004', '赵六', '女', 103, '经济管理学院', 10301, '会计学', 1030101, '会计2401班', '其他群体', 1, 'TYPE001', '一般困难', '1,2,3', '学费减免,生活补助,勤工助学', '家庭经济确实困难', '', NULL, '', NULL, 'DRAFT', NULL, NULL, NULL, NULL, NULL, NULL, NULL, '正在准备申请材料', 0, '000000', 103, '2024-12-05 11:00:00', 103, '2024-12-05 11:00:00'), -(1954048404013946881, 'PK2024005', '2024-2025', 1, 1005, '202401005', '钱七', '男', 104, '机械工程学院', 10401, '机械设计制造及其自动化', 1040101, '机械2401班', '特殊群体', 2, 'TYPE002', '特别困难', '1,2', '学费减免,生活补助', '建档立卡贫困户', '', NULL, '', NULL, 'REJECTED', 1005, '钱七', '2024-11-25 09:00:00', 202, '李老师', '2024-11-27 15:00:00', '材料不够完整,请补充相关证明', '需要补充材料后重新申请', 0, '000000', 103, '2024-11-25 08:30:00', 202, '2024-11-27 15:00:00'); - --- ============================================================================ --- 5. 创建索引优化 --- ============================================================================ - --- 优化查询性能的复合索引 -CREATE INDEX idx_student_status_time ON jcbt_poverty_application(student_id, status, create_time); -CREATE INDEX idx_college_year_status ON jcbt_poverty_application(college_id, academic_year, status); -CREATE INDEX idx_auditor_time ON jcbt_poverty_application(auditor_id, audit_time); - --- ============================================================================ --- 6. 数据统计视图(可选) --- ============================================================================ - --- 创建状态统计视图 -CREATE OR REPLACE VIEW v_poverty_application_statistics AS -SELECT - status, - COUNT(*) as count, - academic_year, - college_name -FROM jcbt_poverty_application -WHERE deleted = 0 -GROUP BY status, academic_year, college_name; - --- ============================================================================ --- 脚本执行完成 --- ============================================================================ --- 执行完成后请检查: --- 1. 表结构是否创建成功: SHOW CREATE TABLE jcbt_poverty_application; --- 2. 菜单权限是否正确: SELECT * FROM sys_menu WHERE menu_name LIKE '%贫困生%'; --- 3. 测试数据是否插入: SELECT COUNT(*) FROM jcbt_poverty_application; --- ============================================================================ diff --git a/backend/sql/jcbt_scholarship_application_menu_permissions.sql b/backend/sql/jcbt_scholarship_application_menu_permissions.sql deleted file mode 100644 index 42211545..00000000 --- a/backend/sql/jcbt_scholarship_application_menu_permissions.sql +++ /dev/null @@ -1,141 +0,0 @@ --- ======================================== --- 奖学金申请管理模块 - 菜单权限配置 --- 创建时间: 2025-01-26 --- 说明: 配置奖学金申请管理功能的菜单和权限 --- ======================================== - -USE hzdyjs_xg; - --- 设置变量 -SET @parent_menu_id = 2015; -- 奖惩补贴管理父菜单ID -SET @menu_order = 3; -- 菜单排序 - --- ===================================================== --- 1. 删除已存在的菜单(开发环境重新创建) --- ===================================================== -DELETE FROM sys_menu WHERE menu_name = '申请管理' AND parent_id = @parent_menu_id; - --- ===================================================== --- 2. 创建申请管理菜单 --- ===================================================== -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('申请管理', @parent_menu_id, @menu_order, 'application', 'jcbt/scholarship/application/index', '', 1, 0, 'C', '0', '0', 'jcbt:scholarshipApplication:list', 'form', 1, NOW(), 1, NOW(), '奖学金申请管理菜单'); - --- 获取刚插入的菜单ID -SET @application_menu_id = LAST_INSERT_ID(); - --- ===================================================== --- 3. 创建申请管理功能按钮权限 --- ===================================================== - --- 3.1 查询权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('申请查询', @application_menu_id, 1, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipApplication:query', '#', 1, NOW(), 1, NOW(), ''); - --- 3.2 新增权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('申请新增', @application_menu_id, 2, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipApplication:add', '#', 1, NOW(), 1, NOW(), ''); - --- 3.3 修改权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('申请修改', @application_menu_id, 3, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipApplication:edit', '#', 1, NOW(), 1, NOW(), ''); - --- 3.4 删除权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('申请删除', @application_menu_id, 4, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipApplication:remove', '#', 1, NOW(), 1, NOW(), ''); - --- 3.5 导出权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('申请导出', @application_menu_id, 5, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipApplication:export', '#', 1, NOW(), 1, NOW(), ''); - --- 3.6 导入权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('申请导入', @application_menu_id, 6, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipApplication:import', '#', 1, NOW(), 1, NOW(), ''); - --- ===================================================== --- 4. 创建业务功能权限 --- ===================================================== - --- 4.1 我的申请权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('我的申请', @application_menu_id, 7, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipApplication:my', '#', 1, NOW(), 1, NOW(), ''); - --- 4.2 待审核申请权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('待审核申请', @application_menu_id, 8, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipApplication:pending', '#', 1, NOW(), 1, NOW(), ''); - --- 4.3 申请资格检查权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('资格检查', @application_menu_id, 9, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipApplication:check', '#', 1, NOW(), 1, NOW(), ''); - --- 4.4 批量审核权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('批量审核', @application_menu_id, 10, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipApplication:approve', '#', 1, NOW(), 1, NOW(), ''); - --- 4.5 提交申请权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('提交申请', @application_menu_id, 11, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipApplication:submit', '#', 1, NOW(), 1, NOW(), ''); - --- 4.6 撤回申请权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('撤回申请', @application_menu_id, 12, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipApplication:withdraw', '#', 1, NOW(), 1, NOW(), ''); - --- 4.7 批量申请权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('批量申请', @application_menu_id, 13, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipApplication:batch', '#', 1, NOW(), 1, NOW(), ''); - --- 4.8 统计权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('申请统计', @application_menu_id, 14, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipApplication:statistics', '#', 1, NOW(), 1, NOW(), ''); - --- ===================================================== --- 5. 创建扩展功能权限 --- ===================================================== - --- 5.1 名额查询权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('名额查询', @application_menu_id, 15, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipApplication:quota', '#', 1, NOW(), 1, NOW(), ''); - --- 5.2 可申请奖学金查询权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('可申请奖学金', @application_menu_id, 16, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipApplication:available', '#', 1, NOW(), 1, NOW(), ''); - --- 5.3 班级信息查询权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('班级信息查询', @application_menu_id, 17, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipApplication:classInfo', '#', 1, NOW(), 1, NOW(), ''); - --- 5.4 学生信息查询权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('学生信息查询', @application_menu_id, 18, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipApplication:students', '#', 1, NOW(), 1, NOW(), ''); - --- 5.5 奖学金类型查询权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('奖学金类型查询', @application_menu_id, 19, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipApplication:scholarshipTypes', '#', 1, NOW(), 1, NOW(), ''); - --- ===================================================== --- 6. 为管理员角色分配权限 --- ===================================================== - --- 获取管理员角色ID (role_key = 'admin') -SET @admin_role_id = (SELECT role_id FROM sys_role WHERE role_key = 'admin' LIMIT 1); - --- 为管理员分配奖学金申请管理菜单权限 -INSERT INTO sys_role_menu (role_id, menu_id) -SELECT @admin_role_id, menu_id -FROM sys_menu -WHERE (menu_id = @application_menu_id OR parent_id = @application_menu_id) -AND NOT EXISTS ( - SELECT 1 FROM sys_role_menu rm - WHERE rm.role_id = @admin_role_id AND rm.menu_id = sys_menu.menu_id -); - --- ===================================================== --- 7. 检查结果 --- ===================================================== -SELECT '奖学金申请管理菜单权限配置完成!' as message; - --- 查看创建的菜单 -SELECT menu_id, menu_name, parent_id, perms, menu_type -FROM sys_menu -WHERE menu_id = @application_menu_id OR parent_id = @application_menu_id -ORDER BY parent_id, order_num; diff --git a/backend/sql/jcbt_scholarship_management.sql b/backend/sql/jcbt_scholarship_management.sql deleted file mode 100644 index 0931ef8a..00000000 --- a/backend/sql/jcbt_scholarship_management.sql +++ /dev/null @@ -1,42 +0,0 @@ --- ===================================================== --- 奖学金管理模块 - 完整数据库表结构 --- 版本: v1.0 --- 创建时间: 2025-01-27 --- 说明: 包含奖学金类型表的完整结构定义 --- ===================================================== - --- 奖学金类型表 -CREATE TABLE IF NOT EXISTS jcbt_scholarship_type ( - id BIGINT(20) PRIMARY KEY COMMENT '主键ID', - type_name VARCHAR(100) NOT NULL COMMENT '奖学金类型名称', - type_code VARCHAR(50) UNIQUE NOT NULL COMMENT '类型代码', - description TEXT COMMENT '详细描述', - amount DECIMAL(10,2) DEFAULT 0.00 COMMENT '金额', - create_dept BIGINT(20) COMMENT '创建部门', - create_by BIGINT(20) COMMENT '创建者', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_by BIGINT(20) COMMENT '更新者', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - tenant_id BIGINT(20) NOT NULL COMMENT '租户ID', - deleted TINYINT(1) DEFAULT 0 COMMENT '删除标记', - is_system TINYINT(1) DEFAULT 0 COMMENT '是否系统内置', - status TINYINT(1) DEFAULT 1 COMMENT '状态', - sort_order INT DEFAULT 0 COMMENT '排序', - KEY idx_status (status) COMMENT '状态索引', - KEY idx_create_time (create_time) COMMENT '创建时间索引', - KEY idx_tenant_deleted (tenant_id, deleted) COMMENT '租户删除索引' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='奖学金类型表'; - --- 如果表已存在,添加create_dept字段(兼容性处理) -ALTER TABLE jcbt_scholarship_type -ADD COLUMN IF NOT EXISTS create_dept BIGINT(20) COMMENT '创建部门' -AFTER is_system; - --- ===================================================== --- 奖学金兼得关系表修复 --- ===================================================== - --- 为jcbt_scholarship_compatibility表添加create_dept字段 -ALTER TABLE jcbt_scholarship_compatibility -ADD COLUMN IF NOT EXISTS create_dept BIGINT(20) COMMENT '创建部门' -AFTER description; diff --git a/backend/sql/jcbt_scholarship_quota_management.sql b/backend/sql/jcbt_scholarship_quota_management.sql deleted file mode 100644 index c1b0cdb2..00000000 --- a/backend/sql/jcbt_scholarship_quota_management.sql +++ /dev/null @@ -1,98 +0,0 @@ --- ======================================== --- 奖学金名额分配模块 - 数据库建表脚本 --- 创建时间: 2025-01-27 --- 模块: ruoyi-jcbt --- 说明: 按批次进行名额分配管理 --- ======================================== - -USE hzdyjs_xg; - --- 删除已存在的表(开发环境) -DROP TABLE IF EXISTS `jcbt_scholarship_quota_adjustment`; -DROP TABLE IF EXISTS `jcbt_scholarship_quota`; - --- ===================================================== --- 1. 名额分配主表 --- ===================================================== -CREATE TABLE `jcbt_scholarship_quota` ( - `id` BIGINT(20) NOT NULL COMMENT '主键ID', - `batch_id` BIGINT(20) NOT NULL COMMENT '批次ID', - `scholarship_type_id` BIGINT(20) NOT NULL COMMENT '奖学金类型ID', - `allocation_level` VARCHAR(20) NOT NULL COMMENT '分配级别:UNIVERSITY-全校,COLLEGE-学院,MAJOR-专业,CLASS-班级', - `target_dept_id` BIGINT(20) DEFAULT NULL COMMENT '目标部门ID(学院/专业/班级)', - `parent_quota_id` BIGINT(20) DEFAULT NULL COMMENT '上级名额ID(层级分配)', - `total_quota` INT NOT NULL DEFAULT 0 COMMENT '总名额', - `used_quota` INT NOT NULL DEFAULT 0 COMMENT '已使用名额', - `reserved_quota` INT NOT NULL DEFAULT 0 COMMENT '预留名额', - `available_quota` INT NOT NULL DEFAULT 0 COMMENT '可用名额', - `allocation_rule` TEXT DEFAULT NULL COMMENT '分配规则(JSON格式)', - `allocation_basis` VARCHAR(500) DEFAULT NULL COMMENT '分配依据说明', - `allocation_method` VARCHAR(20) DEFAULT 'MANUAL' COMMENT '分配方式:MANUAL-手动分配,AUTO-自动分配,PROPORTIONAL-按比例分配', - `is_adjustable` TINYINT(1) DEFAULT 1 COMMENT '是否可调整:0-否,1-是', - `is_locked` TINYINT(1) DEFAULT 0 COMMENT '是否锁定:0-否,1-是', - `status` VARCHAR(20) DEFAULT 'ACTIVE' COMMENT '状态:ACTIVE-生效,INACTIVE-失效,PENDING-待审核', - `remark` VARCHAR(500) DEFAULT NULL COMMENT '备注', - `tenant_id` BIGINT(20) NOT NULL COMMENT '租户ID', - `create_dept` BIGINT(20) DEFAULT NULL COMMENT '创建部门', - `create_by` BIGINT(20) DEFAULT NULL COMMENT '创建者', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_by` BIGINT(20) DEFAULT NULL COMMENT '更新者', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` TINYINT(1) DEFAULT 0 COMMENT '删除标记:0-正常,1-删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_batch_type_dept` (`batch_id`, `scholarship_type_id`, `target_dept_id`, `deleted`), - INDEX `idx_batch_level` (`batch_id`, `allocation_level`), - INDEX `idx_dept_quota` (`target_dept_id`, `total_quota`), - INDEX `idx_parent_quota` (`parent_quota_id`), - INDEX `idx_tenant_status` (`tenant_id`, `status`, `deleted`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='奖学金名额分配表'; - --- ===================================================== --- 2. 名额调整记录表 --- ===================================================== -CREATE TABLE `jcbt_scholarship_quota_adjustment` ( - `id` BIGINT(20) NOT NULL COMMENT '主键ID', - `quota_id` BIGINT(20) NOT NULL COMMENT '名额分配ID', - `adjustment_type` VARCHAR(20) NOT NULL COMMENT '调整类型:INCREASE-增加,DECREASE-减少,TRANSFER-转移', - `from_quota_id` BIGINT(20) DEFAULT NULL COMMENT '源名额ID(转移时使用)', - `to_quota_id` BIGINT(20) DEFAULT NULL COMMENT '目标名额ID(转移时使用)', - `adjustment_amount` INT NOT NULL COMMENT '调整数量', - `before_total` INT NOT NULL COMMENT '调整前总名额', - `after_total` INT NOT NULL COMMENT '调整后总名额', - `adjustment_reason` VARCHAR(500) NOT NULL COMMENT '调整原因', - `approval_status` VARCHAR(20) DEFAULT 'PENDING' COMMENT '审批状态:PENDING-待审批,APPROVED-已通过,REJECTED-已拒绝', - `approval_user_id` BIGINT(20) DEFAULT NULL COMMENT '审批人ID', - `approval_time` DATETIME DEFAULT NULL COMMENT '审批时间', - `approval_remark` VARCHAR(500) DEFAULT NULL COMMENT '审批备注', - `tenant_id` BIGINT(20) NOT NULL COMMENT '租户ID', - `create_dept` BIGINT(20) DEFAULT NULL COMMENT '创建部门', - `create_by` BIGINT(20) DEFAULT NULL COMMENT '创建者', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_by` BIGINT(20) DEFAULT NULL COMMENT '更新者', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` TINYINT(1) DEFAULT 0 COMMENT '删除标记:0-正常,1-删除', - PRIMARY KEY (`id`), - INDEX `idx_quota_id` (`quota_id`), - INDEX `idx_adjustment_type` (`adjustment_type`, `approval_status`), - INDEX `idx_from_to_quota` (`from_quota_id`, `to_quota_id`), - INDEX `idx_tenant` (`tenant_id`, `deleted`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='名额调整记录表'; - --- ===================================================== --- 3. 添加外键约束(可选,根据实际情况) --- ===================================================== --- ALTER TABLE `jcbt_scholarship_quota` --- ADD CONSTRAINT `fk_quota_batch` FOREIGN KEY (`batch_id`) REFERENCES `jcbt_scholarship_batch` (`id`); - --- ALTER TABLE `jcbt_scholarship_quota` --- ADD CONSTRAINT `fk_quota_type` FOREIGN KEY (`scholarship_type_id`) REFERENCES `jcbt_scholarship_type` (`id`); - --- ALTER TABLE `jcbt_scholarship_quota_adjustment` --- ADD CONSTRAINT `fk_adjustment_quota` FOREIGN KEY (`quota_id`) REFERENCES `jcbt_scholarship_quota` (`id`); - --- ===================================================== --- 4. 验证表创建 --- ===================================================== -SHOW TABLES LIKE 'jcbt_scholarship_quota%'; -DESCRIBE jcbt_scholarship_quota; -DESCRIBE jcbt_scholarship_quota_adjustment; diff --git a/backend/sql/jcbt_scholarship_quota_menu_permissions.sql b/backend/sql/jcbt_scholarship_quota_menu_permissions.sql deleted file mode 100644 index 8c50752a..00000000 --- a/backend/sql/jcbt_scholarship_quota_menu_permissions.sql +++ /dev/null @@ -1,134 +0,0 @@ --- ======================================== --- 奖学金名额分配模块 - 菜单权限配置 --- 创建时间: 2025-01-27 --- 说明: 配置名额分配功能的菜单和权限 --- ======================================== - -USE hzdyjs_xg; - --- 设置变量 -SET @parent_menu_id = 2015; -- 奖惩补贴管理父菜单ID -SET @menu_order = 4; -- 菜单排序 - --- ===================================================== --- 1. 删除已存在的菜单(开发环境重新创建) --- ===================================================== -DELETE FROM sys_menu WHERE menu_name = '名额分配管理' AND parent_id = @parent_menu_id; - --- ===================================================== --- 2. 创建名额分配管理菜单 --- ===================================================== -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('名额分配管理', @parent_menu_id, @menu_order, 'scholarshipQuota', 'jcbt/scholarshipQuota/index', '', 1, 0, 'C', '0', '0', 'jcbt:scholarshipQuota:list', 'tree-table', 1, NOW(), 1, NOW(), '奖学金名额分配管理菜单'); - --- 获取刚插入的菜单ID -SET @quota_menu_id = LAST_INSERT_ID(); - --- ===================================================== --- 3. 创建名额分配功能按钮权限 --- ===================================================== - --- 3.1 查询权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('名额分配查询', @quota_menu_id, 1, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipQuota:query', '#', 1, NOW(), 1, NOW(), ''); - --- 3.2 新增权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('名额分配新增', @quota_menu_id, 2, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipQuota:add', '#', 1, NOW(), 1, NOW(), ''); - --- 3.3 修改权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('名额分配修改', @quota_menu_id, 3, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipQuota:edit', '#', 1, NOW(), 1, NOW(), ''); - --- 3.4 删除权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('名额分配删除', @quota_menu_id, 4, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipQuota:remove', '#', 1, NOW(), 1, NOW(), ''); - --- 3.5 导出权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('名额分配导出', @quota_menu_id, 5, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipQuota:export', '#', 1, NOW(), 1, NOW(), ''); - --- 3.6 批量分配权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('批量分配名额', @quota_menu_id, 6, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipQuota:batchAllocate', '#', 1, NOW(), 1, NOW(), ''); - --- 3.7 名额调整权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('名额调整', @quota_menu_id, 7, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipQuota:adjust', '#', 1, NOW(), 1, NOW(), ''); - --- 3.8 名额锁定权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('名额锁定', @quota_menu_id, 8, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipQuota:lock', '#', 1, NOW(), 1, NOW(), ''); - --- 3.9 自动分配权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('自动分配名额', @quota_menu_id, 9, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipQuota:autoAllocate', '#', 1, NOW(), 1, NOW(), ''); - --- 3.10 复制分配权限 -INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES ('复制名额分配', @quota_menu_id, 10, '', '', '', 1, 0, 'F', '0', '0', 'jcbt:scholarshipQuota:copy', '#', 1, NOW(), 1, NOW(), ''); - --- ===================================================== --- 4. 为管理员角色分配权限 --- ===================================================== - --- 4.1 获取管理员角色ID -SET @admin_role_id = (SELECT role_id FROM sys_role WHERE role_key = 'admin' LIMIT 1); - --- 4.2 为管理员角色分配名额分配菜单权限 -INSERT INTO sys_role_menu (role_id, menu_id) -SELECT @admin_role_id, menu_id -FROM sys_menu -WHERE (menu_id = @quota_menu_id OR parent_id = @quota_menu_id) - AND NOT EXISTS ( - SELECT 1 FROM sys_role_menu rm - WHERE rm.role_id = @admin_role_id AND rm.menu_id = sys_menu.menu_id - ); - --- ===================================================== --- 5. 创建字典数据 --- ===================================================== - --- 5.1 分配级别字典 -INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, update_by, update_time, remark) -VALUES ('名额分配级别', 'jcbt_quota_allocation_level', '0', 1, NOW(), 1, NOW(), '奖学金名额分配级别字典类型') -ON DUPLICATE KEY UPDATE dict_name = VALUES(dict_name); - -INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, update_by, update_time, remark) -VALUES -(1, '全校', 'UNIVERSITY', 'jcbt_quota_allocation_level', '', 'primary', 'Y', '0', 1, NOW(), 1, NOW(), '全校级别分配'), -(2, '学院', 'COLLEGE', 'jcbt_quota_allocation_level', '', 'success', 'N', '0', 1, NOW(), 1, NOW(), '学院级别分配'), -(3, '专业', 'MAJOR', 'jcbt_quota_allocation_level', '', 'info', 'N', '0', 1, NOW(), 1, NOW(), '专业级别分配'), -(4, '班级', 'CLASS', 'jcbt_quota_allocation_level', '', 'warning', 'N', '0', 1, NOW(), 1, NOW(), '班级级别分配') -ON DUPLICATE KEY UPDATE dict_label = VALUES(dict_label); - --- 5.2 分配方式字典 -INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, update_by, update_time, remark) -VALUES ('名额分配方式', 'jcbt_quota_allocation_method', '0', 1, NOW(), 1, NOW(), '奖学金名额分配方式字典类型') -ON DUPLICATE KEY UPDATE dict_name = VALUES(dict_name); - -INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, update_by, update_time, remark) -VALUES -(1, '手动分配', 'MANUAL', 'jcbt_quota_allocation_method', '', 'primary', 'Y', '0', 1, NOW(), 1, NOW(), '手动分配名额'), -(2, '自动分配', 'AUTO', 'jcbt_quota_allocation_method', '', 'success', 'N', '0', 1, NOW(), 1, NOW(), '自动分配名额'), -(3, '按比例分配', 'PROPORTIONAL', 'jcbt_quota_allocation_method', '', 'info', 'N', '0', 1, NOW(), 1, NOW(), '按比例分配名额') -ON DUPLICATE KEY UPDATE dict_label = VALUES(dict_label); - --- 5.3 分配状态字典 -INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, update_by, update_time, remark) -VALUES ('名额分配状态', 'jcbt_quota_status', '0', 1, NOW(), 1, NOW(), '奖学金名额分配状态字典类型') -ON DUPLICATE KEY UPDATE dict_name = VALUES(dict_name); - -INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, update_by, update_time, remark) -VALUES -(1, '生效', 'ACTIVE', 'jcbt_quota_status', '', 'success', 'Y', '0', 1, NOW(), 1, NOW(), '名额分配生效'), -(2, '失效', 'INACTIVE', 'jcbt_quota_status', '', 'danger', 'N', '0', 1, NOW(), 1, NOW(), '名额分配失效'), -(3, '待审核', 'PENDING', 'jcbt_quota_status', '', 'warning', 'N', '0', 1, NOW(), 1, NOW(), '名额分配待审核') -ON DUPLICATE KEY UPDATE dict_label = VALUES(dict_label); - --- ===================================================== --- 6. 验证创建结果 --- ===================================================== -SELECT '名额分配管理菜单创建完成!' as message; -SELECT menu_id, menu_name, parent_id, perms FROM sys_menu WHERE menu_name LIKE '%名额分配%' ORDER BY parent_id, order_num; -SELECT dict_type, COUNT(*) as count FROM sys_dict_data WHERE dict_type LIKE 'jcbt_quota_%' GROUP BY dict_type; diff --git a/backend/sql/scholarship_test_data.sql b/backend/sql/scholarship_test_data.sql deleted file mode 100644 index 7d63846a..00000000 --- a/backend/sql/scholarship_test_data.sql +++ /dev/null @@ -1,224 +0,0 @@ --- ==================================================================== --- 奖学金管理模块 - 测试数据脚本 --- 创建时间: 2025-01-27 --- 说明: 包含奖学金类型、班级、申请记录的测试数据 --- ==================================================================== - -USE hzdyjs_xg; - --- ===================================================== --- 1. 清理现有测试数据 --- ===================================================== -DELETE FROM jcbt_scholarship_application WHERE student_name LIKE '测试学生%'; -DELETE FROM jcbt_scholarship_type WHERE type_name LIKE '测试%'; -DELETE FROM xg_class_msg WHERE class_name LIKE '测试%'; - --- ===================================================== --- 2. 插入奖学金类型测试数据 --- ===================================================== -INSERT INTO jcbt_scholarship_type ( - id, type_code, type_name, type_category, level_type, award_amount, - description, application_guide, application_conditions, evaluation_criteria, - compatibility_type, sort_order, status, is_system, tenant_id, deleted, - create_by, create_time, update_by, update_time -) VALUES -( - 1863768901234567001, - 'ACADEMIC_EXCELLENCE', - '优秀学生奖学金', - 'ACADEMIC', - 'FIRST_CLASS', - 3000.00, - '奖励品学兼优的学生', - '申请时需要提交成绩单和获奖证书', - '学期平均分85分以上,无挂科记录', - '按成绩排名评定', - 'COMPATIBLE', - 1, 1, 0, '000000', 0, - 1, NOW(), 1, NOW() -), -( - 1863768901234567002, - 'POVERTY_ASSISTANCE', - '贫困助学金', - 'ASSISTANCE', - 'GENERAL', - 2000.00, - '资助家庭经济困难学生', - '申请时需要提交家庭经济证明', - '家庭经济困难,成绩及格以上', - '按经济困难程度评定', - 'COMPATIBLE', - 2, 1, 0, '000000', 0, - 1, NOW(), 1, NOW() -), -( - 1863768901234567003, - 'INNOVATION_PRIZE', - '创新创业奖学金', - 'INNOVATION', - 'SPECIAL', - 5000.00, - '奖励在创新创业方面表现突出的学生', - '申请时需要提交创新项目材料', - '有创新成果或创业经历', - '按创新项目质量评定', - 'EXCLUSIVE', - 3, 1, 0, '000000', 0, - 1, NOW(), 1, NOW() -); - --- ===================================================== --- 3. 插入班级测试数据 --- ===================================================== -INSERT INTO xg_class_msg ( - id, college_id, college_name, major_id, major_name, - class_code, class_name, class_type, grade, enrollment_year, graduation_year, - class_teacher_id, class_teacher_name, class_status, - planned_capacity, current_student_count, tenant_id, deleted, - create_by, create_time, update_by, update_time -) VALUES -( - 1863768901234568001, - 100, '计算机学院', 101, '软件工程', - 'CS2021001', '测试班级2021级软件工程1班', '1', '2021', 2021, 2025, - 1001, '张老师', '1', - 30, 28, '000000', 0, - 1, NOW(), 1, NOW() -), -( - 1863768901234568002, - 100, '计算机学院', 101, '软件工程', - 'CS2021002', '测试班级2021级软件工程2班', '1', '2021', 2021, 2025, - 1002, '李老师', '1', - 30, 26, '000000', 0, - 1, NOW(), 1, NOW() -), -( - 1863768901234568003, - 200, '机械学院', 201, '机械设计制造及其自动化', - 'ME2021001', '测试班级2021级机械1班', '1', '2021', 2021, 2025, - 2001, '王老师', '1', - 25, 24, '000000', 0, - 1, NOW(), 1, NOW() -); - --- ===================================================== --- 4. 插入申请记录测试数据 --- ===================================================== -INSERT INTO jcbt_scholarship_application ( - id, application_no, batch_id, scholarship_type_id, - student_no, student_name, student_id, dept_id, major_id, class_id, - application_type, current_status, apply_reason, self_evaluation, - calculated_score, final_score, ranking_in_dept, ranking_in_major, - submit_time, tenant_id, deleted, - create_by, create_time, update_by, update_time -) VALUES -( - 1863768901234569001, - 'APPLY20250127001', - 1863768901234560001, -- 假设批次ID - 1863768901234567001, -- 优秀学生奖学金 - '2021001001', '测试学生张三', 1863768901234570001, - 100, 101, 1863768901234568001, -- 计算机学院,软件工程,测试班级1 - 'STUDENT', 'SUBMITTED', '学习成绩优异,积极参与社会实践', - '我在本学期各科成绩均在90分以上,积极参与班级和学院活动,希望能够获得奖学金鼓励。', - 92.5, 93.0, 2, 1, - NOW(), '000000', 0, - 1, NOW(), 1, NOW() -), -( - 1863768901234569002, - 'APPLY20250127002', - 1863768901234560001, -- 假设批次ID - 1863768901234567002, -- 贫困助学金 - '2021001002', '测试学生李四', 1863768901234570002, - 100, 101, 1863768901234568001, -- 计算机学院,软件工程,测试班级1 - 'STUDENT', 'SUBMITTED', '家庭经济困难,需要资助', - '家庭经济困难,父母务农收入微薄,希望通过助学金减轻经济负担,专心学习。', - 78.5, 80.0, 15, 8, - NOW(), '000000', 0, - 1, NOW(), 1, NOW() -), -( - 1863768901234569003, - 'APPLY20250127003', - 1863768901234560001, -- 假设批次ID - 1863768901234567003, -- 创新创业奖学金 - '2021002001', '测试学生王五', 1863768901234570003, - 100, 101, 1863768901234568002, -- 计算机学院,软件工程,测试班级2 - 'RECOMMEND', 'REVIEWING', '在创新创业方面表现突出', - '参与开发了校园APP项目,获得大学生创新创业大赛二等奖,具有很强的创新能力。', - 88.0, 89.5, 5, 3, - NOW(), '000000', 0, - 1, NOW(), 1, NOW() -), -( - 1863768901234569004, - 'APPLY20250127004', - 1863768901234560001, -- 假设批次ID - 1863768901234567001, -- 优秀学生奖学金 - '2021003001', '测试学生赵六', 1863768901234570004, - 200, 201, 1863768901234568003, -- 机械学院,机械设计,测试班级3 - 'STUDENT', 'APPROVED', '学习成绩优秀,综合素质高', - '各科成绩均名列前茅,担任班长职务,热心为同学服务,积极参与各项活动。', - 95.0, 95.5, 1, 1, - DATE_SUB(NOW(), INTERVAL 2 DAY), '000000', 0, - 1, DATE_SUB(NOW(), INTERVAL 2 DAY), 1, NOW() -), -( - 1863768901234569005, - 'APPLY20250127005', - 1863768901234560001, -- 假设批次ID - 1863768901234567002, -- 贫困助学金 - '2021003002', '测试学生孙七', 1863768901234570005, - 200, 201, 1863768901234568003, -- 机械学院,机械设计,测试班级3 - 'STUDENT', 'REJECTED', '经济困难但成绩有待提高', - '虽然家庭经济困难,但我会努力学习,提高成绩,争取下次申请成功。', - 65.0, 68.0, 20, 12, - DATE_SUB(NOW(), INTERVAL 3 DAY), '000000', 0, - 1, DATE_SUB(NOW(), INTERVAL 3 DAY), 1, NOW() -); - --- ===================================================== --- 5. 插入奖学金批次测试数据(如果需要) --- ===================================================== -INSERT IGNORE INTO jcbt_scholarship_batch ( - id, batch_name, batch_code, academic_year, semester, - start_date, end_date, status, description, - tenant_id, deleted, create_by, create_time, update_by, update_time -) VALUES ( - 1863768901234560001, - '2024-2025学年第一学期奖学金申请', - 'BATCH202401', - '2024-2025', '1', - '2024-09-01', '2024-09-30', 'ACTIVE', '2024-2025学年第一学期奖学金申请批次', - '000000', 0, 1, NOW(), 1, NOW() -); - --- ===================================================== --- 6. 验证数据插入 --- ===================================================== -SELECT '=== 奖学金类型数据 ===' AS info; -SELECT id, type_code, type_name, award_amount FROM jcbt_scholarship_type WHERE type_name LIKE '测试%' OR type_name LIKE '%奖学金' OR type_name LIKE '%助学金' LIMIT 5; - -SELECT '=== 班级数据 ===' AS info; -SELECT id, class_code, class_name, college_name, major_name FROM xg_class_msg WHERE class_name LIKE '测试%' LIMIT 5; - -SELECT '=== 申请记录数据 ===' AS info; -SELECT id, application_no, student_name, current_status FROM jcbt_scholarship_application WHERE student_name LIKE '测试学生%' LIMIT 5; - -SELECT '=== 关联查询测试 ===' AS info; -SELECT - a.application_no, - a.student_name, - st.type_name as scholarship_type_name, - c.class_name, - a.current_status -FROM jcbt_scholarship_application a -LEFT JOIN jcbt_scholarship_type st ON a.scholarship_type_id = st.id -LEFT JOIN xg_class_msg c ON a.class_id = c.id -WHERE a.student_name LIKE '测试学生%' -LIMIT 10; - -COMMIT; \ No newline at end of file diff --git a/backend/sql/test_assistance_types_fix.sql b/backend/sql/test_assistance_types_fix.sql deleted file mode 100644 index d27dc40e..00000000 --- a/backend/sql/test_assistance_types_fix.sql +++ /dev/null @@ -1,43 +0,0 @@ --- 测试 assistance_types 字段修复结果 -USE hzdyjs_xg; - -SELECT '=== 检查 assistance_types 字段属性 ===' as message; - -SELECT - COLUMN_NAME as '字段名', - DATA_TYPE as '数据类型', - IS_NULLABLE as '允许NULL', - COLUMN_DEFAULT as '默认值', - COLUMN_COMMENT as '注释' -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' -AND TABLE_NAME = 'jcbt_poverty_application' -AND COLUMN_NAME = 'assistance_types'; - -SELECT '=== 测试插入记录(assistance_types为NULL)===' as message; - --- 插入测试记录,assistance_types 字段为 NULL -INSERT INTO jcbt_poverty_application ( - id, application_no, academic_year, term, student_id, student_no, student_name, - gender, college_id, college_name, major_id, major_name, class_id, class_name, - recognition_group, recognition_type_id, recognition_type_name, - status, create_by, create_time, tenant_id -) VALUES ( - 999999998, 'TEST002', '2024-2025', 1, 1002, '202401002', '测试学生2', - '1', 102, '测试学院', 10102, '测试专业', 1010102, '测试班级2', - '特殊群体', 1, '特困生', - 'DRAFT', 103, NOW(), '000000' -); - -SELECT '=== 查询测试记录 ===' as message; - -SELECT id, student_name, assistance_types, status -FROM jcbt_poverty_application -WHERE id = 999999998; - -SELECT '=== 清理测试数据 ===' as message; - --- 清理测试数据 -DELETE FROM jcbt_poverty_application WHERE id = 999999998; - -SELECT '✅ 测试完成!如果没有错误,说明 assistance_types 字段修复成功!' as result; diff --git a/backend/sql/test_audit_fields_fix.sql b/backend/sql/test_audit_fields_fix.sql deleted file mode 100644 index ee6ebe94..00000000 --- a/backend/sql/test_audit_fields_fix.sql +++ /dev/null @@ -1,35 +0,0 @@ --- 测试审核字段修复结果 -USE hzdyjs_xg; - -SELECT '=== 检查修复后的审核字段 ===' as message; - -SELECT - COLUMN_NAME as '字段名', - DATA_TYPE as '数据类型', - IS_NULLABLE as '允许NULL', - COLUMN_DEFAULT as '默认值', - COLUMN_COMMENT as '注释' -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' -AND TABLE_NAME = 'jcbt_poverty_application' -AND COLUMN_NAME IN ('audit_time', 'audit_opinion', 'auditor_id', 'auditor_name') -ORDER BY COLUMN_NAME; - -SELECT '=== 测试审核字段查询(模拟MyBatis-Plus查询)===' as message; - --- 模拟MyBatis-Plus的查询,包含所有字段 -SELECT - id, application_no, academic_year, term, student_id, student_no, student_name, - gender, college_id, college_name, major_id, major_name, class_id, class_name, - teacher_id, teacher_name, recognition_group, recognition_type_id, recognition_type_code, - recognition_type_name, assistance_type_ids, assistance_type_names, family_economy_summary, - attach_application_form_url, attach_application_form_id, attach_survey_form_url, - attach_survey_form_id, status, submitter_id, submitter_name, submit_time, - auditor_id, auditor_name, audit_time, audit_opinion, remark, - deleted, create_dept, create_by, create_time, update_by, update_time -FROM jcbt_poverty_application -WHERE deleted = 0 AND tenant_id = '000000' -ORDER BY create_time DESC -LIMIT 1; - -SELECT '✅ 测试完成!如果没有 Unknown column 错误,说明字段修复成功!' as result; diff --git a/backend/sql/test_fix_result.sql b/backend/sql/test_fix_result.sql deleted file mode 100644 index 2c407465..00000000 --- a/backend/sql/test_fix_result.sql +++ /dev/null @@ -1,52 +0,0 @@ --- ================================================================ --- 测试 teacher_name 字段修复结果 --- 文件: test_fix_result.sql --- 功能: 验证修复是否成功 --- ================================================================ - -USE hzdyjs_xg; - --- 1. 检查表结构 -SELECT '=== 检查表结构 ===' AS message; -SHOW CREATE TABLE jcbt_poverty_application; - --- 2. 检查关键字段属性 -SELECT '=== 检查关键字段属性 ===' AS message; -SELECT - COLUMN_NAME AS '字段名', - DATA_TYPE AS '数据类型', - IS_NULLABLE AS '允许NULL', - COLUMN_DEFAULT AS '默认值', - COLUMN_COMMENT AS '注释' -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'hzdyjs_xg' - AND TABLE_NAME = 'jcbt_poverty_application' - AND COLUMN_NAME IN ('teacher_id', 'teacher_name') -ORDER BY ORDINAL_POSITION; - --- 3. 测试插入记录(不包含teacher_name) -SELECT '=== 测试插入记录 ===' AS message; - -INSERT INTO jcbt_poverty_application ( - id, application_no, academic_year, term, student_id, student_no, student_name, - gender, college_id, college_name, major_id, major_name, class_id, class_name, - recognition_group, recognition_type_id, recognition_type_name, assistance_types, - status, create_by, create_time, tenant_id -) VALUES ( - 999999999, 'TEST001', '2024-2025', 1, 1001, '202401001', '测试学生', - '1', 101, '计算机学院', 10101, '计算机科学', 1010101, '测试班级', - '特殊群体', 1, '特困生', '助学金', - 'DRAFT', 103, NOW(), '000000' -); - --- 4. 查询测试记录 -SELECT '=== 查询测试记录 ===' AS message; -SELECT - id, student_name, teacher_id, teacher_name, status -FROM jcbt_poverty_application -WHERE id = 999999999; - --- 5. 清理测试数据 -DELETE FROM jcbt_poverty_application WHERE id = 999999999; - -SELECT '✅ 测试完成!如果没有错误,说明修复成功!' AS result; diff --git a/backend/sql/test_missing_fields_fix.sql b/backend/sql/test_missing_fields_fix.sql deleted file mode 100644 index 293018ac..00000000 --- a/backend/sql/test_missing_fields_fix.sql +++ /dev/null @@ -1,105 +0,0 @@ --- ===================================================== --- 奖学金类型表字段修复验证脚本 --- 创建时间: 2025-08-04 --- 说明: 验证申请条件、评定标准、备注字段是否正确添加和工作 --- ===================================================== - --- 1. 验证字段是否存在 -SELECT - COLUMN_NAME as '字段名', - DATA_TYPE as '数据类型', - CHARACTER_MAXIMUM_LENGTH as '最大长度', - IS_NULLABLE as '是否可空', - COLUMN_DEFAULT as '默认值', - COLUMN_COMMENT as '字段注释' -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'jcbt_scholarship_type' - AND COLUMN_NAME IN ('application_conditions', 'evaluation_criteria', 'remark') -ORDER BY ORDINAL_POSITION; - --- 2. 查看表结构(包含新增字段的完整表结构) -DESCRIBE jcbt_scholarship_type; - --- 3. 测试插入数据(包含新增字段) -INSERT INTO jcbt_scholarship_type ( - id, type_code, type_name, type_category, level_type, award_amount, - description, application_guide, application_conditions, evaluation_criteria, remark, - sort_order, status, is_system, compatibility_type, - create_by, create_time, update_by, update_time, tenant_id, deleted -) VALUES ( - 1001, 'TEST_001', '测试奖学金', 'ACADEMIC', 'FIRST_CLASS', 5000.00, - '这是一个测试奖学金描述', - '请按照以下步骤申请:\n1. 填写申请表\n2. 提交相关材料', - '申请条件:\n1. 学习成绩优秀,GPA≥3.5\n2. 无违纪记录\n3. 积极参与社会实践', - '评定标准:\n1. 学习成绩占60%\n2. 综合素质占30%\n3. 社会实践占10%', - '备注:本奖学金每学期评选一次,优秀学生可连续获得', - 1, 1, 0, 'COMPATIBLE', - 1, NOW(), 1, NOW(), '000000', 0 -); - --- 4. 验证数据插入是否成功 -SELECT - id, type_code, type_name, - LEFT(application_conditions, 50) as '申请条件_前50字符', - LEFT(evaluation_criteria, 50) as '评定标准_前50字符', - LEFT(remark, 50) as '备注_前50字符' -FROM jcbt_scholarship_type -WHERE type_code = 'TEST_001'; - --- 5. 测试更新操作 -UPDATE jcbt_scholarship_type -SET - application_conditions = '更新后的申请条件:\n1. GPA≥3.8\n2. 英语四级以上\n3. 无挂科记录', - evaluation_criteria = '更新后的评定标准:\n1. 学习成绩70%\n2. 英语水平20%\n3. 综合表现10%', - remark = '更新后的备注:此奖学金面向大二及以上学生', - update_time = NOW() -WHERE type_code = 'TEST_001'; - --- 6. 验证更新是否成功 -SELECT - id, type_code, type_name, - application_conditions as '申请条件', - evaluation_criteria as '评定标准', - remark as '备注', - update_time as '更新时间' -FROM jcbt_scholarship_type -WHERE type_code = 'TEST_001'; - --- 7. 查询所有奖学金类型的这三个字段(查看现有数据) -SELECT - id, type_code, type_name, - CASE - WHEN application_conditions IS NULL THEN '空值' - WHEN application_conditions = '' THEN '空字符串' - ELSE CONCAT('有值-', LEFT(application_conditions, 20), '...') - END as '申请条件状态', - CASE - WHEN evaluation_criteria IS NULL THEN '空值' - WHEN evaluation_criteria = '' THEN '空字符串' - ELSE CONCAT('有值-', LEFT(evaluation_criteria, 20), '...') - END as '评定标准状态', - CASE - WHEN remark IS NULL THEN '空值' - WHEN remark = '' THEN '空字符串' - ELSE CONCAT('有值-', LEFT(remark, 20), '...') - END as '备注状态' -FROM jcbt_scholarship_type -WHERE deleted = 0 -ORDER BY sort_order, create_time DESC; - --- 8. 清理测试数据 -DELETE FROM jcbt_scholarship_type WHERE type_code = 'TEST_001'; - --- 9. 验证清理结果 -SELECT COUNT(*) as '清理后剩余测试数据数量' -FROM jcbt_scholarship_type -WHERE type_code = 'TEST_001'; - --- ===================================================== --- 测试完成提示 --- ===================================================== -SELECT - '字段修复验证完成' as '状态', - '如果以上查询都正常执行,说明字段修复成功' as '说明', - '现在可以重启后端服务测试详情页面显示' as '下一步'; diff --git a/backend/sql/test_scholarship_basic_data.sql b/backend/sql/test_scholarship_basic_data.sql deleted file mode 100644 index af5c2ae4..00000000 --- a/backend/sql/test_scholarship_basic_data.sql +++ /dev/null @@ -1,131 +0,0 @@ --- ===================================================== --- 奖学金名额分配 - 基础数据检查和测试数据插入 --- 创建时间: 2025-01-27 --- 说明: 检查和创建测试用的基础数据 --- ===================================================== - -USE hzdyjs_xg; - --- ===================================================== --- 1. 检查现有数据 --- ===================================================== - --- 检查奖学金批次数据 -SELECT 'jcbt_scholarship_batch' as table_name, COUNT(*) as record_count FROM jcbt_scholarship_batch WHERE deleted = 0; - --- 检查奖学金类型数据 -SELECT 'jcbt_scholarship_type' as table_name, COUNT(*) as record_count FROM jcbt_scholarship_type WHERE deleted = 0; - --- 检查名额分配数据 -SELECT 'jcbt_scholarship_quota' as table_name, COUNT(*) as record_count FROM jcbt_scholarship_quota WHERE deleted = 0; - --- ===================================================== --- 2. 插入测试批次数据(如果不存在) --- ===================================================== - -INSERT IGNORE INTO jcbt_scholarship_batch ( - id, batch_code, batch_name, academic_year, semester, batch_type, - start_time, end_time, apply_start_time, apply_end_time, - review_start_time, review_end_time, status, description, - create_by, create_time, update_by, update_time, tenant_id, deleted -) VALUES -(1890000000000000001, 'BATCH_2024_1', '2024学年第一学期奖学金评定', '2024-2025', 1, 'REGULAR', - '2024-09-01 00:00:00', '2024-12-31 23:59:59', '2024-10-01 00:00:00', '2024-10-31 23:59:59', - '2024-11-01 00:00:00', '2024-11-30 23:59:59', 'ACTIVE', '2024学年第一学期常规奖学金评定批次', - 1, NOW(), 1, NOW(), 1, 0), -(1890000000000000002, 'BATCH_2024_2', '2024学年第二学期奖学金评定', '2024-2025', 2, 'REGULAR', - '2025-02-01 00:00:00', '2025-06-30 23:59:59', '2025-03-01 00:00:00', '2025-03-31 23:59:59', - '2025-04-01 00:00:00', '2025-04-30 23:59:59', 'DRAFT', '2024学年第二学期常规奖学金评定批次', - 1, NOW(), 1, NOW(), 1, 0); - --- ===================================================== --- 3. 插入测试奖学金类型数据(如果不存在) --- ===================================================== - -INSERT IGNORE INTO jcbt_scholarship_type ( - id, type_code, type_name, type_category, level_type, award_amount, - description, application_guide, application_conditions, evaluation_criteria, - compatibility_type, sort_order, status, is_system, - create_by, create_time, update_by, update_time, tenant_id, deleted -) VALUES -(1890000000000000101, 'GJZXJ', '国家奖学金', 'NATIONAL', 'FIRST_CLASS', 8000.00, - '国家奖学金是为了激励普通本科高校、高等职业学校和高等专科学校学生勤奋学习、努力进取,在德、智、体、美等方面全面发展,由中央政府出资设立的奖励特别优秀学生的奖学金。', - '学生本人提出申请,按学校规定程序推荐上报。', '品学兼优,学习成绩排名在评选范围内位于前10%', - '学习成绩、综合测评、道德品质等综合评定', 'EXCLUSIVE', 1, 1, 1, - 1, NOW(), 1, NOW(), 1, 0), -(1890000000000000102, 'GJLZJ', '国家励志奖学金', 'NATIONAL', 'SECOND_CLASS', 5000.00, - '国家励志奖学金是为了激励普通本科高校、高等职业学校和高等专科学校的家庭经济困难学生勤奋学习、努力进取,在德、智、体、美等方面全面发展,由中央和地方政府共同出资设立的,奖励资助品学兼优的家庭经济困难学生的奖学金。', - '学生本人提出申请,按学校规定程序推荐上报。', '家庭经济困难且品学兼优,学习成绩排名在评选范围内位于前25%', - '学习成绩、综合测评、家庭经济状况等综合评定', 'EXCLUSIVE', 2, 1, 1, - 1, NOW(), 1, NOW(), 1, 0), -(1890000000000000103, 'XXZXJ1', '校级奖学金一等奖', 'SCHOOL', 'FIRST_CLASS', 3000.00, - '学校设立的奖学金,奖励学习成绩优秀、综合素质突出的学生。', - '学生本人提出申请,班级推荐,学院审核。', '学习成绩排名在专业前5%,综合测评排名在专业前10%', - '学习成绩、综合测评、特长发展等', 'COMPATIBLE', 3, 1, 0, - 1, NOW(), 1, NOW(), 1, 0), -(1890000000000000104, 'XXZXJ2', '校级奖学金二等奖', 'SCHOOL', 'SECOND_CLASS', 2000.00, - '学校设立的奖学金,奖励学习成绩良好、综合素质较好的学生。', - '学生本人提出申请,班级推荐,学院审核。', '学习成绩排名在专业前15%,综合测评排名在专业前20%', - '学习成绩、综合测评、特长发展等', 'COMPATIBLE', 4, 1, 0, - 1, NOW(), 1, NOW(), 1, 0), -(1890000000000000105, 'SZZXJ', '社会专项奖学金', 'ENTERPRISE', 'SPECIAL', 1500.00, - '由社会企业、校友或其他组织设立的专项奖学金。', - '学生本人提出申请,按专项奖学金具体要求推荐。', '符合专项奖学金设立的特定条件和要求', - '按照专项奖学金的评定标准执行', 'CUSTOM', 5, 1, 0, - 1, NOW(), 1, NOW(), 1, 0); - --- ===================================================== --- 4. 插入测试名额分配数据(如果不存在) --- ===================================================== - -INSERT IGNORE INTO jcbt_scholarship_quota ( - id, batch_id, scholarship_type_id, allocation_level, target_dept_id, parent_quota_id, - total_quota, used_quota, reserved_quota, available_quota, - allocation_rule, allocation_basis, allocation_method, is_adjustable, is_locked, status, - remark, create_by, create_time, update_by, update_time, tenant_id, deleted -) VALUES --- 国家奖学金 - 全校级分配 -(1890000000000000201, 1890000000000000001, 1890000000000000101, 'UNIVERSITY', NULL, NULL, - 50, 0, 5, 45, '{"type":"university","rule":"total_allocation"}', '根据学校总体规模和历年获奖情况分配', - 'MANUAL', 1, 0, 'ACTIVE', '全校国家奖学金总名额', 1, NOW(), 1, NOW(), 1, 0), - --- 国家励志奖学金 - 全校级分配 -(1890000000000000202, 1890000000000000001, 1890000000000000102, 'UNIVERSITY', NULL, NULL, - 200, 0, 20, 180, '{"type":"university","rule":"total_allocation"}', '根据学校困难学生比例和名额分配', - 'MANUAL', 1, 0, 'ACTIVE', '全校国家励志奖学金总名额', 1, NOW(), 1, NOW(), 1, 0), - --- 校级奖学金一等奖 - 全校级分配 -(1890000000000000203, 1890000000000000001, 1890000000000000103, 'UNIVERSITY', NULL, NULL, - 100, 0, 10, 90, '{"type":"university","rule":"total_allocation"}', '根据学校各学院学生人数比例分配', - 'MANUAL', 1, 0, 'ACTIVE', '全校校级一等奖学金总名额', 1, NOW(), 1, NOW(), 1, 0), - --- 校级奖学金二等奖 - 全校级分配 -(1890000000000000204, 1890000000000000001, 1890000000000000104, 'UNIVERSITY', NULL, NULL, - 300, 0, 30, 270, '{"type":"university","rule":"total_allocation"}', '根据学校各学院学生人数比例分配', - 'MANUAL', 1, 0, 'ACTIVE', '全校校级二等奖学金总名额', 1, NOW(), 1, NOW(), 1, 0); - --- ===================================================== --- 5. 验证插入结果 --- ===================================================== - --- 再次检查数据插入结果 -SELECT '插入后-jcbt_scholarship_batch' as table_name, COUNT(*) as record_count FROM jcbt_scholarship_batch WHERE deleted = 0; -SELECT '插入后-jcbt_scholarship_type' as table_name, COUNT(*) as record_count FROM jcbt_scholarship_type WHERE deleted = 0; -SELECT '插入后-jcbt_scholarship_quota' as table_name, COUNT(*) as record_count FROM jcbt_scholarship_quota WHERE deleted = 0; - --- 查看详细数据 -SELECT - q.id, - b.batch_name, - st.type_name as scholarship_type_name, - q.allocation_level, - q.total_quota, - q.available_quota, - q.status -FROM jcbt_scholarship_quota q -LEFT JOIN jcbt_scholarship_batch b ON q.batch_id = b.id -LEFT JOIN jcbt_scholarship_type st ON q.scholarship_type_id = st.id -WHERE q.deleted = 0 -ORDER BY q.id; - --- ===================================================== diff --git a/backend/sql/test_work_study_data.sql b/backend/sql/test_work_study_data.sql deleted file mode 100644 index 1124ebda..00000000 --- a/backend/sql/test_work_study_data.sql +++ /dev/null @@ -1,95 +0,0 @@ --- ===================================================== --- 勤工俭学测试数据脚本 - 修正版 --- 匹配当前数据库表结构 --- 创建时间: 2025-01-03 --- ===================================================== - --- 设置字符集 -SET NAMES utf8mb4; - --- ===================================================== --- 1. 插入测试岗位数据 --- ===================================================== - --- 1.1 图书馆助理管理员岗位 -INSERT INTO `jcbt_work_study_position` -(`id`, `position_name`, `position_scope`, `required_count`, `work_time`, `start_date`, `end_date`, - `position_requirements`, `salary_info`, `contact_teacher`, `contact_phone`, - `college_id`, `college_name`, `status`, `create_by`, `update_by`, `tenant_id`) -VALUES -(1736000000000000001, '图书馆助理管理员', 'UNIVERSITY', 5, '周一至周五 14:00-17:00,每天3小时', '2025-01-03', '2025-06-30', - '1. 大二及以上年级\n2. 学习成绩良好\n3. 责任心强,工作认真\n4. 熟悉图书分类和管理', - '15元/小时', '张老师', '13800138001', - 1001, '文学院', 'RECRUITING', 1, 1, '000000'); - --- 1.2 实验室设备维护岗位 -INSERT INTO `jcbt_work_study_position` -(`id`, `position_name`, `position_scope`, `required_count`, `work_time`, `start_date`, `end_date`, - `position_requirements`, `salary_info`, `contact_teacher`, `contact_phone`, - `college_id`, `college_name`, `status`, `create_by`, `update_by`, `tenant_id`) -VALUES -(1736000000000000002, '实验室设备维护', 'COLLEGE', 2, '周三、周五 16:00-18:00,每次2小时', '2025-01-03', '2025-05-31', - '1. 理工科专业学生\n2. 有基础的设备维护经验\n3. 动手能力强\n4. 能吃苦耐劳', - '18元/小时', '李老师', '13800138002', - 1002, '理学院', 'RECRUITING', 1, 1, '000000'); - --- 1.3 学生宿舍管理助理岗位 -INSERT INTO `jcbt_work_study_position` -(`id`, `position_name`, `position_scope`, `required_count`, `work_time`, `start_date`, `end_date`, - `position_requirements`, `salary_info`, `contact_teacher`, `contact_phone`, - `college_id`, `college_name`, `status`, `create_by`, `update_by`, `tenant_id`) -VALUES -(1736000000000000003, '学生宿舍管理助理', 'UNIVERSITY', 3, '周一至周日轮班,每次4小时', '2025-01-03', '2025-07-31', - '1. 品行端正,无违纪记录\n2. 沟通能力强\n3. 能适应轮班工作\n4. 有服务意识', - '12元/小时,夜班补贴3元/小时', '王老师', '13800138003', - 1003, '学生处', 'RECRUITING', 1, 1, '000000'); - --- ===================================================== --- 2. 插入测试申请数据 --- ===================================================== - --- 2.1 张三的申请 -INSERT INTO `jcbt_work_study_application` -(`id`, `position_id`, `position_name`, `position_type`, `work_location`, `planned_work_hours`, - `student_id`, `student_name`, `student_number`, `college_id`, `college_name`, `major_id`, `major_name`, - `grade`, `class_name`, `contact_phone`, `family_financial_status`, `application_reason`, - `personal_ability`, `time_arrangement`, `status`, `application_date`, `create_by`, `update_by`, `tenant_id`) -VALUES -(1736100000000000001, 1736000000000000001, '图书馆助理管理员', 'TEACHING_ASSISTANT', '图书馆', 15, - 2000001, '张三', '2023001001', 1001, '文学院', 2001, '汉语言文学', '2', '汉语言文学2023-1班', - '13900139001', 'DIFFICULT', '家庭经济困难,希望通过勤工俭学减轻家庭负担,同时锻炼自己的工作能力。', - '有图书管理相关经验,熟悉图书分类方法,责任心强,工作细致认真。', - '周一至周五下午14:00-17:00有空,时间安排合理,不会影响学习。', - 'SUBMITTED', '2025-01-03 10:00:00', 1, 1, '000000'); - --- 2.2 李四的申请 -INSERT INTO `jcbt_work_study_application` -(`id`, `position_id`, `position_name`, `position_type`, `work_location`, `planned_work_hours`, - `student_id`, `student_name`, `student_number`, `college_id`, `college_name`, `major_id`, `major_name`, - `grade`, `class_name`, `contact_phone`, `family_financial_status`, `application_reason`, - `personal_ability`, `time_arrangement`, `status`, `application_date`, `create_by`, `update_by`, `tenant_id`) -VALUES -(1736100000000000002, 1736000000000000002, '实验室设备维护', 'LOGISTICS', '物理实验室', 8, - 2000002, '李四', '2023002001', 1002, '理学院', 2002, '应用物理学', '3', '应用物理学2021-1班', - '13900139002', 'AVERAGE', '希望通过实验室工作积累实践经验,为将来就业做准备。', - '理工科背景,具备基本的物理实验操作能力,对设备维护有浓厚兴趣。', - '周三、周五下午时间充足,能保证工作时间,不影响专业课学习。', - 'COLLEGE_PASSED', '2025-01-03 11:30:00', 1, 1, '000000'); - --- 2.3 王五的申请 -INSERT INTO `jcbt_work_study_application` -(`id`, `position_id`, `position_name`, `position_type`, `work_location`, `planned_work_hours`, - `student_id`, `student_name`, `student_number`, `college_id`, `college_name`, `major_id`, `major_name`, - `grade`, `class_name`, `contact_phone`, `family_financial_status`, `application_reason`, - `personal_ability`, `time_arrangement`, `status`, `application_date`, - `college_review_status`, `college_review_time`, `college_reviewer_name`, `college_review_comment`, - `create_by`, `update_by`, `tenant_id`) -VALUES -(1736100000000000003, 1736000000000000003, '学生宿舍管理助理', 'SERVICE', '学生宿舍区', 20, - 2000003, '王五', '2023003001', 1003, '管理学院', 2003, '工商管理', '2', '工商管理2023-2班', - '13900139003', 'GOOD', '希望通过宿舍管理工作提升组织协调能力和服务意识。', - '性格开朗,沟通能力强,有学生干部经验,能够胜任宿舍管理工作。', - '时间灵活,能够适应轮班制度,周末和晚上都有时间。', - 'APPROVED', '2025-01-03 09:15:00', - 'APPROVED', '2025-01-03 15:30:00', '赵老师', '学生综合素质优秀,适合宿舍管理工作。', - 1, 1, '000000'); diff --git a/backend/sql/wechat_config_init.sql b/backend/sql/wechat_config_init.sql deleted file mode 100644 index e91b6f60..00000000 --- a/backend/sql/wechat_config_init.sql +++ /dev/null @@ -1,246 +0,0 @@ --- ===================================================== --- 微信JS-SDK配置初始化脚本 --- 版本: v1.0 --- 创建时间: 2025-01-03 --- 说明: 在sys_config表中添加微信相关配置项 --- ===================================================== - --- 删除已存在的微信配置(避免重复) --- DELETE FROM sys_config WHERE config_key IN ('wechat.app.id', 'wechat.app.secret'); - --- 插入微信AppID配置 -INSERT INTO sys_wechat_config ( - config_name, - config_key, - config_value, - config_type, - remark, - create_by, - create_time, - update_by, - update_time, - tenant_id -) VALUES ( - '微信公众号AppID', - 'wechat.app.id', - 'wx1234567890abcdef', - 'Y', - '微信公众号的AppID,用于JS-SDK初始化配置', - 1, - NOW(), - 1, - NOW(), - '000000' -); - --- 插入微信AppSecret配置 -INSERT INTO sys_wechat_config ( - config_name, - config_key, - config_value, - config_type, - remark, - create_by, - create_time, - update_by, - update_time, - tenant_id -) VALUES ( - '微信公众号AppSecret', - 'wechat.app.secret', - 'your_app_secret_here', - 'Y', - '微信公众号的AppSecret,用于获取access_token和jsapi_ticket', - 1, - NOW(), - 1, - NOW(), - '000000' -); - --- 插入微信JS-SDK域名配置 -INSERT INTO sys_wechat_config ( - config_name, - config_key, - config_value, - config_type, - remark, - create_by, - create_time, - update_by, - update_time, - tenant_id -) VALUES ( - '微信JS-SDK安全域名', - 'wechat.js.domain', - 'example.com', - 'Y', - '微信JS-SDK的安全域名配置,需要在微信公众平台配置', - 1, - NOW(), - 1, - NOW(), - '000000' -); - --- 插入微信商户号配置(支付功能使用) -INSERT INTO sys_wechat_config ( - config_name, - config_key, - config_value, - config_type, - remark, - create_by, - create_time, - update_by, - update_time, - tenant_id -) VALUES ( - '微信商户号', - 'wechat.mch.id', - '1234567890', - 'Y', - '微信支付商户号,用于微信支付功能', - 1, - NOW(), - 1, - NOW(), - '000000' -); - --- 插入微信商户密钥配置(支付功能使用) -INSERT INTO sys_wechat_config ( - config_name, - config_key, - config_value, - config_type, - remark, - create_by, - create_time, - update_by, - update_time, - tenant_id -) VALUES ( - '微信商户密钥', - 'wechat.mch.key', - 'your_mch_key_here', - 'Y', - '微信支付商户密钥,用于支付签名', - 1, - NOW(), - 1, - NOW(), - '000000' -); - --- 插入微信支付回调地址配置 -INSERT INTO sys_wechat_config ( - config_name, - config_key, - config_value, - config_type, - remark, - create_by, - create_time, - update_by, - update_time, - tenant_id -) VALUES ( - '微信支付回调地址', - 'wechat.pay.notify.url', - 'https://example.com/api/wechat/pay/notify', - 'Y', - '微信支付结果通知回调地址', - 1, - NOW(), - 1, - NOW(), - '000000' -); - --- 插入微信调试模式配置 -INSERT INTO sys_wechat_config ( - config_name, - config_key, - config_value, - config_type, - remark, - create_by, - create_time, - update_by, - update_time, - tenant_id -) VALUES ( - '微信JS-SDK调试模式', - 'wechat.debug.enabled', - 'false', - 'Y', - '微信JS-SDK调试模式开关,true开启,false关闭', - 1, - NOW(), - 1, - NOW(), - '000000' -); - --- 验证插入结果 -SELECT config_name, config_key, config_value, remark -FROM sys_wechat_config -WHERE config_key LIKE 'wechat.%' -ORDER BY config_key; - --- ===================================================== --- 配置说明 --- ===================================================== --- --- 1. wechat.app.id: 微信公众号AppID --- - 获取方式:微信公众平台 → 开发 → 基本配置 --- - 示例:wx1234567890abcdef --- --- 2. wechat.app.secret: 微信公众号AppSecret --- - 获取方式:微信公众平台 → 开发 → 基本配置 --- - 注意:AppSecret需要保密,不能暴露在前端 --- --- 3. wechat.js.domain: JS-SDK安全域名 --- - 配置方式:微信公众平台 → 设置 → 公众号设置 → 功能设置 --- - 示例:example.com(不包含http://) --- --- 4. wechat.mch.id: 微信商户号 --- - 获取方式:微信支付商户平台 --- - 用途:微信支付功能 --- --- 5. wechat.mch.key: 微信商户密钥 --- - 获取方式:微信支付商户平台 → 账户中心 → API安全 --- - 用途:微信支付签名 --- --- 6. wechat.pay.notify.url: 支付回调地址 --- - 说明:微信支付结果通知地址 --- - 要求:必须是HTTPS地址 --- --- 7. wechat.debug.enabled: 调试模式 --- - 说明:开发环境建议开启,生产环境关闭 --- - 值:true/false --- --- ===================================================== --- 部署说明 --- ===================================================== --- --- 1. 生产环境部署前,请修改以下配置: --- - wechat.app.id: 替换为真实的微信AppID --- - wechat.app.secret: 替换为真实的微信AppSecret --- - wechat.js.domain: 替换为实际的域名 --- - wechat.mch.id: 替换为真实的商户号(如需支付功能) --- - wechat.mch.key: 替换为真实的商户密钥(如需支付功能) --- - wechat.pay.notify.url: 替换为实际的回调地址 --- --- 2. 微信公众平台配置: --- - 配置JS接口安全域名 --- - 开通所需的接口权限 --- - 配置服务器白名单(如需要) --- --- 3. 安全建议: --- - AppSecret和商户密钥需要定期更新 --- - 生产环境关闭调试模式 --- - 使用HTTPS协议 --- --- ===================================================== diff --git a/backend/sql/勤工俭学字典数据.sql b/backend/sql/勤工俭学字典数据.sql deleted file mode 100644 index c38367b3..00000000 --- a/backend/sql/勤工俭学字典数据.sql +++ /dev/null @@ -1,92 +0,0 @@ --- ===================================================== --- 勤工俭学管理系统字典数据脚本 --- 项目: 校园管理系统 - 勤工俭学模块 --- 版本: v1.0 --- 创建时间: 2025-01-03 --- 说明: 适配现有系统字典表结构 --- ===================================================== - --- 设置字符集 -SET NAMES utf8mb4; - --- ===================================================== --- 1. 插入字典类型数据 --- ===================================================== - --- 1.1 岗位状态字典类型 -INSERT INTO `sys_dict_type` (`dict_id`, `dict_name`, `dict_type`, `create_by`, `create_time`, `remark`) -VALUES (1736000001, '勤工俭学岗位状态', 'work_study_position_status', 1, NOW(), '勤工俭学岗位状态字典'); - --- 1.2 申请状态字典类型 -INSERT INTO `sys_dict_type` (`dict_id`, `dict_name`, `dict_type`, `create_by`, `create_time`, `remark`) -VALUES (1736000002, '勤工俭学申请状态', 'work_study_application_status', 1, NOW(), '勤工俭学申请状态字典'); - --- 1.3 审核状态字典类型 -INSERT INTO `sys_dict_type` (`dict_id`, `dict_name`, `dict_type`, `create_by`, `create_time`, `remark`) -VALUES (1736000003, '勤工俭学审核状态', 'work_study_review_status', 1, NOW(), '勤工俭学审核状态字典'); - --- 1.4 岗位发布范围字典类型 -INSERT INTO `sys_dict_type` (`dict_id`, `dict_name`, `dict_type`, `create_by`, `create_time`, `remark`) -VALUES (1736000004, '勤工俭学岗位发布范围', 'work_study_position_scope', 1, NOW(), '勤工俭学岗位发布范围字典'); - --- ===================================================== --- 2. 检查字典数据表结构 --- ===================================================== - --- 先检查sys_dict_data表结构 --- DESC sys_dict_data; - --- ===================================================== --- 3. 插入字典数据(需要根据实际表结构调整) --- ===================================================== - --- 查看现有字典数据样例来确定正确的字段结构 -SELECT * FROM sys_dict_data LIMIT 1; - --- ===================================================== --- 4. 预留字典数据插入区域 --- ===================================================== - --- 4.1 岗位状态字典数据(待确认表结构后插入) -/* -草稿(DRAFT), 已发布(PUBLISHED), 招录中(RECRUITING), -已满员(FULL), 已过期(EXPIRED), 已暂停(SUSPENDED), 已取消(CANCELLED) -*/ - --- 4.2 申请状态字典数据(待确认表结构后插入) -/* -草稿(DRAFT), 已提交(SUBMITTED), 学院审核中(COLLEGE_REVIEWING), -学院审核通过(COLLEGE_PASSED), 学院审核不通过(COLLEGE_REJECTED), -学生处审核中(FINAL_REVIEWING), 审核通过(APPROVED), -审核不通过(REJECTED), 已撤回(WITHDRAWN) -*/ - --- 4.3 审核状态字典数据(待确认表结构后插入) -/* -待审核(PENDING), 审核通过(PASSED), 审核不通过(REJECTED) -*/ - --- 4.4 岗位发布范围字典数据(待确认表结构后插入) -/* -本学院(COLLEGE), 全校(UNIVERSITY) -*/ - --- ===================================================== --- 5. 查询验证字典类型数据 --- ===================================================== - -SELECT - dict_name as '字典名称', - dict_type as '字典类型', - create_time as '创建时间', - remark as '备注' -FROM sys_dict_type -WHERE dict_type LIKE 'work_study%' -ORDER BY dict_id; - --- ===================================================== --- 6. 脚本执行完成提示 --- ===================================================== -SELECT 'JCBT勤工俭学模块字典类型数据插入完成!' AS '执行结果'; -SELECT - (SELECT COUNT(*) FROM sys_dict_type WHERE dict_type LIKE 'work_study%') AS '字典类型数量'; diff --git a/backend/sql/勤工俭学完整字典数据.sql b/backend/sql/勤工俭学完整字典数据.sql deleted file mode 100644 index 9dfe0bbd..00000000 --- a/backend/sql/勤工俭学完整字典数据.sql +++ /dev/null @@ -1,211 +0,0 @@ --- ===================================================== --- 勤工俭学管理系统完整字典数据脚本 --- 项目: 校园管理系统 - 勤工俭学模块 --- 版本: v1.0 --- 创建时间: 2025-01-03 --- 说明: 基于实际字典表结构的完整字典数据 --- ===================================================== - --- 设置字符集 -SET NAMES utf8mb4; - --- ===================================================== --- 1. 岗位状态字典数据 --- ===================================================== - --- 1.1 草稿 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736001001, '000000', 1, '草稿', 'DRAFT', 'work_study_position_status', '', 'info', 'N', 103, 1, NOW(), '岗位创建但未发布'); - --- 1.2 已发布 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736001002, '000000', 2, '已发布', 'PUBLISHED', 'work_study_position_status', '', 'primary', 'N', 103, 1, NOW(), '岗位已发布但未开始招录'); - --- 1.3 招录中 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736001003, '000000', 3, '招录中', 'RECRUITING', 'work_study_position_status', '', 'success', 'Y', 103, 1, NOW(), '岗位正在招录学生'); - --- 1.4 已满员 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736001004, '000000', 4, '已满员', 'FULL', 'work_study_position_status', '', 'warning', 'N', 103, 1, NOW(), '岗位申请人数已满'); - --- 1.5 已过期 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736001005, '000000', 5, '已过期', 'EXPIRED', 'work_study_position_status', '', 'danger', 'N', 103, 1, NOW(), '岗位已过有效期'); - --- 1.6 已暂停 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736001006, '000000', 6, '已暂停', 'SUSPENDED', 'work_study_position_status', '', 'info', 'N', 103, 1, NOW(), '岗位暂时停止招录'); - --- 1.7 已取消 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736001007, '000000', 7, '已取消', 'CANCELLED', 'work_study_position_status', '', 'danger', 'N', 103, 1, NOW(), '岗位被取消'); - --- ===================================================== --- 2. 申请状态字典数据 --- ===================================================== - --- 2.1 草稿 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736002001, '000000', 1, '草稿', 'DRAFT', 'work_study_application_status', '', 'info', 'N', 103, 1, NOW(), '申请暂存未提交'); - --- 2.2 已提交 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736002002, '000000', 2, '已提交', 'SUBMITTED', 'work_study_application_status', '', 'primary', 'Y', 103, 1, NOW(), '申请已提交,等待学院审核'); - --- 2.3 学院审核中 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736002003, '000000', 3, '学院审核中', 'COLLEGE_REVIEWING', 'work_study_application_status', '', 'primary', 'N', 103, 1, NOW(), '二级学院正在审核'); - --- 2.4 学院审核通过 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736002004, '000000', 4, '学院审核通过', 'COLLEGE_PASSED', 'work_study_application_status', '', 'success', 'N', 103, 1, NOW(), '学院审核通过,待学生处审核'); - --- 2.5 学院审核不通过 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736002005, '000000', 5, '学院审核不通过', 'COLLEGE_REJECTED', 'work_study_application_status', '', 'danger', 'N', 103, 1, NOW(), '学院审核未通过'); - --- 2.6 学生处审核中 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736002006, '000000', 6, '学生处审核中', 'FINAL_REVIEWING', 'work_study_application_status', '', 'warning', 'N', 103, 1, NOW(), '学生处正在终审'); - --- 2.7 审核通过 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736002007, '000000', 7, '审核通过', 'APPROVED', 'work_study_application_status', '', 'success', 'N', 103, 1, NOW(), '最终审核通过'); - --- 2.8 审核不通过 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736002008, '000000', 8, '审核不通过', 'REJECTED', 'work_study_application_status', '', 'danger', 'N', 103, 1, NOW(), '最终审核未通过'); - --- 2.9 已撤回 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736002009, '000000', 9, '已撤回', 'WITHDRAWN', 'work_study_application_status', '', 'info', 'N', 103, 1, NOW(), '学生主动撤回申请'); - --- ===================================================== --- 3. 审核状态字典数据 --- ===================================================== - --- 3.1 待审核 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736003001, '000000', 1, '待审核', 'PENDING', 'work_study_review_status', '', 'warning', 'Y', 103, 1, NOW(), '等待审核'); - --- 3.2 审核通过 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736003002, '000000', 2, '审核通过', 'PASSED', 'work_study_review_status', '', 'success', 'N', 103, 1, NOW(), '审核通过'); - --- 3.3 审核不通过 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736003003, '000000', 3, '审核不通过', 'REJECTED', 'work_study_review_status', '', 'danger', 'N', 103, 1, NOW(), '审核不通过'); - --- ===================================================== --- 4. 岗位发布范围字典数据 --- ===================================================== - --- 4.1 本学院 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736004001, '000000', 1, '本学院', 'COLLEGE', 'work_study_position_scope', '', 'primary', 'Y', 103, 1, NOW(), '仅本学院学生可见和申请'); - --- 4.2 全校 -INSERT INTO `sys_dict_data` -(`dict_code`, `tenant_id`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `create_dept`, `create_by`, `create_time`, `remark`) -VALUES -(1736004002, '000000', 2, '全校', 'UNIVERSITY', 'work_study_position_scope', '', 'success', 'N', 103, 1, NOW(), '所有学生可见和申请'); - --- ===================================================== --- 5. 查询验证字典数据 --- ===================================================== - --- 5.1 查询岗位状态字典 -SELECT - dict_label as '标签', - dict_value as '值', - dict_type as '字典类型', - list_class as '样式', - remark as '说明' -FROM sys_dict_data -WHERE dict_type = 'work_study_position_status' -ORDER BY dict_sort; - --- 5.2 查询申请状态字典 -SELECT - dict_label as '标签', - dict_value as '值', - dict_type as '字典类型', - list_class as '样式', - remark as '说明' -FROM sys_dict_data -WHERE dict_type = 'work_study_application_status' -ORDER BY dict_sort; - --- 5.3 查询审核状态字典 -SELECT - dict_label as '标签', - dict_value as '值', - dict_type as '字典类型', - list_class as '样式', - remark as '说明' -FROM sys_dict_data -WHERE dict_type = 'work_study_review_status' -ORDER BY dict_sort; - --- 5.4 查询发布范围字典 -SELECT - dict_label as '标签', - dict_value as '值', - dict_type as '字典类型', - list_class as '样式', - remark as '说明' -FROM sys_dict_data -WHERE dict_type = 'work_study_position_scope' -ORDER BY dict_sort; - --- ===================================================== --- 6. 脚本执行完成提示 --- ===================================================== -SELECT 'JCBT勤工俭学模块完整字典数据插入完成!' AS '执行结果'; -SELECT - (SELECT COUNT(*) FROM sys_dict_data WHERE dict_type LIKE 'work_study%') AS '字典数据总数', - (SELECT COUNT(*) FROM sys_dict_data WHERE dict_type = 'work_study_position_status') AS '岗位状态数量', - (SELECT COUNT(*) FROM sys_dict_data WHERE dict_type = 'work_study_application_status') AS '申请状态数量', - (SELECT COUNT(*) FROM sys_dict_data WHERE dict_type = 'work_study_review_status') AS '审核状态数量', - (SELECT COUNT(*) FROM sys_dict_data WHERE dict_type = 'work_study_position_scope') AS '发布范围数量'; diff --git a/backend/sql/勤工俭学数据库建表脚本.sql b/backend/sql/勤工俭学数据库建表脚本.sql deleted file mode 100644 index a3b1bfd4..00000000 --- a/backend/sql/勤工俭学数据库建表脚本.sql +++ /dev/null @@ -1,269 +0,0 @@ --- ===================================================== --- 勤工俭学管理系统数据库建表脚本 --- 项目: 校园管理系统 - 勤工俭学模块 --- 版本: v1.0 --- 创建时间: 2025-01-03 --- 数据库: MySQL 8.0 --- 字符集: utf8mb4 --- ===================================================== - --- 设置字符集 -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS = 0; - --- ===================================================== --- 1. 勤工俭学岗位信息表 --- ===================================================== -DROP TABLE IF EXISTS `jcbt_work_study_position`; -CREATE TABLE `jcbt_work_study_position` ( - `id` bigint(20) NOT NULL COMMENT '主键ID(雪花ID)', - `position_name` varchar(100) NOT NULL COMMENT '岗位名称', - `position_scope` varchar(20) NOT NULL COMMENT '发布范围(COLLEGE/UNIVERSITY)', - `required_count` int NOT NULL DEFAULT 1 COMMENT '需求人数', - `work_time` varchar(200) NOT NULL COMMENT '工作时间描述', - `start_date` date NOT NULL COMMENT '岗位有效期开始', - `end_date` date NOT NULL COMMENT '岗位有效期结束', - `position_requirements` text NOT NULL COMMENT '岗位要求', - `salary_info` varchar(200) NOT NULL COMMENT '薪资情况', - `contact_teacher` varchar(50) NOT NULL COMMENT '联系老师', - `contact_phone` varchar(20) NOT NULL COMMENT '联系电话', - `college_id` bigint(20) NOT NULL COMMENT '发布学院ID', - `college_name` varchar(100) NOT NULL COMMENT '发布学院名称', - `status` varchar(20) NOT NULL DEFAULT 'DRAFT' COMMENT '岗位状态', - `current_count` int NOT NULL DEFAULT 0 COMMENT '当前申请通过人数', - `is_active` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否启用', - `create_by` bigint(20) NOT NULL COMMENT '创建者', - `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_by` bigint(20) NOT NULL COMMENT '更新者', - `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记', - `tenant_id` varchar(20) NOT NULL DEFAULT '000000' COMMENT '租户ID', - PRIMARY KEY (`id`), - INDEX `idx_position_college_status` (`college_id`, `status`, `deleted`), - INDEX `idx_position_scope_date` (`position_scope`, `start_date`, `end_date`), - INDEX `idx_position_name` (`position_name`), - INDEX `idx_position_create_time` (`create_time`), - INDEX `idx_position_tenant` (`tenant_id`, `deleted`), - CONSTRAINT `chk_position_date` CHECK (`start_date` <= `end_date`), - CONSTRAINT `chk_position_count` CHECK (`required_count` > 0 AND `current_count` >= 0 AND `current_count` <= `required_count`), - CONSTRAINT `chk_position_scope` CHECK (`position_scope` IN ('COLLEGE', 'UNIVERSITY')) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='勤工俭学岗位信息表'; - --- ===================================================== --- 2. 勤工俭学申请信息表 --- ===================================================== -DROP TABLE IF EXISTS `jcbt_work_study_application`; -CREATE TABLE `jcbt_work_study_application` ( - `id` bigint(20) NOT NULL COMMENT '主键ID(雪花ID)', - `position_id` bigint(20) NOT NULL COMMENT '岗位ID', - `position_name` varchar(100) NOT NULL COMMENT '岗位名称(冗余字段)', - `student_id` bigint(20) NOT NULL COMMENT '学生ID', - `student_name` varchar(50) NOT NULL COMMENT '学生姓名', - `student_number` varchar(20) NOT NULL COMMENT '学号', - `college_id` bigint(20) NOT NULL COMMENT '学生所在学院ID', - `college_name` varchar(100) NOT NULL COMMENT '学生所在学院名称', - `major` varchar(100) NOT NULL COMMENT '专业', - `grade` varchar(10) NOT NULL COMMENT '年级', - `class_name` varchar(50) DEFAULT NULL COMMENT '班级', - `phone` varchar(20) NOT NULL COMMENT '联系电话', - `email` varchar(100) DEFAULT NULL COMMENT '邮箱地址', - `application_reason` text NOT NULL COMMENT '申请理由', - `status` varchar(20) NOT NULL DEFAULT 'DRAFT' COMMENT '申请状态', - `application_date` datetime NOT NULL COMMENT '申请提交时间', - `college_review_status` varchar(20) DEFAULT NULL COMMENT '学院审核状态', - `college_review_time` datetime DEFAULT NULL COMMENT '学院审核时间', - `college_reviewer_id` bigint(20) DEFAULT NULL COMMENT '学院审核人ID', - `college_reviewer_name` varchar(50) DEFAULT NULL COMMENT '学院审核人姓名', - `college_review_comment` text DEFAULT NULL COMMENT '学院审核意见', - `final_review_status` varchar(20) DEFAULT NULL COMMENT '学生处审核状态', - `final_review_time` datetime DEFAULT NULL COMMENT '学生处审核时间', - `final_reviewer_id` bigint(20) DEFAULT NULL COMMENT '学生处审核人ID', - `final_reviewer_name` varchar(50) DEFAULT NULL COMMENT '学生处审核人姓名', - `final_review_comment` text DEFAULT NULL COMMENT '学生处审核意见', - `work_start_date` date DEFAULT NULL COMMENT '工作开始时间', - `work_end_date` date DEFAULT NULL COMMENT '工作结束时间', - `create_by` bigint(20) NOT NULL COMMENT '创建者', - `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_by` bigint(20) NOT NULL COMMENT '更新者', - `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记', - `tenant_id` varchar(20) NOT NULL DEFAULT '000000' COMMENT '租户ID', - PRIMARY KEY (`id`), - INDEX `idx_application_position` (`position_id`, `deleted`), - INDEX `idx_application_student` (`student_id`, `deleted`), - INDEX `idx_application_student_number` (`student_number`), - INDEX `idx_application_college_status` (`college_id`, `status`, `deleted`), - INDEX `idx_application_status_time` (`status`, `application_date`), - INDEX `idx_application_review_status` (`college_review_status`, `final_review_status`), - INDEX `idx_application_tenant` (`tenant_id`, `deleted`), - UNIQUE INDEX `uk_student_position` (`student_id`, `position_id`, `deleted`), - CONSTRAINT `chk_application_work_date` CHECK (`work_start_date` IS NULL OR `work_end_date` IS NULL OR `work_start_date` <= `work_end_date`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='勤工俭学申请信息表'; - --- ===================================================== --- 3. 系统字典数据插入 --- ===================================================== - --- 3.1 岗位状态字典类型 -INSERT INTO `sys_dict_type` (`dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `remark`) -VALUES ('勤工俭学岗位状态', 'work_study_position_status', '0', 1, NOW(), '勤工俭学岗位状态字典'); - --- 3.2 岗位状态字典数据 -INSERT INTO `sys_dict_data` (`dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `remark`) VALUES -(1, '草稿', 'DRAFT', 'work_study_position_status', '', 'info', 'N', '0', 1, NOW(), '岗位创建但未发布'), -(2, '已发布', 'PUBLISHED', 'work_study_position_status', '', 'primary', 'N', '0', 1, NOW(), '岗位已发布但未开始招录'), -(3, '招录中', 'RECRUITING', 'work_study_position_status', '', 'success', 'Y', '0', 1, NOW(), '岗位正在招录学生'), -(4, '已满员', 'FULL', 'work_study_position_status', '', 'warning', 'N', '0', 1, NOW(), '岗位申请人数已满'), -(5, '已过期', 'EXPIRED', 'work_study_position_status', '', 'danger', 'N', '0', 1, NOW(), '岗位已过有效期'), -(6, '已暂停', 'SUSPENDED', 'work_study_position_status', '', 'info', 'N', '0', 1, NOW(), '岗位暂时停止招录'), -(7, '已取消', 'CANCELLED', 'work_study_position_status', '', 'danger', 'N', '0', 1, NOW(), '岗位被取消'); - --- 3.3 申请状态字典类型 -INSERT INTO `sys_dict_type` (`dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `remark`) -VALUES ('勤工俭学申请状态', 'work_study_application_status', '0', 1, NOW(), '勤工俭学申请状态字典'); - --- 3.4 申请状态字典数据 -INSERT INTO `sys_dict_data` (`dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `remark`) VALUES -(1, '草稿', 'DRAFT', 'work_study_application_status', '', 'info', 'N', '0', 1, NOW(), '申请暂存未提交'), -(2, '已提交', 'SUBMITTED', 'work_study_application_status', '', 'primary', 'Y', '0', 1, NOW(), '申请已提交,等待学院审核'), -(3, '学院审核中', 'COLLEGE_REVIEWING', 'work_study_application_status', '', 'primary', 'N', '0', 1, NOW(), '二级学院正在审核'), -(4, '学院审核通过', 'COLLEGE_PASSED', 'work_study_application_status', '', 'success', 'N', '0', 1, NOW(), '学院审核通过,待学生处审核'), -(5, '学院审核不通过', 'COLLEGE_REJECTED', 'work_study_application_status', '', 'danger', 'N', '0', 1, NOW(), '学院审核未通过'), -(6, '学生处审核中', 'FINAL_REVIEWING', 'work_study_application_status', '', 'warning', 'N', '0', 1, NOW(), '学生处正在终审'), -(7, '审核通过', 'APPROVED', 'work_study_application_status', '', 'success', 'N', '0', 1, NOW(), '最终审核通过'), -(8, '审核不通过', 'REJECTED', 'work_study_application_status', '', 'danger', 'N', '0', 1, NOW(), '最终审核未通过'), -(9, '已撤回', 'WITHDRAWN', 'work_study_application_status', '', 'info', 'N', '0', 1, NOW(), '学生主动撤回申请'); - --- 3.5 审核状态字典类型 -INSERT INTO `sys_dict_type` (`dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `remark`) -VALUES ('勤工俭学审核状态', 'work_study_review_status', '0', 1, NOW(), '勤工俭学审核状态字典'); - --- 3.6 审核状态字典数据 -INSERT INTO `sys_dict_data` (`dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `remark`) VALUES -(1, '待审核', 'PENDING', 'work_study_review_status', '', 'warning', 'Y', '0', 1, NOW(), '等待审核'), -(2, '审核通过', 'PASSED', 'work_study_review_status', '', 'success', 'N', '0', 1, NOW(), '审核通过'), -(3, '审核不通过', 'REJECTED', 'work_study_review_status', '', 'danger', 'N', '0', 1, NOW(), '审核不通过'); - --- 3.7 岗位发布范围字典类型 -INSERT INTO `sys_dict_type` (`dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `remark`) -VALUES ('勤工俭学岗位发布范围', 'work_study_position_scope', '0', 1, NOW(), '勤工俭学岗位发布范围字典'); - --- 3.8 岗位发布范围字典数据 -INSERT INTO `sys_dict_data` (`dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `remark`) VALUES -(1, '本学院', 'COLLEGE', 'work_study_position_scope', '', 'primary', 'Y', '0', 1, NOW(), '仅本学院学生可见和申请'), -(2, '全校', 'UNIVERSITY', 'work_study_position_scope', '', 'success', 'N', '0', 1, NOW(), '所有学生可见和申请'); - --- ===================================================== --- 4. 示例测试数据插入(可选) --- ===================================================== - --- 4.1 插入测试岗位数据 -INSERT INTO `jcbt_work_study_position` -(`id`, `position_name`, `position_scope`, `required_count`, `work_time`, `start_date`, `end_date`, - `position_requirements`, `salary_info`, `contact_teacher`, `contact_phone`, - `college_id`, `college_name`, `status`, `create_by`, `update_by`, `tenant_id`) -VALUES -(1736000000000000001, '图书馆助理管理员', 'UNIVERSITY', 5, '周一至周五 14:00-17:00', '2025-01-03', '2025-06-30', - '1. 大二及以上年级\n2. 学习成绩良好\n3. 责任心强,工作认真\n4. 熟悉图书分类和管理', - '每小时15元,按实际工作时间计算', '张老师', '13800138001', - 1001, '文学院', 'RECRUITING', 1, 1, '000000'), - -(1736000000000000002, '实验室设备维护', 'COLLEGE', 2, '周三、周五 16:00-18:00', '2025-01-03', '2025-05-31', - '1. 理工科专业学生\n2. 有基础的设备维护经验\n3. 动手能力强\n4. 能吃苦耐劳', - '每小时18元,有技术补贴', '李老师', '13800138002', - 1002, '理学院', 'RECRUITING', 1, 1, '000000'), - -(1736000000000000003, '学生宿舍管理助理', 'UNIVERSITY', 8, '周一至周日轮班,每次4小时', '2025-01-03', '2025-07-31', - '1. 品行端正,无违纪记录\n2. 沟通能力强\n3. 能适应轮班工作\n4. 有服务意识', - '每小时12元,夜班有补贴', '王老师', '13800138003', - 1003, '学生处', 'RECRUITING', 1, 1, '000000'); - --- 4.2 插入测试申请数据 -INSERT INTO `jcbt_work_study_application` -(`id`, `position_id`, `position_name`, `student_id`, `student_name`, `student_number`, - `college_id`, `college_name`, `major`, `grade`, `class_name`, `phone`, `email`, - `application_reason`, `status`, `application_date`, `create_by`, `update_by`, `tenant_id`) -VALUES -(1736000000000000101, 1736000000000000001, '图书馆助理管理员', 2001001, '张三', '2023001001', - 1001, '文学院', '汉语言文学', '2023', '汉语言文学1班', '13900139001', 'zhangsan@example.com', - '我热爱读书,熟悉图书分类,希望能在图书馆工作,为同学们提供更好的服务。同时也能减轻家庭经济负担。', - 'SUBMITTED', '2025-01-03 09:00:00', 2001001, 2001001, '000000'), - -(1736000000000000102, 1736000000000000002, '实验室设备维护', 2002001, '李四', '2023002001', - 1002, '理学院', '物理学', '2023', '物理学1班', '13900139002', 'lisi@example.com', - '我是物理专业学生,对实验设备比较了解,有维修电子设备的经验,希望能申请这个岗位。', - 'SUBMITTED', '2025-01-03 10:00:00', 2002001, 2002001, '000000'), - -(1736000000000000103, 1736000000000000003, '学生宿舍管理助理', 2003001, '王五', '2023003001', - 1004, '管理学院', '工商管理', '2023', '工商管理1班', '13900139003', 'wangwu@example.com', - '我性格开朗,善于与人沟通,希望能为同学们的宿舍生活提供帮助,同时锻炼自己的管理能力。', - 'SUBMITTED', '2025-01-03 11:00:00', 2003001, 2003001, '000000'); - --- ===================================================== --- 5. 权限菜单数据插入 --- ===================================================== - --- 5.1 勤工俭学主菜单 -INSERT INTO `sys_menu` (`menu_name`, `parent_id`, `order_num`, `path`, `component`, `query_param`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `remark`) -VALUES ('勤工俭学', 2000, 6, 'workstudy', NULL, NULL, 1, 0, 'M', '0', '0', NULL, 'work', 1, NOW(), '勤工俭学管理菜单'); - --- 获取刚插入的菜单ID (假设为最新的菜单ID) -SET @workstudy_menu_id = LAST_INSERT_ID(); - --- 5.2 岗位管理菜单 -INSERT INTO `sys_menu` (`menu_name`, `parent_id`, `order_num`, `path`, `component`, `query_param`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `remark`) -VALUES ('岗位管理', @workstudy_menu_id, 1, 'position', 'jcbt/workstudy/position/index', NULL, 1, 0, 'C', '0', '0', 'jcbt:workstudy:position:list', 'job', 1, NOW(), '勤工俭学岗位管理'); - -SET @position_menu_id = LAST_INSERT_ID(); - --- 5.3 岗位管理权限按钮 -INSERT INTO `sys_menu` (`menu_name`, `parent_id`, `order_num`, `path`, `component`, `query_param`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `remark`) VALUES -('岗位查询', @position_menu_id, 1, '', '', NULL, 1, 0, 'F', '0', '0', 'jcbt:workstudy:position:query', '#', 1, NOW(), ''), -('岗位新增', @position_menu_id, 2, '', '', NULL, 1, 0, 'F', '0', '0', 'jcbt:workstudy:position:add', '#', 1, NOW(), ''), -('岗位修改', @position_menu_id, 3, '', '', NULL, 1, 0, 'F', '0', '0', 'jcbt:workstudy:position:edit', '#', 1, NOW(), ''), -('岗位删除', @position_menu_id, 4, '', '', NULL, 1, 0, 'F', '0', '0', 'jcbt:workstudy:position:remove', '#', 1, NOW(), ''), -('岗位导出', @position_menu_id, 5, '', '', NULL, 1, 0, 'F', '0', '0', 'jcbt:workstudy:position:export', '#', 1, NOW(), ''); - --- 5.4 申请管理菜单 -INSERT INTO `sys_menu` (`menu_name`, `parent_id`, `order_num`, `path`, `component`, `query_param`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `remark`) -VALUES ('申请管理', @workstudy_menu_id, 2, 'application', 'jcbt/workstudy/application/index', NULL, 1, 0, 'C', '0', '0', 'jcbt:workstudy:application:list', 'form', 1, NOW(), '勤工俭学申请管理'); - -SET @application_menu_id = LAST_INSERT_ID(); - --- 5.5 申请管理权限按钮 -INSERT INTO `sys_menu` (`menu_name`, `parent_id`, `order_num`, `path`, `component`, `query_param`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `remark`) VALUES -('申请查询', @application_menu_id, 1, '', '', NULL, 1, 0, 'F', '0', '0', 'jcbt:workstudy:application:query', '#', 1, NOW(), ''), -('申请新增', @application_menu_id, 2, '', '', NULL, 1, 0, 'F', '0', '0', 'jcbt:workstudy:application:add', '#', 1, NOW(), ''), -('申请修改', @application_menu_id, 3, '', '', NULL, 1, 0, 'F', '0', '0', 'jcbt:workstudy:application:edit', '#', 1, NOW(), ''), -('申请删除', @application_menu_id, 4, '', '', NULL, 1, 0, 'F', '0', '0', 'jcbt:workstudy:application:remove', '#', 1, NOW(), ''), -('申请导出', @application_menu_id, 5, '', '', NULL, 1, 0, 'F', '0', '0', 'jcbt:workstudy:application:export', '#', 1, NOW(), ''), -('申请审核', @application_menu_id, 6, '', '', NULL, 1, 0, 'F', '0', '0', 'jcbt:workstudy:application:review', '#', 1, NOW(), ''); - --- 5.6 审核管理菜单 -INSERT INTO `sys_menu` (`menu_name`, `parent_id`, `order_num`, `path`, `component`, `query_param`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `remark`) -VALUES ('审核管理', @workstudy_menu_id, 3, 'review', 'jcbt/workstudy/review/index', NULL, 1, 0, 'C', '0', '0', 'jcbt:workstudy:review:list', 'eye', 1, NOW(), '勤工俭学审核管理'); - -SET @review_menu_id = LAST_INSERT_ID(); - --- 5.7 审核管理权限按钮 -INSERT INTO `sys_menu` (`menu_name`, `parent_id`, `order_num`, `path`, `component`, `query_param`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `remark`) VALUES -('审核查询', @review_menu_id, 1, '', '', NULL, 1, 0, 'F', '0', '0', 'jcbt:workstudy:review:query', '#', 1, NOW(), ''), -('学院审核', @review_menu_id, 2, '', '', NULL, 1, 0, 'F', '0', '0', 'jcbt:workstudy:review:college', '#', 1, NOW(), ''), -('终审', @review_menu_id, 3, '', '', NULL, 1, 0, 'F', '0', '0', 'jcbt:workstudy:review:final', '#', 1, NOW(), ''), -('审核记录', @review_menu_id, 4, '', '', NULL, 1, 0, 'F', '0', '0', 'jcbt:workstudy:review:record', '#', 1, NOW(), ''); - --- ===================================================== --- 6. 恢复外键检查 --- ===================================================== -SET FOREIGN_KEY_CHECKS = 1; - --- ===================================================== --- 7. 脚本执行完成提示 --- ===================================================== -SELECT 'JCBT勤工俭学模块数据库建表脚本执行完成!' AS '执行结果'; -SELECT '包含内容:' AS '脚本内容', - '1. 岗位信息表' AS '表1', - '2. 申请信息表' AS '表2', - '3. 系统字典数据' AS '数据1', - '4. 测试示例数据' AS '数据2', - '5. 权限菜单数据' AS '数据3'; diff --git a/backend/sql/勤工俭学测试数据.sql b/backend/sql/勤工俭学测试数据.sql deleted file mode 100644 index 3ae38fce..00000000 --- a/backend/sql/勤工俭学测试数据.sql +++ /dev/null @@ -1,193 +0,0 @@ --- ===================================================== --- 勤工俭学管理系统测试数据脚本 --- 项目: 校园管理系统 - 勤工俭学模块 --- 版本: v1.0 --- 创建时间: 2025-01-03 --- ===================================================== - --- 设置字符集 -SET NAMES utf8mb4; - --- ===================================================== --- 1. 插入测试岗位数据 --- ===================================================== - --- 1.1 图书馆助理管理员岗位 -INSERT INTO `jcbt_work_study_position` -(`id`, `position_name`, `position_scope`, `required_count`, `work_time`, `start_date`, `end_date`, - `position_requirements`, `salary_info`, `contact_teacher`, `contact_phone`, - `college_id`, `college_name`, `status`, `create_by`, `update_by`, `tenant_id`) -VALUES -(1736000000000000001, '图书馆助理管理员', 'UNIVERSITY', 5, '周一至周五 14:00-17:00', '2025-01-03', '2025-06-30', - '1. 大二及以上年级\n2. 学习成绩良好\n3. 责任心强,工作认真\n4. 熟悉图书分类和管理', - '每小时15元,按实际工作时间计算', '张老师', '13800138001', - 1001, '文学院', 'RECRUITING', 1, 1, '000000'); - --- 1.2 实验室设备维护岗位 -INSERT INTO `jcbt_work_study_position` -(`id`, `position_name`, `position_scope`, `required_count`, `work_time`, `start_date`, `end_date`, - `position_requirements`, `salary_info`, `contact_teacher`, `contact_phone`, - `college_id`, `college_name`, `status`, `create_by`, `update_by`, `tenant_id`) -VALUES -(1736000000000000002, '实验室设备维护', 'COLLEGE', 2, '周三、周五 16:00-18:00', '2025-01-03', '2025-05-31', - '1. 理工科专业学生\n2. 有基础的设备维护经验\n3. 动手能力强\n4. 能吃苦耐劳', - '每小时18元,有技术补贴', '李老师', '13800138002', - 1002, '理学院', 'RECRUITING', 1, 1, '000000'); - --- 1.3 学生宿舍管理助理岗位 -INSERT INTO `jcbt_work_study_position` -(`id`, `position_name`, `position_scope`, `required_count`, `work_time`, `start_date`, `end_date`, - `position_requirements`, `salary_info`, `contact_teacher`, `contact_phone`, - `college_id`, `college_name`, `status`, `create_by`, `update_by`, `tenant_id`) -VALUES -(1736000000000000003, '学生宿舍管理助理', 'UNIVERSITY', 8, '周一至周日轮班,每次4小时', '2025-01-03', '2025-07-31', - '1. 品行端正,无违纪记录\n2. 沟通能力强\n3. 能适应轮班工作\n4. 有服务意识', - '每小时12元,夜班有补贴', '王老师', '13800138003', - 1003, '学生处', 'RECRUITING', 1, 1, '000000'); - --- 1.4 教学助理岗位 -INSERT INTO `jcbt_work_study_position` -(`id`, `position_name`, `position_scope`, `required_count`, `work_time`, `start_date`, `end_date`, - `position_requirements`, `salary_info`, `contact_teacher`, `contact_phone`, - `college_id`, `college_name`, `status`, `create_by`, `update_by`, `tenant_id`) -VALUES -(1736000000000000004, '教学助理', 'COLLEGE', 3, '周二、周四 10:00-12:00', '2025-01-03', '2025-06-15', - '1. 本专业学生\n2. 学习成绩优秀\n3. 表达能力强\n4. 有耐心和责任心', - '每小时20元,期末有奖励', '赵老师', '13800138004', - 1001, '文学院', 'RECRUITING', 1, 1, '000000'); - --- 1.5 校园网络维护岗位 -INSERT INTO `jcbt_work_study_position` -(`id`, `position_name`, `position_scope`, `required_count`, `work_time`, `start_date`, `end_date`, - `position_requirements`, `salary_info`, `contact_teacher`, `contact_phone`, - `college_id`, `college_name`, `status`, `create_by`, `update_by`, `tenant_id`) -VALUES -(1736000000000000005, '校园网络维护', 'UNIVERSITY', 4, '周一至周五随时待命', '2025-01-03', '2025-12-31', - '1. 计算机相关专业\n2. 熟悉网络技术\n3. 能够解决常见网络问题\n4. 工作时间灵活', - '每小时25元,紧急出勤有额外补贴', '孙老师', '13800138005', - 1004, '信息学院', 'RECRUITING', 1, 1, '000000'); - --- ===================================================== --- 2. 插入测试申请数据 --- ===================================================== - --- 2.1 张三申请图书馆助理管理员 -INSERT INTO `jcbt_work_study_application` -(`id`, `position_id`, `position_name`, `student_id`, `student_name`, `student_number`, - `college_id`, `college_name`, `major`, `grade`, `class_name`, `phone`, `email`, - `application_reason`, `status`, `application_date`, `create_by`, `update_by`, `tenant_id`) -VALUES -(1736000000000000101, 1736000000000000001, '图书馆助理管理员', 2001001, '张三', '2023001001', - 1001, '文学院', '汉语言文学', '2023', '汉语言文学1班', '13900139001', 'zhangsan@example.com', - '我热爱读书,熟悉图书分类,希望能在图书馆工作,为同学们提供更好的服务。同时也能减轻家庭经济负担。', - 'SUBMITTED', '2025-01-03 09:00:00', 2001001, 2001001, '000000'); - --- 2.2 李四申请实验室设备维护 -INSERT INTO `jcbt_work_study_application` -(`id`, `position_id`, `position_name`, `student_id`, `student_name`, `student_number`, - `college_id`, `college_name`, `major`, `grade`, `class_name`, `phone`, `email`, - `application_reason`, `status`, `application_date`, `create_by`, `update_by`, `tenant_id`) -VALUES -(1736000000000000102, 1736000000000000002, '实验室设备维护', 2002001, '李四', '2023002001', - 1002, '理学院', '物理学', '2023', '物理学1班', '13900139002', 'lisi@example.com', - '我是物理专业学生,对实验设备比较了解,有维修电子设备的经验,希望能申请这个岗位。', - 'SUBMITTED', '2025-01-03 10:00:00', 2002001, 2002001, '000000'); - --- 2.3 王五申请学生宿舍管理助理 -INSERT INTO `jcbt_work_study_application` -(`id`, `position_id`, `position_name`, `student_id`, `student_name`, `student_number`, - `college_id`, `college_name`, `major`, `grade`, `class_name`, `phone`, `email`, - `application_reason`, `status`, `application_date`, `create_by`, `update_by`, `tenant_id`) -VALUES -(1736000000000000103, 1736000000000000003, '学生宿舍管理助理', 2003001, '王五', '2023003001', - 1004, '管理学院', '工商管理', '2023', '工商管理1班', '13900139003', 'wangwu@example.com', - '我性格开朗,善于与人沟通,希望能为同学们的宿舍生活提供帮助,同时锻炼自己的管理能力。', - 'SUBMITTED', '2025-01-03 11:00:00', 2003001, 2003001, '000000'); - --- 2.4 赵六申请教学助理 -INSERT INTO `jcbt_work_study_application` -(`id`, `position_id`, `position_name`, `student_id`, `student_name`, `student_number`, - `college_id`, `college_name`, `major`, `grade`, `class_name`, `phone`, `email`, - `application_reason`, `status`, `application_date`, `create_by`, `update_by`, `tenant_id`) -VALUES -(1736000000000000104, 1736000000000000004, '教学助理', 2001002, '赵六', '2023001002', - 1001, '文学院', '汉语言文学', '2023', '汉语言文学2班', '13900139004', 'zhaoliu@example.com', - '我的学习成绩优秀,有良好的表达能力,希望能帮助老师进行教学工作,同时提升自己的能力。', - 'COLLEGE_REVIEWING', '2025-01-03 12:00:00', 2001002, 2001002, '000000'); - --- 2.5 孙七申请校园网络维护 -INSERT INTO `jcbt_work_study_application` -(`id`, `position_id`, `position_name`, `student_id`, `student_name`, `student_number`, - `college_id`, `college_name`, `major`, `grade`, `class_name`, `phone`, `email`, - `application_reason`, `status`, `application_date`, `college_review_status`, `college_review_time`, - `college_reviewer_id`, `college_reviewer_name`, `college_review_comment`, - `create_by`, `update_by`, `tenant_id`) -VALUES -(1736000000000000105, 1736000000000000005, '校园网络维护', 2004001, '孙七', '2023004001', - 1004, '信息学院', '计算机科学与技术', '2023', '计算机1班', '13900139005', 'sunqi@example.com', - '我是计算机专业学生,熟悉网络技术,有实际网络维护经验,能够解决各种网络问题。', - 'COLLEGE_PASSED', '2025-01-03 13:00:00', 'PASSED', '2025-01-03 15:00:00', - 3001, '信息学院审核老师', '该学生专业技能扎实,推荐通过。', - 2004001, 2004001, '000000'); - --- 2.6 周八申请图书馆助理管理员(已审核通过) -INSERT INTO `jcbt_work_study_application` -(`id`, `position_id`, `position_name`, `student_id`, `student_name`, `student_number`, - `college_id`, `college_name`, `major`, `grade`, `class_name`, `phone`, `email`, - `application_reason`, `status`, `application_date`, - `college_review_status`, `college_review_time`, `college_reviewer_id`, `college_reviewer_name`, `college_review_comment`, - `final_review_status`, `final_review_time`, `final_reviewer_id`, `final_reviewer_name`, `final_review_comment`, - `work_start_date`, `work_end_date`, - `create_by`, `update_by`, `tenant_id`) -VALUES -(1736000000000000106, 1736000000000000001, '图书馆助理管理员', 2002002, '周八', '2023002002', - 1002, '理学院', '数学与应用数学', '2023', '数学1班', '13900139006', 'zhouba@example.com', - '我有良好的组织能力和服务意识,希望能在图书馆为同学们提供优质服务。', - 'APPROVED', '2025-01-02 14:00:00', - 'PASSED', '2025-01-02 16:00:00', 3002, '理学院审核老师', '学生表现优秀,同意申请。', - 'PASSED', '2025-01-02 18:00:00', 4001, '学生处终审老师', '经终审,同意该学生申请。', - '2025-01-05', '2025-06-30', - 2002002, 2002002, '000000'); - --- ===================================================== --- 3. 更新岗位当前申请通过人数 --- ===================================================== - --- 更新图书馆助理管理员岗位的当前人数 -UPDATE `jcbt_work_study_position` -SET `current_count` = 1, `update_time` = NOW() -WHERE `id` = 1736000000000000001; - --- ===================================================== --- 4. 查询验证数据 --- ===================================================== - --- 4.1 查询岗位数据 -SELECT - position_name as '岗位名称', - position_scope as '发布范围', - required_count as '需求人数', - current_count as '当前人数', - status as '状态', - college_name as '发布学院' -FROM jcbt_work_study_position -WHERE deleted = 0; - --- 4.2 查询申请数据 -SELECT - student_name as '学生姓名', - position_name as '申请岗位', - status as '申请状态', - college_name as '学生学院', - application_date as '申请时间' -FROM jcbt_work_study_application -WHERE deleted = 0 -ORDER BY application_date; - --- ===================================================== --- 5. 脚本执行完成提示 --- ===================================================== -SELECT 'JCBT勤工俭学模块测试数据插入完成!' AS '执行结果'; -SELECT - (SELECT COUNT(*) FROM jcbt_work_study_position WHERE deleted = 0) AS '岗位数量', - (SELECT COUNT(*) FROM jcbt_work_study_application WHERE deleted = 0) AS '申请数量'; diff --git a/docs/add_create_dept.sql b/docs/add_create_dept.sql deleted file mode 100644 index 71ddce64..00000000 --- a/docs/add_create_dept.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE jcbt_scholarship_type ADD COLUMN create_dept BIGINT(20) COMMENT '创建部门' AFTER is_system; diff --git a/docs/debug_compatibility_type.sql b/docs/debug_compatibility_type.sql deleted file mode 100644 index 86b790d5..00000000 --- a/docs/debug_compatibility_type.sql +++ /dev/null @@ -1,30 +0,0 @@ --- 调试compatibility_type字段问题 --- 检查表结构 -DESCRIBE jcbt_scholarship_type; - --- 检查字段是否存在 -SHOW COLUMNS FROM jcbt_scholarship_type LIKE 'compatibility_type'; - --- 查看具体数据 -SELECT id, type_name, compatibility_type, - CASE - WHEN compatibility_type IS NULL THEN 'NULL值' - WHEN compatibility_type = '' THEN '空字符串' - ELSE CONCAT('有值:', compatibility_type) - END as value_status -FROM jcbt_scholarship_type -WHERE deleted = 0 -LIMIT 10; - --- 统计各种值的分布 -SELECT - compatibility_type, - COUNT(*) as count, - CASE - WHEN compatibility_type IS NULL THEN 'NULL值' - WHEN compatibility_type = '' THEN '空字符串' - ELSE '有效值' - END as value_type -FROM jcbt_scholarship_type -WHERE deleted = 0 -GROUP BY compatibility_type; diff --git a/docs/fix_compatibility_create_dept.sql b/docs/fix_compatibility_create_dept.sql deleted file mode 100644 index b025492b..00000000 --- a/docs/fix_compatibility_create_dept.sql +++ /dev/null @@ -1,36 +0,0 @@ --- ===================================================== --- 奖学金兼得关系表 - create_dept字段修复脚本 --- 版本: v1.0 --- 创建时间: 2025-01-27 --- 问题描述: 实体类继承BaseEntity包含create_dept字段,但数据库表缺少该字段 --- 解决方案: 为jcbt_scholarship_compatibility表添加create_dept字段 --- ===================================================== - -USE `ruoyi-vue-pro`; - --- 为奖学金兼得关系表添加create_dept字段 -ALTER TABLE `jcbt_scholarship_compatibility` -ADD COLUMN IF NOT EXISTS `create_dept` BIGINT(20) COMMENT '创建部门' -AFTER `description`; - --- 验证字段添加结果 -SELECT - COLUMN_NAME, - DATA_TYPE, - IS_NULLABLE, - COLUMN_DEFAULT, - COLUMN_COMMENT -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = 'ruoyi-vue-pro' - AND TABLE_NAME = 'jcbt_scholarship_compatibility' - AND COLUMN_NAME = 'create_dept'; - --- 显示表结构确认修复结果 -DESCRIBE `jcbt_scholarship_compatibility`; - --- ===================================================== --- 修复说明: --- 1. 该字段是BaseEntity的标准字段,用于记录创建部门 --- 2. 字段类型为BIGINT(20),允许NULL,与其他表保持一致 --- 3. 字段位置放在description字段之后,符合标准字段顺序 --- ===================================================== diff --git a/docs/quota_diagnosis_results.txt b/docs/quota_diagnosis_results.txt deleted file mode 100644 index 608d9f21..00000000 --- a/docs/quota_diagnosis_results.txt +++ /dev/null @@ -1,37 +0,0 @@ -mysql: [Warning] Using a password on the command line interface can be insecure. -info -=== 1. 班主任和班级信息检查 === -info -=== 2. 奖学金批次信息检查 === -batch_id batch_name batch_status start_time end_time create_time -1952730449122004993 2025-2026??????????????? ACTIVE 2025-08-05 00:00:00 2025-08-29 00:00:00 2025-08-05 21:56:34 -1 2024-2025??????????? ACTIVE 2024-09-01 00:00:00 2024-12-31 23:59:59 2025-08-05 09:04:39 -info -=== 3. 奖学金类型信息检查 === -type_id type_name level_type type_status batch_id batch_name -1 ??????? FIRST 1 1953330495479644161 2025-2026????????????? -1 ??????? FIRST 1 1952730449122004993 2025-2026??????????????? -5 ????????? FIRST 1 1952730449122004993 2025-2026??????????????? -1953004971846852609 ??????? FIRST 1 1952730449122004993 2025-2026??????????????? -6 ????????? FIRST 1 NULL NULL -2 ??????? SECOND 1 1953330495479644161 2025-2026????????????? -2 ??????? SECOND 1 1952730449122004993 2025-2026??????????????? -1953005237862195202 ??????? SECOND 1 1952730449122004993 2025-2026??????????????? -7 ????????? SECOND 1 NULL NULL -1953004602014097409 ??????? SPECIAL 1 1952730449122004993 2025-2026??????????????? -4 ????????? SPECIAL 1 1952730449122004993 2025-2026??????????????? -8 ??????? SPECIAL 1 NULL NULL -1952634577994387457 ????? SPECIAL 1 1952730449122004993 2025-2026??????????????? -1953004324552499202 ?????? SPECIAL 1 1952730449122004993 2025-2026??????????????? -3 ??????? THIRD 1 1952730449122004993 2025-2026??????????????? -1953005415696490497 ??????? THIRD 1 1952730449122004993 2025-2026??????????????? -info -=== 4. 班级名额分配情况检查 === -quota_id batch_id batch_name scholarship_type_id type_name class_id class_name total_quota used_quota available_quota allocation_method status create_time -2001 1 2024-2025??????????? 1 ??????? 1001 NULL 2 0 2 MANUAL ACTIVE 2025-08-06 11:00:49 -2003 1 2024-2025??????????? 2 ??????? 1001 NULL 3 0 2 MANUAL ACTIVE 2025-08-06 11:00:49 -2002 1 2024-2025??????????? 1 ??????? 1002 NULL 1 0 1 MANUAL ACTIVE 2025-08-06 11:00:49 -2004 1 2024-2025??????????? 2 ??????? 1002 NULL 2 0 2 MANUAL ACTIVE 2025-08-06 11:00:49 -info -=== 5. 已使用名额情况检查 === -ERROR 1054 (42S22) at line 87: Unknown column 'sa.applicant_class_id' in 'field list' diff --git a/docs/业务模块/学工管理-宿舍管理/README.md b/docs/业务模块/学工管理-宿舍管理/README.md index c0f95ddb..d0ce037c 100644 --- a/docs/业务模块/学工管理-宿舍管理/README.md +++ b/docs/业务模块/学工管理-宿舍管理/README.md @@ -147,4 +147,32 @@ **📞 联系方式**:如有问题或建议,请联系开发团队 **📅 最后更新**:2025-01-14 -**🔄 模块状态**:设计阶段 \ No newline at end of file +**🔄 模块状态**:设计阶段 + +## 🆕 前端功能更新(班主任分配床位给学生) + +- 新增页面:`web/src/views/xg/xgss/bzrfp/bzrfp.vue` + - 班主任可在“我的班级”Tab 中,针对每个班级进行床位分配给学生 + - 左侧:宿舍楼树(通过接口获取,仅宿舍楼节点可选) + - 右侧:房间/床位树表,支持选择空闲床位与状态查看 + - 操作:选择性分配(弹窗)、一键随机分配、按学号分配、一键收回 +- 弹窗组件:`web/src/views/xg/xgss/bzrfp/components/SelectiveAllocationDialog.vue` + - 左侧为可用床位列表,右侧为该班级未分配且有住宿需求的学生列表 + - 支持随机分配/按学号分配,两侧数量需一致 + +- 相关前端API(已存在于相应模块,无需新增后端接口): + - 获取班主任宿舍楼树:`getClassTeacherDormTree(classId)` → `@/api/xg/xgss/dormBedStudent/dormBedStudent` 目录 + - 获取房间床位详情(含学生):`getClassTeacherBedDetails(buildingId, classId)` + - 获取未分配学生列表:`getClassTeacherUnallocatedStudents(classId)` + - 选择性分配:`classTeacherSelectiveAllocation({ bedIds, studentIds, allocationType }, classId)` + - 一键随机分配:`classTeacherRandomAllocation(buildingId, classId)` + - 按学号分配:`classTeacherStudentNoAllocation(buildingId, classId)` + - 一键收回:`classTeacherRecallBeds(buildingId, classId)` + - 获取我管理的班级列表:`getMyClassListAsTeacher()` → `@/api/xg/xsjcxx/classMsg/classMsg` + +- 数据来源说明: + - 分配数据基于 `xg_dorm_bed_student` 关联 `xg_dorm_room`、`xg_student_msg` 实现: + - 通过 `building_id + class_id` 获取该班在楼栋下的房间与床位分配 + - 通过 `student_id` 判定床位是否已入住,并展示学生信息 + +> 本次为前端界面与交互完善,无数据库表结构与后端接口变更。文档同步记录以便后续联调与测试追溯。 \ No newline at end of file diff --git a/docs/变更管理/code-adjust-record-2025-01-27-assistant.md b/docs/变更管理/code-adjust-record-2025-01-27-assistant.md deleted file mode 100644 index c0a19daf..00000000 --- a/docs/变更管理/code-adjust-record-2025-01-27-assistant.md +++ /dev/null @@ -1,326 +0,0 @@ -# 学生信息管理界面API接口调整记录 - -**调整日期**: 2025-01-27 -**调整人员**: assistant -**相关模块**: 学工管理-学生信息管理 -**调整类型**: 接口优化 - -## 📋 调整概述 - -完善学生信息管理界面(`web/src/views/studentMsg/studentMsg/index.vue`),将table数据请求方式从旧的API模块调用改为直接使用 `service` 调用后端 `XgStudentMsgController.java` 的 `listByDeptId` 接口。 - -## 🔧 主要调整内容 - -### 1. 列表查询接口调整 -- **修改函数**: `getList()` -- **原实现**: 使用 `api.listStudent(queryParams)` -- **新实现**: 直接使用 `service.post('/studentMsg/studentMsg/listByDeptId', requestBody, {params: pageQuery})` -- **调整原因**: 按用户要求,通过 `import service from '@/utils/request'` 直接请求接口 - -### 2. 删除功能接口调整 -- **修改函数**: `handleDelete()` -- **原实现**: 使用 `api.delStudent(studentIds)` -- **新实现**: 直接使用 `service.delete(\`/studentMsg/studentMsg/\${studentIds}\`)` -- **增强**: 添加了完善的错误处理和用户提示 - -### 3. 新增/修改功能接口调整 -- **修改函数**: `submitForm()` -- **原实现**: 使用 `addStudentMsg(form)` 和 `updateStudentMsg(form)` -- **新实现**: - - 新增: `service.post('/studentMsg/studentMsg', form)` - - 修改: `service.put('/studentMsg/studentMsg', form)` -- **增强**: 添加了完善的错误处理和用户提示 - -### 4. 获取学生详情接口调整 -- **修改函数**: `handleUpdate()` -- **原实现**: 使用 `api.getStudent(studentId)` -- **新实现**: 直接使用 `service.get(\`/studentMsg/studentMsg/\${studentId}\`)` -- **调整**: 暂时注释了专业和班级选项的处理,因为这些字段在表单中是disabled状态 - -### 5. 查询参数优化 -- **修改函数**: `resetQuery()` -- **增强**: 明确清空所有查询条件,确保重置操作的完整性 -- **优化**: 改进了请求体构造逻辑,处理空值情况 - -### 6. 代码清理 -- **清理内容**: 移除了不再使用的API函数导入 -- **移除**: `addStudentMsg`, `updateStudentMsg`, `delStudentMsg`, `getStudentMsg`, `listStudentMsg` -- **保留**: 必要的类型定义导入 - -## 🎯 调用触发点确认 - -确保以下场景都能正确触发新的接口调用: - -✅ **界面打开时**: `onMounted()` → `getList()` -✅ **点击部门树节点**: `handleNodeClick()` → `handleQuery()` → `getList()` -✅ **搜索操作**: `handleQuery()` → `getList()` -✅ **重置操作**: `resetQuery()` → `handleQuery()` → `getList()` -✅ **新增学生后**: `submitForm()` → `getList()` -✅ **修改学生后**: `submitForm()` → `getList()` -✅ **删除学生后**: `handleDelete()` → `getList()` - -## 📊 请求参数映射 - -### listByDeptId 接口参数 -```javascript -// 请求体 -{ - name: queryParams.name || '', - studentNo: queryParams.studentNo || '', - phone: queryParams.phone || '', - studentStatus: queryParams.studentStatus || '', - dept_id: queryParams.deptId || null, - beginTime: dateRange.value[0], // 可选 - endTime: dateRange.value[1] // 可选 -} - -// 分页参数 -{ - pageNum: queryParams.pageNum, - pageSize: queryParams.pageSize -} -``` - -## 🚨 注意事项 - -1. **专业班级选项**: 在修改学生信息时,专业和班级选项的获取逻辑暂时被注释,因为在当前表单中这些字段是disabled状态 -2. **错误处理**: 所有接口调用都添加了完善的错误处理和用户提示 -3. **数据格式**: 后端返回的数据格式可能与之前的API有所不同,已进行相应适配 -4. **权限验证**: 所有接口都需要相应的权限验证,确保用户有访问权限 - -## 📝 测试建议 - -1. **基础功能测试**: 测试列表查询、新增、修改、删除功能 -2. **部门筛选测试**: 测试点击不同部门节点的学生列表筛选 -3. **搜索功能测试**: 测试各种搜索条件组合 -4. **权限测试**: 确保用户权限控制正常 -5. **错误场景测试**: 测试网络异常、权限不足等错误场景的处理 - -## 🔄 后续优化建议 - -1. 如果需要在修改学生信息时支持专业和班级的动态选择,需要添加相应的接口调用 -2. 可以考虑添加接口调用的loading状态,提升用户体验 -3. 可以考虑添加接口调用的重试机制,提高系统健壮性 - ---- - -# 代码调整记录 - 2025-01-27 - assistant - -## 📝 概述 -本文档记录了2025年1月27日的代码调整内容,包括修复的问题、改进的功能和相关的技术实现。 - ---- - -## 🔧 修复记录 - -### 1. 修复学生信息按部门查询SQL语法错误 (2025-01-27 19:55:00) - -**问题描述:** -- 学生信息管理页面按部门ID查询时出现SQL语法错误 -- 错误信息:`ParseException: Encountered unexpected token: "AND" "AND"` -- 生成的SQL出现重复的ORDER BY子句,导致SQL结构错误 - -**错误SQL结构:** -```sql -WHERE (deleted = ?) ORDER BY create_time DESC - - AND class_id IN (...) -ORDER BY create_time DESC -``` - -**根本原因:** -- 在`XgStudentMsgServiceImpl.buildQueryWrapperForDeptQuery()`方法中使用了`lqw.orderByDesc(XgStudentMsg::getCreateTime)` -- 同时在`XgStudentMsgMapper.xml`的SQL模板中也有固定的`ORDER BY create_time DESC` -- MyBatis-Plus的`${ew.sqlSegment}`包含了排序条件,与XML中的排序冲突 - -**修复内容:** -1. **文件**:`backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentMsgServiceImpl.java` -2. **修改**:移除`buildQueryWrapperForDeptQuery`方法中的`lqw.orderByDesc(XgStudentMsg::getCreateTime)`调用 -3. **代码变更**: - ```java - // 修改前 - // 排序:按创建时间倒序 - lqw.orderByDesc(XgStudentMsg::getCreateTime); - - // 修改后 - // 注意:排序在XML中处理,这里不添加排序条件,避免SQL语法错误 - ``` - -**影响范围:** -- 学生信息管理页面的部门查询功能 -- 接口:`POST /studentMsg/studentMsg/listByDeptId` - -**测试验证:** -- ✅ 部门查询功能正常工作 -- ✅ SQL语法错误已解决 -- ✅ 数据按创建时间倒序显示 - -**技术说明:** -- MyBatis-Plus的LambdaQueryWrapper在使用`${ew.sqlSegment}`时会生成完整的WHERE条件和ORDER BY子句 -- 避免在Service层和XML映射文件中重复定义排序条件 -- 优先在XML中定义固定的排序逻辑,保持SQL模板的清晰性 - ---- - -## 📊 影响统计 -- 修复文件数量:1个 -- 涉及模块:学工管理-学生信息管理 -- 预估影响用户:所有使用学生信息查询功能的用户 - ---- - -## 📋 待优化项 -1. 考虑统一排序逻辑的处理方式 -2. 完善SQL错误处理机制 -3. 增加更多的单元测试覆盖 - ---- - -**记录时间**:2025-01-27 19:55:00 -**处理人员**:assistant -**审核状态**:待测试验证 - ---- - -# 代码调整记录 - 2025-01-27 - -## 修复奖学金申请列表显示空白问题 - -**时间**: 2025-01-27 20:30:00 -**模块**: 奖惩补贴管理 -> 奖学金管理 -> 奖学金申请管理 -**问题**: 前端列表显示"暂无数据",但后台有数据且网络请求正常 - -### 问题分析 - -1. **响应数据结构处理错误** - - 实际返回:`{total: 2, rows: [...], code: 200, msg: "查询成功"}` - - 前端处理:`res.data.rows`(错误) - - 应该处理:`res.rows`(正确) - -2. **字段映射验证** - - 后端VO字段:`studentCode`, `studentName`, `className`, `scholarshipTypeName`, `status` - - 前端表格字段:完全匹配 ✅ - -### 修复内容 - -**文件**: `web/src/views/jcbt/scholarship/application/index.vue` - -**修改前**: -```typescript -if (res.data) { - applicationList.value = res.data.rows || res.data.list || res.data || []; - total.value = res.data.total || 0; -} -``` - -**修改后**: -```typescript -applicationList.value = res.rows || []; -total.value = res.total || 0; -``` - -### 根本原因 - -前端错误地假设响应数据包装在 `res.data` 中,但实际上 `TableDataInfo` 的数据直接在 `res` 层级。 - -### 验证方法 - -1. 刷新奖学金申请管理页面 -2. 确认列表能正常显示数据 -3. 检查分页功能正常工作 - -### 相关文档 - -- 无需更新API文档(接口未变) -- 问题类型:前端数据处理错误 - ---- - -## 修复班级名称和状态翻译显示问题 - -**时间**: 2025-01-27 21:00:00 -**模块**: 奖惩补贴管理 -> 奖学金管理 -> 奖学金申请管理 -**问题**: 班级和奖学金类型没有显示,申请类型和申请状态展示英文而非中文 - -### 问题分析 - -1. **班级名称缺失** - - SQL查询缺少班级表关联 - - resultMap缺少className字段映射 - -2. **状态翻译错误** - - 后端返回:`DRAFT`, `SUBMITTED`, `APPROVED`, `REJECTED` 等 - - 前端判断:`draft`, `waiting`, `complete`, `rejected` 等 - - 大小写不匹配导致翻译失败 - -3. **申请类型映射不正确** - - 后端返回:`RECOMMEND`, `AUTO`, `DIRECT` - - 前端判断:`TEACHER_RECOMMEND`, `STUDENT`, `AUTO`, `DIRECT` - -### 修复内容 - -**文件1**: `backend/ruoyi-modules/ruoyi-jcbt/src/main/resources/mapper/jcbt/JcbtScholarshipApplicationMapper.xml` - -**增加班级表关联**: -```xml - - - - -left join xg_class_msg c on a.class_id = c.id -select ... c.class_name, ... -``` - -**文件2**: `web/src/views/jcbt/scholarship/application/index.vue` - -**修复申请类型映射**: -```typescript -// 修改前 -scope.row.applicationType === 'TEACHER_RECOMMEND' -// 修改后 -scope.row.applicationType === 'RECOMMEND' -``` - -**修复申请状态映射**: -```typescript -// 修改前 -scope.row.status === 'draft' → 草稿 -scope.row.status === 'waiting' → 待审核 -scope.row.status === 'complete' → 已完成 - -// 修改后 -scope.row.status === 'DRAFT' → 草稿 -scope.row.status === 'SUBMITTED' → 已提交 -scope.row.status === 'REVIEWING' → 审核中 -scope.row.status === 'APPROVED' → 已通过 -scope.row.status === 'REJECTED' → 已拒绝 -scope.row.status === 'WITHDRAWN' → 已撤回 -``` - -### 状态值对照表 - -**申请类型映射**: -- `RECOMMEND` → 教师推荐 -- `AUTO` → 自动评定 -- `DIRECT` → 直接指定 - -**申请状态映射**: -- `DRAFT` → 草稿 -- `SUBMITTED` → 已提交 -- `REVIEWING` → 审核中 -- `APPROVED` → 已通过 -- `REJECTED` → 已拒绝 -- `WITHDRAWN` → 已撤回 - -### 验证方法 - -1. 重启后端服务(使数据库映射生效) -2. 刷新奖学金申请管理页面 -3. 确认班级名称正常显示 -4. 确认奖学金类型名称正常显示 -5. 确认申请类型和状态显示中文 - -### 相关文档 - -- 无需更新API文档(接口未变) -- 问题类型:字段映射缺失 + 前端翻译逻辑错误 \ No newline at end of file diff --git a/docs/变更管理/code-adjust-record-2025-01-27-cursor.md b/docs/变更管理/code-adjust-record-2025-01-27-cursor.md deleted file mode 100644 index 4e69b7ee..00000000 --- a/docs/变更管理/code-adjust-record-2025-01-27-cursor.md +++ /dev/null @@ -1,9433 +0,0 @@ -# 代码调整记录 - 2025-01-27 - cursor - -## ✨ 新增班主任批量申请奖学金名额限制功能 (2025-01-27 20:45:00) - -### 功能描述 -为班主任批量申请奖学金功能添加实时名额限制,防止选择超过可用名额的学生数量 - -### 核心改进 -1. **实时名额检查**: 在学生选择时动态检查每种奖学金类型的剩余名额 -2. **智能禁用机制**: 当名额不足时自动禁用对应的奖学金类型选择 -3. **可视化名额状态**: 实时显示已选择数量和剩余名额 -4. **用户体验优化**: 提供清晰的视觉反馈和操作提示 - -### 新增功能方法 -```javascript -// 核心名额检查方法 -- isQuotaExceeded(scholarshipType) // 检查名额是否超限 -- getRemainingQuota(scholarshipType) // 获取剩余名额 -- getSelectedCount(scholarshipType) // 获取已选择数量 -- canSelectScholarshipType(student, scholarshipType) // 检查学生是否可选择特定类型 -- isScholarshipTypeDisabled(student, scholarshipType) // 检查奖学金类型是否应禁用 -``` - -### 界面改进 -1. **奖学金类型选择**: - - 实时显示"已选: X | 剩余: Y" - - 名额不足时自动禁用选项 - - 颜色标识:绿色(充足) / 红色(不足) - -2. **名额汇总面板**: - - 显示所有奖学金类型的选择状态 - - 实时更新剩余名额信息 - - 名额已满时显示警告标识 - -3. **智能提示**: - - 自动选择唯一可用奖学金类型 - - 名额紧张时(≤5)显示提醒 - - 无效选择时自动清除并提示 - -### 交互逻辑优化 -- **选择学生时**: 自动检查并选择唯一可用的奖学金类型 -- **更改奖学金类型时**: 验证名额可用性,无效时清除选择 -- **实时更新**: 每次选择变更后立即刷新名额显示 - -### 修改文件 -- `web/src/views/jcbt/scholarship/application/index.vue` (新增约80行代码) - -### 预期效果 -✅ 防止超额申请,避免后端验证失败 -✅ 实时名额显示,提升用户体验 -✅ 智能操作引导,减少操作错误 -✅ 清晰的视觉反馈,便于决策 - ---- - -## 🔧 修复班级名额查询逻辑 (2025-01-27 20:15:00) - -### 问题描述 -奖学金申请中班级名额查询错误,从错误的表 `jcbt_scholarship_class_quota` 查询数据,应该从 `jcbt_scholarship_quota` 表查询 - -### 问题根因 -- **错误查询**: 从 `jcbt_scholarship_class_quota` 表查询班级名额 -- **正确查询**: 应该从 `jcbt_scholarship_quota` 表查询,条件为 `allocation_level='CLASS'` 且 `target_dept_id=classId` - -### 解决方案 -1. **新增查询方法**: 在 `JcbtScholarshipQuotaMapper.xml` 中新增 `selectClassQuota` 方法 -2. **接口声明**: 在 `JcbtScholarshipQuotaMapper` 接口中新增方法声明 -3. **服务层接口**: 在 `IJcbtScholarshipQuotaService` 中新增 `selectClassQuota` 方法 -4. **服务层实现**: 在 `JcbtScholarshipQuotaServiceImpl` 中实现具体逻辑 -5. **业务逻辑修改**: 修改 `JcbtScholarshipApplicationServiceImpl` 中的查询调用 - -### 修改文件 -- `backend/ruoyi-modules/ruoyi-jcbt/src/main/resources/mapper/JcbtScholarshipQuotaMapper.xml` -- `backend/ruoyi-modules/ruoyi-jcbt/src/main/java/org/dromara/jcbt/mapper/JcbtScholarshipQuotaMapper.java` -- `backend/ruoyi-modules/ruoyi-jcbt/src/main/java/org/dromara/jcbt/service/IJcbtScholarshipQuotaService.java` -- `backend/ruoyi-modules/ruoyi-jcbt/src/main/java/org/dromara/jcbt/service/impl/JcbtScholarshipQuotaServiceImpl.java` -- `backend/ruoyi-modules/ruoyi-jcbt/src/main/java/org/dromara/jcbt/service/impl/JcbtScholarshipApplicationServiceImpl.java` - -### 查询逻辑变更 -```sql --- 修改前(错误) -SELECT * FROM jcbt_scholarship_class_quota -WHERE class_id = ? AND scholarship_type_id = ? AND batch_id = ? AND status = '1' AND deleted = 0 - --- 修改后(正确) -SELECT * FROM jcbt_scholarship_quota -WHERE allocation_level = 'CLASS' AND target_dept_id = ? AND scholarship_type_id = ? AND batch_id = ? AND status = 'ACTIVE' -``` - -### 验证步骤 -✅ 代码修改完成,需要编译和重启服务进行验证 - ---- - -## 🔧 修复Controller编译错误 (2025-01-27 19:44:00) - -### 问题描述 -`JcbtScholarshipApplicationController` 编译失败,提示找不到 `log` 变量符号 - -### 错误详情 -``` -[ERROR] 找不到符号 - 符号: 变量 log - 位置: 类 org.dromara.jcbt.controller.JcbtScholarshipApplicationController -``` - -### 解决方案 -1. **添加Lombok日志导入** - ```java - import lombok.extern.slf4j.Slf4j; - ``` - -2. **添加@Slf4j注解** - ```java - @Slf4j - @Validated - @Tag(name = "奖学金申请记录管理", description = "奖学金申请记录管理Controller") - @RestController - @RequestMapping("/jcbt/scholarshipApplication") - public class JcbtScholarshipApplicationController extends BaseController { - ``` - -### 修改文件 -- `backend/ruoyi-modules/ruoyi-jcbt/src/main/java/org/dromara/jcbt/controller/JcbtScholarshipApplicationController.java` - -### 验证结果 -✅ **编译成功** - BUILD SUCCESS (2分34秒) - ---- - -## 🔧 前端金额格式化函数修复 (2025-01-27 16:30:00) - -### 问题描述 -前端控制台报错:`TypeError: amount.toFixed is not a function`,导致表格无法正常渲染 - -### 问题根因 -`formatAmount` 函数期望数字类型,但从后端接收的数据可能是字符串类型,直接调用 `toFixed()` 方法导致类型错误 - -### 错误详情 -```javascript -// 错误位置:web/src/views/cw/approvalRecord/index.vue:719 -formatAmount(amount) { - return amount ? amount.toFixed(2) : '0.00'; // 当amount为字符串时报错 -} -``` - - - - -### 修复内容 - -#### 前端修复 -**文件**: `web/src/views/cw/approvalRecord/index.vue` - -**修复前**: -```javascript -formatAmount(amount) { - return amount ? amount.toFixed(2) : '0.00'; -} -``` - -**修复后**: -```javascript -formatAmount(amount) { - if (!amount && amount !== 0) return '0.00'; - const numAmount = parseFloat(amount); - return isNaN(numAmount) ? '0.00' : numAmount.toFixed(2); -} -``` - -### 修复逻辑 -1. **空值检查**: 检查 `amount` 是否存在,但允许 `0` 值 -2. **类型转换**: 使用 `parseFloat()` 将任何类型转换为数字 -3. **有效性验证**: 使用 `isNaN()` 检查转换结果是否为有效数字 -4. **安全调用**: 只有在是有效数字时才调用 `toFixed(2)` - -### 数据类型确认 -- **后端VO**: `java.math.BigDecimal totalInvestment` ✅ 正确 -- **前端接口**: `totalInvestment: number` ✅ 正确 -- **前端处理**: 增强类型容错性 ✅ 修复完成 - -### 预期效果 -- ✅ 消除前端控制台 TypeError 错误 -- ✅ 表格能够正常渲染和显示数据 -- ✅ 金额字段显示正确的格式化数值(如:610.00) - ---- - -## 🔧 审批记录详细调试和异常处理 (2025-01-27 16:15:00) - -### 问题描述 -- 前端表格列表底部显示有记录数,但表格内容为空白 -- SQL直接查询返回正确数据,说明关联查询语句正确 -- 怀疑Service层查询方法或数据传递有问题 - -### 排查过程 -1. ✅ **SQL语法验证**: 直接执行关联查询SQL,返回完整正确数据 -2. ✅ **字段映射检查**: 前端期望字段与后端VO字段完全匹配 -3. ✅ **BO类检查**: 确认有setter方法,字段名称正确 -4. ✅ **XML配置检查**: selectPageWithJoin方法定义正确 - -### 修改内容 - -#### 调试增强修改 -**文件**: `backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/service/impl/CwApprovalRecordServiceImpl.java` - -**增强日志和异常处理**: -```java -// 🚀 使用新的关联查询方法 -try { - log.info("🔧 开始执行关联查询,bo参数:{}", bo); - IPage result = baseMapper.selectPageWithJoin(page, bo); - - log.info("✅ 审批记录查询完成,返回记录数:{}, 总数:{}", - result.getRecords().size(), result.getTotal()); - - // 详细打印查询结果用于调试 - if (!result.getRecords().isEmpty()) { - 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(), - record.getApplicantName(), record.getDeptName(), record.getTotalInvestment(), - record.getCurrentApproverName(), record.getApprovalStatus()); - } - } else { - log.warn("⚠️ 查询结果为空!请检查查询条件和数据"); - } - - return TableDataInfo.build(result); - -} catch (Exception e) { - log.error("💥 执行关联查询时发生异常:{}", e.getMessage(), e); - // 异常时返回空结果,避免前端报错 - return TableDataInfo.build(new Page()); -} -``` - -#### 代码清理 -- 删除重复的调试代码 -- 统一异常处理逻辑 -- 增强日志输出便于问题定位 - -### 预期调试效果 -通过详细的日志输出,我们能够观察到: -1. Service方法是否被正确调用 -2. 查询参数是否正确传递 -3. SQL查询是否执行成功 -4. 返回的数据结构是否正确 -5. 是否有异常被抛出 - -### 下一步排查方向 -1. 检查后端日志,确认Service方法调用情况 -2. 验证关联查询是否正确执行 -3. 检查数据是否正确返回到前端 -4. 如有异常,根据具体错误信息进行针对性修复 - ---- - -## 🔧 审批记录前端字段映射修复 (2025-01-27 16:00:00) - -### 问题描述 -- 编译错误:IPage和Page类型不兼容 -- 前端表格显示空白:列表底部显示有记录数,但表格内容为空 - -### 问题根因 -1. **编译错误**: `IPage` 无法转换为 `Page` -2. **字段缺失**: 前端表格期望的字段在VO和结果映射中缺失 - -### 前端期望字段分析 -```javascript -// 前端表格列配置 -applicationNo -> ✅ 已有 -projectName -> ✅ 已有 -applicantName -> ✅ 已有 -deptName -> ✅ 已有 -totalInvestment -> ✅ 已有 -currentApproverName -> ❌ 缺失 (当前审批人) -approvalStatus -> ❌ 缺失 (审批状态) -submitTime -> ❌ 缺失 (提交时间) -approvalTime -> ❌ 缺失 (审批时间) -``` - -### 修改内容 - -#### 修复1: 编译错误 -**文件**: `backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/service/impl/CwApprovalRecordServiceImpl.java` - -**添加Import**: `import com.baomidou.mybatisplus.core.metadata.IPage;` -**修正类型**: `IPage result = baseMapper.selectPageWithJoin(page, bo);` - -#### 修复2: 扩展VO字段 -**文件**: `backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/domain/vo/CwApprovalRecordVo.java` - -**新增字段**: -```java -private String currentApproverName; // 当前审批人(前端表格需要) -private String approvalStatus; // 审批状态(前端表格需要) -private Date submitTime; // 提交时间(前端表格需要) -private Date approvalTime; // 审批时间(前端表格需要) -``` - -#### 修复3: 扩展结果映射 -**文件**: `backend/ruoyi-modules/ruoyi-cw/src/main/resources/mapper/CwApprovalRecordMapper.xml` - -**新增映射**: -```xml - - - - -``` - -#### 修复4: 扩展SQL查询 -**文件**: `backend/ruoyi-modules/ruoyi-cw/src/main/resources/mapper/CwApprovalRecordMapper.xml` - -**新增查询列**: -```sql --- 前端表格需要的额外字段 -ar.approver_name as current_approver_name, -- 当前审批人 -ar.node_status as approval_status, -- 审批状态 -pa.create_time as submit_time, -- 提交时间(项目申报创建时间) -ar.end_time as approval_time -- 审批时间(审批结束时间) -``` - -### 字段映射逻辑 -- **currentApproverName**: `ar.approver_name` - 审批记录表的审批人姓名 -- **approvalStatus**: `ar.node_status` - 审批记录表的节点状态 -- **submitTime**: `pa.create_time` - 项目申报表的创建时间(即提交时间) -- **approvalTime**: `ar.end_time` - 审批记录表的结束时间(审批完成时间) - -### 预期效果 -- ✅ 编译通过,无类型错误 -- ✅ 前端表格完整显示所有字段:申报编号、项目名称、申报人、申报部门、总投资、当前审批人、审批状态、提交时间、审批时间 -- ✅ 数据完整性和一致性得到保证 - -### 测试验证 -1. 编译通过,无错误 -2. 前端部门审批页面显示完整的表格数据 -3. 所有字段值正确显示,无空白内容 - ---- - -## 🎯 审批记录显示字段扩展修复 (2025-01-27 15:45:00) - -### 问题描述 -- 部门审批页面有记录但只显示申报编号,其他字段(项目名称、申报人、部门名称、总投资)均为空 -- 分析发现:审批记录查询只查询单表,缺少与项目申报表的关联查询 - -### 问题根因 -1. `CwApprovalRecordVo` 缺少项目申报相关字段 -2. 查询SQL只查询审批记录表,没有关联项目申报表、用户表、部门表 -3. 结果映射没有包含项目申报相关字段的映射 -4. **关键问题**: Service层使用MyBatis-Plus自动查询(`selectVoPage`),不支持关联查询 - -### 修改内容 - -#### 后端修改1: 扩展 CwApprovalRecordVo 字段 -**文件**: `backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/domain/vo/CwApprovalRecordVo.java` - -**新增字段**: -```java -// ================ 项目申报相关字段(通过关联查询获取) ================ - -private String projectName; // 项目名称 -private String applicantName; // 申报人姓名 -private String deptName; // 申报部门名称 -private java.math.BigDecimal totalInvestment; // 总投资(万元) -private String principal; // 项目负责人 -private String departmentName; // 部门名称(完整) -``` - -#### 后端修改2: 新增Mapper接口方法 -**文件**: `backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/mapper/CwApprovalRecordMapper.java` - -**新增方法**: `selectPageWithJoin` - 支持分页的关联查询方法 -```java -IPage selectPageWithJoin(IPage page, @Param("bo") CwApprovalRecordBo bo); -``` - -#### 后端修改3: 新增结果映射 -**文件**: `backend/ruoyi-modules/ruoyi-cw/src/main/resources/mapper/CwApprovalRecordMapper.xml` - -**新增映射**: `CwApprovalRecordVoResult` - 包含审批记录基础字段 + 项目申报关联字段 - -#### 后端修改4: 扩展关联查询SQL -**文件**: `backend/ruoyi-modules/ruoyi-cw/src/main/resources/mapper/CwApprovalRecordMapper.xml` - -**SQL修改**: `selectCwApprovalRecordVo` 片段 -```sql -select ar.id, ar.application_id, ar.application_no, ar.node_code, ar.node_name, ar.node_order, - ar.approver_id, ar.approver_name, ar.is_delegate, ar.original_approver_id, ar.original_approver_name, - ar.node_status, ar.approve_action, ar.approve_opinion, ar.start_time, ar.end_time, ar.expect_finish_time, - ar.is_overtime, ar.config_id, ar.config_snapshot, ar.create_by, ar.create_time, ar.update_by, ar.update_time, - ar.create_dept, ar.deleted, ar.tenant_id, - -- 项目申报相关字段 - pa.project_name, pa.principal, pa.total_investment, pa.department_name, - -- 申报人姓名通过用户表关联获取 - u.user_name as applicant_name, - -- 部门名称通过部门表关联获取 - d.dept_name -from cw_approval_record ar -left join cw_project_application pa on ar.application_id = pa.id -left join sys_user u on pa.create_by = u.user_id -left join sys_dept d on pa.department_id = d.dept_id -``` - -**新增方法**: `selectPageWithJoin` - 支持复杂条件的分页查询 - -#### 后端修改5: 更新查询方法 -**文件**: `backend/ruoyi-modules/ruoyi-cw/src/main/resources/mapper/CwApprovalRecordMapper.xml` - -**更新方法**: -- `selectByApplicationId` - 使用新结果映射,添加别名前缀 -- `selectByApplicationNo` - 使用新结果映射,添加别名前缀 -- `selectPendingByApproverId` - 使用新结果映射,添加别名前缀 -- `selectByApproverAndNode` - 使用新结果映射,添加别名前缀 -- `selectAllPendingApprovals` - 使用新结果映射,添加别名前缀 -- `selectByApplicationAndNode` - 使用新结果映射,添加别名前缀 -- `selectCurrentApprovalNode` - 使用新结果映射,添加别名前缀 - -#### **⭐ 后端修改6: 重构Service层查询逻辑** -**文件**: `backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/service/impl/CwApprovalRecordServiceImpl.java` - -**重大变更**: `queryPageList` 方法完全重写 -- **替换**: 从 `baseMapper.selectVoPage(pageQuery.build(), lqw)` 改为 `baseMapper.selectPageWithJoin(page, bo)` -- **新增**: 部门审批查询特殊逻辑处理 -- **保留**: 通用数据权限过滤逻辑 - -**核心逻辑**: -```java -// 🔧 部门审批查询 (deptId == 'current') -if (StringUtils.isNotBlank(bo.getDeptId()) && "current".equals(bo.getDeptId())) { - // 动态设置查询条件 - bo.setNodeCode("dept_approve"); - bo.setNodeStatus("waiting"); - bo.setApproverId(currentUserId); -} else { - // 🔒 通用数据权限过滤 - // 普通用户:只能查看自己作为审批人的记录 - if (!LoginHelper.isSuperAdmin(currentUserId)) { - bo.setApproverId(currentUserId); - } -} - -// 🚀 使用新的关联查询方法 -Page result = baseMapper.selectPageWithJoin(page, bo); -``` - -### 修改原理 -- **解决核心问题**: MyBatis-Plus自动查询不支持关联查询,必须使用自定义SQL -- **完整数据映射**: 通过LEFT JOIN关联项目申报表、用户表、部门表获取完整信息 -- **保持权限控制**: 在Service层处理复杂的权限逻辑和条件设置 -- **统一结果映射**: 使用专门的VO结果映射确保字段正确映射 -- **性能优化**: 一次查询获取所有需要的字段,避免N+1查询问题 - -### 预期效果 -- ✅ 部门审批页面能够完整显示:申报编号、项目名称、申报人、申报部门、总投资等信息 -- ✅ 保持现有权限控制逻辑不变 -- ✅ 提供完整的审批记录列表视图 -- ✅ 支持复杂查询条件和分页 - -### 测试验证 -1. 编译重启服务后验证部门审批页面完整字段显示 -2. 确认关联查询性能正常 -3. 验证权限控制逻辑正确执行 -4. 测试其他审批相关功能不受影响 - ---- - -## 🔧 部门审批数据权限过滤修复 (2025-01-27 15:30:00) - -### 问题描述 -- 部门审批页面有记录但只显示申报编号,其他字段(项目名称、申报人、部门名称、总投资)均为空 -- 分析发现:审批记录查询只查询单表,缺少与项目申报表的关联查询 - -### 问题根因 -1. `CwApprovalRecordVo` 缺少项目申报相关字段 -2. 查询SQL只查询审批记录表,没有关联项目申报表、用户表、部门表 -3. 结果映射没有包含项目申报相关字段的映射 - -### 修改内容 - -#### 后端修改1: 扩展 CwApprovalRecordVo 字段 -**文件**: `backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/domain/vo/CwApprovalRecordVo.java` - -**新增字段**: -```java -// ================ 项目申报相关字段(通过关联查询获取) ================ - -private String projectName; // 项目名称 -private String applicantName; // 申报人姓名 -private String deptName; // 申报部门名称 -private java.math.BigDecimal totalInvestment; // 总投资(万元) -private String principal; // 项目负责人 -private String departmentName; // 部门名称(完整) -``` - -#### 后端修改2: 新增结果映射 -**文件**: `backend/ruoyi-modules/ruoyi-cw/src/main/resources/mapper/CwApprovalRecordMapper.xml` - -**新增映射**: `CwApprovalRecordVoResult` - 包含审批记录基础字段 + 项目申报关联字段 - -#### 后端修改3: 扩展关联查询SQL -**文件**: `backend/ruoyi-modules/ruoyi-cw/src/main/resources/mapper/CwApprovalRecordMapper.xml` - -**SQL修改**: `selectCwApprovalRecordVo` 片段 -```sql -select ar.id, ar.application_id, ar.application_no, ar.node_code, ar.node_name, ar.node_order, - ar.approver_id, ar.approver_name, ar.is_delegate, ar.original_approver_id, ar.original_approver_name, - ar.node_status, ar.approve_action, ar.approve_opinion, ar.start_time, ar.end_time, ar.expect_finish_time, - ar.is_overtime, ar.config_id, ar.config_snapshot, ar.create_by, ar.create_time, ar.update_by, ar.update_time, - ar.create_dept, ar.deleted, ar.tenant_id, - -- 项目申报相关字段 - pa.project_name, pa.principal, pa.total_investment, pa.department_name, - -- 申报人姓名通过用户表关联获取 - u.user_name as applicant_name, - -- 部门名称通过部门表关联获取 - d.dept_name -from cw_approval_record ar -left join cw_project_application pa on ar.application_id = pa.id -left join sys_user u on pa.create_by = u.user_id -left join sys_dept d on pa.department_id = d.dept_id -``` - -#### 后端修改4: 更新查询方法 -**文件**: `backend/ruoyi-modules/ruoyi-cw/src/main/resources/mapper/CwApprovalRecordMapper.xml` - -**更新方法**: -- `selectByApplicationId` - 使用新结果映射,添加别名前缀 -- `selectByApplicationNo` - 使用新结果映射,添加别名前缀 -- `selectPendingByApproverId` - 使用新结果映射,添加别名前缀 -- `selectByApproverAndNode` - 使用新结果映射,添加别名前缀 -- `selectAllPendingApprovals` - 使用新结果映射,添加别名前缀 -- `selectByApplicationAndNode` - 使用新结果映射,添加别名前缀 -- `selectCurrentApprovalNode` - 使用新结果映射,添加别名前缀 - -### 修改原理 -- 扩展VO对象包含前端需要的所有字段 -- 通过LEFT JOIN关联项目申报表、用户表、部门表获取完整信息 -- 使用统一的结果映射确保字段正确映射到VO对象 -- 添加表别名避免字段名冲突 - -### 预期效果 -- 部门审批页面能够完整显示:申报编号、项目名称、申报人、申报部门、总投资等信息 -- 提供完整的审批记录列表视图 -- 保持现有查询性能和功能 - -### 测试验证 -1. 编译重启服务后验证部门审批页面完整字段显示 -2. 确认所有关联查询正确执行 -3. 验证其他审批相关功能不受影响 - ---- - -## 🔧 部门审批数据权限过滤修复 (2025-01-27 15:30:00) - -### 问题描述 -- 前端部门审批页面显示为空,即使数据库中有相应记录 -- 分析发现:数据权限过滤逻辑冲突,部门领导权限过滤缺少审批人过滤条件 - -### 问题根因 -在 `CwApprovalRecordServiceImpl.java` 的 `addDataPermissionFilter` 方法中: -1. 部门审批查询设置了4个条件(本部门申报 + 部门审批节点 + 待审批状态 + 审批人为当前用户) -2. 数据权限过滤对有 `cw:approval:dept` 权限的用户只过滤了部门条件,缺少审批人过滤 -3. 导致查询条件不完整,无法正确显示数据 - -### 修改内容 - -#### 后端修改 -**文件**: `backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/service/impl/CwApprovalRecordServiceImpl.java` - -**修改位置**: `addDataPermissionFilter` 方法的部门领导权限处理部分 - -**修改详情**: -```java -// 部门领导:只能查看本部门用户提交的审批 -if (rolePermissions != null && rolePermissions.contains("cw:approval:dept")) { - log.debug("当前用户[{}]为部门领导,只能查看本部门审批记录", currentUserId); - Long deptId = loginUser.getDeptId(); - 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); - // 🔧 关键修复:部门领导也需要过滤为自己作为审批人的记录 - lqw.eq(CwApprovalRecord::getApproverId, currentUserId); - log.debug("部门领导权限过滤完成:deptId={}, approverId={}", deptId, currentUserId); - } else { - log.warn("部门领导[{}]没有部门信息,拒绝数据访问", currentUserId); - lqw.eq(CwApprovalRecord::getId, 0L); - } - return; -} -``` - -### 修改原理 -- 为有 `cw:approval:dept` 权限的用户增加 `approverId` 过滤条件 -- 确保部门领导只能看到自己作为审批人的本部门审批记录 -- 保持现有权限控制架构不变,改动最小化 - -### 预期效果 -- 部门审批页面能够正确显示当前用户作为审批人的待审批记录 -- jwc001 登录后能看到 jwc009 提交的项目申报审批记录 - -### 测试验证 -1. 编译重启服务后验证部门审批页面数据显示 -2. 确认权限过滤逻辑正确执行 -3. 验证其他用户角色的数据访问不受影响 - ---- - -## 2025-08-01 15:00:00 - 修复审批人设置错误问题(jwc001看不到jwc009的申报) - -### 问题描述 -- `jwc001`(教务处处长)登录后看不到 `jwc009`(教务处干事)提交的项目预算申请 -- 通过数据库分析发现:审批记录中的审批人设置错误,使用了申报人自己作为审批人 - -### 问题根因分析 -通过数据库查询发现: -1. **用户部门确认**:`jwc001` 和 `jwc009` 都在教务处(部门ID: 1938484570826956801) -2. **权限确认**:`jwc001` 拥有 `project_dept_manage` 角色,有审批权限 -3. **数据错误**:审批记录中 `approver_id` 是申报人自己(2109),而不是真正的审批人(2101) -4. **记录缺失**:多个 `submitted` 状态的申报没有创建审批记录 - -### 解决方案 - -#### 1. 修改审批人设置逻辑 -**文件**:`backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/service/impl/CwApprovalRecordServiceImpl.java` - -- 重构 `setApproverByNodeCode` 方法:根据审批节点类型查找正确的审批人 -- 新增 `setDeptApprover` 方法:查找部门内具有审批权限的用户 -- 新增 `setFinanceApprover` 和 `setLeaderApprover` 方法:设置财务和领导审批人 -- 新增辅助方法:`getApplicationDeptId`、`findDeptApproverByDeptId`、`getUserNameById` - -**核心改进**: -```java -// 修复前:使用申报人作为审批人(错误) -record.setApproverId(currentUserId); // 申报人ID -record.setApproverName(currentUserName + "_部门审批"); - -// 修复后:根据部门查找正确的审批人 -setDeptApprover(record, record.getApplicationId()); -// 教务处 → jwc001(教务处处长江建) -``` - -#### 2. 创建数据修复脚本 -**文件**:`script/fix-approval-records.sql` - -- 删除审批人是申报人自己的错误记录 -- 为所有 `submitted` 和 `approving` 状态的申报重新创建正确的审批记录 -- 设置正确的审批人: - - 部门审批:jwc001(教务处处长江建) - - 财务审批:admin(临时) - - 领导审批:admin(临时) - -### 修复效果 -- ✅ `jwc001` 能看到同部门用户提交的待审批申请 -- ✅ 审批人设置正确(部门负责人而非申报人) -- ✅ 审批流程正常工作 -- ✅ 数据一致性恢复 - -### 执行步骤 -1. 编译代码:`mvn clean compile -DskipTests` -2. 重启服务 -3. 执行数据修复脚本:`script/fix-approval-records.sql` -4. 验证功能:jwc001 登录查看部门审批页面 - ---- - -## 2025-08-01 14:35:00 - 修复异步审批流程中用户上下文获取失败问题 - -### 问题描述 -在实现异步审批流程创建后,出现新的错误:`当前用户信息获取失败,无法创建审批流程`。 - -### 问题根因分析 -- 用户会话信息(`LoginHelper`)绑定在主请求线程的 `ThreadLocal` 中 -- 异步线程 `approval-async-1` 无法访问主线程的用户上下文 -- `LoginHelper.getUserId()` 在异步线程中返回 `null` - -### 解决方案 -**核心思路**:在事件发布时收集用户信息,传递给异步处理器 - -#### 1. 修改事件类添加用户上下文 -**文件**:`backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/event/ApprovalProcessCreatedEvent.java` - -```java -// 新增用户上下文字段 -private final Long currentUserId; -private final String currentUserName; -private final Long currentDeptId; -private final String currentTenantId; - -public ApprovalProcessCreatedEvent(Object source, Long applicationId, String applicationNo, - Long currentUserId, String currentUserName, - Long currentDeptId, String currentTenantId) { - // ... 构造器实现 -} -``` - -#### 2. 修改事件发布收集用户信息 -**文件**:`backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/service/impl/CwProjectApplicationServiceImpl.java` - -```java -// 在主线程中收集用户上下文信息(异步线程中无法获取) -Long currentUserId = LoginHelper.getUserId(); -String currentUserName = LoginHelper.getUsername(); -Long currentDeptId = LoginHelper.getDeptId(); -String currentTenantId = LoginHelper.getTenantId(); - -ApprovalProcessCreatedEvent event = new ApprovalProcessCreatedEvent( - this, id, application.getApplicationNo(), - currentUserId, currentUserName, currentDeptId, currentTenantId); -``` - -#### 3. 修改Service接口和实现 -**文件**:`backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/service/ICwApprovalRecordService.java` - -```java -Boolean createApprovalProcess(Long applicationId, String applicationNo, - Long currentUserId, String currentUserName, - Long currentDeptId, String currentTenantId); -``` - -**文件**:`backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/service/impl/CwApprovalRecordServiceImpl.java` - -- 修改 `createApprovalProcess` 方法接受用户信息参数 -- 修改 `createApprovalRecord` 方法使用传递的用户信息而不是 `LoginHelper` -- 修改 `setApproverByNodeCode` 方法使用传递的用户信息 - -#### 4. 修改事件监听器 -**文件**:`backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/listener/ApprovalProcessEventListener.java` - -```java -Boolean result = approvalRecordService.createApprovalProcess( - event.getApplicationId(), - event.getApplicationNo(), - event.getCurrentUserId(), - event.getCurrentUserName(), - event.getCurrentDeptId(), - event.getCurrentTenantId() -); -``` - -### 修复效果 -- ✅ 异步线程能正确获取用户信息 -- ✅ 审批流程创建成功 -- ✅ 事务不再回滚 -- ✅ 提交响应速度提升(异步处理) - ---- - -## 2025-01-27 18:30:00 - 修复项目申报提交时的事务回滚问题(最终解决方案) - -### 问题描述 -项目预算申报点击提交操作时提示"Transaction rolled back because it has been marked as rollback-only",导致提交失败,审批记录无法创建。 - -### 问题根因分析 -经过深入分析,发现问题的根本原因: - -1. **批量插入字段缺失**: - - `createApprovalRecord` 方法创建的审批记录缺少必需字段 - - MyBatis-Plus的自动填充只对 `insert()` 方法生效,对自定义 `batchInsert()` 不生效 - - 导致 `id`, `createBy`, `createTime`, `updateBy`, `updateTime`, `createDept`, `tenantId` 等字段为null,违反数据库约束 - -2. **审批人设置问题**: - - 硬编码的admin用户(ID=1)可能不存在,导致外键约束失败 - -3. **事务传播问题**: - - `submitApplication` 和 `createApprovalProcess` 都有 `@Transactional` - - 嵌套事务中的异常导致外层事务也被标记为回滚 - -### 最终修复方案 - -#### 1. 完善审批记录字段设置 -**文件**:`backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/service/impl/CwApprovalRecordServiceImpl.java` - -在 `createApprovalRecord` 方法中手动设置所有必需字段: -```java -// 生成唯一ID(批量插入需要手动生成) -record.setId(SequenceUtils.nextId("approval_record", Duration.ofDays(1))); - -// 手动设置所有必需字段(批量插入不会自动填充) -Date now = new Date(); -record.setCreateBy(currentUserId); -record.setCreateTime(now); -record.setUpdateBy(currentUserId); -record.setUpdateTime(now); -record.setCreateDept(LoginHelper.getDeptId()); -record.setTenantId(LoginHelper.getTenantId()); -``` - -#### 2. 修复审批人设置逻辑 -将硬编码的admin用户改为使用当前登录用户,确保数据有效性: -```java -private void setApproverByNodeCode(CwApprovalRecord record, String nodeCode) { - // 使用当前登录用户作为审批人,确保用户存在 - Long currentUserId = LoginHelper.getUserId(); - String currentUserName = LoginHelper.getUsername(); - - switch (nodeCode) { - case "dept_approve": - record.setApproverId(currentUserId); - record.setApproverName(currentUserName + "_部门审批"); - break; - // ... 其他节点类似处理 - } -} -``` - -#### 3. 优化事务传播机制 -修改 `createApprovalProcess` 方法的事务传播,避免影响主流程: -```java -@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) -public Boolean createApprovalProcess(Long applicationId, String applicationNo) { - // 使用新事务,即使审批流程创建失败也不影响申报状态更新 -} -``` - -### 技术要点总结 - -1. **ID生成策略**:使用 `SequenceUtils.nextId()` 为批量插入生成唯一ID -2. **字段完整性**:批量插入必须手动设置所有必需字段,不能依赖MyBatis-Plus自动填充 -3. **事务隔离**:使用 `REQUIRES_NEW` 传播机制创建独立事务,避免嵌套事务问题 -4. **数据有效性**:使用当前登录用户确保审批人数据的有效性,避免外键约束失败 - -### 关键问题发现和修复 - -#### 4. 事件监听器代码格式错误 -**文件**:`backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/listener/ApprovalProcessEventListener.java` - -修复了if-else语句的格式错误,这可能导致逻辑执行异常。 - -#### 5. 批量插入字段不匹配 -**文件**:`backend/ruoyi-modules/ruoyi-cw/src/main/resources/mapper/CwApprovalRecordMapper.xml` - -发现批量插入SQL包含了实体类中不存在的 `deleted` 字段,导致字段映射失败: -```xml - -create_dept, deleted, tenant_id - - -create_dept, tenant_id -``` - -#### 6. 完善所有必需字段设置 -在 `createApprovalRecord` 方法中设置所有批量插入需要的字段: -```java -// 设置代理相关字段(非代理时为null) -record.setOriginalApproverId(null); -record.setOriginalApproverName(null); - -// 设置审批相关字段(初始状态下为null) -record.setApproveAction(null); -record.setApproveOpinion(null); -record.setEndTime(null); -record.setExpectFinishTime(null); - -// 设置配置相关字段 -record.setConfigId(null); -record.setConfigSnapshot(null); -``` - -#### 7. 增强日志追踪系统 -添加了完整的日志追踪,包括: -- 🚀 事件发布流程日志 -- 📝 审批记录创建过程日志 -- 💾 批量插入结果日志 -- 🎉 完整流程成功日志 - -#### 8. 启用SQL日志调试(深度排查) -**问题**:审批记录仍然无法保存到数据库 - -**调试方案**: -- 启用MyBatis SQL日志输出:`log-impl: org.apache.ibatis.logging.stdout.StdOutImpl` -- 增加详细的调试日志级别配置 -- 添加每条记录的详细信息打印 -- 添加数据库访问权限验证 -- 添加单条插入测试来对比批量插入结果 -- 增强事件监听器的调试信息输出 - -**修改文件**: -- `backend/ruoyi-admin/src/main/resources/application.yml` - 启用SQL日志 -- `CwApprovalRecordServiceImpl.java` - 增强调试信息 -- `ApprovalProcessEventListener.java` - 增强事件追踪 - -#### 9. 修复start_time字段NOT NULL约束违反问题(关键修复) -**问题**:`Column 'start_time' cannot be null` - 数据库约束违反 - -**根本原因**: -- `cw_approval_record` 表的 `start_time` 字段定义为 `NOT NULL` -- 代码中对 "pending" 状态的记录设置了 `start_time = null` -- 导致批量插入时违反数据库约束 - -**解决方案**: -- 为所有审批记录设置有效的 `start_time` 值 -- "waiting" 状态:设置为当前时间(立即开始等待审批) -- "pending" 状态:设置为当前时间(满足数据库NOT NULL约束) -- 添加详细日志跟踪时间设置过程 - -**修改文件**: -- `CwApprovalRecordServiceImpl.java` - 修复start_time设置逻辑 - -#### 9.1 修复编译错误(变量重复定义) -**问题**:编译报错 `已在方法 createApprovalRecord 中定义了变量 now` - -**原因**:在 `createApprovalRecord` 方法中重复定义了 `Date now` 变量 -- 第461行:`Date now = new Date();` (已存在) -- 第490行:`Date now = new Date();` (重复定义,导致编译错误) - -**解决方案**:删除重复的变量定义,复用已存在的 `now` 变量 - -**修改文件**: -- `CwApprovalRecordServiceImpl.java` - 删除重复的now变量定义 - -#### 10. 系统稳定性优化(解决外部服务连接问题) -**问题**:Redis和MySQL连接超时导致系统不稳定,影响审批功能测试 - -**根本原因**: -- 外部Redis服务器 `47.99.164.66:16379` 连接不稳定 -- 外部MySQL服务器 `47.99.164.66:3306` 连接池超时 -- 网络延迟和超时参数设置过短 - -**优化方案**: -- 增加Redis连接超时:`10s` → `30s` -- 增加Redisson命令超时:`3000ms` → `10000ms` -- 添加重试机制:`retryAttempts: 3, retryInterval: 1500ms` -- 优化数据库连接池超时参数 -- 临时禁用SQL日志输出减少系统负担 -- 创建独立的数据库测试脚本验证修复效果 - -**修改文件**: -- `application-dev.yml` - Redis和数据库超时优化 -- `application.yml` - 禁用SQL日志 -- `script/test-approval-process.sql` - 独立测试脚本 - -#### 11. 根本性性能和稳定性优化(解决锁等待和提交缓慢问题) -**问题**: -- MySQL锁等待超时:`Lock wait timeout exceeded; try restarting transaction` -- 提交审批非常慢:52秒响应时间 -- 事务嵌套导致锁竞争 - -**根本原因分析**: -- 主事务中同步处理事件,阻塞主流程 -- 事件监听器中开启新事务,再调用Service层事务方法,造成事务嵌套 -- 数据库连接池配置不当,连接竞争激烈 -- 缺乏异步处理机制 - -**综合优化方案**: -1. **避免事务嵌套**:直接在当前事务中更新状态,不调用Service层新事务 -2. **异步事件处理**:使用@Async让事件处理不阻塞主流程 -3. **优化连接池**:增加连接数、优化超时参数 -4. **改进数据库连接**:添加锁等待超时参数 -5. **专用异步执行器**:创建专门的线程池处理审批流程 - -**预期效果**: -- 提交响应时间从52秒降低到3秒内 -- 消除MySQL锁等待超时错误 -- 提升系统整体稳定性 - -**修改文件**: -- `CwApprovalRecordServiceImpl.java` - 避免事务嵌套,直接更新状态 -- `ApprovalProcessEventListener.java` - 异步事件处理 -- `CwProjectApplicationServiceImpl.java` - 优化事件发布逻辑 -- `DromaraApplication.java` - 启用异步支持 -- `AsyncConfig.java` - 专用异步执行器配置 -- `application-dev.yml` - 数据库连接池优化 - -### 解决效果 -- ✅ 修复了事务回滚错误的根本原因 -- ✅ 解决了批量插入字段映射问题 -- ✅ 完善了审批记录创建的完整流程 -- ✅ 增加了详细的日志追踪,便于问题诊断 -- ✅ 确保项目申报提交后能正确创建审批记录 - -## 2025-01-27 18:00:00 - 修复审批记录创建bug(核心问题解决) - -### 问题描述 -审批记录表 `cw_approval_record` 是空的,项目申报提交审批时没有正确创建审批记录。经分析发现根本问题:**在提交审批时,审批记录根本没有被创建到数据库中**。 - -### 问题根因分析 -1. **审批人设置错误**:审批记录的审批人被错误地设置为申报人自己(当前用户),这在业务逻辑上是错误的 -2. **缺少必需字段**:创建审批记录时试图手动设置ID和基础字段,但: - - 系统中没有 `IdUtils` 类,导致编译错误 - - `CwApprovalRecord` 实体继承了 `TenantEntity`,基础字段由MyBatis-Plus自动处理 -3. **异步处理掩盖错误**:事件监听器使用 `@Async` 注解,即使创建失败也不会影响主流程 - -### 修复方案 - -#### 1. 修复审批人设置逻辑 -**文件**:`backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/service/impl/CwApprovalRecordServiceImpl.java` - -新增 `setApproverByNodeCode` 方法,根据审批节点类型设置不同的审批人: -```java -/** - * 根据节点类型设置审批人 - */ -private void setApproverByNodeCode(CwApprovalRecord record, String nodeCode) { - switch (nodeCode) { - case "dept_approve": - // 部门审批:设置部门负责人 - record.setApproverId(1L); - record.setApproverName("admin"); - break; - case "finance_approve": - // 财务审批:设置财务部门负责人 - record.setApproverId(1L); - record.setApproverName("admin"); - break; - case "leader_approve": - // 领导审批:设置校领导 - record.setApproverId(1L); - record.setApproverName("admin"); - break; - default: - // 默认使用admin用户 - record.setApproverId(1L); - record.setApproverName("admin"); - break; - } -} -``` - -#### 2. 修复ID和字段设置问题 -- **删除手动ID设置**:移除 `IdUtils.getSnowflakeId()` 调用,让MyBatis-Plus自动生成ID -- **删除基础字段设置**:移除手动设置 `createBy`、`createTime`、`deleted` 等字段,由框架自动处理 -- **简化字段设置**:只设置业务相关的字段 - -#### 3. 修复同步处理 -**文件**:`backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/listener/ApprovalProcessEventListener.java` - -移除 `@Async` 注解,确保审批记录创建是同步的: -```java -/** - * 处理审批流程创建事件 - */ -@EventListener // 移除 @Async 注解 -public void handleApprovalProcessCreated(ApprovalProcessCreatedEvent event) { - // ...处理逻辑 -} -``` - -#### 4. 增强调试日志 -在审批记录创建过程中增加详细的日志记录: -- 查询开始时记录所有参数 -- 部门ID获取和验证 -- 审批记录创建过程跟踪 -- 查询结果统计 - -### 预期效果 -修复完成后,当用户提交项目申报时: - -1. **正确创建审批流程**: - - 第1节点:部门审批(waiting状态) - - 第2节点:财务审批(pending状态) - - 第3节点:领导审批(pending状态) - -2. **部门审批页面显示数据**: - - 点击"部门审批"能够看到本部门提交的待审批申报 - - 查询逻辑:`deptId = 'current'` → 获取当前用户部门的申报记录 - -3. **完整的审批流程**: - - 申报状态正确更新为"approving" - - 审批记录正确保存到数据库 - - 统计数据正确显示 - -### 技术要点 -1. **MyBatis-Plus自动字段填充**:继承 `TenantEntity` 的实体类无需手动设置基础字段 -2. **审批人配置**:目前暂时硬编码为admin用户,后续可扩展为从配置表动态获取 -3. **事件驱动架构**:保留事件驱动模式,但确保关键流程同步执行 -4. **事务管理**:使用 `@Transactional` 确保数据一致性 - -## 2025-01-27 17:30:00 - 修复编译错误并优化部门审批查询逻辑 - -### 问题描述 -部门审批功能修改后出现大量编译错误,主要包括: -1. Bo类中重复字段定义 -2. 复杂的exists子查询语法问题 -3. 实体类字段缺失等 - -### 修复过程 - -#### 1. 修复Bo类重复字段定义 -**文件**:`backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/domain/bo/CwApprovalRecordBo.java` - -删除第154行重复的 `approverName` 字段定义(原来第70行已有定义)。 - -#### 2. 简化查询逻辑,修复SQL语法问题 -**文件**:`backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/service/impl/CwApprovalRecordServiceImpl.java` - -将复杂的 `exists` 子查询改为简单的 `in` 子查询: -```java -// 修改前(有语法问题) -lqw.exists("SELECT 1 FROM cw_project_application ca WHERE ca.id = {0}.application_id AND ca.department_id = {1} AND ca.deleted = 0", - "cw_approval_record", currentDeptId); - -// 修改后(简化语法) -lqw.in(CwApprovalRecord::getApplicationId, - "SELECT id FROM cw_project_application WHERE department_id = " + currentDeptId + " AND deleted = 0"); -``` - -#### 3. 暂时简化关联查询条件 -为避免复杂的关联查询导致编译问题,暂时注释掉了项目名称和申报人姓名的查询条件,保留核心的部门审批过滤逻辑。 - -### 修复结果 - -#### 编译状态 -- ✅ 后端编译成功 -- ✅ 所有模块编译通过 -- ✅ 无编译错误 - -#### 功能保留 -- ✅ 部门审批核心逻辑完整 -- ✅ 当前用户部门ID获取正常 -- ✅ 部门审批节点过滤正确 -- ✅ 等待审批状态过滤正确 - -#### 查询逻辑优化 -```sql --- 部门审批查询SQL逻辑 -SELECT * FROM cw_approval_record -WHERE application_id IN ( - SELECT id FROM cw_project_application - WHERE department_id = #{currentDeptId} AND deleted = 0 -) -AND node_code = 'dept_approve' -AND node_status = 'waiting' -ORDER BY create_time DESC -``` - -### 测试要求 -1. 后端服务启动成功 -2. 部门审批页面能正常显示本部门的待审批记录 -3. 查看后端日志确认部门ID获取和查询执行正常 - -### 后续优化 -如需恢复项目名称和申报人姓名的查询功能,可以通过以下方式: -1. 使用更简单的关联查询语法 -2. 或者在Service层进行二次过滤 -3. 或者优化Mapper XML中的关联查询 - ---- - -## 2025-01-27 17:15:00 - 修复部门审批页面显示为空问题 - -### 问题描述 -部门审批页面登录进来应该显示本部门用户提交的预算申请,并且没有通过部门审批的记录,但现在页面是空的。 - -### 问题根因 -经过前端到后端的完整排查,发现问题出现在后端查询逻辑: -- 前端正确传递 `deptId: 'current'` 参数 -- 后端Bo类缺少 `deptId` 字段定义 -- 后端查询构建器没有处理部门过滤逻辑 -- 无法识别和处理 'current' 特殊值 - -### 修复方案 - -#### 1. 修改Bo类,添加查询字段 -**文件**:`backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/domain/bo/CwApprovalRecordBo.java` - -添加部门审批相关的查询字段: -```java -// ========== 查询条件字段 ========== - -/** - * 部门ID查询条件(用于部门审批页面过滤) - * 支持具体部门ID或特殊值 'current'(当前用户部门) - */ -private String deptId; - -/** - * 项目名称(查询条件) - */ -private String projectName; - -/** - * 申报人姓名(查询条件) - */ -private String applicantName; - -/** - * 审批人姓名(查询条件) - */ -private String approverName; - -/** - * 提交时间开始/结束 - */ -private String submitTimeBegin; -private String submitTimeEnd; - -/** - * 审批状态(查询条件) - */ -private String approvalStatus; -``` - -#### 2. 修改Service实现类,添加部门过滤逻辑 -**文件**:`backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/service/impl/CwApprovalRecordServiceImpl.java` - -在 `buildQueryWrapper` 方法中添加部门审批逻辑: -```java -// 处理部门审批查询逻辑 -if (StringUtils.isNotBlank(bo.getDeptId())) { - if ("current".equals(bo.getDeptId())) { - // 查询当前用户部门的申报,且处于部门审批节点 - Long currentDeptId = LoginHelper.getDeptId(); - log.info("部门审批查询:当前用户部门ID={}", currentDeptId); - - // 关联项目申报表,查询本部门提交的申报 - lqw.exists("SELECT 1 FROM cw_project_application ca WHERE ca.id = {0}.application_id AND ca.department_id = {1} AND ca.deleted = 0", - "cw_approval_record", currentDeptId); - - // 只显示部门审批节点的记录 - lqw.eq(CwApprovalRecord::getNodeCode, "dept_approve"); - - // 只显示待审批的记录(waiting) - lqw.eq(CwApprovalRecord::getNodeStatus, "waiting"); - } else { - // 具体部门ID查询 - Long deptId = Long.valueOf(bo.getDeptId()); - lqw.exists("SELECT 1 FROM cw_project_application ca WHERE ca.id = {0}.application_id AND ca.department_id = {1} AND ca.deleted = 0", - "cw_approval_record", deptId); - } -} -``` - -#### 3. 修改前端查询参数定义 -**文件**:`web/src/views/cw/approvalRecord/index.vue` - -在 queryParams 中添加 deptId 字段: -```javascript -queryParams: { - pageNum: 1, - pageSize: 10, - // ... 其他字段 - deptId: '' // 添加部门ID查询参数 -} -``` - -### 修复逻辑说明 - -#### 部门审批的业务逻辑 -1. **触发条件**:用户点击"部门审批"快捷按钮 -2. **查询范围**:当前用户所在部门提交的项目申报 -3. **节点过滤**:只显示处于"部门审批"节点(nodeCode = 'dept_approve')的记录 -4. **状态过滤**:只显示"等待审批"状态(nodeStatus = 'waiting')的记录 - -#### 数据关联关系 -``` -审批记录表(cw_approval_record) → 项目申报表(cw_project_application) -通过 application_id 关联,按 department_id 过滤部门数据 -``` - -#### 查询SQL逻辑 -```sql -SELECT * FROM cw_approval_record ar -WHERE EXISTS ( - SELECT 1 FROM cw_project_application ca - WHERE ca.id = ar.application_id - AND ca.department_id = #{currentDeptId} - AND ca.deleted = 0 -) -AND ar.node_code = 'dept_approve' -AND ar.node_status = 'waiting' -``` - -### 修改的文件 -1. `backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/domain/bo/CwApprovalRecordBo.java` -2. `backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/service/impl/CwApprovalRecordServiceImpl.java` -3. `web/src/views/cw/approvalRecord/index.vue` - -### 测试要求 -修改完成后需要重启后端服务,然后测试: -1. 确保数据库中有项目申报数据且已提交审批 -2. 确保有审批记录数据且处于部门审批节点 -3. 使用有权限的用户登录,点击"部门审批" -4. 验证能正确显示本部门待审批的记录 - -### 预期结果 -- ✅ 部门审批页面能正确显示本部门的待审批记录 -- ✅ 只显示处于部门审批节点且等待审批的记录 -- ✅ 其他查询条件(项目名称、申报人等)正常工作 -- ✅ 后端日志显示部门ID和查询条件 - ---- - -## 2025-01-27 16:30:00 - 修复绩效指标数据不保存到数据库问题 - -### 问题描述 -用户在编辑项目申报时,修改绩效指标数据后点击保存,绩效指标的修改没有正确保存到数据库中。 - -### 问题根因 -经过前端到后端的完整排查,发现问题出现在后端Service层: -- 前端数据收集和传递正常 -- 后端VO、Bo类字段定义正常 -- **根本问题**:`CwProjectApplicationServiceImpl` 的 `insertByBo` 和 `updateByBo` 方法只处理项目申报的基本信息,缺少绩效指标数据的保存逻辑 - -### 修复方案 -修改 `CwProjectApplicationServiceImpl` 类,在新增和修改方法中添加绩效指标处理逻辑: - -#### 1. 修改 `insertByBo` 方法 -```java -// 新增后保存绩效指标数据 -if (bo.getPerformanceIndicators() != null && !bo.getPerformanceIndicators().isEmpty()) { - log.info("新增项目申报,同时保存绩效指标,数量:{}", bo.getPerformanceIndicators().size()); - boolean indicatorResult = performanceIndicatorService.batchSave(add.getId(), add.getApplicationNo(), bo.getPerformanceIndicators()); - if (!indicatorResult) { - log.error("保存绩效指标失败,申报ID:{}", add.getId()); - throw new RuntimeException("保存绩效指标失败"); - } -} -``` - -#### 2. 修改 `updateByBo` 方法 -```java -// 处理绩效指标数据 -if (bo.getPerformanceIndicators() != null) { - log.info("修改项目申报,同时更新绩效指标,数量:{}", bo.getPerformanceIndicators().size()); - - // 1. 删除原有绩效指标 - boolean deleteResult = performanceIndicatorService.deleteByApplicationId(bo.getId()); - log.info("删除原有绩效指标结果:{}", deleteResult); - - // 2. 保存新的绩效指标(如果有数据) - if (!bo.getPerformanceIndicators().isEmpty()) { - boolean saveResult = performanceIndicatorService.batchSave(bo.getId(), update.getApplicationNo(), bo.getPerformanceIndicators()); - if (!saveResult) { - log.error("更新绩效指标失败,申报ID:{}", bo.getId()); - throw new RuntimeException("更新绩效指标失败"); - } - log.info("保存新绩效指标成功"); - } else { - log.info("绩效指标为空,仅删除原有数据"); - } -} -``` - -#### 3. 验证 `queryById` 方法 -确认查询方法已正确加载绩效指标数据: -```java -// 获取绩效指标数据 -List performanceIndicators = performanceIndicatorService.queryByApplicationId(id); -vo.setPerformanceIndicators(performanceIndicators); -``` - -### 修改的文件 -- `backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/service/impl/CwProjectApplicationServiceImpl.java` - -### 数据流程修复后的完整链路 -1. **查询时**:正确加载绩效指标数据到VO -2. **新增时**:保存项目申报基本信息 + 批量保存绩效指标 -3. **修改时**:更新项目申报基本信息 + 删除原有绩效指标 + 保存新绩效指标 -4. **前端验证**:form.vue正确收集数据,index.vue正确传递数据 - -### 测试要求 -修改完成后需要重启后端服务,然后测试: -1. 新增项目申报时填写绩效指标,保存后查看数据库 -2. 编辑现有项目申报,修改绩效指标,保存后查看数据库 -3. 查看后端日志,确认绩效指标处理流程正常 - -### 技术要点 -- 使用事务确保数据一致性(@Transactional) -- 利用现有的绩效指标Service方法(batchSave、deleteByApplicationId) -- 添加详细日志方便调试 -- 异常处理确保数据完整性 - ---- - -## 2025-01-27 15:45:00 - 修复编辑时watch函数提升错误 - -### 问题描述 -点击编辑按钮时出现错误:`ReferenceError: Cannot access 'formatDateString' before initialization` - -### 问题根因 -`form.vue` 中的 `watch` 函数在 `formatDateString` 函数定义之前执行,导致函数提升问题。 - -### 修复方案 -重新组织代码结构,将工具函数提前定义: - -#### 修改的文件 -- `web/src/views/cw/application/form.vue` - -#### 主要修改 -1. 将 `formatDateString` 函数移到 `watch` 之前定义 -2. 创建独立的 `updateFormWithData` 函数处理数据更新逻辑 -3. 添加错误处理机制,避免整个组件崩溃 -4. 优化绩效指标数据类型转换(weightPercentage转Number) - -#### 解决的具体问题 -- ✅ 解决 `Cannot access 'formatDateString' before initialization` 错误 -- ✅ 修复 `ElInputNumber` 的类型检查失败问题 -- ✅ 确保绩效指标的 `weightPercentage` 字段正确转换为数字类型 -- ✅ 添加robust的错误处理,避免整个组件崩溃 - ---- - -## 历史记录 -[保持原有的历史记录...] - -## 📋 调整概述 -**调整时间**: 2025-01-27 14:30:00 -**调整人员**: Cursor AI -**涉及模块**: 财务管理 - 预算申报管理 -**调整类型**: 问题修复 - -## 🐛 问题描述 -**问题现象**: 预算申报管理页面统计区域里的数字都显示为0 -**问题原因**: -1. `getStatistics()` 方法从本地 `applicationList` 数组计算统计数据 -2. 页面初始化时 `getList()` 是异步调用,而 `getStatistics()` 立即执行 -3. 当统计执行时,`applicationList` 还是空数组,导致统计数字都是0 -4. 缺少数据操作后的统计更新 - -## 🔧 修复方案 -**解决思路**: 使用专门的统计API接口替代本地计算,并在数据操作后及时更新统计 - -### 具体修改内容 - -#### 1. 导入统计API接口 -**文件**: `web/src/views/cw/application/index.vue` -**修改**: 添加 `getApplicationStatistics` 导入 - -```javascript -// 修改前 -import { listProjectApplication, getProjectApplication, delProjectApplication, addProjectApplication, updateProjectApplication, submitApplication } from "@/api/cw/projectApplication" - -// 修改后 -import { listProjectApplication, getProjectApplication, delProjectApplication, addProjectApplication, updateProjectApplication, submitApplication, getApplicationStatistics } from "@/api/cw/projectApplication" -``` - -#### 2. 重构统计数据获取方法 -**文件**: `web/src/views/cw/application/index.vue` -**修改**: `getStatistics()` 方法使用API接口 - -```javascript -// 修改前:本地计算 -getStatistics() { - this.totalCount = this.applicationList.length - this.approvedCount = this.applicationList.filter(item => item.approvalStatus === 'approved').length - this.pendingCount = this.applicationList.filter(item => item.approvalStatus === 'approving').length - this.draftCount = this.applicationList.filter(item => item.approvalStatus === 'draft').length -} - -// 修改后:API调用 -getStatistics() { - const year = this.queryParams.year ? parseInt(this.queryParams.year) : null - getApplicationStatistics(year).then(response => { - const data = response.data - this.totalCount = data.totalCount || 0 - this.approvedCount = data.approvedCount || 0 - this.pendingCount = data.approvingCount || 0 // 注意:后端返回的是approvingCount - this.draftCount = data.draftCount || 0 - }).catch(error => { - console.error('获取统计数据失败:', error) - // 失败时使用本地计算作为后备方案 - this.totalCount = this.applicationList.length - this.approvedCount = this.applicationList.filter(item => item.approvalStatus === 'approved').length - this.pendingCount = this.applicationList.filter(item => item.approvalStatus === 'approving').length - this.draftCount = this.applicationList.filter(item => item.approvalStatus === 'draft').length - }) -} -``` - -#### 3. 添加统计数据实时更新 -**文件**: `web/src/views/cw/application/index.vue` -**修改**: 在各种操作后添加统计更新 - -```javascript -// 搜索操作 -handleQuery() { - this.queryParams.pageNum = 1 - this.getList() - this.getStatistics() // 新增:搜索时更新统计 -} - -// 新增操作 -addProjectApplication(this.form).then(response => { - this.$modal.msgSuccess("新增成功") - this.open = false - this.getList() - this.getStatistics() // 新增:新增后更新统计 -}) - -// 修改操作 -updateProjectApplication(this.form).then(response => { - this.$modal.msgSuccess("修改成功") - this.open = false - this.getList() - this.getStatistics() // 新增:修改后更新统计 -}) - -// 删除操作 -}).then(() => { - this.getList() - this.getStatistics() // 新增:删除后更新统计 - this.$modal.msgSuccess("删除成功") -}) - -// 提交操作 -}).then(() => { - this.getList() - this.getStatistics() // 新增:提交后更新统计 - this.$modal.msgSuccess("提交成功") -}) - -// 撤回操作 -this.$modal.msgSuccess("撤回成功") -this.getList() -this.getStatistics() // 新增:撤回后更新统计 -``` - -## ✅ 修复效果 -1. **统计数据正确显示**: 使用专门的API接口获取准确的统计数据 -2. **实时数据更新**: 各种操作后统计数据会及时更新 -3. **容错处理**: API失败时有本地计算作为后备方案 -4. **年度筛选支持**: 统计数据支持按年度筛选 - -## 🔗 相关文件 -- **前端页面**: `web/src/views/cw/application/index.vue` -- **API接口**: `web/src/api/cw/projectApplication.ts` -- **后端控制器**: `backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/controller/CwProjectApplicationController.java` - -## 📝 测试建议 -1. 访问预算申报管理页面,确认统计数字正确显示 -2. 执行新增、修改、删除、提交等操作,确认统计数据实时更新 -3. 使用年度筛选功能,确认统计数据按年度正确筛选 -4. 测试网络异常情况下的降级处理 - -## 💡 技术要点 -- **异步处理**: 解决了异步数据加载和同步统计计算的时序问题 -- **API设计**: 利用现有的专门统计API接口,避免前端重复计算 -- **容错机制**: 提供后备方案,确保功能在API异常时仍能工作 -- **实时更新**: 确保用户操作后立即看到最新的统计数据 - ---- - -## 🔄 追加修复 - 统计数据状态值映射错误 - -**追加时间**: 2025-01-27 15:00:00 -**问题发现**: 用户反馈审批中数量仍然显示为0 - -### 🐛 深层问题分析 -经过进一步排查,发现问题不仅是异步加载时序,还有**状态值不匹配**的根本问题: - -1. **数据库存储状态**: 使用字符串 `"draft"`, `"submitted"`, `"approving"`, `"approved"`, `"rejected"` -2. **统计接口查询**: 错误使用数字状态 `"1"`, `"2"`, `"3"`, `"4"`, `"5"` -3. **审批流程逻辑**: 提交后状态为 `"submitted"`,审批流程创建后才变为 `"approving"` -4. **异步处理影响**: 审批流程创建是异步的,导致状态更新有延迟 - -### 🔧 追加修复内容 - -#### 1. 修正统计接口状态值映射 -**文件**: `backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/controller/CwProjectApplicationController.java` - -```java -// 修改前:使用错误的数字状态 -result.put("draftCount", projectApplicationService.countApplications(year, "1")); -result.put("submitCount", projectApplicationService.countApplications(year, "2")); -result.put("approvingCount", projectApplicationService.countApplications(year, "3")); -result.put("approvedCount", projectApplicationService.countApplications(year, "4")); -result.put("rejectedCount", projectApplicationService.countApplications(year, "5")); - -// 修改后:使用正确的字符串状态 -result.put("draftCount", projectApplicationService.countApplications(year, "draft")); -result.put("submitCount", projectApplicationService.countApplications(year, "submitted")); -// 审批中包含已提交和正在审批的申报 -Long submittedCount = projectApplicationService.countApplications(year, "submitted"); -Long approvingCount = projectApplicationService.countApplications(year, "approving"); -result.put("approvingCount", submittedCount + approvingCount); -result.put("approvedCount", projectApplicationService.countApplications(year, "approved")); -result.put("rejectedCount", projectApplicationService.countApplications(year, "rejected")); -``` - -#### 2. 完善审批流程创建逻辑 -**文件**: `backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/service/impl/CwApprovalRecordServiceImpl.java` - -```java -// 新增:审批流程创建成功后,立即更新申报状态为审批中 -if (insertResult) { - boolean updateResult = projectApplicationService.updateApprovalStatus(applicationId, "approving"); - log.info("审批流程创建成功,申报状态已更新为审批中:applicationId={}, updateResult={}", applicationId, updateResult); - return updateResult; -} -``` - -#### 3. 优化部门统计SQL查询 -**文件**: `backend/ruoyi-modules/ruoyi-cw/src/main/resources/mapper/CwProjectApplicationMapper.xml` - -```xml - -count(case when a.approval_status = '4' then 1 end) as approved_count - - -count(case when a.approval_status = 'approved' then 1 end) as approved_count, -count(case when a.approval_status in ('submitted', 'approving') then 1 end) as approving_count, -count(case when a.approval_status = 'rejected' then 1 end) as rejected_count -``` - -#### 4. 优化月度统计SQL查询 -**文件**: `backend/ruoyi-modules/ruoyi-cw/src/main/resources/mapper/CwProjectApplicationMapper.xml` -- 同样修正状态值映射 -- 增加审批中和已拒绝的统计 - -### ✅ 修复效果增强 -1. **状态值完全匹配**: 统计接口与数据库状态值完全一致 -2. **审批中计算准确**: 包含已提交(`submitted`)和正在审批(`approving`)的申报 -3. **实时状态更新**: 审批流程创建后立即更新申报状态 -4. **统计数据完整**: 部门统计和月度统计也包含完整的状态信息 - -### 💡 技术改进要点 -- **状态值统一**: 全部使用字符串状态,避免数字映射错误 -- **业务逻辑优化**: 审批中状态包含提交和审批两个阶段 -- **异步处理改进**: 确保审批流程创建后状态立即更新 -- **SQL查询优化**: 统计查询包含更完整的状态信息 - ---- - -**状态**: ✅ 已完成 -**验证**: 🔄 待测试 -**影响范围**: 财务管理 - 预算申报管理页面统计功能(完整修复) - -## 🔧 审批按钮权限修复 (2025-01-27 16:45:00) - -### 问题描述 -- 前端显示数据成功,但审批操作按钮不显示 -- 分析发现两个问题:状态判断条件和权限检查不匹配 - -### 问题根因 -1. **状态条件不匹配**: 数据库返回状态为 `waiting`,但前端条件只检查 `submitted` 和 `approving` -2. **权限配置不匹配**: 前端检查 `cw:application:approve`,但用户实际拥有 `cw:approval:process` - -### 修改内容 - -#### 修复1: 状态判断条件 -**文件**: `web/src/views/cw/approvalRecord/index.vue` - -**修改前**: -```html - @@ -1284,4 +1455,8 @@ onMounted(() => { .el-table :deep(.bed-row:hover) { background-color: #e8f0f5 !important; /* 床位行hover时的深一点灰色 */ } + +:deep(.el-table--default .cell) { + color: #000000 !important; +} diff --git a/web/src/views/zsjy/internship/daily/checkin.vue b/web/src/views/zsjy/internship/daily/checkin.vue new file mode 100644 index 00000000..b2b4e222 --- /dev/null +++ b/web/src/views/zsjy/internship/daily/checkin.vue @@ -0,0 +1,695 @@ + + + + + diff --git a/web/src/views/zsjy/internship/daily/index.vue b/web/src/views/zsjy/internship/daily/index.vue new file mode 100644 index 00000000..f7b1e98b --- /dev/null +++ b/web/src/views/zsjy/internship/daily/index.vue @@ -0,0 +1,864 @@ + + + + + diff --git a/web/src/views/zsjy/internship/msgmanage/components/AddDialog.vue b/web/src/views/zsjy/internship/msgmanage/components/AddDialog.vue new file mode 100644 index 00000000..315626bc --- /dev/null +++ b/web/src/views/zsjy/internship/msgmanage/components/AddDialog.vue @@ -0,0 +1,365 @@ + + + + + diff --git a/web/src/views/zsjy/internship/msgmanage/components/EditDialog.vue b/web/src/views/zsjy/internship/msgmanage/components/EditDialog.vue new file mode 100644 index 00000000..d6165b69 --- /dev/null +++ b/web/src/views/zsjy/internship/msgmanage/components/EditDialog.vue @@ -0,0 +1,439 @@ + + + + + diff --git a/web/src/views/zsjy/internship/msgmanage/index.vue b/web/src/views/zsjy/internship/msgmanage/index.vue index eb388ce5..d111ca99 100644 --- a/web/src/views/zsjy/internship/msgmanage/index.vue +++ b/web/src/views/zsjy/internship/msgmanage/index.vue @@ -32,8 +32,8 @@ - - + + @@ -93,7 +93,7 @@
{{ statisticsData.internshipCount || 0 }}
-
实习中
+
已实习
@@ -103,9 +103,9 @@
-
{{ statisticsData.internshipCount || 0 }}
-
已完成
-
实习率:{{ statisticsData.internshipRate || '0.00%' }}
+
{{ statisticsData.internshipRate || '0.00%' }}
+
实习率
+
已实习学生占比
@@ -128,24 +128,29 @@ - - - - + - - - + + + + + + + + - - + + @@ -168,34 +192,49 @@ - +
{{ studentDetail.studentNo }} {{ studentDetail.studentName }} {{ studentDetail.gender }} - {{ studentDetail.mobile }} + {{ studentDetail.phone }} {{ studentDetail.collegeName }} {{ studentDetail.majorName }} {{ studentDetail.className }} - {{ studentDetail.classMasterName }} + {{ studentDetail.classTeacherName }} - + 未实习 + - {{ studentDetail.internshipCompany || '-' }} + {{ studentDetail.companyName || '-' }} {{ studentDetail.internshipPosition || '-' }} - {{ studentDetail.internshipLocation || '-' }} + {{ studentDetail.companyAddress || '-' }} - {{ parseTime(studentDetail.internshipStartTime, '{y}-{m}-{d}') }} + {{ parseTime(studentDetail.internshipStartDate, '{y}-{m}-{d}') }} - - {{ parseTime(studentDetail.internshipEndTime, '{y}-{m}-{d}') }} + {{ parseTime(studentDetail.internshipEndDate, '{y}-{m}-{d}') }} - + {{ studentDetail.contactPersonName || '-' }} + {{ studentDetail.contactPersonPhone || '-' }} + + {{ studentDetail.internshipAllowance }}元/月 + - + + + 已签署 + 未签署 + - + + {{ studentDetail.remark || '-' }}
+ + + + + +
@@ -213,9 +258,12 @@ import { InternshipInfoVO, InternshipInfoQuery, ClassInfoVO, InternshipStatistic import { ComponentInternalInstance, getCurrentInstance, reactive, ref, toRefs } from 'vue'; import { parseTime } from '@/utils/ruoyi'; import { download } from '@/utils/request'; +import { ElMessage } from 'element-plus'; +import AddDialog from './components/AddDialog.vue'; +import EditDialog from './components/EditDialog.vue'; const { proxy } = getCurrentInstance() as ComponentInternalInstance; -const { internship_status } = toRefs(proxy?.useDict('internship_status')); +const { internship_status, sys_user_sex } = toRefs(proxy?.useDict('internship_status', 'sys_user_sex')); const internshipList = ref([]); const classOptions = ref([]); @@ -231,6 +279,20 @@ const total = ref(0); const dateRange = ref<[string, string]>([]); const studentDetail = ref(); +// 弹窗相关状态 +const showAddDialog = ref(false); +const showEditDialog = ref(false); +const selectedStudentInfo = ref<{ + studentId: string; + studentName: string; + studentNo: string; +}>({ + studentId: '', + studentName: '', + studentNo: '' +}); +const selectedStudentId = ref(''); + const data = reactive({ queryParams: { pageNum: 1, @@ -239,9 +301,9 @@ const data = reactive({ studentNo: '', classId: '', internshipStatus: '', - internshipCompany: '', - startTimeBegin: '', - startTimeEnd: '' + companyName: '', + internshipStartDateBegin: '', + internshipStartDateEnd: '' } as InternshipInfoQuery, dialog: { visible: false, @@ -256,19 +318,33 @@ const getList = async () => { loading.value = true; // 处理日期范围 if (dateRange.value && dateRange.value.length === 2) { - queryParams.value.startTimeBegin = dateRange.value[0]; - queryParams.value.startTimeEnd = dateRange.value[1]; + queryParams.value.internshipStartDateBegin = dateRange.value[0]; + queryParams.value.internshipStartDateEnd = dateRange.value[1]; } else { - queryParams.value.startTimeBegin = ''; - queryParams.value.startTimeEnd = ''; + queryParams.value.internshipStartDateBegin = ''; + queryParams.value.internshipStartDateEnd = ''; } + console.log('=== 前端调试日志 ==='); + console.log('1. 查询参数:', JSON.stringify(queryParams.value, null, 2)); + try { const res = await listInternshipInfo(queryParams.value); + console.log('2. API响应完整数据:', JSON.stringify(res, null, 2)); + console.log('3. res.rows类型:', typeof res.rows, '长度:', res.rows ? res.rows.length : 'undefined'); + console.log('4. res.total:', res.total); + console.log('5. res.code:', res.code); + console.log('6. res.msg:', res.msg); + internshipList.value = res.rows; total.value = res.total; + + console.log('7. 赋值后 internshipList.value:', internshipList.value); + console.log('8. 赋值后 total.value:', total.value); + loading.value = false; } catch (error) { + console.error('API调用失败:', error); loading.value = false; } }; @@ -349,6 +425,27 @@ const handleExport = () => { }); }; +/** 新增实习信息按钮操作 */ +const handleAdd = (row: InternshipInfoVO) => { + selectedStudentInfo.value = { + studentId: row.studentId, + studentName: row.studentName, + studentNo: row.studentNo + }; + showAddDialog.value = true; +}; + +/** 编辑实习信息按钮操作 */ +const handleEdit = (row: InternshipInfoVO) => { + selectedStudentId.value = row.studentId; + showEditDialog.value = true; +}; + +/** 弹窗操作成功后刷新列表 */ +const handleDialogSuccess = () => { + getList(); +}; + /** 取消按钮 */ const cancel = () => { dialog.value.visible = false; @@ -381,9 +478,13 @@ const cancel = () => { color: white; position: relative; overflow: hidden; + height: 100px; /* 设置固定高度,确保所有卡片高度一致 */ .statistics-content { flex: 1; + display: flex; + flex-direction: column; + justify-content: center; /* 内容垂直居中 */ .statistics-number { font-size: 28px; @@ -401,6 +502,12 @@ const cancel = () => { opacity: 0.8; margin-top: 2px; } + + .statistics-description { + font-size: 12px; + opacity: 0.8; + margin-top: 2px; + } } .statistics-icon { @@ -445,5 +552,18 @@ const cancel = () => { margin-top: 20px; } } + + .status-tag { + display: inline-block; + padding: 2px 8px; + border-radius: 4px; + font-size: 12px; + font-weight: 500; + + &.not-started { + background-color: #f56c6c; + color: white; + } + } } diff --git a/web/src/views/zsjy/internship/pmonthly/detail.vue b/web/src/views/zsjy/internship/pmonthly/detail.vue new file mode 100644 index 00000000..307b9a97 --- /dev/null +++ b/web/src/views/zsjy/internship/pmonthly/detail.vue @@ -0,0 +1,318 @@ + + + + + diff --git a/web/src/views/zsjy/internship/pmonthly/form.vue b/web/src/views/zsjy/internship/pmonthly/form.vue new file mode 100644 index 00000000..4a8aa5c9 --- /dev/null +++ b/web/src/views/zsjy/internship/pmonthly/form.vue @@ -0,0 +1,590 @@ + + + + + diff --git a/web/src/views/zsjy/internship/pmonthly/index.vue b/web/src/views/zsjy/internship/pmonthly/index.vue new file mode 100644 index 00000000..97dd781a --- /dev/null +++ b/web/src/views/zsjy/internship/pmonthly/index.vue @@ -0,0 +1,388 @@ + + + + + diff --git a/web/src/views/zsjy/internship/visit/index.vue b/web/src/views/zsjy/internship/visit/index.vue new file mode 100644 index 00000000..3392c16c --- /dev/null +++ b/web/src/views/zsjy/internship/visit/index.vue @@ -0,0 +1,1056 @@ + + + + + diff --git a/本地prompt/前端提示词模板-勤工俭学岗位申请.md b/本地prompt/前端提示词模板-勤工俭学岗位申请.md deleted file mode 100644 index 2059a644..00000000 --- a/本地prompt/前端提示词模板-勤工俭学岗位申请.md +++ /dev/null @@ -1,208 +0,0 @@ -# 前端提示词模板(勤工俭学岗位申请) - -> 使用说明:将本文件复制为你的具体任务文件,按占位符填写后直接喂给大模型。建议与后端 Swagger 对齐后再进入编码阶段。 - ---- - -## 一、系统级提示词(长期复用) - -将以下内容作为系统提示词(System Prompt),必要处用你的信息替换占位符。 - -```text -你是资深前端工程师,负责在 {{项目名称:hzdyjs_xg}} 项目中实现「{{模块名称:勤工俭学岗位申请}}」前端。请严格遵循: - -- 技术栈与目录 - - 前端:Vue 3 + TypeScript + Element Plus - - 源码根:web/ - - 路径约定: - - 视图:web/src/views/jcbt/workstudy/Application/ - - 组件:web/src/components/ - - API:web/src/api/jcbt/ - - 类型:web/src/types/jcbt/ - - 路由:web/src/router/modules/ - -- 强制性规范(必须遵循) - - 文档同步更新:docs/开发文档/API/(入口:API文档总览.md) - - 后端接口规范(只读约束):统一响应(R/TableDataInfo)、Bearer 鉴权、@SaCheckPermission、Bean Validation - - 数据库规范(只读约束):雪花ID、逻辑删除、多租户 - -- 交互与质量 - - UI:Element Plus,与现有风格一致;空态/加载/异常态完整 - - 性能:查询防抖、避免重复请求、必要缓存 - - 国际化/中文文案:与现有实现保持一致 - -- 代码要求 - - TypeScript 类型完备;语义化命名;早返回;错误优先 - - 遵循现有 ESLint/Prettier;避免无关重构 - - 不擅自改后端契约;如发现与 Swagger 不一致,先列差异再继续 - -- 开发工作流(必须执行) - 1) 输出页面/路由/数据流草图 - 2) 列出 API 契约(按 Swagger),不明确则占位并提问 - 3) 分里程碑交付:骨架→交互→接口→边界→自测 - 4) 每步更新代码与文档,并附自测清单 - -- 运行与验证 - - 前端:cd web && npm install && npm run dev - - 后端 Swagger:`http://localhost:8080/swagger-ui/index.html` - - 如接口未就绪:可 Mock,但提交前需切换为真实 API - -开始前先输出: -1) 页面/路由/数据流草图 -2) API 列表与请求/响应契约(引用 Swagger) -3) 边界与异常场景清单 -4) 实施计划与里程碑 -``` - ---- - -## 二、任务说明模板(每个功能/里程碑使用) - -将此模板复制后填写: - -```text -【模块】{{模块名称:勤工俭学 - 岗位申请前端}} - -【目标】 -{{目标描述: 例如“实现岗位列表和详情的只读渲染,输出 API 契约清单并对齐 Swagger”}} - -【用户故事/用例(可用 Gherkin)】 -- 作为 {{角色:学生}} ,我可以 {{动作:浏览岗位列表、筛选、查看详情}} -- 我可以 {{动作:发起申请、保存草稿、提交、撤回}} -- 我可以 {{动作:查看我的申请进度与审批记录}} - -【页面/路由】 -- {{页面1:/workstudy/Application}}(描述:{{用途:岗位列表}}) -- {{页面2:/workstudy/Application/:id}}(描述:{{用途:岗位详情}}) -- {{页面3:/workstudy/Application/:id/apply}}(描述:{{用途:发起申请}}) -- {{页面4:/workstudy/Application}}(描述:{{用途:我的申请列表}}) -- {{页面5:/workstudy/Application/:id}}(描述:{{用途:申请详情}}) - -【数据与状态】 -- Position: { id, title, dept, quota, deadline, status, ... }(如需补充:{{补充字段}}) -- Application: { id, positionId, applicantId, status, formData, auditTrail, ... } -- 状态机 Application.status:{{draft → submitted → reviewing → approved | rejected → archived}} -- 前端状态(Pinia):{{筛选条件、分页、选中项、提交进度}} - -【API 列表(按 Swagger 填写,不明先占位)】 -- GET {{/api/workstudy/positions}} → TableDataInfo -- GET {{/api/workstudy/positions/{id}}} → R -- POST {{/api/workstudy/applications}}(draft/submit)→ R -- GET {{/api/workstudy/applications/my}} → TableDataInfo -- GET {{/api/workstudy/applications/{id}}} → R -- PUT {{/api/workstudy/applications/{id}}}(update/withdraw)→ R -- 统一 Header:Authorization: Bearer ;{{clientid、referer}} -- 错误码与消息:{{401/403/500 处理规则}} - -【表单与校验】 -- 必填项:{{字段列表}} -- 规则:{{格式/长度/附件数量/大小/类型限制}} -- 业务校验:{{是否在申请期、是否满员、是否重复申请}} - -【UI/交互要求】 -- 列表:{{搜索防抖、分页、空态、加载态}} -- 详情:{{字段分区、审批时间线}} -- 申请:{{草稿自动保存、提交前二次确认、提交中禁用}} - -【异常与边界】 -- {{岗位满员、窗口关闭、重复申请、网络异常、401/403、上传失败}} -- 幂等:{{提交防重复}} - -【文件与目录(请按此落盘)】 -- 视图: - - web/src/views/jcbt/workstudy/Application/Create.vue - - web/src/views/jcbt/workstudy/Application/List.vue - - web/src/views/jcbt/workstudy/Application/Detail.vue -- 组件(可选): - - web/src/views/jcbt/workstudy/Application/components/ApplicationForm.vue - - web/src/views/jcbt/workstudy/Application/components/ApprovalTimeline.vue -- API: - - web/src/api/jcbt/workstudy/application.ts -- 类型: - - web/src/types/jcbt/workstudy.ts -- 路由: - - web/src/router/modules/workstudy.ts(或按项目既有模块注册) -- 文档: - - docs/开发文档/API/{{勤工俭学岗位申请API.md}} - - docs/开发文档/API/API文档总览.md(添加入口/链接) - -【验收标准】 -- 功能通过自测清单:{{列表检索、详情、草稿、提交、撤回、进度查看}} -- 代码通过:npm run lint、npm run type-check、构建成功 -- 文档已更新:API 文档与总览入口;接口示例与字段说明完整 -- 与 Swagger 一致;如有差异,列出并待校准 - -【交付物】 -- 代码 + 类型 + API 封装 + 路由 + 状态 -- 文档(API/总览更新) -- 自测清单与必要截图 -``` - ---- - -## 三、里程碑模板(示例) - -```text -【里程碑名称】{{里程碑1:页面骨架+路由+API契约确认}} - -【范围】 -- 新增 5 个页面骨架与路由注册 -- 接入岗位列表/详情只读接口 -- 输出 API 契约清单并对齐 Swagger,列出差异点 - -【输出】 -- 可访问的路由与页面骨架 -- position.ts / application.ts 的只读方法 -- types 基础类型定义 -- 文档:勤工俭学岗位申请API.md(初版目录+已实现接口)与总览入口 - -【验证】 -- 路由可访问并能渲染真实数据 -- Lint / Type-Check / Build 通过 -- 已与 Swagger 对齐(或差异清单) -``` - ---- - -## 四、自测清单模板 - -```text -【列表页】 -- [ ] 查询条件输入与重置工作正常 -- [ ] 分页、排序、加载/空态展示正常 -- [ ] 防抖与重复请求控制有效 - -【详情页】 -- [ ] 字段展示齐全、格式正确 -- [ ] 审批时间线正确渲染(如有) - -【申请页】 -- [ ] 表单必填校验、格式校验、限制(数量/大小/类型) -- [ ] 草稿保存、提交、撤回流程 -- [ ] 异常态提示(满员/窗口关闭/重复申请/网络异常) - -【鉴权与错误处理】 -- [ ] 401 触发重新登录 -- [ ] 403 显示权限不足 -- [ ] 全局错误提示与兜底 -``` - ---- - -## 五、提交备注模板 - -```text -【变更摘要】 -{{一句话:本次实现了 …}} - -【影响范围】 -{{页面/模块/路由/权限}} - -【接口与文档】 -- {{新增/修改}} 接口:{{列表}} -- 文档已更新:docs/开发文档/API/{{文件名}}、API文档总览.md - -【验证记录】 -- 本地通过:lint/type-check/build -- 功能自测截图:{{可选}} -```