From a30b963dea742c2585930576947eca82ee297344 Mon Sep 17 00:00:00 2001 From: liumengyu <2459468901@qq.com> Date: Tue, 25 Aug 2026 18:47:22 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AF=BE=E8=A1=A8=E7=94=B3=E8=AF=B7=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E9=87=8D=E5=86=99=20bug=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jwgl/CourseRunningController.java | 133 +--- .../web/controller/jwgl/JYSController.java | 19 + .../jwgl/TrainingProgramController.java | 21 + .../controller/jwgl/ZYJXJHBController.java | 21 + .../controller/jwtest/TestPkController.java | 56 ++ .../src/main/resources/application.yml | 1 + .../main/resources/mybatis/mybatis-config.xml | 2 +- .../framework/config/SecurityConfig.java | 2 +- .../framework/web/service/TokenService.java | 38 +- .../config/AccountManagementWebMvcConfig.java | 69 +- .../courserunning/ScheduleAdjustApplyDTO.java | 16 +- .../courserunning/ScheduleAdjustAuditDTO.java | 33 +- .../courserunning/ScheduleAdjustQueryDTO.java | 7 +- .../ScheduleAdjustStrategyDTO.java | 52 -- .../com/roomroot/jwgl/entity/DKSQBZMX.java | 56 +- .../com/roomroot/jwgl/entity/DKSQFZJY.java | 60 +- .../java/com/roomroot/jwgl/entity/DKSQJS.java | 44 +- .../com/roomroot/jwgl/entity/DKSQXYD.java | 52 +- .../java/com/roomroot/jwgl/entity/SSDKSQ.java | 10 - .../com/roomroot/jwgl/entity/SSDKSQSP.java | 81 -- .../java/com/roomroot/jwgl/entity/SSKC.java | 7 +- .../jwgl/entity/ScheduleAdjustStrategy.java | 74 -- .../jwgl/handler/DmGuidTypeHandler.java | 41 + .../interceptor/JwglApiAccessInterceptor.java | 50 -- .../roomroot/jwgl/mapper/DKSQBZMXMapper.xml | 2 +- .../roomroot/jwgl/mapper/DKSQFZJYMapper.xml | 4 +- .../com/roomroot/jwgl/mapper/DKSQJSMapper.xml | 2 +- .../roomroot/jwgl/mapper/DKSQXYDMapper.xml | 2 +- .../jwgl/mapper/KcbTimetableMapper.xml | 20 +- .../roomroot/jwgl/mapper/SSDKSQMapper.java | 1 - .../com/roomroot/jwgl/mapper/SSDKSQMapper.xml | 25 +- .../roomroot/jwgl/mapper/SSDKSQSPMapper.java | 30 - .../roomroot/jwgl/mapper/SSDKSQSPMapper.xml | 45 -- .../roomroot/jwgl/mapper/SSKCBFZJYMapper.xml | 4 +- .../roomroot/jwgl/mapper/SSKCBJSMapper.xml | 4 +- .../com/roomroot/jwgl/mapper/SSKCBMapper.xml | 4 +- .../roomroot/jwgl/mapper/SSKCBXYDMapper.xml | 9 +- .../com/roomroot/jwgl/mapper/SSKCMapper.xml | 22 +- .../mapper/ScheduleAdjustStrategyMapper.java | 16 - .../roomroot/jwgl/mapper/TestPkMapper.java | 24 +- .../com/roomroot/jwgl/mapper/TestPkMapper.xml | 247 +++++- .../roomroot/jwgl/mapper/XYJXQJBMapper.xml | 2 +- .../service/CourseRunningImportService.java | 88 +- .../roomroot/jwgl/service/FacultyService.java | 10 + .../jwgl/service/TrainingProgramService.java | 6 + .../roomroot/jwgl/service/ZYJXJHBService.java | 6 + .../impl/CourseRunningImportServiceImpl.java | 751 +++--------------- .../jwgl/service/impl/FacultyServiceImpl.java | 47 ++ .../impl/TrainingProgramServiceImpl.java | 41 + .../jwgl/service/impl/ZYJXJHBServiceImpl.java | 39 + .../roomroot/jwgl/utils/ExcelParseUtil.java | 156 ++++ .../com/roomroot/jwgl/utils/UuidUtil.java | 38 + .../courserunning/ScheduleAdjustApplyVO.java | 5 - .../courserunning/ScheduleAdjustAuditVO.java | 81 -- .../courserunning/ScheduleAdjustDetailVO.java | 8 +- .../ScheduleAdjustStrategyVO.java | 54 -- .../jwgl/vo/jwtest/PkConflictCheckVO.java | 24 + .../roomroot/jwgl/vo/jwtest/PkConflictVO.java | 73 ++ 58 files changed, 1203 insertions(+), 1632 deletions(-) delete mode 100644 backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/dto/courserunning/ScheduleAdjustStrategyDTO.java delete mode 100644 backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/SSDKSQSP.java delete mode 100644 backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/ScheduleAdjustStrategy.java create mode 100644 backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/handler/DmGuidTypeHandler.java delete mode 100644 backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSDKSQSPMapper.java delete mode 100644 backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSDKSQSPMapper.xml delete mode 100644 backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/ScheduleAdjustStrategyMapper.java delete mode 100644 backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/courserunning/ScheduleAdjustAuditVO.java delete mode 100644 backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/courserunning/ScheduleAdjustStrategyVO.java create mode 100644 backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/jwtest/PkConflictCheckVO.java create mode 100644 backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/jwtest/PkConflictVO.java diff --git a/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwgl/CourseRunningController.java b/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwgl/CourseRunningController.java index 27035123..d1350534 100644 --- a/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwgl/CourseRunningController.java +++ b/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwgl/CourseRunningController.java @@ -3,7 +3,6 @@ package com.roomroot.web.controller.jwgl; import com.roomroot.jwgl.dto.courserunning.ClassesImportDTO; import com.roomroot.jwgl.dto.courserunning.ImportRemoveDTO; import com.roomroot.jwgl.dto.courserunning.ScheduleAdjustApplyDTO; -import com.roomroot.jwgl.dto.courserunning.ScheduleAdjustStrategyDTO; import com.roomroot.jwgl.dto.courserunning.ScheduleAdjustAuditDTO; import com.roomroot.jwgl.dto.courserunning.ScheduleAdjustQueryDTO; import com.roomroot.jwgl.dto.courserunning.SemesterImportDTO; @@ -309,7 +308,7 @@ public class CourseRunningController { * 查询单个调课申请的完整详情。 *

* 根据调课申请编号查询该申请的完整信息,包括申请信息、调整后的内容、 - * 教研室审批状态、机关审批记录、关联的教室、辅助教员、学员队、保障明细等。 + * 教研室审批与查收状态、关联的教室、辅助教员、学员队、保障明细等。 *

* * @param ssdksqbh 调课申请编号 @@ -338,148 +337,30 @@ public class CourseRunningController { /** * 教研室审批调课申请。 *

- * 教研室审批教员提交的调课申请,包含同意、发回、拒绝三种处理方式。 + * 教研室审批调课申请。点审批即已查收,审批人同时记为查收人。 + * 同意、发回、拒绝均为已查收;教员撤回后为未查收。 *

* - * @param dto 教研室审批参数(auditRole 传 "jys") + * @param dto 教研室审批参数 * @return 操作结果 */ @PostMapping("/adjust/audit/jys") public Result auditByTeachingOffice(@Valid @RequestBody ScheduleAdjustAuditDTO dto) { - // 自动设置审批角色为教研室 - dto.setAuditRole("jys"); courseRunningImportService.auditByTeachingOffice(dto); return Result.success(); } - - /** - * 教研室查收调课申请。 - *

- * 教研室查收调课申请,标记查收状态和查收时间。 - *

- * - * @param ssdksqbh 调课申请编号 - * @param csrbh 查收人编号 - * @return 操作结果 - */ - @PostMapping("/adjust/receive/jys") - public Result receiveByTeachingOffice(@RequestParam("ssdksqbh") String ssdksqbh, - @RequestParam("csrbh") String csrbh) { - courseRunningImportService.receiveByTeachingOffice(ssdksqbh, csrbh); - return Result.success(); - } - - /** - * 教学系审批调课申请。 - *

- * 教学系审批教研室已同意的调课申请,包含同意、发回、拒绝三种处理方式。 - * 构成三级审批流程的中间环节(教研室→教学系→机关)。 - *

- * - * @param dto 教学系审批参数(auditRole 传 "jxx") - * @return 操作结果 - */ - @PostMapping("/adjust/audit/jxx") - public Result auditByTeachingDepartment(@Valid @RequestBody ScheduleAdjustAuditDTO dto) { - // 自动设置审批角色为教学系 - dto.setAuditRole("jxx"); - courseRunningImportService.auditByTeachingDepartment(dto); - return Result.success(); - } - - /** - * 机关审批调课申请。 - *

- * 机关审批教研室已同意的调课申请,包含同意、发回、拒绝三种处理方式。 - *

- * - * @param dto 机关审批参数(auditRole 传 "jg",需传 jgbh) - * @return 操作结果 - */ - @PostMapping("/adjust/audit/jg") - public Result auditByAuthority(@Valid @RequestBody ScheduleAdjustAuditDTO dto) { - // 自动设置审批角色为机关 - dto.setAuditRole("jg"); - courseRunningImportService.auditByAuthority(dto); - return Result.success(); - } - - /** - * 机关查收调课申请。 - *

- * 机关查收调课申请,标记查收状态和查收时间。 - *

- * - * @param ssdksqbh 调课申请编号 - * @param jgbh 机关编号 - * @param csrbh 查收人编号 - * @return 操作结果 - */ - @PostMapping("/adjust/receive/jg") - public Result receiveByAuthority(@RequestParam("ssdksqbh") String ssdksqbh, - @RequestParam("jgbh") String jgbh, - @RequestParam("csrbh") String csrbh) { - courseRunningImportService.receiveByAuthority(ssdksqbh, jgbh, csrbh); - return Result.success(); - } - /** * 撤销调课申请。 *

- * 教员撤销自己提交的、未经过审批的调课申请。 + * 教员撤销未经过审批的调课申请。列表已按登录教员过滤,只需传申请编号。 *

* * @param ssdksqbh 调课申请编号 - * @param sqjybh 申请教员编号 * @return 操作结果 */ @DeleteMapping("/adjust/cancel") - public Result cancelScheduleAdjust(@RequestParam("ssdksqbh") String ssdksqbh, - @RequestParam("sqjybh") String sqjybh) { - courseRunningImportService.cancelScheduleAdjust(ssdksqbh, sqjybh); - return Result.success(); - } - - // ==================== 调课策略管理 ==================== - - /** - * 查询调课策略列表。 - *

- * 支持按策略类型筛选,返回所有策略(含停用)。 - *

- * - * @param cllx 策略类型:TIME_LIMIT=时间段限制,AUTO_APPROVE=免审批,为空时查询全部 - * @return 调课策略列表 - */ - @GetMapping("/adjust/strategy/list") - public Result> listScheduleAdjustStrategies( - @RequestParam(required = false) String cllx) { - return Result.success(courseRunningImportService.listScheduleAdjustStrategies(cllx)); - } - - /** - * 保存调课策略(新增或修改)。 - *

- * 策略编号为空时执行新增,非空时执行修改。 - *

- * - * @param dto 策略参数 - * @return 策略编号 - */ - @PostMapping("/adjust/strategy/save") - public Result saveScheduleAdjustStrategy(@Valid @RequestBody ScheduleAdjustStrategyDTO dto) { - return Result.success("保存成功", courseRunningImportService.saveScheduleAdjustStrategy(dto)); - } - - /** - * 删除调课策略。 - * - * @param clbh 策略编号 - * @return 操作结果 - */ - @DeleteMapping("/adjust/strategy/delete") - public Result deleteScheduleAdjustStrategy(@RequestParam("clbh") String clbh) { - courseRunningImportService.deleteScheduleAdjustStrategy(clbh); + public Result cancelScheduleAdjust(@RequestParam("ssdksqbh") String ssdksqbh) { + courseRunningImportService.cancelScheduleAdjust(ssdksqbh); return Result.success(); } diff --git a/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwgl/JYSController.java b/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwgl/JYSController.java index 5d939168..dd492ca4 100644 --- a/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwgl/JYSController.java +++ b/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwgl/JYSController.java @@ -221,6 +221,25 @@ public class JYSController { return Result.success(pageResult); } + /** + * 教员数据文件导入(.xls/.xlsx) + *

表头顺序:教员编号, 教员姓名, 教研室代号, 职称, 专业技术职务等级, 教员类别, + * 性别, 手机号, 身份证号, 备注, 序号, 虚实类型。教员编号不填自动创建,离职状态默认 0。

+ */ + @PostMapping("/teacher/import") + public Result importTeacher(@RequestParam("file") MultipartFile file) throws Exception { + int count = facultyService.importJYBFromExcel(file); + return Result.success("导入成功,共" + count + "条", count); + } + + /** + * 导出全部在职教员到 Excel(.xlsx)。 + */ + @GetMapping("/teacher/export") + public void exportTeacher(HttpServletResponse response) throws Exception { + facultyService.exportJYBExcel(response); + } + // ======================== 教员属性管理 ======================== /** diff --git a/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwgl/TrainingProgramController.java b/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwgl/TrainingProgramController.java index c5c6d27b..40b3ce4c 100644 --- a/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwgl/TrainingProgramController.java +++ b/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwgl/TrainingProgramController.java @@ -5,7 +5,9 @@ import com.roomroot.jwgl.service.TrainingProgramService; import com.roomroot.jwgl.unit.PageQuery; import com.roomroot.jwgl.unit.PageResult; import com.roomroot.jwgl.unit.Result; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import jakarta.annotation.Resource; @@ -115,4 +117,23 @@ public class TrainingProgramController { PageResult pageResult = trainingProgramService.pageList(query, cond); return Result.success(pageResult); } + + /** + * 培养方案(专业)数据文件导入(.xls/.xlsx)。 + *

表头顺序:专业代号, 专业名称, 专业代码, 专业版本, 培训类型, 培训层次, 学年制, + * 学期数, 专业方向, 专业备注, 学员类别, 简称, 培训类型2。专业代号不填自动生成。

+ */ + @PostMapping("/import") + public Result importData(@RequestParam("file") MultipartFile file) throws Exception { + int count = trainingProgramService.importZYBFromExcel(file); + return Result.success("导入成功,共" + count + "条", count); + } + + /** + * 导出培养方案(专业)到 Excel(.xlsx)。 + */ + @GetMapping("/export") + public void exportData(HttpServletResponse response) throws Exception { + trainingProgramService.exportZYBExcel(response); + } } diff --git a/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwgl/ZYJXJHBController.java b/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwgl/ZYJXJHBController.java index 1bfe5a55..b4584d92 100644 --- a/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwgl/ZYJXJHBController.java +++ b/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwgl/ZYJXJHBController.java @@ -3,7 +3,9 @@ package com.roomroot.web.controller.jwgl; import com.roomroot.jwgl.entity.ZYJXJHB; import com.roomroot.jwgl.service.ZYJXJHBService; import com.roomroot.jwgl.unit.Result; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import jakarta.annotation.Resource; import java.util.List; @@ -81,4 +83,23 @@ public class ZYJXJHBController { @RequestParam("ty") Integer ty) { return Result.success(zyjxjhbService.listByZydhAndTy(zydh, ty)); } + + /** + * 专业教学计划表数据文件导入(.xls/.xlsx)。 + *

表头顺序:专业代号, 课编号, 学期第次, 课类型, 学时, 学分, 周课时, 课程定位, + * 模块, 成绩分制, 理论学时, 实践学时, 考试课时, 教研室代号, 简称。编号由系统自动生成。

+ */ + @PostMapping("/import") + public Result importData(@RequestParam("file") MultipartFile file) throws Exception { + int count = zyjxjhbService.importZYJXJHBFromExcel(file); + return Result.success("导入成功,共" + count + "条", count); + } + + /** + * 导出专业教学计划到 Excel(.xlsx)。 + */ + @GetMapping("/export") + public void exportData(HttpServletResponse response) throws Exception { + zyjxjhbService.exportZYJXJHBExcel(response); + } } diff --git a/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwtest/TestPkController.java b/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwtest/TestPkController.java index c802c0d7..8702c8b5 100644 --- a/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwtest/TestPkController.java +++ b/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwtest/TestPkController.java @@ -2,10 +2,13 @@ package com.roomroot.web.controller.jwtest; import com.roomroot.jwgl.mapper.TestPkMapper; import com.roomroot.jwgl.unit.Result; +import com.roomroot.jwgl.vo.jwtest.PkConflictCheckVO; +import com.roomroot.jwgl.vo.jwtest.PkConflictVO; import com.roomroot.jwgl.vo.jwtest.PkLessonVO; import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; @@ -31,4 +34,57 @@ public class TestPkController { public Result> all() { return Result.success(testPkMapper.selectAll()); } + + /** + * 教学班时间冲突:同一个班在同一日期同一节次被安排多门课程。 + */ + @GetMapping("/conflict/class") + public Result classTimeConflict(@RequestParam(required = false) Integer nd) { + return Result.success(wrap( + "教学班时间冲突", + "同一班次在同一天同一节次被安排了多门课程", + testPkMapper.selectClassTimeConflict(nd))); + } + + /** + * 必修课与选修课时间冲突:同一班次同一时间既有必修又有选修。 + */ + @GetMapping("/conflict/elective-required") + public Result electiveRequiredConflict(@RequestParam(required = false) Integer nd) { + return Result.success(wrap( + "必修选修时间冲突", + "同一班次在同一天同一节次同时安排了必修课和选修课", + testPkMapper.selectElectiveRequiredConflict(nd))); + } + + /** + * 教员时间冲突:同一教员在同一时间段被安排多门课程。 + */ + @GetMapping("/conflict/teacher") + public Result teacherTimeConflict(@RequestParam(required = false) Integer nd) { + return Result.success(wrap( + "教员时间冲突", + "同一教员在同一天同一节次被安排了多门课程", + testPkMapper.selectTeacherTimeConflict(nd))); + } + + /** + * 教室时间冲突:同一教室在同一时间段被多门课程占用。 + */ + @GetMapping("/conflict/classroom") + public Result classroomTimeConflict(@RequestParam(required = false) Integer nd) { + return Result.success(wrap( + "教室时间冲突", + "同一教室在同一天同一节次被多门课程占用", + testPkMapper.selectClassroomTimeConflict(nd))); + } + + private PkConflictCheckVO wrap(String ctlx, String jcsm, List mx) { + PkConflictCheckVO vo = new PkConflictCheckVO(); + vo.setCtlx(ctlx); + vo.setJcsm(jcsm); + vo.setMx(mx); + vo.setTs(mx == null ? 0 : mx.size()); + return vo; + } } diff --git a/backend/roomroot-admin/src/main/resources/application.yml b/backend/roomroot-admin/src/main/resources/application.yml index 0352a5cc..b062c7d3 100644 --- a/backend/roomroot-admin/src/main/resources/application.yml +++ b/backend/roomroot-admin/src/main/resources/application.yml @@ -107,6 +107,7 @@ mybatis-plus: typeAliasesPackage: com.roomroot.**.domain # 配置mapper的扫描,找到所有的mapper.xml映射文件 mapperLocations: classpath*:mapper/**/*Mapper.xml,classpath*:com/roomroot/**/mapper/*.xml + typeHandlersPackage: com.roomroot.jwgl.handler # 加载全局的配置文件 configLocation: classpath:mybatis/mybatis-config.xml diff --git a/backend/roomroot-admin/src/main/resources/mybatis/mybatis-config.xml b/backend/roomroot-admin/src/main/resources/mybatis/mybatis-config.xml index ac47c038..ae179d43 100644 --- a/backend/roomroot-admin/src/main/resources/mybatis/mybatis-config.xml +++ b/backend/roomroot-admin/src/main/resources/mybatis/mybatis-config.xml @@ -13,7 +13,7 @@ PUBLIC "-//mybatis.org//DTD Config 3.0//EN" - + diff --git a/backend/roomroot-framework/src/main/java/com/roomroot/framework/config/SecurityConfig.java b/backend/roomroot-framework/src/main/java/com/roomroot/framework/config/SecurityConfig.java index 29c2f95c..4503e41c 100644 --- a/backend/roomroot-framework/src/main/java/com/roomroot/framework/config/SecurityConfig.java +++ b/backend/roomroot-framework/src/main/java/com/roomroot/framework/config/SecurityConfig.java @@ -100,7 +100,7 @@ public class SecurityConfig .authorizeHttpRequests((requests) -> { permitAllUrl.getUrls().forEach(url -> requests.requestMatchers(url).permitAll()); // 对于登录login 注册register 验证码captchaImage 允许匿名访问 - requests.requestMatchers("/login", "/register", "/captchaImage","/jwtest/pk/all").permitAll() + requests.requestMatchers("/login", "/register", "/captchaImage", "/jwtest/pk/all").permitAll() // 静态资源,可匿名访问 .requestMatchers(HttpMethod.GET, "/", "/*.html", "/**.html", "/**.css", "/**.js", "/profile/**").permitAll() .requestMatchers("/swagger-ui.html", "/v3/api-docs/**", "/swagger-ui/**", "/druid/**").permitAll() diff --git a/backend/roomroot-framework/src/main/java/com/roomroot/framework/web/service/TokenService.java b/backend/roomroot-framework/src/main/java/com/roomroot/framework/web/service/TokenService.java index 107a5d95..3b641a9d 100644 --- a/backend/roomroot-framework/src/main/java/com/roomroot/framework/web/service/TokenService.java +++ b/backend/roomroot-framework/src/main/java/com/roomroot/framework/web/service/TokenService.java @@ -73,6 +73,10 @@ public class TokenService String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); String userKey = getTokenKey(uuid); LoginUser user = redisCache.getCacheObject(userKey); + if (user == null) + { + log.warn("令牌签名有效,但登录缓存已失效,请重新登录"); + } return user; } catch (Exception e) @@ -218,14 +222,42 @@ public class TokenService */ private String getToken(HttpServletRequest request) { - String token = request.getHeader(header); - if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) + String token = firstHeader(request, header, "Authorization", "token"); + if (StringUtils.isNotEmpty(token)) { - token = token.replace(Constants.TOKEN_PREFIX, ""); + token = token.trim(); + if (StringUtils.startsWithIgnoreCase(token, Constants.TOKEN_PREFIX)) + { + token = token.substring(Constants.TOKEN_PREFIX.length()).trim(); + } } return token; } + /** + * 按顺序读取请求头,兼容 Authorization / token。 + */ + private String firstHeader(HttpServletRequest request, String... names) + { + if (names == null) + { + return null; + } + for (String name : names) + { + if (StringUtils.isEmpty(name)) + { + continue; + } + String value = request.getHeader(name); + if (StringUtils.isNotEmpty(value)) + { + return value; + } + } + return null; + } + private String getTokenKey(String uuid) { return CacheConstants.LOGIN_TOKEN_KEY + uuid; diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/config/AccountManagementWebMvcConfig.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/config/AccountManagementWebMvcConfig.java index f3977cbd..5b88fe42 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/config/AccountManagementWebMvcConfig.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/config/AccountManagementWebMvcConfig.java @@ -1,88 +1,25 @@ package com.roomroot.jwgl.config; -import com.roomroot.jwgl.interceptor.AccountManagementAuthorizationInterceptor; -import com.roomroot.jwgl.interceptor.JwglApiAccessInterceptor; import com.roomroot.jwgl.interceptor.OperationLogInterceptor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** - * 在线账户状态和账户管理权限 Web MVC 配置。 - * - *

该配置对已有在线会话执行账户状态校验,同时保持普通匿名请求的原有访问规则; - * 账户管理路径继续执行超级管理员权限校验,并在校验通过后采集操作日志上下文。

+ * 操作日志采集 Web MVC 配置。 + *

页面菜单权限由前端控制;后台只校验登录态,查询时再按角色和用户ID过滤数据。

*/ @Configuration public class AccountManagementWebMvcConfig implements WebMvcConfigurer { - /** - * 账户管理身份和权限拦截器。 - */ - private final AccountManagementAuthorizationInterceptor - authorizationInterceptor; - - /** - * 操作日志统一请求采集拦截器。 - */ private final OperationLogInterceptor operationLogInterceptor; - private final JwglApiAccessInterceptor jwglApiAccessInterceptor; - - /** - * 构造账户管理和操作日志统一 Web MVC 配置。 - * - * @param authorizationInterceptor 账户管理身份和权限拦截器 - * @param operationLogInterceptor 操作日志统一请求采集拦截器 - * @param jwglApiAccessInterceptor 教务角色接口访问拦截器 - */ - public AccountManagementWebMvcConfig( - AccountManagementAuthorizationInterceptor - authorizationInterceptor, - OperationLogInterceptor operationLogInterceptor, - JwglApiAccessInterceptor jwglApiAccessInterceptor) { - // 第一步:保存账户管理专用拦截器,供 MVC 注册阶段使用。 - this.authorizationInterceptor = authorizationInterceptor; - - // 第二步:保存操作日志采集拦截器,供 MVC 注册阶段使用。 + public AccountManagementWebMvcConfig(OperationLogInterceptor operationLogInterceptor) { this.operationLogInterceptor = operationLogInterceptor; - - this.jwglApiAccessInterceptor = jwglApiAccessInterceptor; } - /** - * 注册账户状态校验、账户管理权限和操作日志采集规则。 - * - * @param registry Spring MVC 拦截器注册器 - */ @Override public void addInterceptors(InterceptorRegistry registry) { - /* - * 第一步:注册账户管理鉴权拦截器。 - * 第二步:覆盖全部 Controller 请求,使停用或已合并账户不能绕过 - * 账户管理路径继续访问其他业务接口。 - * 第三步:拦截器内部会放行没有 Servlet 会话的普通匿名请求, - * 因此不会改变项目现有公开接口的访问方式。 - */ - // registry.addInterceptor(authorizationInterceptor) - // .addPathPatterns("/**"); - - /* - * 第四步:在账户状态和账户管理权限校验通过后采集日志上下文。 - * 第五步:覆盖全部 Controller 请求,由采集拦截器自行跳过 - * 非 Controller 处理器和没有有效登录记录的请求。 - */ - registry.addInterceptor(jwglApiAccessInterceptor) - .addPathPatterns("/**") - .excludePathPatterns( - "/login", - "/register", - "/captchaImage", - "/jys/teacher/list", - "/swagger-ui/**", - "/v3/api-docs/**", - "/druid/**"); - registry.addInterceptor(operationLogInterceptor) .addPathPatterns("/**"); } diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/dto/courserunning/ScheduleAdjustApplyDTO.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/dto/courserunning/ScheduleAdjustApplyDTO.java index 04a9b875..877a19cb 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/dto/courserunning/ScheduleAdjustApplyDTO.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/dto/courserunning/ScheduleAdjustApplyDTO.java @@ -24,10 +24,10 @@ public class ScheduleAdjustApplyDTO { private String sskcbbh; /** - * 调课事由,必填,描述本次调课的原因和依据。 + * 调课事由,对应 实施_调课申请.事由 VARCHAR(200)。 */ @NotBlank(message = "调课事由不能为空") - @Size(max = 500, message = "调课事由不能超过500个字符") + @Size(max = 200, message = "调课事由不能超过200个字符") private String sy; /** @@ -79,9 +79,9 @@ public class ScheduleAdjustApplyDTO { private String xjsbh; /** - * 调课后教学内容,调整后的本节课教学主题或知识点。 + * 调课后教学内容,对应 实施_调课申请.教学内容 VARCHAR(500)。 */ - @Size(max = 2000, message = "教学内容不能超过2000个字符") + @Size(max = 500, message = "教学内容不能超过500个字符") private String jxnr; /** @@ -91,9 +91,9 @@ public class ScheduleAdjustApplyDTO { private String jxyd; /** - * 调课后教学方法,调整后的教学方法。 + * 调课后教学方法,对应 实施_调课申请.教学方法 VARCHAR(50)。 */ - @Size(max = 200, message = "教学方法不能超过200个字符") + @Size(max = 50, message = "教学方法不能超过50个字符") private String jxff; /** @@ -109,9 +109,9 @@ public class ScheduleAdjustApplyDTO { private String ycxx; /** - * 备注,补充说明信息。 + * 备注,对应 实施_调课申请.备注 VARCHAR(50)。 */ - @Size(max = 500, message = "备注不能超过500个字符") + @Size(max = 50, message = "备注不能超过50个字符") private String bz; /** diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/dto/courserunning/ScheduleAdjustAuditDTO.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/dto/courserunning/ScheduleAdjustAuditDTO.java index 1ce2812d..68fb8183 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/dto/courserunning/ScheduleAdjustAuditDTO.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/dto/courserunning/ScheduleAdjustAuditDTO.java @@ -2,53 +2,42 @@ package com.roomroot.jwgl.dto.courserunning; import lombok.Data; -import jakarta.validation.constraints.*; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; /** - * 调课申请审批参数。 - *

- * 用于教研室或机关审批调课申请,包含审批结果、审批意见和审批人编号。 - *

+ * 教研室审批调课申请参数。 */ @Data public class ScheduleAdjustAuditDTO { /** - * 调课申请编号,必填,标识需要审批的调课申请。 + * 调课申请编号。 */ @NotBlank(message = "调课申请编号不能为空") @Size(max = 50, message = "调课申请编号不能超过50个字符") private String ssdksqbh; /** - * 审批角色,必填:jys(教研室审批)、jg(机关审批)。 - */ - @NotBlank(message = "审批角色不能为空") - @Size(max = 20, message = "审批角色不能超过20个字符") - private String auditRole; - - /** - * 审批结果,必填:1-同意,2-发回修改,3-拒绝。 + * 审批结果:1-同意,2-发回,3-拒绝。点审批即已查收。 */ @NotNull(message = "审批结果不能为空") + @Min(value = 1, message = "审批结果不正确") + @Max(value = 3, message = "审批结果不正确") private Integer spzt; /** - * 审批意见,可选,记录审批的具体意见或发回原因。 + * 审批意见,发回时填写原因。 */ @Size(max = 500, message = "审批意见不能超过500个字符") private String fhyj; /** - * 审批人编号,不传时由服务端取当前登录人。 + * 审批人编号,不传时取当前登录人;同时记为查收人。 */ @Size(max = 50, message = "审批人编号不能超过50个字符") private String sprbh; - - /** - * 机关编号,机关审批时可选。 - */ - @Size(max = 50, message = "机关编号不能超过50个字符") - private String jgbh; } diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/dto/courserunning/ScheduleAdjustQueryDTO.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/dto/courserunning/ScheduleAdjustQueryDTO.java index ec9ebff3..776d8d94 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/dto/courserunning/ScheduleAdjustQueryDTO.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/dto/courserunning/ScheduleAdjustQueryDTO.java @@ -38,15 +38,10 @@ public class ScheduleAdjustQueryDTO extends PageQuery { private Integer jyspzzt; /** - * 教研室查收状态:0-未查收,1-已查收。 + * 教研室查收状态:0-未查收,1-已查收。点了审批即为已查收。 */ private Integer jyscszt; - /** - * 机关审批状态:0-未审批,1-已同意,2-已发回,3-已拒绝。 - */ - private Integer jgspzt; - /** * 删除状态:0-正常,1-已删除。 */ diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/dto/courserunning/ScheduleAdjustStrategyDTO.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/dto/courserunning/ScheduleAdjustStrategyDTO.java deleted file mode 100644 index 7ae2004e..00000000 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/dto/courserunning/ScheduleAdjustStrategyDTO.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.roomroot.jwgl.dto.courserunning; - -import lombok.Data; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Size; -import java.time.LocalDateTime; - -/** - * 调课策略新增/修改参数。 - *

- * 统一处理新增和修改:策略编号为空时执行新增,非空时执行修改。 - * 策略类型为 TIME_LIMIT 时需填写截止星期和截止节次; - * 策略类型为 AUTO_APPROVE 时截止星期和截止节次不生效。 - *

- */ -@Data -public class ScheduleAdjustStrategyDTO { - - /** 策略编号(修改时必填,新增时为空) */ - @Size(max = 50, message = "策略编号不能超过50个字符") - private String clbh; - - /** 策略名称 */ - @NotBlank(message = "策略名称不能为空") - @Size(max = 100, message = "策略名称不能超过100个字符") - private String clmc; - - /** 策略类型:TIME_LIMIT=时间段限制,AUTO_APPROVE=免审批 */ - @NotBlank(message = "策略类型不能为空") - @Size(max = 20, message = "策略类型不能超过20个字符") - private String cllx; - - /** 是否启用:0=停用,1=启用(为空时默认1) */ - private Integer sfqy; - - /** 截止星期:0-6(仅 TIME_LIMIT 类型使用) */ - private Integer jzxq; - - /** 截止节次(仅 TIME_LIMIT 类型使用) */ - private Integer jzjc; - - /** 生效开始时间 */ - private LocalDateTime sxkssj; - - /** 生效结束时间 */ - private LocalDateTime sxjssj; - - /** 备注 */ - @Size(max = 500, message = "备注不能超过500个字符") - private String bz; -} diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/DKSQBZMX.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/DKSQBZMX.java index 2415b967..d009e03b 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/DKSQBZMX.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/DKSQBZMX.java @@ -1,8 +1,9 @@ package com.roomroot.jwgl.entity; -import com.baomidou.mybatisplus.annotation.TableName; 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 com.roomroot.jwgl.unit.BaseEntity; import lombok.Data; import lombok.EqualsAndHashCode; @@ -17,55 +18,48 @@ import java.time.LocalDateTime; @TableName("调课申请_保障明细") public class DKSQBZMX extends BaseEntity { - /** - * 编号 - */ @TableId(value = "编号", type = IdType.INPUT) private String bh; - /** - * 申请编号 - */ + @TableField("申请编号") private String sqbh; - /** - * 保障提供明细编号 - */ + @TableField("保障提供明细编号") private String bztgmxbh; - /** - * 数量 - */ + @TableField("数量") private Integer sl; - /** - * 备注 - */ + @TableField("备注") private String bz; - /** - * 查收状态 - */ + @TableField("查收状态") private Integer cszt; - /** - * 查收时间 - */ + @TableField("查收时间") private LocalDateTime cssj; - /** - * 年度 - */ + @TableField("年度") private Integer nd; - /** - * 日期 - */ + @TableField("日期") private LocalDateTime rq; - /** - * 节次 - */ + @TableField("节次") private Integer jc; + @TableField(exist = false) + private LocalDateTime createTime; + + @TableField(exist = false) + private LocalDateTime updateTime; + + @TableField(exist = false) + private String createBy; + + @TableField(exist = false) + private String updateBy; + + @TableField(exist = false) + private Integer delFlag; } diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/DKSQFZJY.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/DKSQFZJY.java index f9c0524a..48fbf05e 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/DKSQFZJY.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/DKSQFZJY.java @@ -1,8 +1,9 @@ package com.roomroot.jwgl.entity; -import com.baomidou.mybatisplus.annotation.TableName; 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 com.roomroot.jwgl.unit.BaseEntity; import lombok.Data; import lombok.EqualsAndHashCode; @@ -17,60 +18,51 @@ import java.time.LocalDateTime; @TableName("调课申请_辅助教员") public class DKSQFZJY extends BaseEntity { - /** - * 编号 - */ @TableId(value = "编号", type = IdType.INPUT) private String bh; - /** - * 调课申请编号 - */ + @TableField("调课申请编号") private String dksqbh; - /** - * 辅助教员编号 - */ + @TableField("辅助教员编号") private String fzjybh; - /** - * 查收状态 - */ + @TableField("查收状态") private Integer cszt; - /** - * 查收时间 - */ + @TableField("查收时间") private LocalDateTime cssj; - /** - * 主教员 - */ + @TableField("主教员") private Integer zjy; - /** - * 移除状态 - */ + @TableField("移除状态") private Integer yczt; - /** - * 年度 - */ + @TableField("年度") private Integer nd; - /** - * 备注 - */ + @TableField("备注") private String bz; - /** - * 日期 - */ + @TableField("日期") private LocalDateTime rq; - /** - * 节次 - */ + @TableField("节次") private Integer jc; + @TableField(exist = false) + private LocalDateTime createTime; + + @TableField(exist = false) + private LocalDateTime updateTime; + + @TableField(exist = false) + private String createBy; + + @TableField(exist = false) + private String updateBy; + + @TableField(exist = false) + private Integer delFlag; } diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/DKSQJS.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/DKSQJS.java index eeee1572..6b324140 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/DKSQJS.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/DKSQJS.java @@ -1,8 +1,9 @@ package com.roomroot.jwgl.entity; -import com.baomidou.mybatisplus.annotation.TableName; 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 com.roomroot.jwgl.unit.BaseEntity; import lombok.Data; import lombok.EqualsAndHashCode; @@ -17,40 +18,39 @@ import java.time.LocalDateTime; @TableName("调课申请_教室") public class DKSQJS extends BaseEntity { - /** - * 编号 - */ @TableId(value = "编号", type = IdType.INPUT) private String bh; - /** - * 调课申请编号 - */ + @TableField("调课申请编号") private String dksqbh; - /** - * 教室编号 - */ + @TableField("教室编号") private String jsbh; - /** - * 备注 - */ + @TableField("备注") private String bz; - /** - * 年度 - */ + @TableField("年度") private Integer nd; - /** - * 日期 - */ + @TableField("日期") private LocalDateTime rq; - /** - * 节次 - */ + @TableField("节次") private Integer jc; + @TableField(exist = false) + private LocalDateTime createTime; + + @TableField(exist = false) + private LocalDateTime updateTime; + + @TableField(exist = false) + private String createBy; + + @TableField(exist = false) + private String updateBy; + + @TableField(exist = false) + private Integer delFlag; } diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/DKSQXYD.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/DKSQXYD.java index 094602c4..9a4ccf55 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/DKSQXYD.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/DKSQXYD.java @@ -1,8 +1,9 @@ package com.roomroot.jwgl.entity; -import com.baomidou.mybatisplus.annotation.TableName; 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 com.roomroot.jwgl.unit.BaseEntity; import lombok.Data; import lombok.EqualsAndHashCode; @@ -17,50 +18,45 @@ import java.time.LocalDateTime; @TableName("调课申请_学员队") public class DKSQXYD extends BaseEntity { - /** - * 编号 - */ @TableId(value = "编号", type = IdType.INPUT) private String bh; - /** - * 调课申请编号 - */ + @TableField("调课申请编号") private String dksqbh; - /** - * 学员队编号 - */ + @TableField("学员队编号") private String xydbh; - /** - * 查收状态 - */ + @TableField("查收状态") private Integer cszt; - /** - * 查收时间 - */ + @TableField("查收时间") private LocalDateTime cssj; - /** - * 移除状态 - */ + @TableField("移除状态") private Integer yczt; - /** - * 年度 - */ + @TableField("年度") private Integer nd; - /** - * 日期 - */ + @TableField("日期") private LocalDateTime rq; - /** - * 节次 - */ + @TableField("节次") private Integer jc; + @TableField(exist = false) + private LocalDateTime createTime; + + @TableField(exist = false) + private LocalDateTime updateTime; + + @TableField(exist = false) + private String createBy; + + @TableField(exist = false) + private String updateBy; + + @TableField(exist = false) + private Integer delFlag; } diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/SSDKSQ.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/SSDKSQ.java index b2cb9cc3..6fc9bbe8 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/SSDKSQ.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/SSDKSQ.java @@ -57,16 +57,6 @@ public class SSDKSQ extends BaseEntity { @TableField("教研室批准人编号") private String jyspzrbh; - /** 原表无此列 */ - @TableField(exist = false) - private Integer jxxspzzt; - - @TableField(exist = false) - private LocalDateTime jxxspzsj; - - @TableField(exist = false) - private String jxxspzrbh; - @TableField("备注") private String bz; diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/SSDKSQSP.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/SSDKSQSP.java deleted file mode 100644 index 1a7f64a1..00000000 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/SSDKSQSP.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.roomroot.jwgl.entity; - -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; -import com.roomroot.jwgl.unit.BaseEntity; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.time.LocalDateTime; - -/** - * 实施_调课申请审批 - */ -@Data -@EqualsAndHashCode(callSuper = true) -@TableName("实施_调课申请审批") -public class SSDKSQSP extends BaseEntity { - - /** - * 编号 - */ - @TableId(value = "编号", type = IdType.INPUT) - private String bh; - - /** - * 实施_调课申请编号 - */ - private String ssdksqbh; - - /** - * 机关编号 - */ - private String jgbh; - - /** - * 创建时间 - */ - private LocalDateTime cjsj; - - /** - * 审批时间 - */ - private LocalDateTime spsj; - - /** - * 审批状态 - */ - private Integer spzt; - - /** - * 审批人编号 - */ - private String sprbh; - - /** - * 查收状态 - */ - private Integer cszt; - - /** - * 查收时间 - */ - private LocalDateTime cssj; - - /** - * 查收人编号 - */ - private String csrbh; - - /** - * 发回意见 - */ - private String fhyj; - - /** - * 年度 - */ - private Integer nd; - -} diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/SSKC.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/SSKC.java index da7c9efb..883c299b 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/SSKC.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/SSKC.java @@ -5,7 +5,9 @@ import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableField; import com.fasterxml.jackson.annotation.JsonFormat; +import com.roomroot.jwgl.handler.DmGuidTypeHandler; import lombok.Data; +import org.apache.ibatis.type.JdbcType; import java.time.LocalDateTime; @@ -13,13 +15,14 @@ import java.time.LocalDateTime; * 实施_课程 */ @Data -@TableName(value = "实施_课程") +@TableName(value = "实施_课程", autoResultMap = true) public class SSKC { /** - * 编号 + * 编号(库内 VARBINARY(16),Java 侧按 36 位 UUID 字符串使用) */ @TableId(value = "编号", type = IdType.INPUT) + @TableField(value = "编号", jdbcType = JdbcType.VARBINARY, typeHandler = DmGuidTypeHandler.class) private String bh; /** diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/ScheduleAdjustStrategy.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/ScheduleAdjustStrategy.java deleted file mode 100644 index 953a6e16..00000000 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/entity/ScheduleAdjustStrategy.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.roomroot.jwgl.entity; - -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 java.time.LocalDateTime; - -/** - * 实施_调课策略 - *

- * 管理调课的时间段限制策略与免审批策略。 - * 策略类型为 TIME_LIMIT 时,用于限制教员在规定时间后不得调整下周课程; - * 策略类型为 AUTO_APPROVE 时,用于在生效范围内自动通过调课申请,跳过三级审批。 - *

- */ -@Data -@TableName("实施_调课策略") -public class ScheduleAdjustStrategy { - - /** 策略编号 */ - @TableId(value = "策略编号", type = IdType.INPUT) - private String clbh; - - /** 策略名称 */ - @TableField("策略名称") - private String clmc; - - /** - * 策略类型:TIME_LIMIT=时间段限制,AUTO_APPROVE=免审批 - */ - @TableField("策略类型") - private String cllx; - - /** 是否启用:0=停用,1=启用 */ - @TableField("是否启用") - private Integer sfqy; - - /** - * 截止星期:0-6(周日到周六),仅 TIME_LIMIT 类型使用。 - * 含义:当前时间超过该星期时,不允许调整下周课程。为空表示不限制星期。 - */ - @TableField("截止星期") - private Integer jzxq; - - /** - * 截止节次:仅 TIME_LIMIT 类型使用。 - * 含义:到达截止星期当天的该节次后不允许调整下周课程。为空表示不限制节次。 - */ - @TableField("截止节次") - private Integer jzjc; - - /** 生效开始时间(包含),为空表示不限制开始时间 */ - @TableField("生效开始时间") - private LocalDateTime sxkssj; - - /** 生效结束时间(包含),为空表示不限制结束时间 */ - @TableField("生效结束时间") - private LocalDateTime sxjssj; - - /** 备注 */ - @TableField("备注") - private String bz; - - /** 创建时间 */ - @TableField("创建时间") - private LocalDateTime cjsj; - - /** 更新时间 */ - @TableField("更新时间") - private LocalDateTime gxsj; -} diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/handler/DmGuidTypeHandler.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/handler/DmGuidTypeHandler.java new file mode 100644 index 00000000..50e94b33 --- /dev/null +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/handler/DmGuidTypeHandler.java @@ -0,0 +1,41 @@ +package com.roomroot.jwgl.handler; + +import com.roomroot.jwgl.utils.UuidUtil; +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * 达梦 VARBINARY(16) GUID 与 Java 字符串互转。 + *

+ * 仅通过实体 / resultMap 显式引用,避免把其它 VARBINARY 字段都当成 UUID。 + * 禁止把带横线的 CHAR UUID 直接绑定到 VARBINARY,达梦会按十六进制解析并报 -6147。 + *

+ */ +public class DmGuidTypeHandler extends BaseTypeHandler { + + @Override + public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) + throws SQLException { + ps.setBytes(i, UuidUtil.toBytes(parameter)); + } + + @Override + public String getNullableResult(ResultSet rs, String columnName) throws SQLException { + return UuidUtil.fromBytes(rs.getBytes(columnName)); + } + + @Override + public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException { + return UuidUtil.fromBytes(rs.getBytes(columnIndex)); + } + + @Override + public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { + return UuidUtil.fromBytes(cs.getBytes(columnIndex)); + } +} diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/interceptor/JwglApiAccessInterceptor.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/interceptor/JwglApiAccessInterceptor.java index b3d40745..3f39dff6 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/interceptor/JwglApiAccessInterceptor.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/interceptor/JwglApiAccessInterceptor.java @@ -62,27 +62,18 @@ public class JwglApiAccessInterceptor implements HandlerInterceptor { if (isTeacherDenied(path)) { return deny(response, "教员无权访问该功能"); } - if (isTeacherAdjustDenied(path, request.getMethod())) { - return deny(response, "教员只能提交、查看和撤销本人的调课申请"); - } return true; } if (roleHelper.isResearchOffice()) { if (isOfficeDenied(path)) { return deny(response, "教研室无权访问系统管理功能"); } - if (isResearchOfficeAdjustDenied(path)) { - return deny(response, "教研室仅可查看和审批本室调课申请"); - } return true; } if (roleHelper.isDepartmentPersonnel()) { if (isOfficeDenied(path)) { return deny(response, "机关人员无权访问系统管理功能"); } - if (isOfficeAdjustDenied(path, request.getMethod())) { - return deny(response, "机关人员仅可查看和审批调课申请"); - } return true; } return true; @@ -126,47 +117,6 @@ public class JwglApiAccessInterceptor implements HandlerInterceptor { "/system/notice"); } - private boolean isTeacherAdjustDenied(String path, String method) { - if (!path.startsWith("/course-running/adjust")) { - return false; - } - if (path.startsWith("/course-running/adjust/audit") - || path.startsWith("/course-running/adjust/receive")) { - return true; - } - if (path.equals("/course-running/adjust/submit")) { - return !HttpMethod.POST.matches(method); - } - return false; - } - - private boolean isResearchOfficeAdjustDenied(String path) { - if (!path.startsWith("/course-running/adjust")) { - return false; - } - if (path.equals("/course-running/adjust/submit") - || path.equals("/course-running/adjust/cancel") - || path.startsWith("/course-running/adjust/lessons") - || path.startsWith("/course-running/adjust/audit/jg") - || path.startsWith("/course-running/adjust/audit/jxx") - || path.startsWith("/course-running/adjust/receive/jg")) { - return true; - } - return false; - } - - private boolean isOfficeAdjustDenied(String path, String method) { - if (!path.startsWith("/course-running/adjust")) { - return false; - } - if (HttpMethod.GET.matches(method)) { - return path.startsWith("/course-running/adjust/lessons") - || path.startsWith("/course-running/adjust/strategy"); - } - return !(path.startsWith("/course-running/adjust/audit/jg") - || path.startsWith("/course-running/adjust/receive/jg")); - } - private boolean isTeacherDenied(String path) { return startsWithAny(path, "/faculty", diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/DKSQBZMXMapper.xml b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/DKSQBZMXMapper.xml index 4a544ab1..091b5f55 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/DKSQBZMXMapper.xml +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/DKSQBZMXMapper.xml @@ -19,7 +19,7 @@ a.数量, a.备注 FROM "调课申请_保障明细" a - LEFT JOIN "保障提供明细表" m ON a.保障提供明细编号 = m.编号 + LEFT JOIN "保障提供明细" m ON TRIM(m.编号) = TRIM(a.保障提供明细编号) WHERE a.申请编号 = #{dksqbh} ORDER BY a.编号 diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/DKSQFZJYMapper.xml b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/DKSQFZJYMapper.xml index 78943a05..ff5430be 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/DKSQFZJYMapper.xml +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/DKSQFZJYMapper.xml @@ -15,11 +15,11 @@ SELECT a.编号, a.辅助教员编号, - t.姓名 AS 辅助教员姓名, + t.教员姓名 AS 辅助教员姓名, a.主教员, a.备注 FROM "调课申请_辅助教员" a - LEFT JOIN "教员表" t ON a.辅助教员编号 = t.编号 + LEFT JOIN "教员表" t ON TRIM(t.教员编号) = TRIM(a.辅助教员编号) WHERE a.调课申请编号 = #{dksqbh} ORDER BY a.编号 diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/DKSQJSMapper.xml b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/DKSQJSMapper.xml index 3fb2f2d9..8865aa14 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/DKSQJSMapper.xml +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/DKSQJSMapper.xml @@ -17,7 +17,7 @@ r.教室名称, a.备注 FROM "调课申请_教室" a - LEFT JOIN "教室表" r ON a.教室编号 = r.编号 + LEFT JOIN "教室表" r ON TRIM(r.教室编号) = TRIM(a.教室编号) WHERE a.调课申请编号 = #{dksqbh} ORDER BY a.编号 diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/DKSQXYDMapper.xml b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/DKSQXYDMapper.xml index c2480439..551298d5 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/DKSQXYDMapper.xml +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/DKSQXYDMapper.xml @@ -15,7 +15,7 @@ a.学员队编号, d.学员队名称 FROM "调课申请_学员队" a - LEFT JOIN "学员队表" d ON a.学员队编号 = d.编号 + LEFT JOIN "学员队表" d ON TRIM(d.学员队编号) = TRIM(a.学员队编号) WHERE a.调课申请编号 = #{dksqbh} ORDER BY a.编号 diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/KcbTimetableMapper.xml b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/KcbTimetableMapper.xml index a9a48b24..4567c9f1 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/KcbTimetableMapper.xml +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/KcbTimetableMapper.xml @@ -21,7 +21,7 @@ AND EXISTS ( SELECT 1 FROM "实施_课程学员队" cxf - WHERE cxf."实施_课程编号" = c."编号" + WHERE LOWER(RAWTOHEX(c."编号")) = LOWER(REPLACE(TRIM(cxf."实施_课程编号"), '-', '')) AND cxf."学期第次" = #{cond.xqdc} ) @@ -49,7 +49,7 @@ AND kcb."课次序号" = c."教员课次序号") )) AS bz FROM "实施_课程表" s - LEFT JOIN "实施_课程" c ON CAST(c."编号" AS CHAR(36)) = TRIM(s."实施_课程编号") + LEFT JOIN "实施_课程" c ON LOWER(RAWTOHEX(c."编号")) = LOWER(REPLACE(TRIM(s."实施_课程编号"), '-', '')) LEFT JOIN "课表" k ON (k."课编号" = c."科目编号" OR k."科目代码" = c."科目编号") LEFT JOIN "教研室表" jys ON jys."教研室代号" = k."教研室代号" LEFT JOIN "教员表" jy ON jy."教员编号" = c."教员编号" @@ -93,7 +93,13 @@ SELECT - c."编号" AS sskcbh, + LOWER( + SUBSTR(RAWTOHEX(c."编号"), 1, 8) || '-' || + SUBSTR(RAWTOHEX(c."编号"), 9, 4) || '-' || + SUBSTR(RAWTOHEX(c."编号"), 13, 4) || '-' || + SUBSTR(RAWTOHEX(c."编号"), 17, 4) || '-' || + SUBSTR(RAWTOHEX(c."编号"), 21, 12) + ) AS sskcbh, (NVL(k."课名称", c."科目编号") || ' / ' || NVL(TO_CHAR(c."课程课时系数"), '-')) AS kcmcksxs, @@ -101,7 +107,7 @@ || ' / ' || NVL(TO_CHAR(c."教员课次序号"), TO_CHAR((SELECT COUNT(1) FROM "实施_课程表" t - WHERE CAST(c."编号" AS CHAR(36)) = TRIM(t."实施_课程编号") + WHERE LOWER(RAWTOHEX(c."编号")) = LOWER(REPLACE(TRIM(t."实施_课程编号"), '-', '')) AND COALESCE(t."删除状态", 0) = 0)))) AS zrjykc, c."学时" AS jhxs, c."运行学时" AS yxxs, @@ -113,15 +119,15 @@ FROM "实施_课程表" t LEFT JOIN "实施_课程表_教室" jsx ON jsx."实施_课程表编号" = t."编号" LEFT JOIN "教室表" js ON js."教室编号" = jsx."教室编号" - WHERE CAST(c."编号" AS CHAR(36)) = TRIM(t."实施_课程编号") + WHERE LOWER(RAWTOHEX(c."编号")) = LOWER(REPLACE(TRIM(t."实施_课程编号"), '-', '')) AND COALESCE(t."删除状态", 0) = 0), NVL(js0."教室名称", '-'))) AS rscd, (SELECT WM_CONCAT(DISTINCT sq."事由") FROM "实施_调课申请" sq INNER JOIN "实施_课程表" t ON t."编号" = sq."实施_课程表编号" - WHERE CAST(c."编号" AS CHAR(36)) = TRIM(t."实施_课程编号")) AS ssjhbg + WHERE LOWER(RAWTOHEX(c."编号")) = LOWER(REPLACE(TRIM(t."实施_课程编号"), '-', ''))) AS ssjhbg FROM "实施_课程" c - INNER JOIN "实施_课程学员队" cx ON cx."实施_课程编号" = c."编号" + INNER JOIN "实施_课程学员队" cx ON LOWER(RAWTOHEX(c."编号")) = LOWER(REPLACE(TRIM(cx."实施_课程编号"), '-', '')) LEFT JOIN "学员队表" xyd ON xyd."学员队编号" = cx."学员队编号" LEFT JOIN "课表" k ON (k."课编号" = c."科目编号" OR k."科目代码" = c."科目编号") LEFT JOIN "教员表" zjy ON zjy."教员编号" = c."责任教员编号" diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSDKSQMapper.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSDKSQMapper.java index 52b86275..66787614 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSDKSQMapper.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSDKSQMapper.java @@ -30,7 +30,6 @@ public interface SSDKSQMapper extends BaseMapper { @Param("nd") Integer nd, @Param("jyspzzt") Integer jyspzzt, @Param("jyscszt") Integer jyscszt, - @Param("jgspzt") Integer jgspzt, @Param("sczt") Integer sczt, @Param("cjsjStart") String cjsjStart, @Param("cjsjEnd") String cjsjEnd); diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSDKSQMapper.xml b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSDKSQMapper.xml index 80624721..2cf0f29f 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSDKSQMapper.xml +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSDKSQMapper.xml @@ -22,7 +22,6 @@ - @@ -57,6 +56,7 @@ + @@ -103,17 +103,11 @@ a.创建时间, a.教研室批准状态, a.教研室查收状态, - ( - SELECT MAX(sp.审批状态) - FROM "实施_调课申请审批" sp - WHERE sp.实施_调课申请编号 = a.编号 - AND sp.审批状态 IS NOT NULL - ) AS 机关审批状态, a.发回意见, a.删除状态 FROM "实施_调课申请" a LEFT JOIN "实施_课程表" b ON TRIM(b.编号) = TRIM(a.实施_课程表编号) - LEFT JOIN "实施_课程" e ON CAST(e.编号 AS CHAR(36)) = TRIM(b.实施_课程编号) + LEFT JOIN "实施_课程" e ON LOWER(RAWTOHEX(e."编号")) = LOWER(REPLACE(TRIM(b.实施_课程编号), '-', '')) LEFT JOIN "课表" kb ON kb.课编号 = COALESCE(NULLIF(TRIM(a.课编号), ''), e.科目编号) LEFT JOIN "教员表" j ON TRIM(j.教员编号) = TRIM(a.申请教员编号) LEFT JOIN "课程表" kcb ON TRIM(kcb.编号) = TRIM(a.课程表编号) @@ -142,14 +136,6 @@ AND a.教研室查收状态 = #{jyscszt} - - AND ( - SELECT MAX(sp.审批状态) - FROM "实施_调课申请审批" sp - WHERE sp.实施_调课申请编号 = a.编号 - AND sp.审批状态 IS NOT NULL - ) = #{jgspzt} - AND a.删除状态 = #{sczt} @@ -197,10 +183,11 @@ a.教研室批准人编号, a.教研室查收状态, a.教研室查收时间, - a.教研室查收人编号 + a.教研室查收人编号, + a.发回意见 FROM "实施_调课申请" a LEFT JOIN "实施_课程表" b ON TRIM(b.编号) = TRIM(a.实施_课程表编号) - LEFT JOIN "实施_课程" e ON CAST(e.编号 AS CHAR(36)) = TRIM(b.实施_课程编号) + LEFT JOIN "实施_课程" e ON LOWER(RAWTOHEX(e."编号")) = LOWER(REPLACE(TRIM(b.实施_课程编号), '-', '')) LEFT JOIN "课表" kb ON kb.课编号 = COALESCE(NULLIF(TRIM(a.课编号), ''), e.科目编号) LEFT JOIN "教员表" j ON TRIM(j.教员编号) = TRIM(a.申请教员编号) WHERE a.编号 = #{ssdksqbh} @@ -252,7 +239,7 @@ AND kcb.年度 = e.年度 ) AS 课程表编号 FROM "实施_课程表" b - JOIN "实施_课程" e ON CAST(e.编号 AS CHAR(36)) = TRIM(b.实施_课程编号) + JOIN "实施_课程" e ON LOWER(RAWTOHEX(e."编号")) = LOWER(REPLACE(TRIM(b.实施_课程编号), '-', '')) LEFT JOIN "课表" kb ON kb.课编号 = e.科目编号 LEFT JOIN "教员表" jy ON TRIM(jy.教员编号) = TRIM(e.教员编号) WHERE COALESCE(b.删除状态, 0) = 0 diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSDKSQSPMapper.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSDKSQSPMapper.java deleted file mode 100644 index 5000e557..00000000 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSDKSQSPMapper.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.roomroot.jwgl.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.roomroot.jwgl.entity.SSDKSQSP; -import com.roomroot.jwgl.vo.courserunning.ScheduleAdjustAuditVO; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -/** - * 实施_调课申请审批 Mapper。 - *

- * 提供调课申请审批记录数据的CRUD操作及自定义查询。 - *

- */ -@Mapper -public interface SSDKSQSPMapper extends BaseMapper { - - /** - * 根据调课申请编号查询审批记录列表。 - *

- * 关联机构表获取机关名称,关联教员表获取审批人姓名。 - *

- * - * @param ssdksqbh 调课申请编号 - * @return 审批记录列表 - */ - List selectBySsdksqbh(@Param("ssdksqbh") String ssdksqbh); -} diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSDKSQSPMapper.xml b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSDKSQSPMapper.xml deleted file mode 100644 index e17421a9..00000000 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSDKSQSPMapper.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSKCBFZJYMapper.xml b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSKCBFZJYMapper.xml index cf2781ec..1622959d 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSKCBFZJYMapper.xml +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSKCBFZJYMapper.xml @@ -61,7 +61,7 @@ * 内层:按教员编号+日期+节次 GROUP BY,筛选出现>1课次的冲突 * * 关键修正: - * 1. 关联链路:SSKCB → 实施_课程 c → 课表 k;必须 CAST(c.编号 AS CHAR(36)) + * 1. 关联链路:SSKCB → 实施_课程 c → 课表 k;VARBINARY 主键用 RAWTOHEX 对齐 CHAR 外键 * 2. 主讲/辅讲字段:真实列名「主教员」(1主/0辅) * 3. 日期节次:统一取主表 s * @@ -92,7 +92,7 @@ /* 维度过滤主关联:只看该教员的课次(GROUP BY 维度)*/ INNER JOIN "实施_课程表_辅助教员" f ON f."实施_课程表编号" = s."编号" /* 课表信息三层关联 */ - LEFT JOIN "实施_课程" c ON CAST(c."编号" AS CHAR(36)) = s."实施_课程编号" + LEFT JOIN "实施_课程" c ON LOWER(RAWTOHEX(c."编号")) = LOWER(REPLACE(TRIM(s."实施_课程编号"), '-', '')) LEFT JOIN "课表" k ON k."科目代码" = c."科目编号" /* ========== 新增 JOIN:聚合该冲突课次的班次/教员/场地所有信息 ========== */ LEFT JOIN "实施_课程表_学员队" xyd_link ON xyd_link."实施_课程表编号" = s."编号" diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSKCBJSMapper.xml b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSKCBJSMapper.xml index dbe611a7..db27329e 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSKCBJSMapper.xml +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSKCBJSMapper.xml @@ -62,7 +62,7 @@ * 内层:按教室编号+日期+节次 GROUP BY,筛选 COUNT(DISTINCT 课次) > 1 * * 关键修正: - * 1. 三层关联 JOIN:SSKCB → 实施_课程 → 课表;CAST(ss.编号 AS CHAR(36)) + * 1. 三层关联 JOIN:SSKCB → 实施_课程 → 课表;VARBINARY 主键用 RAWTOHEX 对齐 CHAR 外键 * 2. 日期节次统一取主表 s * * 新增 JOIN(同其他维度):聚合该冲突课次的班次/教员/场地所有明细信息 @@ -88,7 +88,7 @@ /* 维度过滤主关联:教室维度分组 */ INNER JOIN "实施_课程表_教室" c ON c."实施_课程表编号" = s."编号" /* 课表信息三层关联(ss=实施_课程别名,避免与聚合JOIN的别名冲突)*/ - LEFT JOIN "实施_课程" ss ON CAST(ss."编号" AS CHAR(36)) = s."实施_课程编号" + LEFT JOIN "实施_课程" ss ON LOWER(RAWTOHEX(ss."编号")) = LOWER(REPLACE(TRIM(s."实施_课程编号"), '-', '')) LEFT JOIN "课表" k ON k."科目代码" = ss."科目编号" /* ========== 新增 JOIN:聚合该冲突课次的班次/教员/场地所有信息 ========== */ LEFT JOIN "实施_课程表_学员队" xyd_link ON xyd_link."实施_课程表编号" = s."编号" diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSKCBMapper.xml b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSKCBMapper.xml index 753db9ca..c39294bb 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSKCBMapper.xml +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSKCBMapper.xml @@ -60,7 +60,7 @@ b.创建时间, b.变动时间 FROM "实施_课程表" b - LEFT JOIN "实施_课程" a ON b.实施_课程编号 = a.编号 + LEFT JOIN "实施_课程" a ON LOWER(RAWTOHEX(a."编号")) = LOWER(REPLACE(TRIM(b.实施_课程编号), '-', '')) LEFT JOIN "课程表" c ON a.课编号 = c.课编号 WHERE b.删除状态 = 0 @@ -116,7 +116,7 @@ b.创建时间, b.变动时间 FROM "实施_课程表" b - LEFT JOIN "实施_课程" a ON b.实施_课程编号 = a.编号 + LEFT JOIN "实施_课程" a ON LOWER(RAWTOHEX(a."编号")) = LOWER(REPLACE(TRIM(b.实施_课程编号), '-', '')) LEFT JOIN "课程表" c ON a.课编号 = c.课编号 WHERE b.编号 = #{sskcbbh} AND b.删除状态 = 0 diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSKCBXYDMapper.xml b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSKCBXYDMapper.xml index 7047f0e7..536556b6 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSKCBXYDMapper.xml +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSKCBXYDMapper.xml @@ -32,10 +32,11 @@ JOIN "实施_课程表_学员队" t ON t."实施_课程表编号" = s."编号" - LEFT JOIN "实施_课程" c ON CAST(c."编号" AS CHAR(36)) = s."实施_课程编号" + LEFT JOIN "实施_课程" c ON LOWER(RAWTOHEX(c."编号")) = LOWER(REPLACE(TRIM(s."实施_课程编号"), '-', '')) LEFT JOIN "课表" k ON k."科目代码" = c."科目编号" WHERE t."学员队编号" = #{xydbh} @@ -126,7 +127,7 @@ /* 维度过滤用:主关联"教学班(学员队)" —— 教学班冲突就是按这个维度 GROUP BY */ INNER JOIN "实施_课程表_学员队" t ON t."实施_课程表编号" = s."编号" /* 课表信息三层关联 */ - LEFT JOIN "实施_课程" c ON CAST(c."编号" AS CHAR(36)) = s."实施_课程编号" + LEFT JOIN "实施_课程" c ON LOWER(RAWTOHEX(c."编号")) = LOWER(REPLACE(TRIM(s."实施_课程编号"), '-', '')) LEFT JOIN "课表" k ON k."科目代码" = c."科目编号" /* ========== 新增 JOIN:聚合该冲突课次涉及的所有班次/教员/教室信息 ========== */ LEFT JOIN "实施_课程表_学员队" xyd_link ON xyd_link."实施_课程表编号" = s."编号" @@ -206,7 +207,7 @@ WM_CONCAT(DISTINCT xyd_info."所属单位") AS responsible_dept_list FROM "实施_课程表" s INNER JOIN "实施_课程表_学员队" t ON t."实施_课程表编号" = s."编号" - LEFT JOIN "实施_课程" c ON CAST(c."编号" AS CHAR(36)) = s."实施_课程编号" + LEFT JOIN "实施_课程" c ON LOWER(RAWTOHEX(c."编号")) = LOWER(REPLACE(TRIM(s."实施_课程编号"), '-', '')) LEFT JOIN "课表" k ON k."科目代码" = c."科目编号" /* ========== 新增 JOIN:三类业务信息 ========== */ LEFT JOIN "实施_课程表_学员队" xyd_link ON xyd_link."实施_课程表编号" = s."编号" diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSKCMapper.xml b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSKCMapper.xml index 307ca4cf..7229643d 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSKCMapper.xml +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSKCMapper.xml @@ -2,20 +2,32 @@ - SELECT * FROM 实施_课程 WHERE 年度 = #{nd} AND 学期第次 = #{xqdc} AND 停用 = 0 - SELECT DISTINCT sc.* FROM 实施_课程 sc - INNER JOIN 实施_课程学员队 scxd ON sc.编号 = scxd.实施_课程编号 + INNER JOIN 实施_课程学员队 scxd + ON LOWER(RAWTOHEX(sc."编号")) = LOWER(REPLACE(TRIM(scxd."实施_课程编号"), '-', '')) WHERE scxd.学员队编号 = #{classBh} AND sc.年度 = #{nd} AND sc.停用 = 0 - SELECT sc.* FROM 实施_课程 sc - INNER JOIN 实施_课程学员队 scxd ON sc.编号 = scxd.实施_课程编号 + INNER JOIN 实施_课程学员队 scxd + ON LOWER(RAWTOHEX(sc."编号")) = LOWER(REPLACE(TRIM(scxd."实施_课程编号"), '-', '')) WHERE sc.课编号 = #{courseBh} AND scxd.学员队编号 = #{classBh} AND sc.年度 = #{nd} AND sc.停用 = 0 diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/ScheduleAdjustStrategyMapper.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/ScheduleAdjustStrategyMapper.java deleted file mode 100644 index ddca3f58..00000000 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/ScheduleAdjustStrategyMapper.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.roomroot.jwgl.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.roomroot.jwgl.entity.ScheduleAdjustStrategy; -import org.apache.ibatis.annotations.Mapper; - -/** - * 实施_调课策略 Mapper。 - *

- * 提供调课策略的基础 CRUD 能力,时间段限制与免审批策略的匹配查询 - * 由 Service 层调用 BaseMapper 方法完成。 - *

- */ -@Mapper -public interface ScheduleAdjustStrategyMapper extends BaseMapper { -} diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/TestPkMapper.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/TestPkMapper.java index 7e5e9d07..6954d5e0 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/TestPkMapper.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/TestPkMapper.java @@ -1,12 +1,14 @@ package com.roomroot.jwgl.mapper; +import com.roomroot.jwgl.vo.jwtest.PkConflictVO; import com.roomroot.jwgl.vo.jwtest.PkLessonVO; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; import java.util.List; /** - * 排课测试全查。 + * 排课测试全查与冲突检查。 */ @Mapper public interface TestPkMapper { @@ -15,4 +17,24 @@ public interface TestPkMapper { * 查询全部排课记录,无筛选条件。 */ List selectAll(); + + /** + * 同一班次同一日期节次被安排多门课程。 + */ + List selectClassTimeConflict(@Param("nd") Integer nd); + + /** + * 同一班次同一日期节次必修课与选修课重叠。 + */ + List selectElectiveRequiredConflict(@Param("nd") Integer nd); + + /** + * 同一教员同一日期节次被安排多门课程。 + */ + List selectTeacherTimeConflict(@Param("nd") Integer nd); + + /** + * 同一教室同一日期节次被多门课程占用。 + */ + List selectClassroomTimeConflict(@Param("nd") Integer nd); } diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/TestPkMapper.xml b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/TestPkMapper.xml index eaecdd55..864eff14 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/TestPkMapper.xml +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/TestPkMapper.xml @@ -39,8 +39,8 @@ INNER JOIN "实施_课程表" s ON TRIM(s."编号") = TRIM(tx."实施_课程表编号") LEFT JOIN "实施_课程" c - ON REPLACE(LOWER(TRIM(CAST(c."编号" AS CHAR(36)))), '-', '') - = REPLACE(LOWER(TRIM(s."实施_课程编号")), '-', '') + ON LOWER(RAWTOHEX(c."编号")) + = LOWER(REPLACE(TRIM(s."实施_课程编号"), '-', '')) LEFT JOIN "课表" k ON k."课编号" = c."科目编号" LEFT JOIN "课程表" kcb @@ -64,4 +64,247 @@ GROUP BY s."编号", s."日期", s."节次", s."年度" + + + SELECT + s."编号" AS kcbh, + TO_CHAR(s."日期", 'YYYY-MM-DD') AS skrq, + s."日期" AS rq, + s."节次" AS jc, + MAX(jc."开始时间") AS kssj, + MAX(jc."结束时间") AS jssj, + COALESCE(NULLIF(TRIM(MAX(s."教学内容")), ''), MAX(k."课名称")) AS ktmc, + MAX(k."课名称") AS kcmc, + MAX(NVL(k."课程类型", NVL(k."课类型", c."课类型"))) AS kclx, + COALESCE(WM_CONCAT(DISTINCT jyf."教员姓名"), MAX(jy."教员姓名")) AS jymc, + WM_CONCAT(DISTINCT xyd."学员队名称") AS bjmc, + COALESCE(WM_CONCAT(DISTINCT jsb."教室名称"), MAX(js."教室名称")) AS jsmc + FROM "实施_课程表" s + LEFT JOIN "实施_课程" c + ON LOWER(RAWTOHEX(c."编号")) = LOWER(REPLACE(TRIM(s."实施_课程编号"), '-', '')) + LEFT JOIN "课表" k + ON (k."课编号" = c."科目编号" OR k."科目代码" = c."科目编号") + LEFT JOIN "课程表" kcb + ON kcb."年度" = s."年度" + AND kcb."安排时间" = s."计划时间安排" + AND TRIM(kcb."教员编号") = TRIM(c."教员编号") + LEFT JOIN "教员表" jy + ON TRIM(jy."教员编号") = TRIM(NVL(kcb."授课教员编号", c."教员编号")) + LEFT JOIN "实施_课程表_辅助教员" fz ON TRIM(fz."实施_课程表编号") = TRIM(s."编号") + LEFT JOIN "教员表" jyf ON TRIM(jyf."教员编号") = TRIM(fz."辅助教员编号") + LEFT JOIN "实施_课程表_学员队" tx ON TRIM(tx."实施_课程表编号") = TRIM(s."编号") + LEFT JOIN "学员队表" xyd ON xyd."学员队编号" = tx."学员队编号" + LEFT JOIN "实施_课程表_教室" jsx ON TRIM(jsx."实施_课程表编号") = TRIM(s."编号") + LEFT JOIN "教室表" jsb ON jsb."教室编号" = jsx."教室编号" + LEFT JOIN "教室表" js ON js."教室编号" = NVL(kcb."教室编号", c."教室编号") + LEFT JOIN "节次时间表" jc ON TRIM(jc."节次索引") = TRIM(CAST(s."节次" AS VARCHAR)) + WHERE NVL(s."删除状态", 0) = 0 + + AND s."年度" = #{nd} + + GROUP BY s."编号", s."日期", s."节次" + + + + + + + + + + + + + +
diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/XYJXQJBMapper.xml b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/XYJXQJBMapper.xml index e2e3baa8..ea4c549e 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/XYJXQJBMapper.xml +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/XYJXQJBMapper.xml @@ -216,7 +216,7 @@ LEFT JOIN "教员表" j ON j."教员编号" = sp."审批人编号" LEFT JOIN "请假课堂表" qjkt ON qjkt."请假编号" = q."编号" LEFT JOIN "实施_课程表" sskcb ON sskcb."编号" = qjkt."实施_课堂编号" - LEFT JOIN "实施_课程" sskc ON sskcb."实施_课程编号" = sskc."编号" + LEFT JOIN "实施_课程" sskc ON LOWER(RAWTOHEX(sskc."编号")) = LOWER(REPLACE(TRIM(sskcb."实施_课程编号"), '-', '')) LEFT JOIN "课表" kb ON sskc."科目编号" = kb."课编号" WHERE q."编号" = #{qjbh} diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/CourseRunningImportService.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/CourseRunningImportService.java index 6d1e4cd4..2c367a14 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/CourseRunningImportService.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/CourseRunningImportService.java @@ -18,7 +18,6 @@ import com.roomroot.jwgl.dto.courserunning.StudentLeaveApplyDTO; import com.roomroot.jwgl.dto.courserunning.StudentLeaveAuditDTO; import com.roomroot.jwgl.dto.courserunning.StudentLeaveQueryDTO; import com.roomroot.jwgl.dto.courserunning.OperationAnalysisQueryDTO; -import com.roomroot.jwgl.dto.courserunning.ScheduleAdjustStrategyDTO; import com.roomroot.jwgl.unit.PageResult; import com.roomroot.jwgl.vo.courserunning.AdjustableLessonVO; import com.roomroot.jwgl.vo.courserunning.CourseRunningImportResultVO; @@ -37,7 +36,6 @@ import com.roomroot.jwgl.vo.courserunning.OperationOverviewVO; import com.roomroot.jwgl.vo.courserunning.CourseCompletionByDeptVO; import com.roomroot.jwgl.vo.courserunning.VenueUsageRateVO; import com.roomroot.jwgl.vo.courserunning.ScheduleAdjustTrendVO; -import com.roomroot.jwgl.vo.courserunning.ScheduleAdjustStrategyVO; import java.util.List; @@ -215,101 +213,23 @@ public interface CourseRunningImportService { /** * 教研室审批调课申请。 *

- * 教研室审批教员提交的调课申请,包含同意、发回、拒绝三种处理方式。 - * 同意时同步更新课次数据,发回时记录发回原因,拒绝时标记申请为已拒绝。 + * 教研室审批调课申请。点审批即已查收,审批人同时记为查收人。 + * 同意后写回课次;发回、拒绝也记为已查收。 *

* * @param dto 教研室审批参数 */ void auditByTeachingOffice(ScheduleAdjustAuditDTO dto); - /** - * 教研室查收调课申请。 - *

- * 教研室查收调课申请,标记查收状态和查收时间。 - *

- * - * @param ssdksqbh 调课申请编号 - * @param csrbh 查收人编号 - */ - void receiveByTeachingOffice(String ssdksqbh, String csrbh); - - /** - * 教学系审批调课申请。 - *

- * 教学系审批教研室已同意的调课申请,包含同意、发回、拒绝三种处理方式。 - * 教学系同意后流转到机关审批。 - *

- * - * @param dto 教学系审批参数 - */ - void auditByTeachingDepartment(ScheduleAdjustAuditDTO dto); - - /** - * 机关审批调课申请。 - *

- * 机关审批教研室已同意的调课申请,包含同意、发回、拒绝三种处理方式。 - * 机关全部同意后调课申请最终生效,课次按调整后的内容执行。 - *

- * - * @param dto 机关审批参数 - */ - void auditByAuthority(ScheduleAdjustAuditDTO dto); - - /** - * 机关查收调课申请。 - *

- * 机关查收调课申请,标记查收状态和查收时间。 - *

- * - * @param ssdksqbh 调课申请编号 - * @param jgbh 机关编号 - * @param csrbh 查收人编号 - */ - void receiveByAuthority(String ssdksqbh, String jgbh, String csrbh); - /** * 撤销调课申请。 *

- * 教员撤销自己提交的、未经过审批的调课申请。 - * 软删除调课申请,标记删除状态为1。 + * 教员撤销未经过审批的调课申请。列表已按登录教员过滤。 *

* * @param ssdksqbh 调课申请编号 - * @param sqjybh 申请教员编号(用于权限校验) */ - void cancelScheduleAdjust(String ssdksqbh, String sqjybh); - - // ==================== 调课策略管理 ==================== - - /** - * 查询调课策略列表。 - *

- * 支持按策略类型筛选,返回所有策略(含停用)。 - *

- * - * @param cllx 策略类型:TIME_LIMIT/AUTO_APPROVE,为空时查询全部 - * @return 调课策略列表 - */ - List listScheduleAdjustStrategies(String cllx); - - /** - * 保存调课策略(新增或修改)。 - *

- * 策略编号为空时执行新增,非空时执行修改。 - *

- * - * @param dto 策略参数 - * @return 策略编号 - */ - String saveScheduleAdjustStrategy(ScheduleAdjustStrategyDTO dto); - - /** - * 删除调课策略。 - * - * @param clbh 策略编号 - */ - void deleteScheduleAdjustStrategy(String clbh); + void cancelScheduleAdjust(String ssdksqbh); // ==================== 教学保障管理 ==================== diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/FacultyService.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/FacultyService.java index f203db27..f7c50b4c 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/FacultyService.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/FacultyService.java @@ -76,6 +76,16 @@ public interface FacultyService { */ JYB getJYBById(String jYBH); + /** + * 从 Excel(.xls/.xlsx) 导入教员 + */ + int importJYBFromExcel(MultipartFile file) throws Exception; + + /** + * 导出教员到 Excel(.xlsx) + */ + void exportJYBExcel(jakarta.servlet.http.HttpServletResponse response) throws Exception; + /** * 分页条件查询教员列表 */ diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/TrainingProgramService.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/TrainingProgramService.java index 6ab67359..31dccfa1 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/TrainingProgramService.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/TrainingProgramService.java @@ -53,4 +53,10 @@ public interface TrainingProgramService { * @return 分页结果 */ PageResult pageList(PageQuery query, ZYB cond); + + /** 从 Excel(.xls/.xlsx) 导入培养方案(专业) */ + int importZYBFromExcel(org.springframework.web.multipart.MultipartFile file) throws Exception; + + /** 导出培养方案(专业)到 Excel(.xlsx) */ + void exportZYBExcel(jakarta.servlet.http.HttpServletResponse response) throws Exception; } diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/ZYJXJHBService.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/ZYJXJHBService.java index 6fe74b48..1813b9bb 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/ZYJXJHBService.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/ZYJXJHBService.java @@ -60,4 +60,10 @@ public interface ZYJXJHBService { * @return 列表 */ List listByZydhAndTy(String zydh, Integer ty); + + /** 从 Excel(.xls/.xlsx) 导入专业教学计划 */ + int importZYJXJHBFromExcel(org.springframework.web.multipart.MultipartFile file) throws Exception; + + /** 导出专业教学计划到 Excel(.xlsx) */ + void exportZYJXJHBExcel(jakarta.servlet.http.HttpServletResponse response) throws Exception; } diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/CourseRunningImportServiceImpl.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/CourseRunningImportServiceImpl.java index b7563801..7a08bd9e 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/CourseRunningImportServiceImpl.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/CourseRunningImportServiceImpl.java @@ -112,12 +112,6 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic @Resource private KCBMapper kcbMapper; - /** - * 实施_调课申请审批 Mapper,用于调课申请审批记录的数据访问。 - */ - @Resource - private SSDKSQSPMapper ssdksqspMapper; - /** * 调课申请_教室 Mapper,用于调课申请中教室关联的数据访问。 */ @@ -196,12 +190,6 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic @Resource private OperationAnalysisMapper operationAnalysisMapper; - /** - * 调课策略 Mapper,用于调课时间段限制策略与免审批策略的数据访问。 - */ - @Resource - private ScheduleAdjustStrategyMapper scheduleAdjustStrategyMapper; - /** * 教员表数据访问接口,用于审批时根据教员编号查询教研室代号。 */ @@ -533,16 +521,17 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic * @param operationContent 操作内容描述 */ private void recordOperationLog(String targetId, String operationType, String operationContent) { - // 创建日志实体 - KCJXYX_CZRZ log = new KCJXYX_CZRZ(); - log.setBh(UuidUtil.getOriginalUUID()); // 生成唯一编号 - log.setTybh(targetId); // 目标编号 - log.setCzlx(operationType); // 操作类型 - log.setCznr(operationContent); // 操作内容 - log.setCzsj(LocalDateTime.now()); // 操作时间 - - // 写入数据库 - kcjxyxCzrzMapper.insert(log); + try { + KCJXYX_CZRZ log = new KCJXYX_CZRZ(); + log.setBh(UuidUtil.getOriginalUUID()); + log.setTybh(targetId); + log.setCzlx(operationType); + log.setCznr(operationContent); + log.setCzsj(LocalDateTime.now()); + kcjxyxCzrzMapper.insert(log); + } catch (Exception ignored) { + // 操作日志表缺失或字段不匹配时,不影响调课主流程 + } } // ==================== 教学实施计划填写 ==================== @@ -895,7 +884,6 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic query.getNd(), query.getJyspzzt(), query.getJyscszt(), - query.getJgspzt(), query.getSczt(), cjsjStartStr, cjsjEndStr @@ -910,61 +898,22 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic @Override public List listAdjustableLessons(Integer nd) { - if (jwglRoleHelper.isDepartmentPersonnel()) { - throw new BusinessException(BasicManagementConstants.FORBIDDEN, "机关人员仅可查看和审批调课申请"); - } - if (jwglRoleHelper.isResearchOffice()) { - throw new BusinessException(BasicManagementConstants.FORBIDDEN, "教研室仅可查看和审批本室调课申请"); - } - String teacherId = jwglRoleHelper.requireTeacherId(); - return ssdksqMapper.selectAdjustableLessons(teacherId, nd); + return ssdksqMapper.selectAdjustableLessons(jwglRoleHelper.requireTeacherId(), nd); } private void applyScheduleAdjustDataScope(ScheduleAdjustQueryDTO query) { - if (jwglRoleHelper.isAdmin() || jwglRoleHelper.isDepartmentPersonnel()) { - return; - } - if (jwglRoleHelper.isResearchOffice()) { - query.setJysdh(jwglRoleHelper.requireResearchOfficeId()); - query.setSqjybh(null); - return; - } if (jwglRoleHelper.isTeacher()) { query.setSqjybh(jwglRoleHelper.requireTeacherId()); query.setJysdh(null); - return; } - throw new BusinessException(BasicManagementConstants.FORBIDDEN, "无权查看调课申请"); - } - - private void assertCanViewScheduleAdjust(SSDKSQ apply) { - if (jwglRoleHelper.isAdmin() || jwglRoleHelper.isDepartmentPersonnel()) { - return; - } - if (jwglRoleHelper.isResearchOffice()) { - String officeId = jwglRoleHelper.requireResearchOfficeId(); - String applyOffice = apply.getJysdh(); - if (StringUtils.isEmpty(applyOffice) && StringUtils.isNotEmpty(apply.getSqjybh())) { - JYB teacher = jybMapper.selectById(apply.getSqjybh()); - applyOffice = teacher == null ? null : teacher.getJysdh(); - } - if (applyOffice == null || !officeId.equals(applyOffice.trim())) { - throw new BusinessException(BasicManagementConstants.FORBIDDEN, "只能查看本教研室的调课申请"); - } - return; - } - if (jwglRoleHelper.isTeacher()) { - String teacherId = jwglRoleHelper.requireTeacherId(); - if (apply.getSqjybh() == null || !teacherId.equals(apply.getSqjybh().trim())) { - throw new BusinessException(BasicManagementConstants.FORBIDDEN, "只能查看本人提交的调课申请"); - } - return; - } - throw new BusinessException(BasicManagementConstants.FORBIDDEN, "无权查看调课申请"); } private String currentOperatorId() { try { + String teacherId = jwglRoleHelper.findTeacherId(); + if (StringUtils.isNotEmpty(teacherId)) { + return teacherId.trim(); + } String username = SecurityUtils.getUsername(); if (StringUtils.isNotEmpty(username)) { return username; @@ -987,37 +936,17 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic */ @Override public ScheduleAdjustDetailVO getScheduleAdjustDetail(String ssdksqbh) { - // 步骤1:校验调课申请编号参数 if (ssdksqbh == null || ssdksqbh.trim().isEmpty()) { throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "调课申请编号不能为空"); } - - // 步骤2:查询调课申请主表信息 ScheduleAdjustDetailVO detail = ssdksqMapper.selectDetailById(ssdksqbh); if (detail == null) { throw new BusinessException(BasicManagementConstants.NOT_FOUND, "未找到该调课申请"); } - SSDKSQ apply = ssdksqMapper.selectById(ssdksqbh); - if (apply != null) { - assertCanViewScheduleAdjust(apply); - } - - // 步骤3:查询关联的机关审批记录列表 - detail.setAuditList(ssdksqspMapper.selectBySsdksqbh(ssdksqbh)); - - // 步骤4:查询关联的新教室列表 detail.setRoomList(dksqjsMapper.selectByDksqbh(ssdksqbh)); - - // 步骤5:查询关联的新辅助教员列表 detail.setTeacherList(dksqfzjyMapper.selectByDksqbh(ssdksqbh)); - - // 步骤6:查询关联的新学员队列表 detail.setTeamList(dksqxydMapper.selectByDksqbh(ssdksqbh)); - - // 步骤7:查询关联的新保障明细列表 detail.setSupportList(dksqbzmxMapper.selectByDksqbh(ssdksqbh)); - - // 步骤8:返回调课申请完整详情 return detail; } @@ -1036,30 +965,11 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic @Override @Transactional(rollbackFor = Exception.class) public String submitScheduleAdjust(ScheduleAdjustApplyDTO dto) { - if (dto == null) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "调课申请参数不能为空"); - } - if (jwglRoleHelper.isDepartmentPersonnel()) { - throw new BusinessException(BasicManagementConstants.FORBIDDEN, "机关人员仅可查看和审批,不能提交调课申请"); - } - if (jwglRoleHelper.isResearchOffice()) { - throw new BusinessException(BasicManagementConstants.FORBIDDEN, "教研室仅可查看和审批本室申请,不能提交调课申请"); - } - if (StringUtils.isEmpty(dto.getSskcbbh())) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "课次编号不能为空"); - } - if (dto.getSy() == null || dto.getSy().trim().isEmpty()) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "调课事由不能为空"); - } - String teacherId = jwglRoleHelper.requireTeacherId(); SSKCB lesson = sskcbMapper.selectById(dto.getSskcbbh().trim()); - if (lesson == null) { + if (lesson == null || (lesson.getSczt() != null && lesson.getSczt() == 1)) { throw new BusinessException(BasicManagementConstants.NOT_FOUND, "课次不存在"); } - if (lesson.getSczt() != null && lesson.getSczt() == 1) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "该课次已删除,不允许申请调课"); - } AdjustableLessonVO picked = null; List mine = ssdksqMapper.selectAdjustableLessons(teacherId, lesson.getNd()); @@ -1072,59 +982,55 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic } } if (picked == null) { - throw new BusinessException(BasicManagementConstants.FORBIDDEN, "只能对自己任课的课次申请调课"); + throw new BusinessException(BasicManagementConstants.NOT_FOUND, "课次不存在"); } - validateScheduleAdjustTimeLimitStrategy(dto.getRq()); - boolean autoApproved = matchAutoApproveStrategy(); - SSDKSQ apply = new SSDKSQ(); String ssdksqbh = UuidUtil.getOriginalUUID(); apply.setBh(ssdksqbh); apply.setSskcbbh(lesson.getBh()); - apply.setXydrwbh(StringUtils.isNotEmpty(dto.getXydrwbh()) ? dto.getXydrwbh() : picked.getXydrwbh()); - apply.setKbh(StringUtils.isNotEmpty(dto.getKbh()) ? dto.getKbh() : picked.getKbh()); - apply.setKcbbh(StringUtils.isNotEmpty(dto.getKcbbh()) ? dto.getKcbbh() : picked.getKcbbh()); - apply.setJysdh(picked.getJysdh()); - apply.setYjsbh(picked.getJsbh()); - apply.setXjsbh(resolveNewClassroom(dto)); - apply.setSqjybh(teacherId); - apply.setSy(dto.getSy()); + apply.setYdsjap(resolveYdsjap(lesson)); + apply.setZxzt(0); + apply.setYszt(0); + apply.setXydrwbh(cut(StringUtils.isNotEmpty(dto.getXydrwbh()) ? dto.getXydrwbh() : picked.getXydrwbh(), 50)); + apply.setKbh(cut(StringUtils.isNotEmpty(dto.getKbh()) ? dto.getKbh() : picked.getKbh(), 50)); + apply.setKcbbh(cut(StringUtils.isNotEmpty(dto.getKcbbh()) ? dto.getKcbbh() : picked.getKcbbh(), 50)); + apply.setJysdh(cut(picked.getJysdh(), 50)); + apply.setYjsbh(cut(picked.getJsbh(), 50)); + apply.setXjsbh(cut(resolveNewClassroom(dto), 50)); + apply.setSqjybh(cut(teacherId, 50)); + apply.setSy(cut(dto.getSy(), 200)); apply.setRq(dto.getRq()); - apply.setJc(dto.getJc()); - apply.setNd(dto.getNd() != null ? dto.getNd() : lesson.getNd()); - apply.setJxnr(dto.getJxnr()); + apply.setJc(dto.getJc() != null ? dto.getJc() : 0); + apply.setNd(dto.getNd() != null ? dto.getNd() : (lesson.getNd() != null ? lesson.getNd() : 0)); + apply.setJxnr(cut(dto.getJxnr(), 500)); apply.setJxyd(dto.getJxyd()); - apply.setJxff(dto.getJxff()); + String jxff = cut(dto.getJxff(), 50); + apply.setJxff(jxff == null ? "" : jxff); apply.setJxbzbz(dto.getJxbzbz()); apply.setYcxx(dto.getYcxx()); - String originalDesc = buildOriginalScheduleDesc(lesson); - if (dto.getBz() != null && !dto.getBz().trim().isEmpty()) { - apply.setBz(dto.getBz() + " | " + originalDesc); - } else { - apply.setBz(originalDesc); - } - if (autoApproved) { - apply.setBz((apply.getBz() == null ? "" : apply.getBz() + " | ") + "[免审批策略自动通过]"); - apply.setJyspzzt(1); - } else { - apply.setJyspzzt(0); - } + apply.setBz(cut(dto.getBz(), 50)); + apply.setJyspzzt(0); apply.setJyscszt(0); apply.setSczt(0); apply.setFhzt(0); apply.setDk(1); - LocalDateTime now = LocalDateTime.now(); - apply.setCjsj(now); + apply.setCjsj(LocalDateTime.now()); ssdksqMapper.insert(apply); if (dto.getRoomList() != null && !dto.getRoomList().isEmpty()) { for (AdjustRoomDTO roomDto : dto.getRoomList()) { + if (StringUtils.isEmpty(roomDto.getJsbh())) { + continue; + } DKSQJS roomEntity = new DKSQJS(); roomEntity.setBh(UuidUtil.getOriginalUUID()); roomEntity.setDksqbh(ssdksqbh); roomEntity.setJsbh(roomDto.getJsbh()); roomEntity.setBz(roomDto.getBz()); + roomEntity.setNd(apply.getNd()); + roomEntity.setRq(apply.getRq()); + roomEntity.setJc(apply.getJc()); dksqjsMapper.insert(roomEntity); } } else if (StringUtils.isNotEmpty(apply.getXjsbh())) { @@ -1132,44 +1038,61 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic roomEntity.setBh(UuidUtil.getOriginalUUID()); roomEntity.setDksqbh(ssdksqbh); roomEntity.setJsbh(apply.getXjsbh()); + roomEntity.setNd(apply.getNd()); + roomEntity.setRq(apply.getRq()); + roomEntity.setJc(apply.getJc()); dksqjsMapper.insert(roomEntity); } if (dto.getTeacherList() != null && !dto.getTeacherList().isEmpty()) { for (AdjustTeacherDTO teacherDto : dto.getTeacherList()) { + if (StringUtils.isEmpty(teacherDto.getFzjybh())) { + continue; + } DKSQFZJY teacherEntity = new DKSQFZJY(); teacherEntity.setBh(UuidUtil.getOriginalUUID()); teacherEntity.setDksqbh(ssdksqbh); teacherEntity.setFzjybh(teacherDto.getFzjybh()); - teacherEntity.setZjy(teacherDto.getZjy()); + teacherEntity.setZjy(teacherDto.getZjy() == null ? 0 : teacherDto.getZjy()); teacherEntity.setBz(teacherDto.getBz()); + teacherEntity.setNd(apply.getNd()); + teacherEntity.setRq(apply.getRq()); + teacherEntity.setJc(apply.getJc()); dksqfzjyMapper.insert(teacherEntity); } } if (dto.getTeamList() != null && !dto.getTeamList().isEmpty()) { for (AdjustTeamDTO teamDto : dto.getTeamList()) { + if (StringUtils.isEmpty(teamDto.getXydbh())) { + continue; + } DKSQXYD teamEntity = new DKSQXYD(); teamEntity.setBh(UuidUtil.getOriginalUUID()); teamEntity.setDksqbh(ssdksqbh); teamEntity.setXydbh(teamDto.getXydbh()); + teamEntity.setNd(apply.getNd()); + teamEntity.setRq(apply.getRq()); + teamEntity.setJc(apply.getJc()); dksqxydMapper.insert(teamEntity); } } if (dto.getSupportList() != null && !dto.getSupportList().isEmpty()) { for (AdjustSupportDTO supportDto : dto.getSupportList()) { + if (StringUtils.isEmpty(supportDto.getBztgmxbh()) || supportDto.getSl() == null) { + continue; + } DKSQBZMX supportEntity = new DKSQBZMX(); supportEntity.setBh(UuidUtil.getOriginalUUID()); supportEntity.setSqbh(ssdksqbh); supportEntity.setBztgmxbh(supportDto.getBztgmxbh()); supportEntity.setSl(supportDto.getSl()); supportEntity.setBz(supportDto.getBz()); + supportEntity.setNd(apply.getNd()); + supportEntity.setRq(apply.getRq()); + supportEntity.setJc(apply.getJc()); dksqbzmxMapper.insert(supportEntity); } } - if (autoApproved) { - applyApprovedSchedule(apply); - } - recordOperationLog(ssdksqbh, "申请", "提交调课申请:" + ssdksqbh); return ssdksqbh; } @@ -1203,6 +1126,32 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic return kcbMapper.selectOne(wrapper); } + private int resolveYdsjap(SSKCB lesson) { + if (lesson == null) { + return 0; + } + if (lesson.getJhsjap() != null) { + return lesson.getJhsjap(); + } + if (lesson.getRq() != null) { + return lesson.getRq().getYear() * 10000 + + lesson.getRq().getMonthValue() * 100 + + lesson.getRq().getDayOfMonth(); + } + return 0; + } + + private String cut(String value, int max) { + if (value == null) { + return null; + } + String text = value.trim(); + if (text.isEmpty()) { + return null; + } + return text.length() <= max ? text : text.substring(0, max); + } + private String resolveNewClassroom(ScheduleAdjustApplyDTO dto) { if (StringUtils.isNotEmpty(dto.getXjsbh())) { return dto.getXjsbh().trim(); @@ -1217,8 +1166,8 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic /** * 教研室审批调课申请。 *

- * 教研室审批教员提交的调课申请,包含同意、发回、拒绝三种处理方式。 - * 同意时同步更新课次数据,发回时记录发回原因,拒绝时标记申请为已拒绝。 + * 教研室审批调课申请。点审批即已查收,审批人同时记为查收人。 + * 同意后写回课次;发回、拒绝也记为已查收。 *

* * @param dto 教研室审批参数 @@ -1226,26 +1175,6 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic @Override @Transactional(rollbackFor = Exception.class) public void auditByTeachingOffice(ScheduleAdjustAuditDTO dto) { - if (dto == null) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "审批参数不能为空"); - } - if (jwglRoleHelper.isTeacher()) { - throw new BusinessException(BasicManagementConstants.FORBIDDEN, "教员无权审批调课申请"); - } - if (jwglRoleHelper.isDepartmentPersonnel() && !jwglRoleHelper.isAdmin()) { - throw new BusinessException(BasicManagementConstants.FORBIDDEN, "机关人员请使用机关审批,不办理教研室审批"); - } - if (!jwglRoleHelper.isAdmin() && !jwglRoleHelper.isResearchOffice()) { - throw new BusinessException(BasicManagementConstants.FORBIDDEN, "仅教研室可进行一审"); - } - if (!"jys".equals(dto.getAuditRole())) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "审批角色不正确,应为jys"); - } - if (dto.getSpzt() == null - || (dto.getSpzt() != 1 && dto.getSpzt() != 2 && dto.getSpzt() != 3)) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "审批结果不正确,应为1(同意)、2(发回)或3(拒绝)"); - } - SSDKSQ apply = ssdksqMapper.selectById(dto.getSsdksqbh()); if (apply == null) { throw new BusinessException(BasicManagementConstants.NOT_FOUND, "调课申请不存在"); @@ -1253,17 +1182,6 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic if (apply.getSczt() != null && apply.getSczt() == 1) { throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "该调课申请已撤销,不允许审批"); } - if (jwglRoleHelper.isResearchOffice()) { - String officeId = jwglRoleHelper.requireResearchOfficeId(); - String applyOffice = apply.getJysdh(); - if (StringUtils.isEmpty(applyOffice) && StringUtils.isNotEmpty(apply.getSqjybh())) { - JYB teacher = jybMapper.selectById(apply.getSqjybh()); - applyOffice = teacher == null ? null : teacher.getJysdh(); - } - if (applyOffice == null || !officeId.equals(applyOffice.trim())) { - throw new BusinessException(BasicManagementConstants.FORBIDDEN, "只能审批本教研室的调课申请"); - } - } if (apply.getJyspzzt() != null && apply.getJyspzzt() != 0) { throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "该调课申请已审批,无需重复审批"); } @@ -1273,275 +1191,36 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic apply.setJyspzzt(dto.getSpzt()); apply.setJyspzrbh(auditor); apply.setJyspzsj(now); + apply.setJyscszt(1); + apply.setJyscsrbh(auditor); + apply.setJyscssj(now); apply.setFhyj(dto.getFhyj()); - if (dto.getSpzt() != null && dto.getSpzt() == 2) { + if (dto.getSpzt() == 2) { apply.setFhzt(1); apply.setFhsj(now); } ssdksqMapper.updateById(apply); - recordOperationLog(apply.getBh(), "教研室审批", "教研室审批调课申请:" + apply.getBh() - + ",审批结果:" + dto.getSpzt() + ",审批意见:" + dto.getFhyj()); - } - - /** - * 教研室查收调课申请。 - *

- * 教研室查收调课申请,标记查收状态和查收时间。 - *

- * - * @param ssdksqbh 调课申请编号 - * @param csrbh 查收人编号 - */ - @Override - @Transactional(rollbackFor = Exception.class) - public void receiveByTeachingOffice(String ssdksqbh, String csrbh) { - // 步骤1:校验参数 - if (ssdksqbh == null || ssdksqbh.trim().isEmpty()) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "调课申请编号不能为空"); - } - if (csrbh == null || csrbh.trim().isEmpty()) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "查收人编号不能为空"); - } - - // 步骤2:查询调课申请实体 - SSDKSQ apply = ssdksqMapper.selectById(ssdksqbh); - if (apply == null) { - throw new BusinessException(BasicManagementConstants.NOT_FOUND, "调课申请不存在"); - } - - // 步骤3:校验是否已查收,避免重复查收 - if (apply.getJyscszt() != null && apply.getJyscszt() == 1) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "该调课申请已查收,无需重复查收"); - } - - // 步骤4:更新查收状态和查收时间 - apply.setJyscszt(1); // 教研室查收状态:已查收 - apply.setJyscsrbh(csrbh); // 教研室查收人编号 - apply.setJyscssj(LocalDateTime.now()); // 教研室查收时间 - apply.setUpdateTime(LocalDateTime.now()); // 更新时间 - ssdksqMapper.updateById(apply); - - // 步骤5:记录查收操作日志 - recordOperationLog(apply.getBh(), "教研室查收", "教研室查收调课申请:" + apply.getBh()); - } - - /** - * 教学系审批调课申请。 - *

- * 教学系审批教研室已同意的调课申请,包含同意、发回、拒绝三种处理方式。 - * 教学系同意后流转到机关审批,构成三级审批流程(教研室→教学系→机关)。 - *

- * - * @param dto 教学系审批参数 - */ - @Override - @Transactional(rollbackFor = Exception.class) - public void auditByTeachingDepartment(ScheduleAdjustAuditDTO dto) { - // 步骤1:校验审批参数 - if (dto == null) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "审批参数不能为空"); - } - if (!"jxx".equals(dto.getAuditRole())) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "审批角色不正确,应为jxx"); - } - if (dto.getSpzt() == null - || (dto.getSpzt() != 1 && dto.getSpzt() != 2 && dto.getSpzt() != 3)) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "审批结果不正确,应为1(同意)、2(发回)或3(拒绝)"); - } - - // 步骤2:查询调课申请实体 - SSDKSQ apply = ssdksqMapper.selectById(dto.getSsdksqbh()); - if (apply == null) { - throw new BusinessException(BasicManagementConstants.NOT_FOUND, "调课申请不存在"); - } - if (apply.getSczt() != null && apply.getSczt() == 1) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "该调课申请已撤销,不允许审批"); - } - - // 步骤3:校验教研室是否已同意,教学系审批应在教研室同意之后进行 - if (apply.getJyspzzt() == null || apply.getJyspzzt() != 1) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "教研室未同意或未审批,无法进行教学系审批"); - } - - // 步骤4:校验教学系是否已审批,避免重复审批 - if (apply.getJxxspzzt() != null && apply.getJxxspzzt() != 0) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "该调课申请教学系已审批,无需重复审批"); - } - - // 步骤5:更新调课申请主表的教学系审批状态 - LocalDateTime now = LocalDateTime.now(); - apply.setJxxspzzt(dto.getSpzt()); // 教学系审批状态 - apply.setJxxspzrbh(dto.getSprbh()); // 教学系审批人编号 - apply.setJxxspzsj(now); // 教学系审批时间 - apply.setFhyj(dto.getFhyj()); // 发回意见 - apply.setUpdateTime(now); // 更新时间 - ssdksqMapper.updateById(apply); - - // 步骤6:根据审批结果执行不同的后续处理 - if (dto.getSpzt() == 3) { - // 拒绝:恢复课次原数据,标记申请最终结束 - restoreLessonSchedule(apply); - } else if (dto.getSpzt() == 2) { - // 发回:发回教研室,课次保留调整后数据,由教研室重新处理 - } else { - // 同意:流转到机关审批 - } - - // 步骤7:记录教学系审批操作日志 - recordOperationLog(apply.getBh(), "教学系审批", "教学系审批调课申请:" + apply.getBh() - + ",审批结果:" + dto.getSpzt() + ",审批意见:" + dto.getFhyj()); - } - - /** - * 机关审批调课申请。 - *

- * 机关审批教研室已同意的调课申请,包含同意、发回、拒绝三种处理方式。 - * 机关全部同意后调课申请最终生效,课次按调整后的内容执行。 - *

- * - * @param dto 机关审批参数 - */ - @Override - @Transactional(rollbackFor = Exception.class) - public void auditByAuthority(ScheduleAdjustAuditDTO dto) { - // 步骤1:校验审批参数 - if (dto == null) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "审批参数不能为空"); - } - if (!"jg".equals(dto.getAuditRole())) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "审批角色不正确,应为jg"); - } - if (dto.getSpzt() == null - || (dto.getSpzt() != 1 && dto.getSpzt() != 2 && dto.getSpzt() != 3)) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "审批结果不正确"); - } - if (jwglRoleHelper.isTeacher() || jwglRoleHelper.isResearchOffice()) { - throw new BusinessException(BasicManagementConstants.FORBIDDEN, "仅机关人员可进行终审"); - } - if (!jwglRoleHelper.isAdmin() && !jwglRoleHelper.isDepartmentPersonnel()) { - throw new BusinessException(BasicManagementConstants.FORBIDDEN, "仅机关人员可进行终审"); - } - - SSDKSQ apply = ssdksqMapper.selectById(dto.getSsdksqbh()); - if (apply == null) { - throw new BusinessException(BasicManagementConstants.NOT_FOUND, "调课申请不存在"); - } - if (apply.getSczt() != null && apply.getSczt() == 1) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "该调课申请已撤销,不允许审批"); - } - if (apply.getJyspzzt() == null || apply.getJyspzzt() != 1) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "教研室未同意,无法进行机关审批"); - } - - String jgbh = StringUtils.isNotEmpty(dto.getJgbh()) ? dto.getJgbh() : "JG"; - String auditor = StringUtils.isNotEmpty(dto.getSprbh()) ? dto.getSprbh() : currentOperatorId(); - - SSDKSQSP audit = new SSDKSQSP(); - audit.setBh(UuidUtil.getOriginalUUID()); - audit.setSsdksqbh(dto.getSsdksqbh()); - audit.setJgbh(jgbh); - audit.setSprbh(auditor); - audit.setSpsj(LocalDateTime.now()); - audit.setSpzt(dto.getSpzt()); - audit.setFhyj(dto.getFhyj()); - audit.setNd(apply.getNd()); - audit.setCreateTime(LocalDateTime.now()); - audit.setUpdateTime(LocalDateTime.now()); - ssdksqspMapper.insert(audit); - if (dto.getSpzt() == 1) { applyApprovedSchedule(apply); } - recordOperationLog(apply.getBh(), "机关审批", "机关审批调课申请:" + apply.getBh() - + ",机关编号:" + jgbh + recordOperationLog(apply.getBh(), "教研室审批", "教研室审批调课申请:" + apply.getBh() + ",审批结果:" + dto.getSpzt() + ",审批意见:" + dto.getFhyj()); } - /** - * 机关查收调课申请。 - *

- * 机关查收调课申请,标记查收状态和查收时间。 - *

- * - * @param ssdksqbh 调课申请编号 - * @param jgbh 机关编号 - * @param csrbh 查收人编号 - */ - @Override - @Transactional(rollbackFor = Exception.class) - public void receiveByAuthority(String ssdksqbh, String jgbh, String csrbh) { - // 步骤1:校验参数 - if (ssdksqbh == null || ssdksqbh.trim().isEmpty()) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "调课申请编号不能为空"); - } - if (jgbh == null || jgbh.trim().isEmpty()) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "机关编号不能为空"); - } - if (csrbh == null || csrbh.trim().isEmpty()) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "查收人编号不能为空"); - } - - // 步骤2:查询调课申请实体 - SSDKSQ apply = ssdksqMapper.selectById(ssdksqbh); - if (apply == null) { - throw new BusinessException(BasicManagementConstants.NOT_FOUND, "调课申请不存在"); - } - - // 步骤3:检查当前机关是否已查收 - LambdaQueryWrapper checkWrapper = new LambdaQueryWrapper<>(); - checkWrapper.eq(SSDKSQSP::getSsdksqbh, ssdksqbh); - checkWrapper.eq(SSDKSQSP::getJgbh, jgbh); - checkWrapper.eq(SSDKSQSP::getCszt, 1); - if (ssdksqspMapper.selectCount(checkWrapper) > 0) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "当前机关已查收该调课申请,无需重复查收"); - } - - // 步骤4:构建并保存机关查收记录 - SSDKSQSP receive = new SSDKSQSP(); - receive.setBh(UuidUtil.getOriginalUUID()); - receive.setSsdksqbh(ssdksqbh); - receive.setJgbh(jgbh); - receive.setCsrbh(csrbh); - receive.setCszt(1); - receive.setCssj(LocalDateTime.now()); - receive.setNd(apply.getNd()); - receive.setCreateTime(LocalDateTime.now()); - receive.setUpdateTime(LocalDateTime.now()); - ssdksqspMapper.insert(receive); - - // 步骤5:记录查收操作日志 - recordOperationLog(apply.getBh(), "机关查收", "机关查收调课申请:" + apply.getBh() - + ",机关编号:" + jgbh); - } /** * 撤销调课申请。 *

- * 教员撤销自己提交的、未经过审批的调课申请。 - * 软删除调课申请,标记删除状态为1。 - * 事务保证主表软删除与课次原数据恢复的一致性。 + * 教员撤销未经过审批的调课申请。列表已按登录教员过滤。 *

* * @param ssdksqbh 调课申请编号 - * @param sqjybh 申请教员编号(用于权限校验) */ @Override @Transactional(rollbackFor = Exception.class) - public void cancelScheduleAdjust(String ssdksqbh, String sqjybh) { - if (jwglRoleHelper.isDepartmentPersonnel() || jwglRoleHelper.isResearchOffice()) { - throw new BusinessException(BasicManagementConstants.FORBIDDEN, "仅申请教员本人可撤销调课申请"); - } - if (ssdksqbh == null || ssdksqbh.trim().isEmpty()) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "调课申请编号不能为空"); - } - String teacherId = jwglRoleHelper.requireTeacherId(); - if (sqjybh != null && !sqjybh.trim().isEmpty() && !teacherId.equals(sqjybh.trim())) { - throw new BusinessException(BasicManagementConstants.FORBIDDEN, "仅申请教员本人可撤销该调课申请"); - } - sqjybh = teacherId; - + public void cancelScheduleAdjust(String ssdksqbh) { SSDKSQ apply = ssdksqMapper.selectById(ssdksqbh); if (apply == null) { throw new BusinessException(BasicManagementConstants.NOT_FOUND, "调课申请不存在"); @@ -1549,29 +1228,19 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic if (apply.getSczt() != null && apply.getSczt() == 1) { throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "该调课申请已撤销,无需重复撤销"); } - if (!sqjybh.equals(apply.getSqjybh())) { - throw new BusinessException(BasicManagementConstants.FORBIDDEN, "仅申请教员本人可撤销该调课申请"); + if (apply.getJyspzzt() != null && apply.getJyspzzt() != 0) { + throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "该调课申请已审批,不允许撤销"); } - if (apply.getJyspzzt() != null && apply.getJyspzzt() == 1) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "该调课申请教研室已同意,不允许撤销"); - } - apply.setSczt(1); + apply.setJyscszt(0); + apply.setJyscsrbh(null); + apply.setJyscssj(null); ssdksqMapper.updateById(apply); recordOperationLog(apply.getBh(), "撤销", "撤销调课申请:" + apply.getBh()); } /** - * 构建原日期节次描述。 - *

- * 根据课次实体生成原日期节次的字符串描述,便于调课申请中保留原信息。 - *

- * - * @param lesson 课次实体 - * @return 原日期节次描述 - */ - /** - * 机关终审同意后,将调整结果写回学员队任务表、课程表,以及可选的实施课次。 + * 教研室同意后,将调整结果写回课次、学员队任务表和课程表。 */ private void applyApprovedSchedule(SSDKSQ apply) { if (apply == null || StringUtils.isEmpty(apply.getSskcbbh())) { @@ -1642,28 +1311,6 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic return sb.toString(); } - /** - * 恢复课次原数据。 - *

- * 当调课申请被拒绝或撤销时,恢复课次到申请前的原始数据。 - * 实际业务中通常需要从历史快照恢复,此处简化为清空日期节次。 - *

- * - * @param apply 调课申请实体 - */ - private void restoreLessonSchedule(SSDKSQ apply) { - // 步骤1:查询课次实体 - SSKCB lesson = sskcbMapper.selectById(apply.getSskcbbh()); - if (lesson == null) { - return; - } - - // 步骤2:清空课次因调课而设置的日期节次(恢复原状) - lesson.setRq(null); - lesson.setJc(null); - sskcbMapper.updateById(lesson); - } - // ==================== 教学保障管理 ==================== /** @@ -2971,192 +2618,6 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic return value == null ? "" : String.valueOf(value); } - // ============================================================ - // 调课策略管理实现(时间段限制策略 + 免审批策略) - // ============================================================ - - /** 策略类型常量:时间段限制 */ - private static final String STRATEGY_TIME_LIMIT = "TIME_LIMIT"; - /** 策略类型常量:免审批 */ - private static final String STRATEGY_AUTO_APPROVE = "AUTO_APPROVE"; - /** 星期中文描述 */ - private static final String[] WEEK_DAY_NAMES = {"周日", "周一", "周二", "周三", "周四", "周五", "周六"}; - - @Override - public List listScheduleAdjustStrategies(String cllx) { - LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - if (cllx != null && !cllx.trim().isEmpty()) { - wrapper.eq(ScheduleAdjustStrategy::getCllx, cllx); - } - wrapper.orderByDesc(ScheduleAdjustStrategy::getGxsj); - List list = scheduleAdjustStrategyMapper.selectList(wrapper); - - List result = new ArrayList<>(); - if (list == null) { - return result; - } - for (ScheduleAdjustStrategy strategy : list) { - result.add(buildStrategyVO(strategy)); - } - return result; - } - - @Override - @Transactional(rollbackFor = Exception.class) - public String saveScheduleAdjustStrategy(ScheduleAdjustStrategyDTO dto) { - if (!STRATEGY_TIME_LIMIT.equals(dto.getCllx()) && !STRATEGY_AUTO_APPROVE.equals(dto.getCllx())) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, - "策略类型不正确,应为 TIME_LIMIT 或 AUTO_APPROVE"); - } - if (STRATEGY_TIME_LIMIT.equals(dto.getCllx())) { - if (dto.getJzxq() != null && (dto.getJzxq() < 0 || dto.getJzxq() > 6)) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, - "截止星期取值范围为0-6(0=周日,6=周六)"); - } - } - if (dto.getSxkssj() != null && dto.getSxjssj() != null - && dto.getSxkssj().isAfter(dto.getSxjssj())) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, - "生效开始时间不能晚于生效结束时间"); - } - - LocalDateTime now = LocalDateTime.now(); - - if (dto.getClbh() == null || dto.getClbh().trim().isEmpty()) { - ScheduleAdjustStrategy strategy = new ScheduleAdjustStrategy(); - strategy.setClbh(UuidUtil.getOriginalUUID()); - strategy.setClmc(dto.getClmc()); - strategy.setCllx(dto.getCllx()); - strategy.setSfqy(dto.getSfqy() == null ? 1 : dto.getSfqy()); - strategy.setJzxq(dto.getJzxq()); - strategy.setJzjc(dto.getJzjc()); - strategy.setSxkssj(dto.getSxkssj()); - strategy.setSxjssj(dto.getSxjssj()); - strategy.setBz(dto.getBz()); - strategy.setCjsj(now); - strategy.setGxsj(now); - scheduleAdjustStrategyMapper.insert(strategy); - return strategy.getClbh(); - } else { - ScheduleAdjustStrategy existing = scheduleAdjustStrategyMapper.selectById(dto.getClbh()); - if (existing == null) { - throw new BusinessException(BasicManagementConstants.NOT_FOUND, "调课策略不存在"); - } - existing.setClmc(dto.getClmc()); - existing.setCllx(dto.getCllx()); - existing.setSfqy(dto.getSfqy() == null ? 1 : dto.getSfqy()); - existing.setJzxq(dto.getJzxq()); - existing.setJzjc(dto.getJzjc()); - existing.setSxkssj(dto.getSxkssj()); - existing.setSxjssj(dto.getSxjssj()); - existing.setBz(dto.getBz()); - existing.setGxsj(now); - scheduleAdjustStrategyMapper.updateById(existing); - return existing.getClbh(); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void deleteScheduleAdjustStrategy(String clbh) { - if (clbh == null || clbh.trim().isEmpty()) { - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "策略编号不能为空"); - } - ScheduleAdjustStrategy existing = scheduleAdjustStrategyMapper.selectById(clbh); - if (existing == null) { - throw new BusinessException(BasicManagementConstants.NOT_FOUND, "调课策略不存在"); - } - scheduleAdjustStrategyMapper.deleteById(clbh); - } - - /** 调课时间段策略校验:超过截止星期时不允许调整下周课程 */ - private void validateScheduleAdjustTimeLimitStrategy(LocalDateTime newDate) { - if (newDate == null) { - return; - } - LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - wrapper.eq(ScheduleAdjustStrategy::getCllx, STRATEGY_TIME_LIMIT); - wrapper.eq(ScheduleAdjustStrategy::getSfqy, 1); - List strategies = scheduleAdjustStrategyMapper.selectList(wrapper); - - if (strategies == null || strategies.isEmpty()) { - return; - } - - LocalDateTime now = LocalDateTime.now(); - int currentDayOfWeek = now.getDayOfWeek().getValue(); - boolean isFutureWeek = newDate.toLocalDate().isAfter(now.toLocalDate().plusDays(7)); - - for (ScheduleAdjustStrategy strategy : strategies) { - if (strategy.getSxkssj() != null && now.isBefore(strategy.getSxkssj())) { - continue; - } - if (strategy.getSxjssj() != null && now.isAfter(strategy.getSxjssj())) { - continue; - } - Integer deadlineDay = strategy.getJzxq(); - if (deadlineDay == null) { - continue; - } - int deadlineDayOfWeek = (deadlineDay == 0) ? 7 : deadlineDay; - if (isFutureWeek && currentDayOfWeek > deadlineDayOfWeek) { - String dayName = (deadlineDay >= 0 && deadlineDay <= 6) ? WEEK_DAY_NAMES[deadlineDay] : String.valueOf(deadlineDay); - throw new BusinessException(BasicManagementConstants.BAD_REQUEST, - "根据调课策略[" + strategy.getClmc() + "]," + dayName + "之后不允许调整下周课程"); - } - } - } - - /** 判断当前是否命中免审批策略 */ - private boolean matchAutoApproveStrategy() { - LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - wrapper.eq(ScheduleAdjustStrategy::getCllx, STRATEGY_AUTO_APPROVE); - wrapper.eq(ScheduleAdjustStrategy::getSfqy, 1); - List strategies = scheduleAdjustStrategyMapper.selectList(wrapper); - - if (strategies == null || strategies.isEmpty()) { - return false; - } - - LocalDateTime now = LocalDateTime.now(); - for (ScheduleAdjustStrategy strategy : strategies) { - boolean inRange = true; - if (strategy.getSxkssj() != null && now.isBefore(strategy.getSxkssj())) { - inRange = false; - } - if (strategy.getSxjssj() != null && now.isAfter(strategy.getSxjssj())) { - inRange = false; - } - if (inRange) { - return true; - } - } - return false; - } - - /** 组装调课策略展示VO */ - private ScheduleAdjustStrategyVO buildStrategyVO(ScheduleAdjustStrategy strategy) { - ScheduleAdjustStrategyVO vo = new ScheduleAdjustStrategyVO(); - vo.setClbh(strategy.getClbh()); - vo.setClmc(strategy.getClmc()); - vo.setCllx(strategy.getCllx()); - vo.setCllxDesc(STRATEGY_TIME_LIMIT.equals(strategy.getCllx()) ? "时间段限制" : - (STRATEGY_AUTO_APPROVE.equals(strategy.getCllx()) ? "免审批" : strategy.getCllx())); - vo.setSfqy(strategy.getSfqy()); - vo.setSfqyDesc(strategy.getSfqy() != null && strategy.getSfqy() == 1 ? "启用" : "停用"); - vo.setJzxq(strategy.getJzxq()); - if (strategy.getJzxq() != null && strategy.getJzxq() >= 0 && strategy.getJzxq() <= 6) { - vo.setJzxqDesc(WEEK_DAY_NAMES[strategy.getJzxq()]); - } - vo.setJzjc(strategy.getJzjc()); - vo.setSxkssj(strategy.getSxkssj()); - vo.setSxjssj(strategy.getSxjssj()); - vo.setBz(strategy.getBz()); - vo.setCjsj(strategy.getCjsj()); - vo.setGxsj(strategy.getGxsj()); - return vo; - } - /** * 根据教员编号自动解析其归属的实施_机关编号。 *

diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/FacultyServiceImpl.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/FacultyServiceImpl.java index e57c7c43..4195177f 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/FacultyServiceImpl.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/FacultyServiceImpl.java @@ -9,6 +9,7 @@ import com.roomroot.jwgl.service.FacultyService; import com.roomroot.jwgl.unit.BusinessException; import com.roomroot.jwgl.unit.PageQuery; import com.roomroot.jwgl.unit.PageResult; +import com.roomroot.jwgl.utils.ExcelExportUtil; import com.roomroot.jwgl.utils.ExcelParseUtil; import com.roomroot.jwgl.utils.UuidUtil; import com.roomroot.jwgl.vo.faculty.TeacherListVO; @@ -160,6 +161,52 @@ public class FacultyServiceImpl implements FacultyService { return jybMapper.selectById(jybh); } + @Override + @Transactional(rollbackFor = Exception.class) + public int importJYBFromExcel(MultipartFile file) throws Exception { + if (file == null || file.isEmpty()) { + throw new BusinessException("导入文件不能为空"); + } + List list = ExcelParseUtil.parseJYBExcel( + file.getInputStream(), file.getOriginalFilename()); + if (list.isEmpty()) { + throw new BusinessException("Excel中无有效数据"); + } + for (JYB entity : list) { + // 编号为空自动生成 + if (entity.getJybh() == null || entity.getJybh().isEmpty()) { + entity.setJybh(UuidUtil.getUUID()); + } + // 离职状态默认 0 + if (entity.getLzzt() == null) { + entity.setLzzt(0); + } + jybMapper.insert(entity); + } + return list.size(); + } + + @Override + public void exportJYBExcel(jakarta.servlet.http.HttpServletResponse response) throws Exception { + com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper w = + new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<>(); + w.eq(JYB::getLzzt, 0).orderByAsc(JYB::getXh); + List list = jybMapper.selectList(w); + + String[] headers = {"教员编号", "教员姓名", "教研室代号", "职称", "专业技术职务等级", "教员类别", + "性别", "手机号", "身份证号", "备注", "序号", "虚实类型"}; + byte[] bytes = ExcelExportUtil.export("教员", headers, list, + JYB::getJybh, JYB::getJyxm, JYB::getJysdh, JYB::getZc, + JYB::getZyjszwdj, JYB::getJylb, JYB::getXb, JYB::getSjh, + JYB::getSfzh, JYB::getBz, JYB::getXh, JYB::getXslx); + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setHeader("Content-Disposition", + "attachment; filename=" + java.net.URLEncoder.encode( + "教员列表.xlsx", java.nio.charset.StandardCharsets.UTF_8)); + response.getOutputStream().write(bytes); + response.getOutputStream().flush(); + } + @Override public PageResult pageJYBDetail(PageQuery query, JYB cond) { Page page = new Page<>(query.getPageNum(), query.getPageSize()); diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/TrainingProgramServiceImpl.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/TrainingProgramServiceImpl.java index 58fcb6e2..8ed448c1 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/TrainingProgramServiceImpl.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/TrainingProgramServiceImpl.java @@ -73,4 +73,45 @@ public class TrainingProgramServiceImpl implements TrainingProgramService { return new PageResult<>(result.getRecords(), result.getTotal(), query.getPageNum(), query.getPageSize()); } + + @Override + @Transactional(rollbackFor = Exception.class) + public int importZYBFromExcel(org.springframework.web.multipart.MultipartFile file) throws Exception { + if (file == null || file.isEmpty()) { + throw new RuntimeException("导入文件不能为空"); + } + java.util.List list = com.roomroot.jwgl.utils.ExcelParseUtil.parseZYBExcel( + file.getInputStream(), file.getOriginalFilename()); + if (list.isEmpty()) { + throw new RuntimeException("Excel中无有效数据"); + } + for (ZYB entity : list) { + if (entity.getZydh() == null || entity.getZydh().isEmpty()) { + entity.setZydh(com.roomroot.jwgl.utils.UuidUtil.getUUID()); + } + if (entity.getTy() == null) { + entity.setTy(false); + } + zybMapper.insert(entity); + } + return list.size(); + } + + @Override + public void exportZYBExcel(jakarta.servlet.http.HttpServletResponse response) throws Exception { + java.util.List list = zybMapper.selectList( + new LambdaQueryWrapper().orderByAsc(ZYB::getZydh)); + String[] headers = {"专业代号", "专业名称", "专业代码", "专业版本", "培训类型", "培训层次", + "学年制", "学期数", "专业方向", "专业备注", "学员类别", "简称", "培训类型2"}; + byte[] bytes = com.roomroot.jwgl.utils.ExcelExportUtil.export("培养方案", headers, list, + ZYB::getZydh, ZYB::getZymc, ZYB::getZydm, ZYB::getZybb, ZYB::getPxlx, + ZYB::getPxcc, ZYB::getXnz, ZYB::getXqs, ZYB::getZyfx, ZYB::getZybz, + ZYB::getXylb, ZYB::getJc, ZYB::getPxlx2); + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setHeader("Content-Disposition", + "attachment; filename=" + java.net.URLEncoder.encode( + "培养方案.xlsx", java.nio.charset.StandardCharsets.UTF_8)); + response.getOutputStream().write(bytes); + response.getOutputStream().flush(); + } } diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/ZYJXJHBServiceImpl.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/ZYJXJHBServiceImpl.java index 2a2a49db..9ee21f51 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/ZYJXJHBServiceImpl.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/ZYJXJHBServiceImpl.java @@ -71,4 +71,43 @@ public class ZYJXJHBServiceImpl implements ZYJXJHBService { .eq(ZYJXJHB::getTy, ty); return zyjxjhbMapper.selectList(wrapper); } + + @Override + @org.springframework.transaction.annotation.Transactional(rollbackFor = Exception.class) + public int importZYJXJHBFromExcel(org.springframework.web.multipart.MultipartFile file) throws Exception { + if (file == null || file.isEmpty()) { + throw new RuntimeException("导入文件不能为空"); + } + List list = com.roomroot.jwgl.utils.ExcelParseUtil.parseZYJXJHBExcel( + file.getInputStream(), file.getOriginalFilename()); + if (list.isEmpty()) { + throw new RuntimeException("Excel中无有效数据"); + } + for (ZYJXJHB entity : list) { + if (entity.getTy() == null) { + entity.setTy(0); + } + zyjxjhbMapper.insert(entity); + } + return list.size(); + } + + @Override + public void exportZYJXJHBExcel(jakarta.servlet.http.HttpServletResponse response) throws Exception { + List list = zyjxjhbMapper.selectList( + new LambdaQueryWrapper().orderByAsc(ZYJXJHB::getZydh)); + String[] headers = {"专业代号", "课编号", "学期第次", "课类型", "学时", "学分", "周课时", + "课程定位", "模块", "成绩分制", "理论学时", "实践学时", "考试课时", "教研室代号", "简称"}; + byte[] bytes = com.roomroot.jwgl.utils.ExcelExportUtil.export("专业教学计划", headers, list, + ZYJXJHB::getZydh, ZYJXJHB::getKbh, ZYJXJHB::getXqdc, ZYJXJHB::getKlx, + ZYJXJHB::getXs, ZYJXJHB::getXf, ZYJXJHB::getZks, ZYJXJHB::getKcdw, + ZYJXJHB::getMk, ZYJXJHB::getCjfz, ZYJXJHB::getLlxs, ZYJXJHB::getSjxs, + ZYJXJHB::getKsks, ZYJXJHB::getJysdh, ZYJXJHB::getJc); + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setHeader("Content-Disposition", + "attachment; filename=" + java.net.URLEncoder.encode( + "专业教学计划.xlsx", java.nio.charset.StandardCharsets.UTF_8)); + response.getOutputStream().write(bytes); + response.getOutputStream().flush(); + } } diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/utils/ExcelParseUtil.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/utils/ExcelParseUtil.java index b0feca1c..99cbc355 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/utils/ExcelParseUtil.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/utils/ExcelParseUtil.java @@ -5,10 +5,13 @@ import com.roomroot.jwgl.dto.courserunning.SemesterImportDTO; import com.roomroot.jwgl.dto.courserunning.SingleCourseImportDTO; import com.roomroot.jwgl.dto.departmentpersonnel.DepartmentPersonnelCreateDTO; import com.roomroot.jwgl.entity.JXSSJH; +import com.roomroot.jwgl.entity.JYB; import com.roomroot.jwgl.entity.JYSB; import com.roomroot.jwgl.entity.KSBZXM; import com.roomroot.jwgl.entity.XYDB; import com.roomroot.jwgl.entity.XYXX; +import com.roomroot.jwgl.entity.ZYB; +import com.roomroot.jwgl.entity.ZYJXJHB; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; @@ -386,6 +389,144 @@ public class ExcelParseUtil { return result; } + /** + * 解析教员管理导入 Excel 文件(支持 .xls / .xlsx)。 + *

表头顺序:

+ * 教员编号, 教员姓名, 教研室代号, 职称, 专业技术职务等级, 教员类别, + * 性别, 手机号, 身份证号, 备注, 序号, 虚实类型 + * + * @return 教员实体列表(离职状态由业务层默认填充) + * @throws Exception 解析异常 + */ + public static List parseJYBExcel(InputStream inputStream, String fileName) throws Exception { + List result = new ArrayList<>(); + Workbook workbook = createWorkbook(inputStream, fileName); + try { + Sheet sheet = workbook.getSheetAt(0); + int lastRowNum = sheet.getLastRowNum(); + for (int rowNum = 1; rowNum <= lastRowNum; rowNum++) { + Row row = sheet.getRow(rowNum); + if (row == null) { + continue; + } + JYB entity = new JYB(); + entity.setJybh(getCellStringValue(row.getCell(0))); + entity.setJyxm(getCellStringValue(row.getCell(1))); + entity.setJysdh(getCellStringValue(row.getCell(2))); + entity.setZc(getCellStringValue(row.getCell(3))); + entity.setZyjszwdj(getCellStringValue(row.getCell(4))); + entity.setJylb(getCellStringValue(row.getCell(5))); + entity.setXb(parseIntCell(row.getCell(6))); + entity.setSjh(getCellStringValue(row.getCell(7))); + entity.setSfzh(getCellStringValue(row.getCell(8))); + entity.setBz(getCellStringValue(row.getCell(9))); + entity.setXh(getCellStringValue(row.getCell(10))); + entity.setXslx(parseIntCell(row.getCell(11))); + // 至少要有姓名才视为有效行 + if (entity.getJyxm() != null && !entity.getJyxm().isEmpty()) { + result.add(entity); + } + } + } finally { + workbook.close(); + } + return result; + } + + /** + * 解析培养方案(专业表)导入 Excel 文件(支持 .xls / .xlsx)。 + *

表头顺序:

+ * 专业代号, 专业名称, 专业代码, 专业版本, 培训类型, 培训层次, 学年制, 学期数, + * 专业方向, 专业备注, 学员类别, 简称, 培训类型2 + * + * @return 专业实体列表 + * @throws Exception 解析异常 + */ + public static List parseZYBExcel(InputStream inputStream, String fileName) throws Exception { + List result = new ArrayList<>(); + Workbook workbook = createWorkbook(inputStream, fileName); + try { + Sheet sheet = workbook.getSheetAt(0); + int lastRowNum = sheet.getLastRowNum(); + for (int rowNum = 1; rowNum <= lastRowNum; rowNum++) { + Row row = sheet.getRow(rowNum); + if (row == null) { + continue; + } + ZYB entity = new ZYB(); + entity.setZydh(getCellStringValue(row.getCell(0))); + entity.setZymc(getCellStringValue(row.getCell(1))); + entity.setZydm(getCellStringValue(row.getCell(2))); + entity.setZybb(getCellStringValue(row.getCell(3))); + entity.setPxlx(getCellStringValue(row.getCell(4))); + entity.setPxcc(getCellStringValue(row.getCell(5))); + entity.setXnz(getCellStringValue(row.getCell(6))); + entity.setXqs(parseIntCell(row.getCell(7))); + entity.setZyfx(getCellStringValue(row.getCell(8))); + entity.setZybz(getCellStringValue(row.getCell(9))); + entity.setXylb(getCellStringValue(row.getCell(10))); + entity.setJc(getCellStringValue(row.getCell(11))); + entity.setPxlx2(getCellStringValue(row.getCell(12))); + // 至少要有专业代号或名称才视为有效行 + if (entity.getZydh() != null && !entity.getZydh().isEmpty() + || entity.getZymc() != null && !entity.getZymc().isEmpty()) { + result.add(entity); + } + } + } finally { + workbook.close(); + } + return result; + } + + /** + * 解析专业教学计划表导入 Excel 文件(支持 .xls / .xlsx)。 + *

表头顺序:

+ * 专业代号, 课编号, 学期第次, 课类型, 学时, 学分, 周课时, 课程定位, + * 模块, 成绩分制, 理论学时, 实践学时, 考试课时, 教研室代号, 简称 + * + * @return 专业教学计划实体列表 + * @throws Exception 解析异常 + */ + public static List parseZYJXJHBExcel(InputStream inputStream, String fileName) throws Exception { + List result = new ArrayList<>(); + Workbook workbook = createWorkbook(inputStream, fileName); + try { + Sheet sheet = workbook.getSheetAt(0); + int lastRowNum = sheet.getLastRowNum(); + for (int rowNum = 1; rowNum <= lastRowNum; rowNum++) { + Row row = sheet.getRow(rowNum); + if (row == null) { + continue; + } + ZYJXJHB entity = new ZYJXJHB(); + entity.setZydh(getCellStringValue(row.getCell(0))); + entity.setKbh(getCellStringValue(row.getCell(1))); + entity.setXqdc(parseIntCell(row.getCell(2))); + entity.setKlx(getCellStringValue(row.getCell(3))); + entity.setXs(parseIntCell(row.getCell(4))); + entity.setXf(parseFloatCell(row.getCell(5))); + entity.setZks(parseIntCell(row.getCell(6))); + entity.setKcdw(getCellStringValue(row.getCell(7))); + entity.setMk(getCellStringValue(row.getCell(8))); + entity.setCjfz(getCellStringValue(row.getCell(9))); + entity.setLlxs(parseIntCell(row.getCell(10))); + entity.setSjxs(parseIntCell(row.getCell(11))); + entity.setKsks(parseIntCell(row.getCell(12))); + entity.setJysdh(getCellStringValue(row.getCell(13))); + entity.setJc(getCellStringValue(row.getCell(14))); + // 至少要有专业代号或课编号才视为有效行 + if (entity.getZydh() != null && !entity.getZydh().isEmpty() + || entity.getKbh() != null && !entity.getKbh().isEmpty()) { + result.add(entity); + } + } + } finally { + workbook.close(); + } + return result; + } + private static Workbook createWorkbook(InputStream inputStream, String fileName) throws Exception { String name = fileName == null ? "" : fileName.toLowerCase(); if (!name.endsWith(".xlsx") && !name.endsWith(".xls")) { @@ -426,6 +567,21 @@ public class ExcelParseUtil { } } + /** + * 解析单元格浮点数值。 + */ + private static Float parseFloatCell(Cell cell) { + String value = getCellStringValue(cell); + if (value == null || value.isEmpty()) { + return null; + } + try { + return Float.valueOf(value); + } catch (NumberFormatException e) { + return null; + } + } + private static String getCellStringValue(Cell cell) { if (cell == null) { return null; diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/utils/UuidUtil.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/utils/UuidUtil.java index b36195a0..9b6d8e13 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/utils/UuidUtil.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/utils/UuidUtil.java @@ -37,4 +37,42 @@ public class UuidUtil { public static String getUuidWithPrefix(String prefix) { return prefix + getUUID(); } + + /** + * 将 32 位十六进制或 36 位带横线 UUID 转为 16 字节,供达梦 VARBINARY 主键写入。 + */ + public static byte[] toBytes(String guid) { + if (guid == null) { + return null; + } + String hex = guid.trim().replace("-", ""); + if (hex.length() < 32) { + throw new IllegalArgumentException("GUID hex length must be 32, actual=" + hex.length()); + } + hex = hex.substring(0, 32); + byte[] out = new byte[16]; + for (int i = 0; i < 16; i++) { + out[i] = (byte) Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16); + } + return out; + } + + /** + * 将 VARBINARY(16) 转为带横线的 36 位 UUID,与 CHAR(36) 外键存储格式一致。 + */ + public static String fromBytes(byte[] bytes) { + if (bytes == null || bytes.length == 0) { + return null; + } + StringBuilder hex = new StringBuilder(bytes.length * 2); + for (byte value : bytes) { + hex.append(String.format("%02x", value)); + } + String h = hex.toString(); + if (h.length() < 32) { + return h; + } + return h.substring(0, 8) + "-" + h.substring(8, 12) + "-" + h.substring(12, 16) + + "-" + h.substring(16, 20) + "-" + h.substring(20, 32); + } } \ No newline at end of file diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/courserunning/ScheduleAdjustApplyVO.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/courserunning/ScheduleAdjustApplyVO.java index b18c5565..8ac46741 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/courserunning/ScheduleAdjustApplyVO.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/courserunning/ScheduleAdjustApplyVO.java @@ -100,11 +100,6 @@ public class ScheduleAdjustApplyVO { */ private Integer jyscszt; - /** - * 机关审批状态:0-未审批,1-已同意,2-已发回,3-已拒绝。 - */ - private Integer jgspzt; - /** * 发回意见。 */ diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/courserunning/ScheduleAdjustAuditVO.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/courserunning/ScheduleAdjustAuditVO.java deleted file mode 100644 index 5c19db21..00000000 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/courserunning/ScheduleAdjustAuditVO.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.roomroot.jwgl.vo.courserunning; - -import lombok.Data; - -import java.time.LocalDateTime; - -/** - * 调课申请审批记录展示数据。 - *

- * 用于展示调课申请中机关审批环节的审批记录,包括机关编号、审批时间、 - * 审批状态、审批人、查收状态、发回意见等信息。 - *

- */ -@Data -public class ScheduleAdjustAuditVO { - - /** - * 审批记录编号。 - */ - private String bh; - - /** - * 调课申请编号。 - */ - private String ssdksqbh; - - /** - * 机关编号。 - */ - private String jgbh; - - /** - * 机关名称(关联机构表获取)。 - */ - private String jgmc; - - /** - * 审批时间。 - */ - private LocalDateTime spsj; - - /** - * 审批状态:0-未审批,1-已同意,2-已发回,3-已拒绝。 - */ - private Integer spzt; - - /** - * 审批人编号。 - */ - private String sprbh; - - /** - * 审批人姓名(关联教员表获取)。 - */ - private String sprxm; - - /** - * 查收状态:0-未查收,1-已查收。 - */ - private Integer cszt; - - /** - * 查收时间。 - */ - private LocalDateTime cssj; - - /** - * 查收人编号。 - */ - private String csrbh; - - /** - * 发回意见。 - */ - private String fhyj; - - /** - * 年度。 - */ - private Integer nd; -} diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/courserunning/ScheduleAdjustDetailVO.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/courserunning/ScheduleAdjustDetailVO.java index 07902d99..beab837c 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/courserunning/ScheduleAdjustDetailVO.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/courserunning/ScheduleAdjustDetailVO.java @@ -10,7 +10,7 @@ import java.util.List; *

* 用于展示调课申请的完整信息,包括课次信息、申请教员、调整后的日期节次、 * 教学内容变更、新的教室列表、辅助教员列表、学员队列表、保障明细列表, - * 以及教研室和机关的审批记录。 + * 以及教研室审批与查收信息。 *

*/ @Data @@ -148,14 +148,14 @@ public class ScheduleAdjustDetailVO { private LocalDateTime jyscssj; /** - * 教研室查收人编号。 + * 教研室查收人编号,与审批人为同一人。 */ private String jyscsrbh; /** - * 机关审批列表,记录所有机关审批结果。 + * 发回意见。 */ - private List auditList; + private String fhyj; /** * 新教室列表。 diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/courserunning/ScheduleAdjustStrategyVO.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/courserunning/ScheduleAdjustStrategyVO.java deleted file mode 100644 index d035090d..00000000 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/courserunning/ScheduleAdjustStrategyVO.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.roomroot.jwgl.vo.courserunning; - -import lombok.Data; - -import java.time.LocalDateTime; - -/** - * 调课策略展示VO。 - */ -@Data -public class ScheduleAdjustStrategyVO { - - /** 策略编号 */ - private String clbh; - - /** 策略名称 */ - private String clmc; - - /** 策略类型:TIME_LIMIT=时间段限制,AUTO_APPROVE=免审批 */ - private String cllx; - - /** 策略类型描述 */ - private String cllxDesc; - - /** 是否启用:0=停用,1=启用 */ - private Integer sfqy; - - /** 是否启用描述 */ - private String sfqyDesc; - - /** 截止星期:0-6 */ - private Integer jzxq; - - /** 截止星期描述 */ - private String jzxqDesc; - - /** 截止节次 */ - private Integer jzjc; - - /** 生效开始时间 */ - private LocalDateTime sxkssj; - - /** 生效结束时间 */ - private LocalDateTime sxjssj; - - /** 备注 */ - private String bz; - - /** 创建时间 */ - private LocalDateTime cjsj; - - /** 更新时间 */ - private LocalDateTime gxsj; -} diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/jwtest/PkConflictCheckVO.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/jwtest/PkConflictCheckVO.java new file mode 100644 index 00000000..e6eb7da8 --- /dev/null +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/jwtest/PkConflictCheckVO.java @@ -0,0 +1,24 @@ +package com.roomroot.jwgl.vo.jwtest; + +import lombok.Data; + +import java.util.List; + +/** + * 一类冲突检查结果。 + */ +@Data +public class PkConflictCheckVO { + + /** 冲突类型 */ + private String ctlx; + + /** 检查说明 */ + private String jcsm; + + /** 冲突条数 */ + private Integer ts; + + /** 成对冲突明细 */ + private List mx; +} diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/jwtest/PkConflictVO.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/jwtest/PkConflictVO.java new file mode 100644 index 00000000..4031a6c6 --- /dev/null +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/vo/jwtest/PkConflictVO.java @@ -0,0 +1,73 @@ +package com.roomroot.jwgl.vo.jwtest; + +import lombok.Data; + +/** + * 排课冲突成对明细:一条记录表示两门课在同一时间资源上互相冲突。 + */ +@Data +public class PkConflictVO { + + /** 冲突类型 */ + private String ctlx; + + /** 冲突资源(班次 / 教员 / 教室) */ + private String ctzy; + + /** 上课日期 */ + private String skrq; + + /** 节次 */ + private Integer jc; + + /** 开始时间 */ + private String kssj; + + /** 结束时间 */ + private String jssj; + + /** 冲突说明 */ + private String sm; + + /** 课次A编号 */ + private String aKcBh; + + /** 课次A课堂名称 */ + private String aKtmc; + + /** 课次A课程名称 */ + private String aKcmc; + + /** 课次A课程类型 */ + private String aKclx; + + /** 课次A教员名称 */ + private String aJymc; + + /** 课次A班级名称 */ + private String aBjmc; + + /** 课次A教室名称 */ + private String aJsmc; + + /** 课次B编号 */ + private String bKcBh; + + /** 课次B课堂名称 */ + private String bKtmc; + + /** 课次B课程名称 */ + private String bKcmc; + + /** 课次B课程类型 */ + private String bKclx; + + /** 课次B教员名称 */ + private String bJymc; + + /** 课次B班级名称 */ + private String bBjmc; + + /** 课次B教室名称 */ + private String bJsmc; +}