Compare commits

...

97 Commits

Author SHA1 Message Date
zhaichao 81ef173454 ip变更 2026-09-10 16:07:25 +08:00
zhaichao b7110ccc9a 登录页 2026-09-07 10:59:35 +08:00
zhaichao 72f0482427 锁屏删除 2026-09-07 10:45:46 +08:00
zhaichao 449651dbd4 登录页面 2026-09-07 10:41:14 +08:00
zhaichao f62b5e394c 登录页修改 2026-09-07 09:28:59 +08:00
zhaichao 933af8ca13 切图 2026-09-02 15:00:12 +08:00
zhaichao 4c9cfb8d7f J教学日志管理 2026-08-28 20:31:41 +08:00
zhaichao f32d19e411 学员权限 2026-08-28 17:59:39 +08:00
zhaichao fa2832cf3b 学期校历 2026-08-28 14:45:19 +08:00
zhaichao d37c05aaad 校历 2026-08-28 14:24:51 +08:00
zhaichao ab3daeed65 选修课隐藏按钮 2026-08-28 11:45:17 +08:00
zhaichao 7f853afff6 校历编辑 2026-08-28 11:40:34 +08:00
zhaichao 2183b33a7f 首页通知公告权限调整 2026-08-28 09:25:57 +08:00
yangbin b34efc847d Merge remote-tracking branch 'origin/main'
# Conflicts:
#	frontend/src/layout/components/SemesterBar.vue
#	frontend/src/views/classHour/plan/index.vue
2026-08-27 17:04:52 +08:00
yangbin be7b91ac20 全局操作栏固定修复 和切换学期配色调整 2026-08-27 17:02:32 +08:00
zhaichao c2cc535d80 移除冗余文件 2026-08-27 16:49:04 +08:00
zhaichao 108d848b4b 学期 2026-08-27 16:44:07 +08:00
zhaichao 6e472028c8 教学大纲 2026-08-27 16:39:04 +08:00
zhaichao 1e852d03a9 人才培养 2026-08-27 16:38:52 +08:00
zhaichao b93375ee7a 学科专业 2026-08-27 16:38:44 +08:00
zhaichao f2071455d1 联教 2026-08-27 16:21:55 +08:00
zhaichao b33e6b4a52 顶部学期切换栏优化 2026-08-27 16:21:42 +08:00
zhaichao 5a81ce54b0 Merge branch 'main' of https://gitea.fengyingkj.top/zhaichao/education 2026-08-27 15:36:12 +08:00
zhaichao 743ec5de14 人才培养方案 2026-08-27 15:36:09 +08:00
yangbin 06715c0767 人才培养方案 新增调整 2026-08-27 15:31:19 +08:00
zhaichao 18f538cca3 人培 2026-08-27 15:30:08 +08:00
zhaichao 0de768b459 Merge branch 'main' of https://gitea.fengyingkj.top/zhaichao/education 2026-08-27 15:28:17 +08:00
zhaichao 61d563489c 大纲 2026-08-27 15:28:11 +08:00
yangbin 2328a6c938 全局用到全量查询的下拉框 做模糊查询 2026-08-27 14:40:16 +08:00
yangbin 26072ffe4e 若干前端bug修复 2026-08-27 12:01:33 +08:00
zhaichao c8990ab1a4 机关教学日志 2026-08-27 11:52:34 +08:00
zhaichao ffdcfaa350 教研室管理 2026-08-27 10:42:49 +08:00
zhaichao 90e03e1faf 模板 2026-08-27 10:31:21 +08:00
zhaichao 25f37a1d9a 移除缓存相关按钮 2026-08-27 09:36:52 +08:00
yangbin 65b96c0732 学员信息维护列表展示问题修复 2026-08-27 09:31:23 +08:00
yangbin 2778604e99 Merge remote-tracking branch 'origin/main' 2026-08-26 18:08:17 +08:00
yangbin 42e692fc8c 学员信息维护成绩表单展示 2026-08-26 18:08:09 +08:00
zhaichao 87335c4c51 Merge branch 'main' of https://gitea.fengyingkj.top/zhaichao/education 2026-08-26 18:01:24 +08:00
zhaichao 2c684c3dbf 教研室模板 2026-08-26 18:01:20 +08:00
yangbin 0877228043 学员管理前两项的调整 2026-08-26 18:00:43 +08:00
zhaichao f2aac5aa30 Merge branch 'main' of https://gitea.fengyingkj.top/zhaichao/education 2026-08-26 17:54:25 +08:00
zhaichao 73c00286e7 学员队模板 2026-08-26 17:54:22 +08:00
yangbin ed6dfd6059 学员管理界面 导航栏列表展示优化 2026-08-26 17:20:24 +08:00
zhaichao 91c2b7c0a7 教研室管理 2026-08-26 17:08:01 +08:00
zhaichao 6f59fa29e1 教研室管理 2026-08-26 17:06:33 +08:00
zhaichao 42fb981c8c 教研室管理表格新增停用 2026-08-26 16:13:28 +08:00
zhaichao 2c4cc2aaaf 学员成绩管理条件查询 2026-08-26 16:12:39 +08:00
zhaichao ebbbd5fb01 校历删除功能完善 2026-08-26 15:04:52 +08:00
zhaichao e821ac7d97 教学大纲接口变更 2026-08-26 14:22:25 +08:00
zhaichao ca7d7b6869 Merge branch 'main' of https://gitea.fengyingkj.top/zhaichao/education 2026-08-26 14:12:30 +08:00
zhaichao d0d871f7c6 教学大纲 2026-08-26 14:12:19 +08:00
zhaichao 4f9b543c74 课表调整申请新增 2026-08-26 14:12:00 +08:00
zhaichao 9ae21acaf8 校历 2026-08-26 14:10:01 +08:00
yangbin f0b8ba57ff 课表调整小卡片逻辑优化 2026-08-26 11:43:37 +08:00
yangbin 5b8c7b0e7b 人才培养方案新增表单调整 2026-08-26 10:44:36 +08:00
yangbin cf04ac37df Merge remote-tracking branch 'origin/main' 2026-08-26 10:07:11 +08:00
yangbin 3443d24d42 学员信息维护籍贯列表项的居中展示 2026-08-26 10:06:39 +08:00
zhaichao 09578b9b58 样式变更 2026-08-26 10:06:16 +08:00
zhaichao f32fdc664d chore: 取消追踪 node_modules 2026-08-26 09:57:30 +08:00
zhaichao f2ad0509c4 恢复最新前端代码 2026-08-26 09:52:26 +08:00
zhaichao 12cac0fce3 Merge branch 'main' of https://gitea.fengyingkj.top/zhaichao/education 2026-08-26 09:40:27 +08:00
zhaichao 394eb0285d Merge remote-tracking branch 'upstream/main' 2026-08-26 09:31:34 +08:00
zhaichao 9b8c4bfab6 合并 main 分支并处理冲突 2026-08-26 09:27:48 +08:00
yangbin bfb1afb470 课表以及课表调整申请 2026-08-25 20:07:13 +08:00
yangbin 303d15e234 学员信息维护 2026-08-25 19:48:36 +08:00
yangbin 13d72c54f2 停用 2026-08-25 19:25:55 +08:00
liumengyu a30b963dea 课表申请功能重写 bug修复 2026-08-25 18:47:22 +08:00
zhaichao 0240f410b6 教员 2026-08-25 18:21:53 +08:00
zhaichao 76edcb8cb3 Merge branch 'main' of https://gitea.fengyingkj.top/zhaichao/education 2026-08-25 18:08:32 +08:00
zhaichao 5c107426c1 教员管理,教研室管理 2026-08-25 18:08:29 +08:00
yangbin 7f178cb23f 全部列表页列表项 展示居中 2026-08-25 17:52:41 +08:00
yangbin 0d3c89a0c4 人才培养方案查询框的 字典录入 接口对接 2026-08-25 17:45:04 +08:00
yangbin 2adb096ebe Merge remote-tracking branch 'origin/main' 2026-08-25 17:45:00 +08:00
zhaichao 7fa8041bf7 教研室管理查询区新增是否停用 2026-08-25 17:44:32 +08:00
yangbin d7d138b8ef 新增教学场地弹窗内容调整 2026-08-25 17:39:37 +08:00
zhaichao 169b30a5c2 Merge branch 'main' of https://gitea.fengyingkj.top/zhaichao/education 2026-08-25 17:24:36 +08:00
zhaichao 94e43d7d8b 角色页面控制台报错 2026-08-25 17:24:31 +08:00
yangbin 05c1bce6d3 Merge remote-tracking branch 'origin/main' 2026-08-25 17:21:08 +08:00
yangbin 3276f47928 教学业务:教学场地里的教学场地类型下拉框 反向覆盖问题修复 2026-08-25 17:20:55 +08:00
zhaichao a731eafe29 报错修复 2026-08-25 17:17:00 +08:00
zhaichao f60db56719 Merge branch 'main' of https://gitea.fengyingkj.top/zhaichao/education 2026-08-25 17:05:48 +08:00
zhaichao 872eb4c84a 序号跨页连续 2026-08-25 17:05:41 +08:00
yangbin 99436d14c5 “分组教学任务”的“任务名称”列已居中
同步修正“教学任务计划管理”中的相同列
检查并统一了教学场地、培养方案、课表、调课、课表冲突、班次学期等列表中的短名称和编号列
2026-08-25 16:59:49 +08:00
yangbin 9d45526af2 Merge remote-tracking branch 'origin/main' 2026-08-25 16:52:32 +08:00
yangbin c98cd0aaca 分组教学任务查不到数据bug修复 2026-08-25 16:52:02 +08:00
zhaichao af7370def4 教学大纲新增对话框停用改复选框 2026-08-25 15:45:10 +08:00
yangbin 99ae7a7947 Merge remote-tracking branch 'origin/main' 2026-08-25 15:40:19 +08:00
yangbin b2d27c87d2 教学大纲字典统一 新增专业 课程等字典调整 2026-08-25 15:40:13 +08:00
zhaichao ada2d04736 Merge branch 'main' of https://gitea.fengyingkj.top/zhaichao/education 2026-08-25 15:11:14 +08:00
zhaichao 5a55d74a86 学期信息新增重复校验 2026-08-25 15:11:01 +08:00
yangbin e89863900e tab页面修改 只保持最近的八个页面 2026-08-25 15:09:20 +08:00
yangbin 2d738549b5 Merge remote-tracking branch 'origin/main' 2026-08-25 14:53:43 +08:00
yangbin d58d044c6a 全局主按钮 颜色样式统一 2026-08-25 14:53:34 +08:00
yangbin 6f61e87303 Merge remote-tracking branch 'origin/main' 2026-08-25 14:46:35 +08:00
yangbin cab512e033 全局列表展示下侧的滚动条隐藏 2026-08-25 14:46:28 +08:00
liumengyu aa4145fd33 后端代码 2026-08-24 17:48:55 +08:00
zhaichao e0485f7366 Merge pull request 'Main' (#2) from zhaichao/education:main into main
Reviewed-on: liweijie/education#2
2026-08-24 07:39:18 +00:00
177 changed files with 27201 additions and 3766 deletions
-1
View File
@@ -2,7 +2,6 @@
<project version="4"> <project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true"> <component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="DM" uuid="a3a7e92a-b8da-4229-8b83-5db11f9db9a9"> <data-source source="LOCAL" name="DM" uuid="a3a7e92a-b8da-4229-8b83-5db11f9db9a9">
<driver-ref>9fcb59ee-b3c5-470d-89df-ad01e7cabb15</driver-ref>
<imported>true</imported> <imported>true</imported>
<configured-by-url>true</configured-by-url> <configured-by-url>true</configured-by-url>
<remarks>$PROJECT_DIR$/backend/roomroot-admin/src/main/resources/application-druid.yml</remarks> <remarks>$PROJECT_DIR$/backend/roomroot-admin/src/main/resources/application-druid.yml</remarks>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourcePerFileMappings">
<file url="file://$PROJECT_DIR$/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/mapper/SSDKSQMapper.xml" value="a3a7e92a-b8da-4229-8b83-5db11f9db9a9" />
</component>
</project>
@@ -3,7 +3,6 @@ package com.roomroot.web.controller.jwgl;
import com.roomroot.jwgl.dto.courserunning.ClassesImportDTO; import com.roomroot.jwgl.dto.courserunning.ClassesImportDTO;
import com.roomroot.jwgl.dto.courserunning.ImportRemoveDTO; import com.roomroot.jwgl.dto.courserunning.ImportRemoveDTO;
import com.roomroot.jwgl.dto.courserunning.ScheduleAdjustApplyDTO; 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.ScheduleAdjustAuditDTO;
import com.roomroot.jwgl.dto.courserunning.ScheduleAdjustQueryDTO; import com.roomroot.jwgl.dto.courserunning.ScheduleAdjustQueryDTO;
import com.roomroot.jwgl.dto.courserunning.SemesterImportDTO; import com.roomroot.jwgl.dto.courserunning.SemesterImportDTO;
@@ -290,24 +289,26 @@ public class CourseRunningController {
/** /**
* 分页查询调课申请列表。 * 分页查询调课申请列表。
* <p>
* 根据查询条件分页查询调课申请列表,支持按实施_课程表编号、申请教员、
* 年度、审批状态、查收状态、删除状态、申请日期范围筛选。
* </p>
*
* @param query 查询参数(包含分页信息和筛选条件)
* @return 分页调课申请列表
*/ */
@GetMapping("/adjust/list") @GetMapping("/adjust/list")
public Result<PageResult<ScheduleAdjustApplyVO>> queryScheduleAdjustList(ScheduleAdjustQueryDTO query) { public Result<PageResult<ScheduleAdjustApplyVO>> queryScheduleAdjustList(ScheduleAdjustQueryDTO query) {
return Result.success(courseRunningImportService.queryScheduleAdjustList(query)); return Result.success(courseRunningImportService.queryScheduleAdjustList(query));
} }
/**
* 查询当前教员可供调课的课次(学员队任务表 + 课表 + 课程表)。
*/
@GetMapping("/adjust/lessons")
public Result<List<AdjustableLessonVO>> listAdjustableLessons(
@RequestParam(value = "nd", required = false) Integer nd) {
return Result.success(courseRunningImportService.listAdjustableLessons(nd));
}
/** /**
* 查询单个调课申请的完整详情。 * 查询单个调课申请的完整详情。
* <p> * <p>
* 根据调课申请编号查询该申请的完整信息,包括申请信息、调整后的内容、 * 根据调课申请编号查询该申请的完整信息,包括申请信息、调整后的内容、
* 教研室审批状态、机关审批记录、关联的教室、辅助教员、学员队、保障明细等。 * 教研室审批与查收状态、关联的教室、辅助教员、学员队、保障明细等。
* </p> * </p>
* *
* @param ssdksqbh 调课申请编号 * @param ssdksqbh 调课申请编号
@@ -336,148 +337,30 @@ public class CourseRunningController {
/** /**
* 教研室审批调课申请。 * 教研室审批调课申请。
* <p> * <p>
* 教研室审批教员提交的调课申请,包含同意、发回、拒绝三种处理方式。 * 教研室审批调课申请。点审批即已查收,审批人同时记为查收人。
* 同意、发回、拒绝均为已查收;教员撤回后为未查收。
* </p> * </p>
* *
* @param dto 教研室审批参数(auditRole 传 "jys") * @param dto 教研室审批参数
* @return 操作结果 * @return 操作结果
*/ */
@PostMapping("/adjust/audit/jys") @PostMapping("/adjust/audit/jys")
public Result<Void> auditByTeachingOffice(@Valid @RequestBody ScheduleAdjustAuditDTO dto) { public Result<Void> auditByTeachingOffice(@Valid @RequestBody ScheduleAdjustAuditDTO dto) {
// 自动设置审批角色为教研室
dto.setAuditRole("jys");
courseRunningImportService.auditByTeachingOffice(dto); courseRunningImportService.auditByTeachingOffice(dto);
return Result.success(); return Result.success();
} }
/**
* 教研室查收调课申请。
* <p>
* 教研室查收调课申请,标记查收状态和查收时间。
* </p>
*
* @param ssdksqbh 调课申请编号
* @param csrbh 查收人编号
* @return 操作结果
*/
@PostMapping("/adjust/receive/jys")
public Result<Void> receiveByTeachingOffice(@RequestParam("ssdksqbh") String ssdksqbh,
@RequestParam("csrbh") String csrbh) {
courseRunningImportService.receiveByTeachingOffice(ssdksqbh, csrbh);
return Result.success();
}
/**
* 教学系审批调课申请。
* <p>
* 教学系审批教研室已同意的调课申请,包含同意、发回、拒绝三种处理方式。
* 构成三级审批流程的中间环节(教研室→教学系→机关)。
* </p>
*
* @param dto 教学系审批参数(auditRole 传 "jxx")
* @return 操作结果
*/
@PostMapping("/adjust/audit/jxx")
public Result<Void> auditByTeachingDepartment(@Valid @RequestBody ScheduleAdjustAuditDTO dto) {
// 自动设置审批角色为教学系
dto.setAuditRole("jxx");
courseRunningImportService.auditByTeachingDepartment(dto);
return Result.success();
}
/**
* 机关审批调课申请。
* <p>
* 机关审批教研室已同意的调课申请,包含同意、发回、拒绝三种处理方式。
* </p>
*
* @param dto 机关审批参数(auditRole 传 "jg",需传 jgbh)
* @return 操作结果
*/
@PostMapping("/adjust/audit/jg")
public Result<Void> auditByAuthority(@Valid @RequestBody ScheduleAdjustAuditDTO dto) {
// 自动设置审批角色为机关
dto.setAuditRole("jg");
courseRunningImportService.auditByAuthority(dto);
return Result.success();
}
/**
* 机关查收调课申请。
* <p>
* 机关查收调课申请,标记查收状态和查收时间。
* </p>
*
* @param ssdksqbh 调课申请编号
* @param jgbh 机关编号
* @param csrbh 查收人编号
* @return 操作结果
*/
@PostMapping("/adjust/receive/jg")
public Result<Void> receiveByAuthority(@RequestParam("ssdksqbh") String ssdksqbh,
@RequestParam("jgbh") String jgbh,
@RequestParam("csrbh") String csrbh) {
courseRunningImportService.receiveByAuthority(ssdksqbh, jgbh, csrbh);
return Result.success();
}
/** /**
* 撤销调课申请。 * 撤销调课申请。
* <p> * <p>
* 教员撤销自己提交的、未经过审批的调课申请。 * 教员撤销未经过审批的调课申请。列表已按登录教员过滤,只需传申请编号。
* </p> * </p>
* *
* @param ssdksqbh 调课申请编号 * @param ssdksqbh 调课申请编号
* @param sqjybh 申请教员编号
* @return 操作结果 * @return 操作结果
*/ */
@DeleteMapping("/adjust/cancel") @DeleteMapping("/adjust/cancel")
public Result<Void> cancelScheduleAdjust(@RequestParam("ssdksqbh") String ssdksqbh, public Result<Void> cancelScheduleAdjust(@RequestParam("ssdksqbh") String ssdksqbh) {
@RequestParam("sqjybh") String sqjybh) { courseRunningImportService.cancelScheduleAdjust(ssdksqbh);
courseRunningImportService.cancelScheduleAdjust(ssdksqbh, sqjybh);
return Result.success();
}
// ==================== 调课策略管理 ====================
/**
* 查询调课策略列表。
* <p>
* 支持按策略类型筛选,返回所有策略(含停用)。
* </p>
*
* @param cllx 策略类型:TIME_LIMIT=时间段限制,AUTO_APPROVE=免审批,为空时查询全部
* @return 调课策略列表
*/
@GetMapping("/adjust/strategy/list")
public Result<List<ScheduleAdjustStrategyVO>> listScheduleAdjustStrategies(
@RequestParam(required = false) String cllx) {
return Result.success(courseRunningImportService.listScheduleAdjustStrategies(cllx));
}
/**
* 保存调课策略(新增或修改)。
* <p>
* 策略编号为空时执行新增,非空时执行修改。
* </p>
*
* @param dto 策略参数
* @return 策略编号
*/
@PostMapping("/adjust/strategy/save")
public Result<String> saveScheduleAdjustStrategy(@Valid @RequestBody ScheduleAdjustStrategyDTO dto) {
return Result.success("保存成功", courseRunningImportService.saveScheduleAdjustStrategy(dto));
}
/**
* 删除调课策略。
*
* @param clbh 策略编号
* @return 操作结果
*/
@DeleteMapping("/adjust/strategy/delete")
public Result<Void> deleteScheduleAdjustStrategy(@RequestParam("clbh") String clbh) {
courseRunningImportService.deleteScheduleAdjustStrategy(clbh);
return Result.success(); return Result.success();
} }
@@ -1,20 +1,41 @@
package com.roomroot.web.controller.jwgl; package com.roomroot.web.controller.jwgl;
import com.roomroot.jwgl.dto.faculty.AddTeacherDTO; import com.roomroot.jwgl.dto.faculty.AddTeacherDTO;
import com.roomroot.jwgl.dto.jys.ElectiveCourseBatchDTO;
import com.roomroot.jwgl.dto.jys.ElectiveCourseQuery;
import com.roomroot.jwgl.dto.jys.ElectiveCourseSaveDTO;
import com.roomroot.jwgl.dto.jys.TeachingTaskQuery;
import com.roomroot.jwgl.dto.jys.TeachingTaskSaveDTO;
import com.roomroot.jwgl.entity.JYB; import com.roomroot.jwgl.entity.JYB;
import com.roomroot.jwgl.entity.JYSB; import com.roomroot.jwgl.entity.JYSB;
import com.roomroot.jwgl.entity.JYSX; import com.roomroot.jwgl.entity.JYSX;
import com.roomroot.jwgl.entity.KB; import com.roomroot.jwgl.entity.KB;
import com.roomroot.jwgl.service.ElectiveCourseService;
import com.roomroot.jwgl.service.FacultyService; import com.roomroot.jwgl.service.FacultyService;
import com.roomroot.jwgl.service.KBService; import com.roomroot.jwgl.service.KBService;
import com.roomroot.jwgl.service.TeachingTaskManageService;
import com.roomroot.jwgl.unit.PageQuery; import com.roomroot.jwgl.unit.PageQuery;
import com.roomroot.jwgl.unit.PageResult; import com.roomroot.jwgl.unit.PageResult;
import com.roomroot.jwgl.unit.Result; import com.roomroot.jwgl.unit.Result;
import com.roomroot.jwgl.vo.faculty.TeacherListVO; import com.roomroot.jwgl.vo.faculty.TeacherListVO;
import com.roomroot.jwgl.vo.jys.ElectiveCourseVO;
import com.roomroot.jwgl.vo.jys.TeachingOfficeBookCourseVO;
import com.roomroot.jwgl.vo.jys.TeachingOfficeBookVO;
import com.roomroot.jwgl.vo.jys.TeachingTaskDetailVO;
import com.roomroot.jwgl.vo.jys.TeachingTaskSquadVO;
import com.roomroot.jwgl.vo.jys.TeachingTaskVO;
import com.roomroot.jwgl.unit.PageQuery;
import com.roomroot.jwgl.unit.PageResult;
import com.roomroot.jwgl.unit.Result;
import com.roomroot.jwgl.vo.faculty.TeacherListVO;
import com.roomroot.jwgl.vo.jys.ElectiveCourseVO;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/** /**
* 教研室管理(教研室管理 * 教研室管理(教研室管理
* 教员管理 * 教员管理
@@ -32,6 +53,12 @@ public class JYSController {
@Resource @Resource
private KBService kbService; private KBService kbService;
@Resource
private ElectiveCourseService electiveCourseService;
@Resource
private TeachingTaskManageService teachingTaskManageService;
// ======================== 教研室管理 ======================== // ======================== 教研室管理 ========================
/** /**
@@ -194,6 +221,25 @@ public class JYSController {
return Result.success(pageResult); return Result.success(pageResult);
} }
/**
* 教员数据文件导入(.xls/.xlsx)
* <p>表头顺序:教员编号, 教员姓名, 教研室代号, 职称, 专业技术职务等级, 教员类别,
* 性别, 手机号, 身份证号, 备注, 序号, 虚实类型。教员编号不填自动创建,离职状态默认 0。</p>
*/
@PostMapping("/teacher/import")
public Result<Integer> 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);
}
// ======================== 教员属性管理 ======================== // ======================== 教员属性管理 ========================
/** /**
@@ -303,4 +349,233 @@ public class JYSController {
cond.setJc(jc); cond.setJc(jc);
return Result.success(kbService.pageList(new PageQuery(pageNum, pageSize), cond)); return Result.success(kbService.pageList(new PageQuery(pageNum, pageSize), cond));
} }
// ======================== 选修课管理 ========================
/**
* 分页查询选修课。
* <p>
* 联查学员队任务表、学员队表、课表,课类型含“选修”。
* 支持按授课教员、课程科目名称、可选班次年级、选修班名称、选择课状态、培训层次筛选。
* </p>
*/
@GetMapping("/elective/list")
public Result<PageResult<ElectiveCourseVO>> listElective(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "20") Integer pageSize,
@RequestParam(required = false) String jyxm,
@RequestParam(required = false) String kmc,
@RequestParam(required = false) String kxbcnj,
@RequestParam(required = false) String xxbmc,
@RequestParam(required = false) String xkzt,
@RequestParam(required = false) String pxcc) {
ElectiveCourseQuery cond = new ElectiveCourseQuery();
cond.setJyxm(jyxm);
cond.setKmc(kmc);
cond.setKxbcnj(kxbcnj);
cond.setXxbmc(xxbmc);
cond.setXkzt(xkzt);
cond.setPxcc(pxcc);
return Result.success(electiveCourseService.pageList(new PageQuery(pageNum, pageSize), cond));
}
/**
* 新建选修课。
*/
@PostMapping("/elective/add")
public Result<Void> addElective(@RequestBody ElectiveCourseSaveDTO dto) {
electiveCourseService.add(dto);
return Result.success();
}
/**
* 所选批量开放报名。
*/
@PostMapping("/elective/batch-open")
public Result<Integer> batchOpenElective(@RequestBody ElectiveCourseBatchDTO dto) {
int count = electiveCourseService.batchOpenRegistration(dto == null ? null : dto.getBhList());
return Result.success("已开放报名 " + count + " 条", count);
}
/**
* 所选批量取消开放报名。
*/
@PostMapping("/elective/batch-cancel-open")
public Result<Integer> batchCancelOpenElective(@RequestBody ElectiveCourseBatchDTO dto) {
int count = electiveCourseService.batchCancelOpenRegistration(dto == null ? null : dto.getBhList());
return Result.success("已取消开放报名 " + count + " 条", count);
}
/**
* 批量停止报名。
*/
@PostMapping("/elective/batch-stop")
public Result<Integer> batchStopElective(@RequestBody ElectiveCourseBatchDTO dto) {
int count = electiveCourseService.batchStopRegistration(dto == null ? null : dto.getBhList());
return Result.success("已停止报名 " + count + " 条", count);
}
/**
* 所选批量根据学员反向确定班次范围。
*/
@PostMapping("/elective/batch-reverse-range")
public Result<Integer> batchReverseRangeElective(@RequestBody ElectiveCourseBatchDTO dto) {
int count = electiveCourseService.batchReverseClassRange(dto == null ? null : dto.getBhList());
return Result.success("已根据学员反向确定班次范围 " + count + " 条", count);
}
/**
* 下载选修班列表。
*/
@GetMapping("/elective/export")
public void exportElective(
@RequestParam(required = false) String jyxm,
@RequestParam(required = false) String kmc,
@RequestParam(required = false) String kxbcnj,
@RequestParam(required = false) String xxbmc,
@RequestParam(required = false) String xkzt,
@RequestParam(required = false) String pxcc,
HttpServletResponse response) {
ElectiveCourseQuery cond = new ElectiveCourseQuery();
cond.setJyxm(jyxm);
cond.setKmc(kmc);
cond.setKxbcnj(kxbcnj);
cond.setXxbmc(xxbmc);
cond.setXkzt(xkzt);
cond.setPxcc(pxcc);
electiveCourseService.exportClassList(cond, response);
}
/**
* 下载选修班学员名单 Excel。
*/
@GetMapping("/elective/students/export-excel")
public void exportElectiveStudentExcel(@RequestParam("bhList") List<String> bhList,
HttpServletResponse response) {
electiveCourseService.exportStudentExcel(bhList, response);
}
/**
* 下载选修班学员名单 Word。
*/
@GetMapping("/elective/students/export-word")
public void exportElectiveStudentWord(@RequestParam("bhList") List<String> bhList,
HttpServletResponse response) {
electiveCourseService.exportStudentWord(bhList, response);
}
/**
* 上传选修课数据。
*/
@PostMapping("/elective/import")
public Result<Integer> importElective(@RequestParam("file") MultipartFile file) throws Exception {
int count = electiveCourseService.importData(file);
return Result.success("导入成功,共" + count + "条", count);
}
// ======================== 教学任务管理 ========================
/**
* 分页查询教学任务列表。
* <p>含教研室数、学员队数、合班前后课程任务数和学时。未传年度时按当前学期。</p>
*/
@GetMapping("/task/list")
public Result<PageResult<TeachingTaskVO>> listTask(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "20") Integer pageSize,
@RequestParam(required = false) String rwmc,
@RequestParam(required = false) Integer nd,
@RequestParam(required = false) Integer xqdc,
@RequestParam(required = false) String zt) {
TeachingTaskQuery cond = new TeachingTaskQuery();
cond.setRwmc(rwmc);
cond.setNd(nd);
cond.setXqdc(xqdc);
cond.setZt(zt);
return Result.success(teachingTaskManageService.pageList(new PageQuery(pageNum, pageSize), cond));
}
/**
* 教学任务详情(含教研室任务书、学员队两个页签)。
*/
@GetMapping("/task/get")
public Result<TeachingTaskDetailVO> getTask(@RequestParam("bh") String bh) {
return Result.success(teachingTaskManageService.getDetail(bh));
}
/**
* 新增教学任务。
* <p>机关发布;关联学员队后按专业教学计划和课表归属教研室生成教研室任务书。</p>
*/
@PostMapping("/task/add")
public Result<Void> addTask(@RequestBody TeachingTaskSaveDTO dto) {
teachingTaskManageService.add(dto);
return Result.success();
}
/**
* 编辑教学任务。
*/
@PostMapping("/task/update")
public Result<Void> updateTask(@RequestBody TeachingTaskSaveDTO dto) {
teachingTaskManageService.update(dto);
return Result.success();
}
/**
* 删除教学任务(同时逻辑删除教研室任务书,并解除学员队关联)。
*/
@PostMapping("/task/delete")
public Result<Void> deleteTask(@RequestParam("bh") String bh) {
teachingTaskManageService.delete(bh);
return Result.success();
}
/**
* 发布教学任务,并同步发布关联的教研室任务书。
*/
@PostMapping("/task/publish")
public Result<Integer> publishTask(@RequestParam("bh") String bh) {
int count = teachingTaskManageService.publish(bh);
return Result.success("已发布,同步教研室任务书 " + count + " 份", count);
}
/**
* 教研室任务书列表。
*/
@GetMapping("/task/office-book/list")
public Result<List<TeachingOfficeBookVO>> listTaskOfficeBooks(@RequestParam("jxrwbh") String jxrwbh) {
return Result.success(teachingTaskManageService.listOfficeBooks(jxrwbh));
}
/**
* 教研室任务书详细(该教研室在本任务下的课程任务)。
*/
@GetMapping("/task/office-book/courses")
public Result<List<TeachingOfficeBookCourseVO>> listTaskOfficeBookCourses(
@RequestParam("jxrwbh") String jxrwbh,
@RequestParam("jysdh") String jysdh) {
return Result.success(teachingTaskManageService.listOfficeBookCourses(jxrwbh, jysdh));
}
/**
* 教学任务已关联学员队。
*/
@GetMapping("/task/squad/list")
public Result<List<TeachingTaskSquadVO>> listTaskSquads(@RequestParam("jxrwbh") String jxrwbh) {
return Result.success(teachingTaskManageService.listBoundSquads(jxrwbh));
}
/**
* 某学期可选学员队(未占用或占用当前任务)。年度、学期第次为空时取当前学期。
*/
@GetMapping("/task/squad/available")
public Result<List<TeachingTaskSquadVO>> listAvailableTaskSquads(
@RequestParam(required = false) Integer nd,
@RequestParam(required = false) Integer xqdc,
@RequestParam(required = false) String jxrwbh) {
return Result.success(teachingTaskManageService.listAvailableSquads(nd, xqdc, jxrwbh));
}
} }
@@ -1,8 +1,14 @@
package com.roomroot.web.controller.jwgl; package com.roomroot.web.controller.jwgl;
import com.roomroot.jwgl.dto.kcb.TimetableQuery;
import com.roomroot.jwgl.entity.KCB; import com.roomroot.jwgl.entity.KCB;
import com.roomroot.jwgl.service.KCBService; import com.roomroot.jwgl.service.KCBService;
import com.roomroot.jwgl.unit.PageQuery;
import com.roomroot.jwgl.unit.PageResult;
import com.roomroot.jwgl.unit.Result; import com.roomroot.jwgl.unit.Result;
import com.roomroot.jwgl.vo.kcb.ClassTimetableVO;
import com.roomroot.jwgl.vo.kcb.DailyWeeklyTimetableVO;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
@@ -69,4 +75,52 @@ public class KCBController {
public Result<List<KCB>> list() { public Result<List<KCB>> list() {
return Result.success(kcbService.list()); return Result.success(kcbService.list());
} }
/**
* 今日课表。未传年度时按当前时间取本学期,可按队别班次筛选。
*/
@GetMapping("/today/list")
public Result<List<DailyWeeklyTimetableVO>> listToday(TimetableQuery cond) {
return Result.success(kcbService.listToday(cond));
}
/**
* 本周课表(周一至周日)。未传年度时按当前时间取本学期,可按队别班次筛选。
*/
@GetMapping("/week/list")
public Result<List<DailyWeeklyTimetableVO>> listWeek(TimetableQuery cond) {
return Result.success(kcbService.listWeek(cond));
}
/**
* 班次课表。未传年度时按当前时间取本学期,可按队别班次筛选。
*/
@GetMapping("/class/list")
public Result<PageResult<ClassTimetableVO>> listClass(PageQuery query, TimetableQuery cond) {
return Result.success(kcbService.pageClassTimetable(query, cond));
}
/**
* 下载学期教学实施计划 Excel。
*/
@GetMapping("/class/export-plan")
public void exportSemesterPlan(TimetableQuery cond, HttpServletResponse response) {
kcbService.exportSemesterPlan(cond, response);
}
/**
* 下载学期成绩表 Excel。
*/
@GetMapping("/class/export-grades")
public void exportSemesterGrades(TimetableQuery cond, HttpServletResponse response) {
kcbService.exportSemesterGrades(cond, response);
}
/**
* 班次课程列表另存 Excel。
*/
@GetMapping("/class/export-courses")
public void exportClassCourses(TimetableQuery cond, HttpServletResponse response) {
kcbService.exportClassCourses(cond, response);
}
} }
@@ -25,6 +25,10 @@ public class SemesterController {
*/ */
@PostMapping("/add") @PostMapping("/add")
public Result<Void> add(@RequestBody XQ xq) { public Result<Void> add(@RequestBody XQ xq) {
if (semesterService.getById(xq.getNd()) != null) {
return Result.error(400, "该年度已存在,不能重复添加");
}
semesterService.add(xq); semesterService.add(xq);
return Result.success(); return Result.success();
} }
@@ -5,7 +5,9 @@ import com.roomroot.jwgl.service.TrainingProgramService;
import com.roomroot.jwgl.unit.PageQuery; import com.roomroot.jwgl.unit.PageQuery;
import com.roomroot.jwgl.unit.PageResult; import com.roomroot.jwgl.unit.PageResult;
import com.roomroot.jwgl.unit.Result; import com.roomroot.jwgl.unit.Result;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
@@ -115,4 +117,23 @@ public class TrainingProgramController {
PageResult<ZYB> pageResult = trainingProgramService.pageList(query, cond); PageResult<ZYB> pageResult = trainingProgramService.pageList(query, cond);
return Result.success(pageResult); return Result.success(pageResult);
} }
/**
* 培养方案(专业)数据文件导入(.xls/.xlsx)。
* <p>表头顺序:专业代号, 专业名称, 专业代码, 专业版本, 培训类型, 培训层次, 学年制,
* 学期数, 专业方向, 专业备注, 学员类别, 简称, 培训类型2。专业代号不填自动生成。</p>
*/
@PostMapping("/import")
public Result<Integer> 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);
}
} }
@@ -3,7 +3,9 @@ package com.roomroot.web.controller.jwgl;
import com.roomroot.jwgl.entity.ZYJXJHB; import com.roomroot.jwgl.entity.ZYJXJHB;
import com.roomroot.jwgl.service.ZYJXJHBService; import com.roomroot.jwgl.service.ZYJXJHBService;
import com.roomroot.jwgl.unit.Result; import com.roomroot.jwgl.unit.Result;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import java.util.List; import java.util.List;
@@ -81,4 +83,23 @@ public class ZYJXJHBController {
@RequestParam("ty") Integer ty) { @RequestParam("ty") Integer ty) {
return Result.success(zyjxjhbService.listByZydhAndTy(zydh, ty)); return Result.success(zyjxjhbService.listByZydhAndTy(zydh, ty));
} }
/**
* 专业教学计划表数据文件导入(.xls/.xlsx)。
* <p>表头顺序:专业代号, 课编号, 学期第次, 课类型, 学时, 学分, 周课时, 课程定位,
* 模块, 成绩分制, 理论学时, 实践学时, 考试课时, 教研室代号, 简称。编号由系统自动生成。</p>
*/
@PostMapping("/import")
public Result<Integer> 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);
}
} }
@@ -0,0 +1,90 @@
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;
/**
* 排课测试接口。
*/
@RestController
@RequestMapping("/jwtest/pk")
public class TestPkController {
@Resource
private TestPkMapper testPkMapper;
/**
* 全查排课记录,无筛选条件。
* <p>
* 回显:课堂名称、课程名称、教师名称、班级名称、教室名称、
* 上课日期、开始时间、结束时间、学年学期、周次、应到人数。
* </p>
*/
@GetMapping("/all")
public Result<List<PkLessonVO>> all() {
return Result.success(testPkMapper.selectAll());
}
/**
* 教学班时间冲突:同一个班在同一日期同一节次被安排多门课程。
*/
@GetMapping("/conflict/class")
public Result<PkConflictCheckVO> classTimeConflict(@RequestParam(required = false) Integer nd) {
return Result.success(wrap(
"教学班时间冲突",
"同一班次在同一天同一节次被安排了多门课程",
testPkMapper.selectClassTimeConflict(nd)));
}
/**
* 必修课与选修课时间冲突:同一班次同一时间既有必修又有选修。
*/
@GetMapping("/conflict/elective-required")
public Result<PkConflictCheckVO> electiveRequiredConflict(@RequestParam(required = false) Integer nd) {
return Result.success(wrap(
"必修选修时间冲突",
"同一班次在同一天同一节次同时安排了必修课和选修课",
testPkMapper.selectElectiveRequiredConflict(nd)));
}
/**
* 教员时间冲突:同一教员在同一时间段被安排多门课程。
*/
@GetMapping("/conflict/teacher")
public Result<PkConflictCheckVO> teacherTimeConflict(@RequestParam(required = false) Integer nd) {
return Result.success(wrap(
"教员时间冲突",
"同一教员在同一天同一节次被安排了多门课程",
testPkMapper.selectTeacherTimeConflict(nd)));
}
/**
* 教室时间冲突:同一教室在同一时间段被多门课程占用。
*/
@GetMapping("/conflict/classroom")
public Result<PkConflictCheckVO> classroomTimeConflict(@RequestParam(required = false) Integer nd) {
return Result.success(wrap(
"教室时间冲突",
"同一教室在同一天同一节次被多门课程占用",
testPkMapper.selectClassroomTimeConflict(nd)));
}
private PkConflictCheckVO wrap(String ctlx, String jcsm, List<PkConflictVO> mx) {
PkConflictCheckVO vo = new PkConflictCheckVO();
vo.setCtlx(ctlx);
vo.setJcsm(jcsm);
vo.setMx(mx);
vo.setTs(mx == null ? 0 : mx.size());
return vo;
}
}
@@ -107,6 +107,7 @@ mybatis-plus:
typeAliasesPackage: com.roomroot.**.domain typeAliasesPackage: com.roomroot.**.domain
# 配置mapper的扫描,找到所有的mapper.xml映射文件 # 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml,classpath*:com/roomroot/**/mapper/*.xml mapperLocations: classpath*:mapper/**/*Mapper.xml,classpath*:com/roomroot/**/mapper/*.xml
typeHandlersPackage: com.roomroot.jwgl.handler
# 加载全局的配置文件 # 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml configLocation: classpath:mybatis/mybatis-config.xml
@@ -13,7 +13,7 @@ PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
<setting name="defaultExecutorType" value="SIMPLE" /> <setting name="defaultExecutorType" value="SIMPLE" />
<!-- 指定 MyBatis 所用日志的具体实现 --> <!-- 指定 MyBatis 所用日志的具体实现 -->
<setting name="logImpl" value="SLF4J" /> <setting name="logImpl" value="SLF4J" />
<!-- 使用驼峰命名法转换字段 --> <!-- 使用驼峰命名法转换字段 -->
<!-- <setting name="mapUnderscoreToCamelCase" value="true"/> --> <!-- <setting name="mapUnderscoreToCamelCase" value="true"/> -->
</settings> </settings>
@@ -100,7 +100,7 @@ public class SecurityConfig
.authorizeHttpRequests((requests) -> { .authorizeHttpRequests((requests) -> {
permitAllUrl.getUrls().forEach(url -> requests.requestMatchers(url).permitAll()); permitAllUrl.getUrls().forEach(url -> requests.requestMatchers(url).permitAll());
// 对于登录login 注册register 验证码captchaImage 允许匿名访问 // 对于登录login 注册register 验证码captchaImage 允许匿名访问
requests.requestMatchers("/login", "/register", "/captchaImage","/jys/teacher/list").permitAll() requests.requestMatchers("/login", "/register", "/captchaImage", "/jwtest/pk/all").permitAll()
// 静态资源,可匿名访问 // 静态资源,可匿名访问
.requestMatchers(HttpMethod.GET, "/", "/*.html", "/**.html", "/**.css", "/**.js", "/profile/**").permitAll() .requestMatchers(HttpMethod.GET, "/", "/*.html", "/**.html", "/**.css", "/**.js", "/profile/**").permitAll()
.requestMatchers("/swagger-ui.html", "/v3/api-docs/**", "/swagger-ui/**", "/druid/**").permitAll() .requestMatchers("/swagger-ui.html", "/v3/api-docs/**", "/swagger-ui/**", "/druid/**").permitAll()
@@ -73,6 +73,10 @@ public class TokenService
String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
String userKey = getTokenKey(uuid); String userKey = getTokenKey(uuid);
LoginUser user = redisCache.getCacheObject(userKey); LoginUser user = redisCache.getCacheObject(userKey);
if (user == null)
{
log.warn("令牌签名有效,但登录缓存已失效,请重新登录");
}
return user; return user;
} }
catch (Exception e) catch (Exception e)
@@ -218,14 +222,42 @@ public class TokenService
*/ */
private String getToken(HttpServletRequest request) private String getToken(HttpServletRequest request)
{ {
String token = request.getHeader(header); String token = firstHeader(request, header, "Authorization", "token");
if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) 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; 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) private String getTokenKey(String uuid)
{ {
return CacheConstants.LOGIN_TOKEN_KEY + uuid; return CacheConstants.LOGIN_TOKEN_KEY + uuid;
@@ -1,88 +1,25 @@
package com.roomroot.jwgl.config; package com.roomroot.jwgl.config;
import com.roomroot.jwgl.interceptor.AccountManagementAuthorizationInterceptor;
import com.roomroot.jwgl.interceptor.JwglApiAccessInterceptor;
import com.roomroot.jwgl.interceptor.OperationLogInterceptor; import com.roomroot.jwgl.interceptor.OperationLogInterceptor;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/** /**
* 在线账户状态和账户管理权限 Web MVC 配置。 * 操作日志采集 Web MVC 配置。
* * <p>页面菜单权限由前端控制;后台只校验登录态,查询时再按角色和用户ID过滤数据。</p>
* <p>该配置对已有在线会话执行账户状态校验,同时保持普通匿名请求的原有访问规则;
* 账户管理路径继续执行超级管理员权限校验,并在校验通过后采集操作日志上下文。</p>
*/ */
@Configuration @Configuration
public class AccountManagementWebMvcConfig implements WebMvcConfigurer { public class AccountManagementWebMvcConfig implements WebMvcConfigurer {
/**
* 账户管理身份和权限拦截器。
*/
private final AccountManagementAuthorizationInterceptor
authorizationInterceptor;
/**
* 操作日志统一请求采集拦截器。
*/
private final OperationLogInterceptor operationLogInterceptor; private final OperationLogInterceptor operationLogInterceptor;
private final JwglApiAccessInterceptor jwglApiAccessInterceptor; public AccountManagementWebMvcConfig(OperationLogInterceptor operationLogInterceptor) {
/**
* 构造账户管理和操作日志统一 Web MVC 配置。
*
* @param authorizationInterceptor 账户管理身份和权限拦截器
* @param operationLogInterceptor 操作日志统一请求采集拦截器
* @param jwglApiAccessInterceptor 教务角色接口访问拦截器
*/
public AccountManagementWebMvcConfig(
AccountManagementAuthorizationInterceptor
authorizationInterceptor,
OperationLogInterceptor operationLogInterceptor,
JwglApiAccessInterceptor jwglApiAccessInterceptor) {
// 第一步:保存账户管理专用拦截器,供 MVC 注册阶段使用。
this.authorizationInterceptor = authorizationInterceptor;
// 第二步:保存操作日志采集拦截器,供 MVC 注册阶段使用。
this.operationLogInterceptor = operationLogInterceptor; this.operationLogInterceptor = operationLogInterceptor;
this.jwglApiAccessInterceptor = jwglApiAccessInterceptor;
} }
/**
* 注册账户状态校验、账户管理权限和操作日志采集规则。
*
* @param registry Spring MVC 拦截器注册器
*/
@Override @Override
public void addInterceptors(InterceptorRegistry registry) { 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) registry.addInterceptor(operationLogInterceptor)
.addPathPatterns("/**"); .addPathPatterns("/**");
} }
@@ -17,17 +17,17 @@ import java.util.List;
public class ScheduleAdjustApplyDTO { public class ScheduleAdjustApplyDTO {
/** /**
* 实施_课程表编号,必填,标识需要调整的课次。 * 实施_课程表编号,必填,对应实施_调课申请.实施_课程表编号。
*/ */
@NotBlank(message = "课次编号不能为空") @NotBlank(message = "课次编号不能为空")
@Size(max = 50, message = "课次编号不能超过50个字符") @Size(max = 50, message = "课次编号不能超过50个字符")
private String sskcbbh; private String sskcbbh;
/** /**
* 调课事由,必填,描述本次调课的原因和依据。 * 调课事由,对应 实施_调课申请.事由 VARCHAR(200)。
*/ */
@NotBlank(message = "调课事由不能为空") @NotBlank(message = "调课事由不能为空")
@Size(max = 500, message = "调课事由不能超过500个字符") @Size(max = 200, message = "调课事由不能超过200个字符")
private String sy; private String sy;
/** /**
@@ -49,16 +49,39 @@ public class ScheduleAdjustApplyDTO {
private Integer nd; private Integer nd;
/** /**
* 申请教员编号,必填,标识提交申请的教员。 * 申请教员编号,教员提交时由服务端按登录人写入,前端可不传。
*/ */
@NotBlank(message = "申请教员编号不能为空")
@Size(max = 50, message = "申请教员编号不能超过50个字符") @Size(max = 50, message = "申请教员编号不能超过50个字符")
private String sqjybh; private String sqjybh;
/** /**
* 调课后教学内容,调整后的本节课教学主题或知识点。 * 学员队任务表编号,由服务端根据实施课次反查,前端可不传。
*/ */
@Size(max = 2000, message = "教学内容不能超过2000个字符") @Size(max = 50, message = "学员队任务编号不能超过50个字符")
private String xydrwbh;
/**
* 课表.课编号。
*/
@Size(max = 50, message = "课编号不能超过50个字符")
private String kbh;
/**
* 课程表编号。
*/
@Size(max = 50, message = "课程表编号不能超过50个字符")
private String kcbbh;
/**
* 拟调整教室编号。
*/
@Size(max = 50, message = "教室编号不能超过50个字符")
private String xjsbh;
/**
* 调课后教学内容,对应 实施_调课申请.教学内容 VARCHAR(500)。
*/
@Size(max = 500, message = "教学内容不能超过500个字符")
private String jxnr; private String jxnr;
/** /**
@@ -68,9 +91,9 @@ public class ScheduleAdjustApplyDTO {
private String jxyd; private String jxyd;
/** /**
* 调课后教学方法,调整后的教学方法。 * 调课后教学方法,对应 实施_调课申请.教学方法 VARCHAR(50)。
*/ */
@Size(max = 200, message = "教学方法不能超过200个字符") @Size(max = 50, message = "教学方法不能超过50个字符")
private String jxff; private String jxff;
/** /**
@@ -86,9 +109,9 @@ public class ScheduleAdjustApplyDTO {
private String ycxx; private String ycxx;
/** /**
* 备注,补充说明信息。 * 备注,对应 实施_调课申请.备注 VARCHAR(50)。
*/ */
@Size(max = 500, message = "备注不能超过500个字符") @Size(max = 50, message = "备注不能超过50个字符")
private String bz; private String bz;
/** /**
@@ -2,54 +2,42 @@ package com.roomroot.jwgl.dto.courserunning;
import lombok.Data; 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; import jakarta.validation.constraints.Size;
/** /**
* 调课申请审批参数。 * 教研室审批调课申请参数。
* <p>
* 用于教研室或机关审批调课申请,包含审批结果、审批意见和审批人编号。
* </p>
*/ */
@Data @Data
public class ScheduleAdjustAuditDTO { public class ScheduleAdjustAuditDTO {
/** /**
* 调课申请编号,必填,标识需要审批的调课申请。 * 调课申请编号。
*/ */
@NotBlank(message = "调课申请编号不能为空") @NotBlank(message = "调课申请编号不能为空")
@Size(max = 50, message = "调课申请编号不能超过50个字符") @Size(max = 50, message = "调课申请编号不能超过50个字符")
private String ssdksqbh; private String ssdksqbh;
/** /**
* 审批角色,必填:jys(教研室审批)、jg(机关审批)。 * 审批结果:1-同意,2-发回,3-拒绝。点审批即已查收。
*/
@NotBlank(message = "审批角色不能为空")
@Size(max = 20, message = "审批角色不能超过20个字符")
private String auditRole;
/**
* 审批结果,必填:1-同意,2-发回修改,3-拒绝。
*/ */
@NotNull(message = "审批结果不能为空") @NotNull(message = "审批结果不能为空")
@Min(value = 1, message = "审批结果不正确")
@Max(value = 3, message = "审批结果不正确")
private Integer spzt; private Integer spzt;
/** /**
* 审批意见,可选,记录审批的具体意见或发回原因。 * 审批意见,发回时填写原因。
*/ */
@Size(max = 500, message = "审批意见不能超过500个字符") @Size(max = 500, message = "审批意见不能超过500个字符")
private String fhyj; private String fhyj;
/** /**
* 审批人编号,必填,标识审批人身份。 * 审批人编号,不传时取当前登录人;同时记为查收人。
*/ */
@NotBlank(message = "审批人编号不能为空")
@Size(max = 50, message = "审批人编号不能超过50个字符") @Size(max = 50, message = "审批人编号不能超过50个字符")
private String sprbh; private String sprbh;
/**
* 机关编号,机关审批时必填,标识审批机关。
*/
@Size(max = 50, message = "机关编号不能超过50个字符")
private String jgbh;
} }
@@ -38,20 +38,30 @@ public class ScheduleAdjustQueryDTO extends PageQuery {
private Integer jyspzzt; private Integer jyspzzt;
/** /**
* 教研室查收状态:0-未查收,1-已查收。 * 教研室查收状态:0-未查收,1-已查收。点了审批即为已查收。
*/ */
private Integer jyscszt; private Integer jyscszt;
/**
* 机关审批状态:0-未审批,1-已同意,2-已发回,3-已拒绝。
*/
private Integer jgspzt;
/** /**
* 删除状态:0-正常,1-已删除。 * 删除状态:0-正常,1-已删除。
*/ */
private Integer sczt; private Integer sczt;
/**
* 教研室代号,教研室角色由服务端强制写入,前端不必传。
*/
private String jysdh;
/**
* 学员队任务表编号。
*/
private String xydrwbh;
/**
* 课编号。
*/
private String kbh;
/** /**
* 申请日期范围开始。 * 申请日期范围开始。
*/ */
@@ -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;
/**
* 调课策略新增/修改参数。
* <p>
* 统一处理新增和修改:策略编号为空时执行新增,非空时执行修改。
* 策略类型为 TIME_LIMIT 时需填写截止星期和截止节次;
* 策略类型为 AUTO_APPROVE 时截止星期和截止节次不生效。
* </p>
*/
@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;
}
@@ -0,0 +1,15 @@
package com.roomroot.jwgl.dto.jys;
import lombok.Data;
import java.util.List;
/**
* 选修课批量操作参数。
*/
@Data
public class ElectiveCourseBatchDTO {
/** 学员队任务编号列表 */
private List<String> bhList;
}
@@ -0,0 +1,62 @@
package com.roomroot.jwgl.dto.jys;
import com.roomroot.common.annotation.Excel;
import lombok.Data;
/**
* 选修课数据文件导入行。
*/
@Data
public class ElectiveCourseImportDTO {
@Excel(name = "授课教员")
private String jyxm;
@Excel(name = "课程科目名称")
private String kmc;
@Excel(name = "可选班次年级")
private String kxbcnj;
@Excel(name = "选修班名称")
private String xxbmc;
@Excel(name = "选择课状态")
private String xkzt;
@Excel(name = "培训层次")
private String pxcc;
@Excel(name = "教材")
private String jcmc;
@Excel(name = "教学场地")
private String jsmc;
@Excel(name = "计划学时")
private Integer jhxs;
@Excel(name = "运行学时")
private Integer yxxs;
@Excel(name = "学分")
private Float xf;
@Excel(name = "开课日期")
private String kkrq;
@Excel(name = "结课日期")
private String jkrq;
@Excel(name = "报名开始日期")
private String bmkssj;
@Excel(name = "报名结束日期")
private String bmjssj;
@Excel(name = "计划人数")
private Integer jhrs;
@Excel(name = "要求说明")
private String yqsm;
}
@@ -0,0 +1,33 @@
package com.roomroot.jwgl.dto.jys;
import lombok.Data;
import java.util.List;
/**
* 选修课管理查询条件。
*/
@Data
public class ElectiveCourseQuery {
/** 授课教员姓名(模糊) */
private String jyxm;
/** 课程科目名称(模糊) */
private String kmc;
/** 可选班次年级(模糊,匹配年级或可选队列班次) */
private String kxbcnj;
/** 选修班名称(模糊) */
private String xxbmc;
/** 选择课状态:拟制 / 开放报名 / 停止报名 */
private String xkzt;
/** 培训层次 */
private String pxcc;
/** 任务编号列表(学员名单导出时使用) */
private List<String> bhList;
}
@@ -0,0 +1,70 @@
package com.roomroot.jwgl.dto.jys;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 新建选修课。
* <p>写入学员队任务表(课类型含“选修”),并同步拼班信息中的报名日期与要求说明。</p>
*/
@Data
public class ElectiveCourseSaveDTO {
/** 年度,空则取当前年 */
private Integer nd;
/** 学员队编号(选修班),与选修班名称二选一 */
private String xydbh;
/** 选修班名称。学员队编号为空时按名称查找或新建选修班 */
private String xxbmc;
/** 年级(新建选修班时使用) */
private String nj;
/** 课编号 */
private String kbh;
/** 学期第次 */
private Integer xqdc;
/** 授课教员编号 */
private String jybh;
/** 教室编号 */
private String jsbh;
/** 计划人数 */
private Integer rs;
/** 计划学时 */
private Integer xs;
/** 学分 */
private Float xf;
/** 教研室代号 */
private String jysdh;
/** 备注 */
private String bz;
/** 要求说明 */
private String yqsm;
/** 显示可选队列班次 */
private String kxdlbc;
/** 开课日期 */
private LocalDateTime kkrq;
/** 结课日期 */
private LocalDateTime jkrq;
/** 报名开始日期 */
private LocalDateTime bmkssj;
/** 报名结束日期 */
private LocalDateTime bmjssj;
}
@@ -0,0 +1,22 @@
package com.roomroot.jwgl.dto.jys;
import lombok.Data;
/**
* 教学任务列表查询条件。
*/
@Data
public class TeachingTaskQuery {
/** 任务名称(模糊) */
private String rwmc;
/** 年度,空则本学期年度 */
private Integer nd;
/** 学期第次 */
private Integer xqdc;
/** 状态:上报 / 发布 */
private String zt;
}
@@ -0,0 +1,32 @@
package com.roomroot.jwgl.dto.jys;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
/**
* 新增 / 编辑教学任务。
* <p>机关发布教学任务,并关联学员队(班次);教研室任务书按课程归属教研室自动生成。</p>
*/
@Data
public class TeachingTaskSaveDTO {
/** 编号,编辑时必填 */
private String bh;
/** 年度,空则取当前学期 */
private Integer nd;
/** 学期第次,空则取当前学期 */
private Integer xqdc;
/** 任务名称 */
private String rwmc;
/** 结束时间 */
private LocalDateTime jssj;
/** 关联的学员队年度学期基本信息编号 */
private List<String> xydxqbhList;
}
@@ -0,0 +1,23 @@
package com.roomroot.jwgl.dto.kcb;
import lombok.Data;
/**
* 今日 / 本周 / 班次课表查询条件。
* <p>未传年度、学期时按当前时间取本学期。</p>
*/
@Data
public class TimetableQuery {
/** 年度,空则本学期年度 */
private Integer nd;
/** 学期第次,空则本学期序号 */
private Integer xqdc;
/** 学员队编号(队别班次) */
private String xydbh;
/** 学员队名称(队别班次模糊) */
private String xydmc;
}
@@ -1,8 +1,9 @@
package com.roomroot.jwgl.entity; package com.roomroot.jwgl.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.roomroot.jwgl.unit.BaseEntity; import com.roomroot.jwgl.unit.BaseEntity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@@ -17,55 +18,48 @@ import java.time.LocalDateTime;
@TableName("调课申请_保障明细") @TableName("调课申请_保障明细")
public class DKSQBZMX extends BaseEntity { public class DKSQBZMX extends BaseEntity {
/**
* 编号
*/
@TableId(value = "编号", type = IdType.INPUT) @TableId(value = "编号", type = IdType.INPUT)
private String bh; private String bh;
/** @TableField("申请编号")
* 申请编号
*/
private String sqbh; private String sqbh;
/** @TableField("保障提供明细编号")
* 保障提供明细编号
*/
private String bztgmxbh; private String bztgmxbh;
/** @TableField("数量")
* 数量
*/
private Integer sl; private Integer sl;
/** @TableField("备注")
* 备注
*/
private String bz; private String bz;
/** @TableField("查收状态")
* 查收状态
*/
private Integer cszt; private Integer cszt;
/** @TableField("查收时间")
* 查收时间
*/
private LocalDateTime cssj; private LocalDateTime cssj;
/** @TableField("年度")
* 年度
*/
private Integer nd; private Integer nd;
/** @TableField("日期")
* 日期
*/
private LocalDateTime rq; private LocalDateTime rq;
/** @TableField("节次")
* 节次
*/
private Integer jc; 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;
} }
@@ -1,8 +1,9 @@
package com.roomroot.jwgl.entity; package com.roomroot.jwgl.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.roomroot.jwgl.unit.BaseEntity; import com.roomroot.jwgl.unit.BaseEntity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@@ -17,60 +18,51 @@ import java.time.LocalDateTime;
@TableName("调课申请_辅助教员") @TableName("调课申请_辅助教员")
public class DKSQFZJY extends BaseEntity { public class DKSQFZJY extends BaseEntity {
/**
* 编号
*/
@TableId(value = "编号", type = IdType.INPUT) @TableId(value = "编号", type = IdType.INPUT)
private String bh; private String bh;
/** @TableField("调课申请编号")
* 调课申请编号
*/
private String dksqbh; private String dksqbh;
/** @TableField("辅助教员编号")
* 辅助教员编号
*/
private String fzjybh; private String fzjybh;
/** @TableField("查收状态")
* 查收状态
*/
private Integer cszt; private Integer cszt;
/** @TableField("查收时间")
* 查收时间
*/
private LocalDateTime cssj; private LocalDateTime cssj;
/** @TableField("主教员")
* 主教员
*/
private Integer zjy; private Integer zjy;
/** @TableField("移除状态")
* 移除状态
*/
private Integer yczt; private Integer yczt;
/** @TableField("年度")
* 年度
*/
private Integer nd; private Integer nd;
/** @TableField("备注")
* 备注
*/
private String bz; private String bz;
/** @TableField("日期")
* 日期
*/
private LocalDateTime rq; private LocalDateTime rq;
/** @TableField("节次")
* 节次
*/
private Integer jc; 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;
} }
@@ -1,8 +1,9 @@
package com.roomroot.jwgl.entity; package com.roomroot.jwgl.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.roomroot.jwgl.unit.BaseEntity; import com.roomroot.jwgl.unit.BaseEntity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@@ -17,40 +18,39 @@ import java.time.LocalDateTime;
@TableName("调课申请_教室") @TableName("调课申请_教室")
public class DKSQJS extends BaseEntity { public class DKSQJS extends BaseEntity {
/**
* 编号
*/
@TableId(value = "编号", type = IdType.INPUT) @TableId(value = "编号", type = IdType.INPUT)
private String bh; private String bh;
/** @TableField("调课申请编号")
* 调课申请编号
*/
private String dksqbh; private String dksqbh;
/** @TableField("教室编号")
* 教室编号
*/
private String jsbh; private String jsbh;
/** @TableField("备注")
* 备注
*/
private String bz; private String bz;
/** @TableField("年度")
* 年度
*/
private Integer nd; private Integer nd;
/** @TableField("日期")
* 日期
*/
private LocalDateTime rq; private LocalDateTime rq;
/** @TableField("节次")
* 节次
*/
private Integer jc; 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;
} }
@@ -1,8 +1,9 @@
package com.roomroot.jwgl.entity; package com.roomroot.jwgl.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.roomroot.jwgl.unit.BaseEntity; import com.roomroot.jwgl.unit.BaseEntity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@@ -17,50 +18,45 @@ import java.time.LocalDateTime;
@TableName("调课申请_学员队") @TableName("调课申请_学员队")
public class DKSQXYD extends BaseEntity { public class DKSQXYD extends BaseEntity {
/**
* 编号
*/
@TableId(value = "编号", type = IdType.INPUT) @TableId(value = "编号", type = IdType.INPUT)
private String bh; private String bh;
/** @TableField("调课申请编号")
* 调课申请编号
*/
private String dksqbh; private String dksqbh;
/** @TableField("学员队编号")
* 学员队编号
*/
private String xydbh; private String xydbh;
/** @TableField("查收状态")
* 查收状态
*/
private Integer cszt; private Integer cszt;
/** @TableField("查收时间")
* 查收时间
*/
private LocalDateTime cssj; private LocalDateTime cssj;
/** @TableField("移除状态")
* 移除状态
*/
private Integer yczt; private Integer yczt;
/** @TableField("年度")
* 年度
*/
private Integer nd; private Integer nd;
/** @TableField("日期")
* 日期
*/
private LocalDateTime rq; private LocalDateTime rq;
/** @TableField("节次")
* 节次
*/
private Integer jc; 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;
} }
@@ -3,6 +3,7 @@ package com.roomroot.jwgl.entity;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField;
import com.roomroot.jwgl.unit.BaseEntity; import com.roomroot.jwgl.unit.BaseEntity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@@ -24,163 +25,134 @@ public class SSDKSQ extends BaseEntity {
private String bh; private String bh;
/** /**
* 实施_课程表编号 * 实施_课程表编号(原表主关联:指向实施_课程表)
*/ */
@TableField("实施_课程表编号")
private String sskcbbh; private String sskcbbh;
/** @TableField("预调时间安排")
* 预调时间安排
*/
private Integer ydsjap; private Integer ydsjap;
/** @TableField("创建时间")
* 创建时间
*/
private LocalDateTime cjsj; private LocalDateTime cjsj;
/** @TableField("执行人编号")
* 执行人编号
*/
private String zxrbh; private String zxrbh;
/** @TableField("执行时间")
* 执行时间
*/
private LocalDateTime zxsj; private LocalDateTime zxsj;
/** @TableField("执行状态")
* 执行状态
*/
private Integer zxzt; private Integer zxzt;
/** @TableField("事由")
* 事由
*/
private String sy; private String sy;
/** @TableField("教研室批准状态")
* 教研室批准状态
*/
private Integer jyspzzt; private Integer jyspzzt;
/** @TableField("教研室批准时间")
* 教研室批准时间
*/
private LocalDateTime jyspzsj; private LocalDateTime jyspzsj;
/** @TableField("教研室批准人编号")
* 教研室批准人编号
*/
private String jyspzrbh; private String jyspzrbh;
/** @TableField("备注")
* 教学系批准状态
*/
private Integer jxxspzzt;
/**
* 教学系批准时间
*/
private LocalDateTime jxxspzsj;
/**
* 教学系批准人编号
*/
private String jxxspzrbh;
/**
* 备注
*/
private String bz; private String bz;
/** @TableField("申请教员编号")
* 申请教员编号
*/
private String sqjybh; private String sqjybh;
/** @TableField("延时状态")
* 延时状态
*/
private Integer yszt; private Integer yszt;
/** @TableField("教研室查收状态")
* 教研室查收状态
*/
private Integer jyscszt; private Integer jyscszt;
/** @TableField("教研室查收时间")
* 教研室查收时间
*/
private LocalDateTime jyscssj; private LocalDateTime jyscssj;
/** @TableField("教研室查收人编号")
* 教研室查收人编号
*/
private String jyscsrbh; private String jyscsrbh;
/** @TableField("删除状态")
* 删除状态
*/
private Integer sczt; private Integer sczt;
/** @TableField("发回状态")
* 发回状态
*/
private Integer fhzt; private Integer fhzt;
/** @TableField("发回意见")
* 发回意见
*/
private String fhyj; private String fhyj;
/** @TableField("发回时间")
* 发回时间
*/
private LocalDateTime fhsj; private LocalDateTime fhsj;
/** @TableField("调课")
* 调课
*/
private Integer dk; private Integer dk;
/** @TableField("年度")
* 年度
*/
private Integer nd; private Integer nd;
/** /** 拟调整后的上课日期 */
* 日期 @TableField("日期")
*/
private LocalDateTime rq; private LocalDateTime rq;
/** /** 拟调整后的节次 */
* 节次 @TableField("节次")
*/
private Integer jc; private Integer jc;
/** @TableField("教学内容")
* 教学内容
*/
private String jxnr; private String jxnr;
/** @TableField("教学要点")
* 教学要点
*/
private String jxyd; private String jxyd;
/** @TableField("教学方法")
* 教学方法
*/
private String jxff; private String jxff;
/** @TableField("教学保障备注")
* 教学保障备注
*/
private String jxbzbz; private String jxbzbz;
/** @TableField("用车信息")
* 用车信息
*/
private String ycxx; private String ycxx;
/** 学员队任务表编号 */
@TableField("学员队任务编号")
private String xydrwbh;
/** 课表.课编号 */
@TableField("课编号")
private String kbh;
/** 课程表编号 */
@TableField("课程表编号")
private String kcbbh;
/** 申请教员所属教研室代号,用于教研室数据范围过滤 */
@TableField("教研室代号")
private String jysdh;
/** 原教室编号 */
@TableField("原教室编号")
private String yjsbh;
/** 拟调整教室编号 */
@TableField("新教室编号")
private String xjsbh;
@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;
} }
@@ -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;
}
@@ -5,7 +5,9 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import com.roomroot.jwgl.handler.DmGuidTypeHandler;
import lombok.Data; import lombok.Data;
import org.apache.ibatis.type.JdbcType;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -13,13 +15,14 @@ import java.time.LocalDateTime;
* 实施_课程 * 实施_课程
*/ */
@Data @Data
@TableName(value = "实施_课程") @TableName(value = "实施_课程", autoResultMap = true)
public class SSKC { public class SSKC {
/** /**
* 编号 * 编号(库内 VARBINARY(16),Java 侧按 36 位 UUID 字符串使用)
*/ */
@TableId(value = "编号", type = IdType.INPUT) @TableId(value = "编号", type = IdType.INPUT)
@TableField(value = "编号", jdbcType = JdbcType.VARBINARY, typeHandler = DmGuidTypeHandler.class)
private String bh; private String bh;
/** /**
@@ -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;
/**
* 实施_调课策略
* <p>
* 管理调课的时间段限制策略与免审批策略。
* 策略类型为 TIME_LIMIT 时,用于限制教员在规定时间后不得调整下周课程;
* 策略类型为 AUTO_APPROVE 时,用于在生效范围内自动通过调课申请,跳过三级审批。
* </p>
*/
@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;
}
@@ -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 字符串互转。
* <p>
* 仅通过实体 / resultMap 显式引用,避免把其它 VARBINARY 字段都当成 UUID。
* 禁止把带横线的 CHAR UUID 直接绑定到 VARBINARY,达梦会按十六进制解析并报 -6147。
* </p>
*/
public class DmGuidTypeHandler extends BaseTypeHandler<String> {
@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));
}
}
@@ -62,27 +62,18 @@ public class JwglApiAccessInterceptor implements HandlerInterceptor {
if (isTeacherDenied(path)) { if (isTeacherDenied(path)) {
return deny(response, "教员无权访问该功能"); return deny(response, "教员无权访问该功能");
} }
if (isTeacherAdjustDenied(path, request.getMethod())) {
return deny(response, "教员只能提交、查看和撤销本人的调课申请");
}
return true; return true;
} }
if (roleHelper.isResearchOffice()) { if (roleHelper.isResearchOffice()) {
if (isOfficeDenied(path)) { if (isOfficeDenied(path)) {
return deny(response, "教研室无权访问系统管理功能"); return deny(response, "教研室无权访问系统管理功能");
} }
if (isResearchOfficeAdjustDenied(path)) {
return deny(response, "教研室仅可查看和审批本室调课申请");
}
return true; return true;
} }
if (roleHelper.isDepartmentPersonnel()) { if (roleHelper.isDepartmentPersonnel()) {
if (isOfficeDenied(path)) { if (isOfficeDenied(path)) {
return deny(response, "机关人员无权访问系统管理功能"); return deny(response, "机关人员无权访问系统管理功能");
} }
if (isOfficeAdjustDenied(path, request.getMethod())) {
return deny(response, "机关人员仅可查看和审批调课申请");
}
return true; return true;
} }
return true; return true;
@@ -92,63 +83,38 @@ public class JwglApiAccessInterceptor implements HandlerInterceptor {
if (startsWithAny(path, "/getInfo", "/getRouters", "/logout", "/system/user/profile")) { if (startsWithAny(path, "/getInfo", "/getRouters", "/logout", "/system/user/profile")) {
return true; return true;
} }
// 通知已读属于个人操作,布局顶栏会调用。
if (HttpMethod.POST.matches(method)
&& startsWithAny(path, "/system/notice/markRead", "/system/notice/markReadAll")) {
return true;
}
if (!HttpMethod.GET.matches(method)) { if (!HttpMethod.GET.matches(method)) {
return false; return false;
} }
return startsWithAny(path, return startsWithAny(path,
// 学期列表 / 学期校历(只读;新增、修改、删除仍拦截)
"/semester",
"/xqxlb", "/xqxlb",
"/kcb",
"/studentTeamOutputSchedule",
"/classCalendar", "/classCalendar",
// 本人课表
"/kcb/today",
"/kcb/week",
"/kcb/class",
"/studentTeamOutputSchedule",
"/course-running/plan/list", "/course-running/plan/list",
"/course-running/plan/detail", "/course-running/plan/detail",
"/course-running/plan/export", "/course-running/plan/export",
// 本人学籍预警、个人信息、成绩
"/student-records/warning-result", "/student-records/warning-result",
"/student-records/warning-condition",
"/student-records/get", "/student-records/get",
"/student-records/list", "/student-records/list",
"/student-records/grades", "/student-records/grades",
"/student-records/process-grades"); "/student-records/process-grades",
} // 布局公共只读:字典、参数、通知
"/system/dict/data/type",
private boolean isTeacherAdjustDenied(String path, String method) { "/system/config/configKey",
if (!path.startsWith("/course-running/adjust")) { "/system/notice");
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) { private boolean isTeacherDenied(String path) {
@@ -19,7 +19,7 @@
a.数量, a.数量,
a.备注 a.备注
FROM "调课申请_保障明细" a FROM "调课申请_保障明细" a
LEFT JOIN "保障提供明细表" m ON a.保障提供明细编号 = m.编号 LEFT JOIN "保障提供明细" m ON TRIM(m.编号) = TRIM(a.保障提供明细编号)
WHERE a.申请编号 = #{dksqbh} WHERE a.申请编号 = #{dksqbh}
ORDER BY a.编号 ORDER BY a.编号
</select> </select>
@@ -15,11 +15,11 @@
SELECT SELECT
a.编号, a.编号,
a.辅助教员编号, a.辅助教员编号,
t.姓名 AS 辅助教员姓名, t.教员姓名 AS 辅助教员姓名,
a.主教员, a.主教员,
a.备注 a.备注
FROM "调课申请_辅助教员" a FROM "调课申请_辅助教员" a
LEFT JOIN "教员表" t ON a.辅助教员编号 = t.编号 LEFT JOIN "教员表" t ON TRIM(t.教员编号) = TRIM(a.辅助教员编号)
WHERE a.调课申请编号 = #{dksqbh} WHERE a.调课申请编号 = #{dksqbh}
ORDER BY a.编号 ORDER BY a.编号
</select> </select>
@@ -17,7 +17,7 @@
r.教室名称, r.教室名称,
a.备注 a.备注
FROM "调课申请_教室" a FROM "调课申请_教室" a
LEFT JOIN "教室表" r ON a.教室编号 = r.编号 LEFT JOIN "教室表" r ON TRIM(r.教室编号) = TRIM(a.教室编号)
WHERE a.调课申请编号 = #{dksqbh} WHERE a.调课申请编号 = #{dksqbh}
ORDER BY a.编号 ORDER BY a.编号
</select> </select>
@@ -15,7 +15,7 @@
a.学员队编号, a.学员队编号,
d.学员队名称 d.学员队名称
FROM "调课申请_学员队" a FROM "调课申请_学员队" a
LEFT JOIN "学员队表" d ON a.学员队编号 = d.编号 LEFT JOIN "学员队表" d ON TRIM(d.学员队编号) = TRIM(a.学员队编号)
WHERE a.调课申请编号 = #{dksqbh} WHERE a.调课申请编号 = #{dksqbh}
ORDER BY a.编号 ORDER BY a.编号
</select> </select>
@@ -0,0 +1,37 @@
package com.roomroot.jwgl.mapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.roomroot.jwgl.dto.jys.ElectiveCourseQuery;
import com.roomroot.jwgl.vo.jys.ElectiveCourseStudentVO;
import com.roomroot.jwgl.vo.jys.ElectiveCourseVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
/**
* 选修课管理自定义查询(学员队任务表 + 学员队表 + 课表)。
*/
@Mapper
public interface ElectiveCourseMapper {
Page<ElectiveCourseVO> selectElectiveCoursePage(Page<ElectiveCourseVO> page,
@Param("cond") ElectiveCourseQuery cond);
List<ElectiveCourseVO> selectElectiveCourseList(@Param("cond") ElectiveCourseQuery cond);
List<ElectiveCourseStudentVO> selectElectiveStudents(@Param("xydbh") String xydbh);
List<String> selectOriginalClassNames(@Param("xydbh") String xydbh);
Integer countPinban(@Param("xydbh") String xydbh);
int insertPinban(@Param("xydbh") String xydbh, @Param("zt") String zt,
@Param("jhkssj") LocalDateTime jhkssj, @Param("jhjssj") LocalDateTime jhjssj,
@Param("yqsm") String yqsm, @Param("bmdtj") String bmdtj, @Param("bz") String bz);
int updatePinban(@Param("xydbh") String xydbh, @Param("zt") String zt,
@Param("jhkssj") LocalDateTime jhkssj, @Param("jhjssj") LocalDateTime jhjssj,
@Param("yqsm") String yqsm, @Param("bmdtj") String bmdtj);
}
@@ -0,0 +1,201 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.roomroot.jwgl.mapper.ElectiveCourseMapper">
<resultMap id="ElectiveCourseResultMap" type="com.roomroot.jwgl.vo.jys.ElectiveCourseVO">
<result column="bh" property="bh"/>
<result column="nd" property="nd"/>
<result column="xydbh" property="xydbh"/>
<result column="kbh" property="kbh"/>
<result column="jybh" property="jybh"/>
<result column="jsbh" property="jsbh"/>
<result column="kmc" property="kmc"/>
<result column="jcmc" property="jcmc"/>
<result column="jyxm" property="jyxm"/>
<result column="jsmc" property="jsmc"/>
<result column="kxdlbc" property="kxdlbc"/>
<result column="jhxs" property="jhxs"/>
<result column="yxxs" property="yxxs"/>
<result column="xf" property="xf"/>
<result column="jhyxxsxf" property="jhyxxsxf"/>
<result column="kkjkrq" property="kkjkrq"/>
<result column="bmrq" property="bmrq"/>
<result column="jhrs" property="jhrs"/>
<result column="yqsm" property="yqsm"/>
<result column="xxbmc" property="xxbmc"/>
<result column="xkzt" property="xkzt"/>
<result column="pxcc" property="pxcc"/>
</resultMap>
<resultMap id="ElectiveStudentResultMap" type="com.roomroot.jwgl.vo.jys.ElectiveCourseStudentVO">
<result column="xxbmc" property="xxbmc"/>
<result column="kmc" property="kmc"/>
<result column="xh_no" property="xhNo"/>
<result column="xm" property="xm"/>
<result column="xb" property="xb"/>
<result column="lxdh" property="lxdh"/>
</resultMap>
<sql id="ElectiveCourseColumns">
t."编号" AS bh,
t."年度" AS nd,
t."学员队编号" AS xydbh,
t."课编号" AS kbh,
t."教员编号" AS jybh,
t."教室编号" AS jsbh,
k."课名称" AS kmc,
(SELECT WM_CONCAT(DISTINCT jc."名称")
FROM "课程教材" kj
LEFT JOIN "教材信息" jc ON jc."编号" = kj."教材编号"
WHERE kj."课编号" = t."课编号") AS jcmc,
jy."教员姓名" AS jyxm,
(SELECT MAX(js."教室名称") FROM "教室表" js WHERE js."教室编号" = t."教室编号") AS jsmc,
COALESCE(pb."白名单条件", xyd."学员队名称") AS kxdlbc,
COALESCE(sskc.jhxs, t."学时", k."学时") AS jhxs,
sskc.yxxs AS yxxs,
COALESCE(sskc.xf, t."学分", k."学分") AS xf,
(NVL(TO_CHAR(COALESCE(sskc.jhxs, t."学时", k."学时")), '-')
|| ' / ' || NVL(TO_CHAR(sskc.yxxs), '-')
|| ' / ' || NVL(TO_CHAR(COALESCE(sskc.xf, t."学分", k."学分")), '-')) AS jhyxxsxf,
CASE
WHEN COALESCE(xq.kxrq, xyd."入学日期") IS NULL THEN NULL
ELSE TO_CHAR(COALESCE(xq.kxrq, xyd."入学日期"), 'YYYY-MM-DD')
|| ' ~ '
|| NVL(TO_CHAR(COALESCE(xq.jsrq, xyd."毕业日期"), 'YYYY-MM-DD'), '-')
END AS kkjkrq,
CASE
WHEN COALESCE(pb."计划开始时间", q."入学日期") IS NULL THEN NULL
ELSE TO_CHAR(COALESCE(pb."计划开始时间", q."入学日期"), 'YYYY-MM-DD')
|| ' ~ '
|| NVL(TO_CHAR(COALESCE(pb."计划结束时间", q."结束注册时间"), 'YYYY-MM-DD'), '-')
END AS bmrq,
COALESCE(t."人数", xyd."学员队人数") AS jhrs,
pb."要求说明" AS yqsm,
xyd."学员队名称" AS xxbmc,
COALESCE(pb."状态",
CASE t."选择" WHEN 1 THEN '开放报名' WHEN 2 THEN '停止报名' ELSE '拟制' END) AS xkzt,
k."培训层次" AS pxcc
</sql>
<sql id="ElectiveCourseFromWhere">
FROM "学员队任务表" t
INNER JOIN "学员队表" xyd ON t."学员队编号" = xyd."学员队编号"
INNER JOIN "课表" k ON t."课编号" = k."课编号"
LEFT JOIN "教员表" jy ON t."教员编号" = jy."教员编号"
LEFT JOIN "学员队拼班信息" pb ON pb."学员队编号" = t."学员队编号"
LEFT JOIN "学员队期表" q ON q."学员队编号" = t."学员队编号"
LEFT JOIN (
SELECT "学员队编号", "年度", "学期第次",
MIN("开学日期") AS kxrq, MAX("结束日期") AS jsrq
FROM "学员队年度学期基本信息表"
GROUP BY "学员队编号", "年度", "学期第次"
) xq ON xq."学员队编号" = t."学员队编号"
AND xq."年度" = t."年度"
AND xq."学期第次" = t."学期第次"
LEFT JOIN (
SELECT "科目编号", "教员编号", "年度",
MAX("学时") AS jhxs, MAX("运行学时") AS yxxs, MAX("学分") AS xf
FROM "实施_课程"
GROUP BY "科目编号", "教员编号", "年度"
) sskc ON sskc."科目编号" = t."课编号"
AND (sskc."教员编号" = t."教员编号" OR t."教员编号" IS NULL)
AND (sskc."年度" = t."年度" OR t."年度" IS NULL)
<where>
t."课类型" LIKE '%选修%'
AND (t.DEL_FLAG = 0 OR t.DEL_FLAG IS NULL)
<if test="cond != null and cond.jyxm != null and cond.jyxm != ''">
AND jy."教员姓名" LIKE CONCAT('%', #{cond.jyxm}, '%')
</if>
<if test="cond != null and cond.kmc != null and cond.kmc != ''">
AND k."课名称" LIKE CONCAT('%', #{cond.kmc}, '%')
</if>
<if test="cond != null and cond.kxbcnj != null and cond.kxbcnj != ''">
AND (xyd."年级" LIKE CONCAT('%', #{cond.kxbcnj}, '%')
OR pb."白名单条件" LIKE CONCAT('%', #{cond.kxbcnj}, '%')
OR xyd."学员队名称" LIKE CONCAT('%', #{cond.kxbcnj}, '%'))
</if>
<if test="cond != null and cond.xxbmc != null and cond.xxbmc != ''">
AND xyd."学员队名称" LIKE CONCAT('%', #{cond.xxbmc}, '%')
</if>
<if test="cond != null and cond.xkzt != null and cond.xkzt != ''">
AND COALESCE(pb."状态",
CASE t."选择" WHEN 1 THEN '开放报名' WHEN 2 THEN '停止报名' ELSE '拟制' END) = #{cond.xkzt}
</if>
<if test="cond != null and cond.pxcc != null and cond.pxcc != ''">
AND k."培训层次" = #{cond.pxcc}
</if>
<if test="cond != null and cond.bhList != null and cond.bhList.size() &gt; 0">
AND t."编号" IN
<foreach collection="cond.bhList" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</if>
</where>
</sql>
<select id="selectElectiveCoursePage" resultMap="ElectiveCourseResultMap">
SELECT
<include refid="ElectiveCourseColumns"/>
<include refid="ElectiveCourseFromWhere"/>
ORDER BY t."年度" DESC, t."编号"
</select>
<select id="selectElectiveCourseList" resultMap="ElectiveCourseResultMap">
SELECT
<include refid="ElectiveCourseColumns"/>
<include refid="ElectiveCourseFromWhere"/>
ORDER BY t."年度" DESC, t."编号"
</select>
<select id="selectElectiveStudents" resultMap="ElectiveStudentResultMap">
SELECT
xyd."学员队名称" AS xxbmc,
NULL AS kmc,
s."学号" AS xh_no,
s."姓名" AS xm,
s."性别" AS xb,
s."联系电话" AS lxdh
FROM "学员信息表" s
LEFT JOIN "学员队期表" q ON q."编号" = s."学员队期编号"
LEFT JOIN "学员队表" xyd ON xyd."学员队编号" = #{xydbh}
WHERE s."当前班次编号" = #{xydbh}
OR q."学员队编号" = #{xydbh}
ORDER BY s."学号"
</select>
<select id="selectOriginalClassNames" resultType="java.lang.String">
SELECT DISTINCT COALESCE(orig."学员队名称", prev."学员队名称")
FROM "学员信息表" s
LEFT JOIN "学员队期表" q ON q."编号" = s."学员队期编号"
LEFT JOIN "学员队表" orig
ON orig."学员队编号" = q."学员队编号"
AND orig."学员队编号" != #{xydbh}
LEFT JOIN "学员队表" prev ON prev."学员队编号" = s."上一个部别编号"
WHERE (s."当前班次编号" = #{xydbh} OR q."学员队编号" = #{xydbh})
AND COALESCE(orig."学员队名称", prev."学员队名称") IS NOT NULL
</select>
<select id="countPinban" resultType="java.lang.Integer">
SELECT COUNT(1) FROM "学员队拼班信息" WHERE "学员队编号" = #{xydbh}
</select>
<insert id="insertPinban">
INSERT INTO "学员队拼班信息"(
"学员队编号", "状态", "计划开始时间", "计划结束时间", "要求说明", "白名单条件", "备注"
) VALUES (
#{xydbh}, #{zt}, #{jhkssj}, #{jhjssj}, #{yqsm}, #{bmdtj}, #{bz}
)
</insert>
<update id="updatePinban">
UPDATE "学员队拼班信息"
<set>
<if test="zt != null">"状态" = #{zt},</if>
<if test="jhkssj != null">"计划开始时间" = #{jhkssj},</if>
<if test="jhjssj != null">"计划结束时间" = #{jhjssj},</if>
<if test="yqsm != null">"要求说明" = #{yqsm},</if>
<if test="bmdtj != null">"白名单条件" = #{bmdtj},</if>
</set>
WHERE "学员队编号" = #{xydbh}
</update>
</mapper>
@@ -6,6 +6,7 @@ import com.roomroot.jwgl.entity.JYB;
import com.roomroot.jwgl.vo.faculty.TeacherListVO; import com.roomroot.jwgl.vo.faculty.TeacherListVO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/** /**
* 教员表 Mapper * 教员表 Mapper
@@ -27,4 +28,9 @@ public interface JYBMapper extends BaseMapper<JYB> {
/** 教员列表双表联查分页 */ /** 教员列表双表联查分页 */
Page<TeacherListVO> selectTeacherPageList(Page<TeacherListVO> page, @Param("jyb") JYB cond); Page<TeacherListVO> selectTeacherPageList(Page<TeacherListVO> page, @Param("jyb") JYB cond);
@Select("SELECT TRIM(b.\"教员编号\") FROM \"教员登录表\" b "
+ "JOIN \"登录信息表\" a ON TRIM(a.\"编号\") = TRIM(b.\"登录信息编号\") "
+ "WHERE TRIM(a.\"账号\") = #{username}")
String selectTeacherIdByAccount(@Param("username") String username);
} }
@@ -0,0 +1,28 @@
package com.roomroot.jwgl.mapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.roomroot.jwgl.dto.kcb.TimetableQuery;
import com.roomroot.jwgl.vo.kcb.ClassTimetableVO;
import com.roomroot.jwgl.vo.kcb.DailyWeeklyTimetableVO;
import com.roomroot.jwgl.vo.kcb.SemesterGradeExportVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 今日 / 本周 / 班次课表查询。
*/
@Mapper
public interface KcbTimetableMapper {
List<DailyWeeklyTimetableVO> selectLessonTimetable(@Param("cond") TimetableQuery cond,
@Param("startDate") String startDate, @Param("endDate") String endDate);
Page<ClassTimetableVO> selectClassTimetable(Page<ClassTimetableVO> page,
@Param("cond") TimetableQuery cond);
List<ClassTimetableVO> selectClassTimetableList(@Param("cond") TimetableQuery cond);
List<SemesterGradeExportVO> selectSemesterGrades(@Param("cond") TimetableQuery cond);
}
@@ -0,0 +1,176 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.roomroot.jwgl.mapper.KcbTimetableMapper">
<sql id="LessonTeamFilter">
<if test="cond != null and cond.xydbh != null and cond.xydbh != ''">
AND EXISTS (
SELECT 1 FROM "实施_课程表_学员队" txf
WHERE txf."实施_课程表编号" = s."编号"
AND txf."学员队编号" = #{cond.xydbh}
)
</if>
<if test="cond != null and cond.xydmc != null and cond.xydmc != ''">
AND EXISTS (
SELECT 1 FROM "实施_课程表_学员队" txf
INNER JOIN "学员队表" xydf ON xydf."学员队编号" = txf."学员队编号"
WHERE txf."实施_课程表编号" = s."编号"
AND xydf."学员队名称" LIKE CONCAT('%', #{cond.xydmc}, '%')
)
</if>
<if test="cond != null and cond.xqdc != null">
AND EXISTS (
SELECT 1 FROM "实施_课程学员队" cxf
WHERE LOWER(RAWTOHEX(c."编号")) = LOWER(REPLACE(TRIM(cxf."实施_课程编号"), '-', ''))
AND cxf."学期第次" = #{cond.xqdc}
)
</if>
</sql>
<select id="selectLessonTimetable" resultType="com.roomroot.jwgl.vo.kcb.DailyWeeklyTimetableVO">
SELECT
s."编号" AS bh,
TO_CHAR(s."日期", 'YYYY-MM-DD') || ' 第' || CAST(s."节次" AS VARCHAR) || '节' AS sksj,
MAX(k."课名称") AS kc,
MAX(jys."教研室名称") AS zrdw,
WM_CONCAT(DISTINCT xyd."学员队名称") AS bc,
COALESCE(WM_CONCAT(DISTINCT jyf."教员姓名"), MAX(jy."教员姓名")) AS jy,
COALESCE(WM_CONCAT(DISTINCT js."教室名称"), MAX(js2."教室名称")) AS cd,
TRIM(NVL(MAX(s."教学内容"), '')
|| CASE
WHEN MAX(s."教学方法") IS NOT NULL AND MAX(s."教学方法") != ''
THEN ' / ' || MAX(s."教学方法")
ELSE ''
END) AS jxnrxff,
COALESCE(MAX(s."教务备注"), MAX(s."教员备注"), MAX(
(SELECT MAX(kcb."备注") FROM "课程表" kcb
WHERE kcb."年度" = s."年度"
AND kcb."教员编号" = c."教员编号"
AND kcb."课次序号" = c."教员课次序号")
)) AS bz
FROM "实施_课程表" 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."教员编号"
LEFT JOIN "实施_课程表_学员队" tx ON tx."实施_课程表编号" = s."编号"
LEFT JOIN "学员队表" xyd ON xyd."学员队编号" = tx."学员队编号"
LEFT JOIN "实施_课程表_教室" jsx ON jsx."实施_课程表编号" = s."编号"
LEFT JOIN "教室表" js ON js."教室编号" = jsx."教室编号"
LEFT JOIN "教室表" js2 ON js2."教室编号" = c."教室编号"
LEFT JOIN "实施_课程表_辅助教员" fz ON fz."实施_课程表编号" = s."编号"
LEFT JOIN "教员表" jyf ON jyf."教员编号" = fz."辅助教员编号"
WHERE COALESCE(s."删除状态", 0) = 0
<if test="cond != null and cond.nd != null">
AND s."年度" = #{cond.nd}
</if>
<if test="startDate != null and startDate != ''">
AND TO_CHAR(s."日期", 'YYYY-MM-DD') &gt;= #{startDate}
</if>
<if test="endDate != null and endDate != ''">
AND TO_CHAR(s."日期", 'YYYY-MM-DD') &lt;= #{endDate}
</if>
<include refid="LessonTeamFilter"/>
GROUP BY s."编号", s."日期", s."节次", s."年度", c."教员编号", c."教员课次序号"
ORDER BY s."日期", s."节次", s."编号"
</select>
<sql id="ClassTimetableWhere">
WHERE 1 = 1
<if test="cond != null and cond.nd != null">
AND COALESCE(cx."年度", c."年度") = #{cond.nd}
</if>
<if test="cond != null and cond.xqdc != null">
AND cx."学期第次" = #{cond.xqdc}
</if>
<if test="cond != null and cond.xydbh != null and cond.xydbh != ''">
AND cx."学员队编号" = #{cond.xydbh}
</if>
<if test="cond != null and cond.xydmc != null and cond.xydmc != ''">
AND xyd."学员队名称" LIKE CONCAT('%', #{cond.xydmc}, '%')
</if>
</sql>
<sql id="ClassTimetableSelect">
SELECT
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,
(NVL(zjy."教员姓名", '-')
|| ' / '
|| NVL(TO_CHAR(c."教员课次序号"),
TO_CHAR((SELECT COUNT(1) FROM "实施_课程表" t
WHERE LOWER(RAWTOHEX(c."编号")) = LOWER(REPLACE(TRIM(t."实施_课程编号"), '-', ''))
AND COALESCE(t."删除状态", 0) = 0)))) AS zrjykc,
c."学时" AS jhxs,
c."运行学时" AS yxxs,
COALESCE(k."考核方式", c."成绩分制") AS khlxfs,
sjy."教员姓名" AS ssjy,
(NVL(TO_CHAR(cx."人数"), NVL(TO_CHAR(xyd."学员队人数"), '-'))
|| ' / '
|| NVL((SELECT WM_CONCAT(DISTINCT js."教室名称")
FROM "实施_课程表" t
LEFT JOIN "实施_课程表_教室" jsx ON jsx."实施_课程表编号" = t."编号"
LEFT JOIN "教室表" js ON js."教室编号" = jsx."教室编号"
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 LOWER(RAWTOHEX(c."编号")) = LOWER(REPLACE(TRIM(t."实施_课程编号"), '-', ''))) AS ssjhbg
FROM "实施_课程" 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."责任教员编号"
LEFT JOIN "教员表" sjy ON sjy."教员编号" = c."教员编号"
LEFT JOIN "教室表" js0 ON js0."教室编号" = c."教室编号"
</sql>
<select id="selectClassTimetable" resultType="com.roomroot.jwgl.vo.kcb.ClassTimetableVO">
<include refid="ClassTimetableSelect"/>
<include refid="ClassTimetableWhere"/>
ORDER BY k."课名称", c."编号"
</select>
<select id="selectClassTimetableList" resultType="com.roomroot.jwgl.vo.kcb.ClassTimetableVO">
<include refid="ClassTimetableSelect"/>
<include refid="ClassTimetableWhere"/>
ORDER BY k."课名称", c."编号"
</select>
<select id="selectSemesterGrades" resultType="com.roomroot.jwgl.vo.kcb.SemesterGradeExportVO">
SELECT
s."学号" AS xh,
s."姓名" AS xm,
COALESCE(k."课名称", g."科目编号") AS kcmc,
g."平时成绩" AS pscj,
g."考试成绩" AS kscj,
g."最终成绩" AS zzcj,
g."考试情况" AS ksqk,
g."年度" AS nd
FROM "学员课程成绩" g
LEFT JOIN "学员信息表" s ON s."编号" = g."学员编号"
LEFT JOIN "课表" k ON (k."课编号" = g."科目编号" OR k."科目代码" = g."科目编号")
LEFT JOIN "学员队表" xyd ON xyd."学员队编号" = g."学员队编号"
WHERE 1 = 1
<if test="cond != null and cond.nd != null">
AND g."年度" = #{cond.nd}
</if>
<if test="cond != null and cond.xydbh != null and cond.xydbh != ''">
AND g."学员队编号" = #{cond.xydbh}
</if>
<if test="cond != null and cond.xydmc != null and cond.xydmc != ''">
AND xyd."学员队名称" LIKE CONCAT('%', #{cond.xydmc}, '%')
</if>
ORDER BY s."学号", k."课名称"
</select>
</mapper>
@@ -3,43 +3,30 @@ package com.roomroot.jwgl.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.roomroot.jwgl.entity.SSDKSQ; import com.roomroot.jwgl.entity.SSDKSQ;
import com.roomroot.jwgl.vo.courserunning.AdjustableLessonVO;
import com.roomroot.jwgl.vo.courserunning.ScheduleAdjustApplyVO; import com.roomroot.jwgl.vo.courserunning.ScheduleAdjustApplyVO;
import com.roomroot.jwgl.vo.courserunning.ScheduleAdjustDetailVO; import com.roomroot.jwgl.vo.courserunning.ScheduleAdjustDetailVO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.List;
/** /**
* 实施_调课申请 Mapper。 * 实施_调课申请 Mapper。
* <p>
* 提供调课申请数据的CRUD操作及调课管理相关的自定义查询。
* </p>
*/ */
@Mapper @Mapper
public interface SSDKSQMapper extends BaseMapper<SSDKSQ> { public interface SSDKSQMapper extends BaseMapper<SSDKSQ> {
/** /**
* 分页查询调课申请列表。 * 分页查询调课申请列表。
* <p>
* 支持按实施_课程表编号、申请教员编号、年度、教研室审批状态、
* 教研室查收状态、删除状态和申请日期范围筛选。
* 关联课程表获取课程名称,关联教员表获取申请教员姓名。
* </p>
*
* @param page MyBatis-Plus 分页对象
* @param sskcbbh 实施_课程表编号
* @param sqjybh 申请教员编号
* @param nd 年度
* @param jyspzzt 教研室审批状态
* @param jyscszt 教研室查收状态
* @param sczt 删除状态
* @param cjsjStart 申请日期范围开始
* @param cjsjEnd 申请日期范围结束
* @return 分页调课申请列表
*/ */
Page<ScheduleAdjustApplyVO> selectPageList( Page<ScheduleAdjustApplyVO> selectPageList(
Page<ScheduleAdjustApplyVO> page, Page<ScheduleAdjustApplyVO> page,
@Param("sskcbbh") String sskcbbh, @Param("sskcbbh") String sskcbbh,
@Param("xydrwbh") String xydrwbh,
@Param("kbh") String kbh,
@Param("sqjybh") String sqjybh, @Param("sqjybh") String sqjybh,
@Param("jysdh") String jysdh,
@Param("nd") Integer nd, @Param("nd") Integer nd,
@Param("jyspzzt") Integer jyspzzt, @Param("jyspzzt") Integer jyspzzt,
@Param("jyscszt") Integer jyscszt, @Param("jyscszt") Integer jyscszt,
@@ -48,13 +35,12 @@ public interface SSDKSQMapper extends BaseMapper<SSDKSQ> {
@Param("cjsjEnd") String cjsjEnd); @Param("cjsjEnd") String cjsjEnd);
/** /**
* 根据调课申请编号查询调课申请详情。 * 根据调课申请编号查询详情。
* <p>
* 关联课程表获取课程名称,关联教员表获取申请教员姓名。
* </p>
*
* @param ssdksqbh 调课申请编号
* @return 调课申请详情
*/ */
ScheduleAdjustDetailVO selectDetailById(@Param("ssdksqbh") String ssdksqbh); ScheduleAdjustDetailVO selectDetailById(@Param("ssdksqbh") String ssdksqbh);
/**
* 查询可供调课的课次(学员队任务表 + 课表 + 课程表)。
*/
List<AdjustableLessonVO> selectAdjustableLessons(@Param("jybh") String jybh, @Param("nd") Integer nd);
} }
@@ -2,11 +2,17 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.roomroot.jwgl.mapper.SSDKSQMapper"> <mapper namespace="com.roomroot.jwgl.mapper.SSDKSQMapper">
<!-- 调课申请列表结果映射 -->
<resultMap id="ScheduleAdjustApplyMap" type="com.roomroot.jwgl.vo.courserunning.ScheduleAdjustApplyVO"> <resultMap id="ScheduleAdjustApplyMap" type="com.roomroot.jwgl.vo.courserunning.ScheduleAdjustApplyVO">
<result column="调课申请编号" property="ssdksqbh"/> <result column="调课申请编号" property="ssdksqbh"/>
<result column="实施_课程表编号" property="sskcbbh"/> <result column="实施_课程表编号" property="sskcbbh"/>
<result column="学员队任务编号" property="xydrwbh"/>
<result column="课编号" property="kbh"/>
<result column="课程表编号" property="kcbbh"/>
<result column="课程名称" property="kcmc"/> <result column="课程名称" property="kcmc"/>
<result column="学员队名称" property="xydmc"/>
<result column="教研室代号" property="jysdh"/>
<result column="原教室编号" property="yjsbh"/>
<result column="新教室编号" property="xjsbh"/>
<result column="申请教员编号" property="sqjybh"/> <result column="申请教员编号" property="sqjybh"/>
<result column="申请教员姓名" property="sqjyxm"/> <result column="申请教员姓名" property="sqjyxm"/>
<result column="事由" property="sy"/> <result column="事由" property="sy"/>
@@ -20,11 +26,17 @@
<result column="删除状态" property="sczt"/> <result column="删除状态" property="sczt"/>
</resultMap> </resultMap>
<!-- 调课申请详情结果映射 -->
<resultMap id="ScheduleAdjustDetailMap" type="com.roomroot.jwgl.vo.courserunning.ScheduleAdjustDetailVO"> <resultMap id="ScheduleAdjustDetailMap" type="com.roomroot.jwgl.vo.courserunning.ScheduleAdjustDetailVO">
<result column="调课申请编号" property="ssdksqbh"/> <result column="调课申请编号" property="ssdksqbh"/>
<result column="实施_课程表编号" property="sskcbbh"/> <result column="实施_课程表编号" property="sskcbbh"/>
<result column="学员队任务编号" property="xydrwbh"/>
<result column="课编号" property="kbh"/>
<result column="课程表编号" property="kcbbh"/>
<result column="课程名称" property="kcmc"/> <result column="课程名称" property="kcmc"/>
<result column="学员队名称" property="xydmc"/>
<result column="教研室代号" property="jysdh"/>
<result column="原教室编号" property="yjsbh"/>
<result column="新教室编号" property="xjsbh"/>
<result column="申请教员编号" property="sqjybh"/> <result column="申请教员编号" property="sqjybh"/>
<result column="申请教员姓名" property="sqjyxm"/> <result column="申请教员姓名" property="sqjyxm"/>
<result column="事由" property="sy"/> <result column="事由" property="sy"/>
@@ -44,16 +56,46 @@
<result column="教研室查收状态" property="jyscszt"/> <result column="教研室查收状态" property="jyscszt"/>
<result column="教研室查收时间" property="jyscssj"/> <result column="教研室查收时间" property="jyscssj"/>
<result column="教研室查收人编号" property="jyscsrbh"/> <result column="教研室查收人编号" property="jyscsrbh"/>
<result column="发回意见" property="fhyj"/>
</resultMap> </resultMap>
<!-- 分页查询调课申请列表 --> <resultMap id="AdjustableLessonMap" type="com.roomroot.jwgl.vo.courserunning.AdjustableLessonVO">
<result column="实施_课程表编号" property="sskcbbh"/>
<result column="上课日期" property="rq"/>
<result column="节次" property="jc"/>
<result column="学员队任务编号" property="xydrwbh"/>
<result column="课编号" property="kbh"/>
<result column="课程名称" property="kcmc"/>
<result column="学员队编号" property="xydbh"/>
<result column="学员队名称" property="xydmc"/>
<result column="教员编号" property="jybh"/>
<result column="课次序号" property="kcxh"/>
<result column="教室编号" property="jsbh"/>
<result column="年度" property="nd"/>
<result column="教研室代号" property="jysdh"/>
<result column="课程表编号" property="kcbbh"/>
</resultMap>
<!-- 以实施_调课申请为主表,经实施_课程表关联课表、课程表、学员队任务表 -->
<select id="selectPageList" resultMap="ScheduleAdjustApplyMap"> <select id="selectPageList" resultMap="ScheduleAdjustApplyMap">
SELECT SELECT
a.编号 AS 调课申请编号, a.编号 AS 调课申请编号,
a.实施_课程表编号, a.实施_课程表编号,
c.课名称 AS 课程名称, a.学员队任务编号,
COALESCE(a.课编号, e.科目编号) AS 课编号,
a.课程表编号,
kb.课名称 AS 课程名称,
(
SELECT MAX(xd.学员队名称)
FROM "实施_课程表_学员队" xyd
LEFT JOIN "学员队表" xd ON xd.学员队编号 = xyd.学员队编号
WHERE xyd.实施_课程表编号 = b.编号
) AS 学员队名称,
COALESCE(a.教研室代号, j.教研室代号) AS 教研室代号,
a.原教室编号,
a.新教室编号,
a.申请教员编号, a.申请教员编号,
j.姓名 AS 申请教员姓名, j.教员姓名 AS 申请教员姓名,
a.事由, a.事由,
a.日期 AS 新日期, a.日期 AS 新日期,
a.节次 AS 新节次, a.节次 AS 新节次,
@@ -64,17 +106,27 @@
a.发回意见, a.发回意见,
a.删除状态 a.删除状态
FROM "实施_调课申请" a FROM "实施_调课申请" a
LEFT JOIN "实施_课程表" b ON a.实施_课程表编号 = b.编号 LEFT JOIN "实施_课程表" b ON TRIM(b.编号) = TRIM(a.实施_课程表编号)
LEFT JOIN "实施_课程" e ON b.实施_课程编号 = e.编号 LEFT JOIN "实施_课程" e ON LOWER(RAWTOHEX(e."编号")) = LOWER(REPLACE(TRIM(b.实施_课程编号), '-', ''))
LEFT JOIN "课程表" c ON e.课编号 = c.课编号 LEFT JOIN "课表" kb ON kb.课编号 = COALESCE(NULLIF(TRIM(a.课编号), ''), e.科目编号)
LEFT JOIN "教员表" j ON a.申请教员编号 = j.编号 LEFT JOIN "教员表" j ON TRIM(j.教员编号) = TRIM(a.申请教员编号)
LEFT JOIN "课程表" kcb ON TRIM(kcb.编号) = TRIM(a.课程表编号)
WHERE 1=1 WHERE 1=1
<if test="sskcbbh != null and sskcbbh != ''"> <if test="sskcbbh != null and sskcbbh != ''">
AND a.实施_课程表编号 = #{sskcbbh} AND a.实施_课程表编号 = #{sskcbbh}
</if> </if>
<if test="xydrwbh != null and xydrwbh != ''">
AND a.学员队任务编号 = #{xydrwbh}
</if>
<if test="kbh != null and kbh != ''">
AND COALESCE(a.课编号, e.科目编号) = #{kbh}
</if>
<if test="sqjybh != null and sqjybh != ''"> <if test="sqjybh != null and sqjybh != ''">
AND a.申请教员编号 = #{sqjybh} AND a.申请教员编号 = #{sqjybh}
</if> </if>
<if test="jysdh != null and jysdh != ''">
AND COALESCE(a.教研室代号, j.教研室代号) = #{jysdh}
</if>
<if test="nd != null"> <if test="nd != null">
AND a.年度 = #{nd} AND a.年度 = #{nd}
</if> </if>
@@ -96,14 +148,25 @@
ORDER BY a.创建时间 DESC ORDER BY a.创建时间 DESC
</select> </select>
<!-- 根据调课申请编号查询详情 -->
<select id="selectDetailById" resultMap="ScheduleAdjustDetailMap"> <select id="selectDetailById" resultMap="ScheduleAdjustDetailMap">
SELECT SELECT
a.编号 AS 调课申请编号, a.编号 AS 调课申请编号,
a.实施_课程表编号, a.实施_课程表编号,
c.课名称 AS 课程名称, a.学员队任务编号,
COALESCE(a.课编号, e.科目编号) AS 课编号,
a.课程表编号,
kb.课名称 AS 课程名称,
(
SELECT MAX(xd.学员队名称)
FROM "实施_课程表_学员队" xyd
LEFT JOIN "学员队表" xd ON xd.学员队编号 = xyd.学员队编号
WHERE xyd.实施_课程表编号 = b.编号
) AS 学员队名称,
COALESCE(a.教研室代号, j.教研室代号) AS 教研室代号,
a.原教室编号,
a.新教室编号,
a.申请教员编号, a.申请教员编号,
j.姓名 AS 申请教员姓名, j.教员姓名 AS 申请教员姓名,
a.事由, a.事由,
a.日期 AS 新日期, a.日期 AS 新日期,
a.节次 AS 新节次, a.节次 AS 新节次,
@@ -120,13 +183,73 @@
a.教研室批准人编号, a.教研室批准人编号,
a.教研室查收状态, a.教研室查收状态,
a.教研室查收时间, a.教研室查收时间,
a.教研室查收人编号 a.教研室查收人编号,
a.发回意见
FROM "实施_调课申请" a FROM "实施_调课申请" a
LEFT JOIN "实施_课程表" b ON a.实施_课程表编号 = b.编号 LEFT JOIN "实施_课程表" b ON TRIM(b.编号) = TRIM(a.实施_课程表编号)
LEFT JOIN "实施_课程" e ON b.实施_课程编号 = e.编号 LEFT JOIN "实施_课程" e ON LOWER(RAWTOHEX(e."编号")) = LOWER(REPLACE(TRIM(b.实施_课程编号), '-', ''))
LEFT JOIN "课程表" c ON e.课编号 = c.课编号 LEFT JOIN "课表" kb ON kb.课编号 = COALESCE(NULLIF(TRIM(a.课编号), ''), e.科目编号)
LEFT JOIN "教员表" j ON a.申请教员编号 = j.编号 LEFT JOIN "教员表" j ON TRIM(j.教员编号) = TRIM(a.申请教员编号)
WHERE a.编号 = #{ssdksqbh} WHERE a.编号 = #{ssdksqbh}
</select> </select>
<!-- 教员可选课次:实施_课程表 + 课表 + 课程表 + 学员队任务表 -->
<select id="selectAdjustableLessons" resultMap="AdjustableLessonMap">
SELECT
b.编号 AS 实施_课程表编号,
b.日期 AS 上课日期,
b.节次,
(
SELECT MAX(rw.编号)
FROM "学员队任务表" rw
WHERE rw.课编号 = e.科目编号
AND rw.教员编号 = e.教员编号
AND rw.年度 = b.年度
AND COALESCE(rw.DEL_FLAG, 0) = 0
) AS 学员队任务编号,
e.科目编号 AS 课编号,
COALESCE(kb.课名称, (
SELECT MAX(k2.课名称) FROM "课表" k2 WHERE k2.科目代码 = e.科目编号
)) AS 课程名称,
(
SELECT MAX(xyd.学员队编号)
FROM "实施_课程表_学员队" xyd
WHERE xyd.实施_课程表编号 = b.编号
) AS 学员队编号,
(
SELECT MAX(xd.学员队名称)
FROM "实施_课程表_学员队" xyd
LEFT JOIN "学员队表" xd ON xd.学员队编号 = xyd.学员队编号
WHERE xyd.实施_课程表编号 = b.编号
) AS 学员队名称,
e.教员编号,
e.教员课次序号 AS 课次序号,
COALESCE(e.教室编号, (
SELECT MAX(js.教室编号)
FROM "实施_课程表_教室" js
WHERE js.实施_课程表编号 = b.编号
)) AS 教室编号,
b.年度,
jy.教研室代号,
(
SELECT MAX(kcb.编号)
FROM "课程表" kcb
WHERE kcb.教员编号 = e.教员编号
AND kcb.课次序号 = e.教员课次序号
AND kcb.年度 = e.年度
) AS 课程表编号
FROM "实施_课程表" 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
<if test="jybh != null and jybh != ''">
AND e.教员编号 = #{jybh}
</if>
<if test="nd != null">
AND b.年度 = #{nd}
</if>
ORDER BY b.年度 DESC, b.日期 DESC, b.节次 ASC
</select>
</mapper> </mapper>
@@ -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。
* <p>
* 提供调课申请审批记录数据的CRUD操作及自定义查询。
* </p>
*/
@Mapper
public interface SSDKSQSPMapper extends BaseMapper<SSDKSQSP> {
/**
* 根据调课申请编号查询审批记录列表。
* <p>
* 关联机构表获取机关名称,关联教员表获取审批人姓名。
* </p>
*
* @param ssdksqbh 调课申请编号
* @return 审批记录列表
*/
List<ScheduleAdjustAuditVO> selectBySsdksqbh(@Param("ssdksqbh") String ssdksqbh);
}
@@ -1,45 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.roomroot.jwgl.mapper.SSDKSQSPMapper">
<!-- 调课申请审批记录结果映射 -->
<resultMap id="ScheduleAdjustAuditMap" type="com.roomroot.jwgl.vo.courserunning.ScheduleAdjustAuditVO">
<result column="编号" property="bh"/>
<result column="实施_调课申请编号" property="ssdksqbh"/>
<result column="机关编号" property="jgbh"/>
<result column="机关名称" property="jgmc"/>
<result column="审批时间" property="spsj"/>
<result column="审批状态" property="spzt"/>
<result column="审批人编号" property="sprbh"/>
<result column="审批人姓名" property="sprxm"/>
<result column="查收状态" property="cszt"/>
<result column="查收时间" property="cssj"/>
<result column="查收人编号" property="csrbh"/>
<result column="发回意见" property="fhyj"/>
<result column="年度" property="nd"/>
</resultMap>
<!-- 根据调课申请编号查询审批记录列表 -->
<select id="selectBySsdksqbh" resultMap="ScheduleAdjustAuditMap">
SELECT
a.编号,
a.实施_调课申请编号,
a.机关编号,
j.名称 AS 机关名称,
a.审批时间,
a.审批状态,
a.审批人编号,
t.姓名 AS 审批人姓名,
a.查收状态,
a.查收时间,
a.查收人编号,
a.发回意见,
a.年度
FROM "实施_调课申请审批" a
LEFT JOIN "机构表" j ON a.机关编号 = j.编号
LEFT JOIN "教员表" t ON a.审批人编号 = t.编号
WHERE a.实施_调课申请编号 = #{ssdksqbh}
ORDER BY a.创建时间 DESC
</select>
</mapper>
@@ -61,7 +61,7 @@
* 内层:按教员编号+日期+节次 GROUP BY,筛选出现>1课次的冲突 * 内层:按教员编号+日期+节次 GROUP BY,筛选出现>1课次的冲突
* *
* 关键修正: * 关键修正:
* 1. 关联链路:SSKCB → 实施_课程 c → 课表 k;必须 CAST(c.编号 AS CHAR(36)) * 1. 关联链路:SSKCB → 实施_课程 c → 课表 k;VARBINARY 主键用 RAWTOHEX 对齐 CHAR 外键
* 2. 主讲/辅讲字段:真实列名「主教员」(1主/0辅) * 2. 主讲/辅讲字段:真实列名「主教员」(1主/0辅)
* 3. 日期节次:统一取主表 s * 3. 日期节次:统一取主表 s
* *
@@ -92,7 +92,7 @@
/* 维度过滤主关联:只看该教员的课次(GROUP BY 维度)*/ /* 维度过滤主关联:只看该教员的课次(GROUP BY 维度)*/
INNER JOIN "实施_课程表_辅助教员" f ON f."实施_课程表编号" = s."编号" 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."科目编号" LEFT JOIN "课表" k ON k."科目代码" = c."科目编号"
/* ========== 新增 JOIN:聚合该冲突课次的班次/教员/场地所有信息 ========== */ /* ========== 新增 JOIN:聚合该冲突课次的班次/教员/场地所有信息 ========== */
LEFT JOIN "实施_课程表_学员队" xyd_link ON xyd_link."实施_课程表编号" = s."编号" LEFT JOIN "实施_课程表_学员队" xyd_link ON xyd_link."实施_课程表编号" = s."编号"
@@ -62,7 +62,7 @@
* 内层:按教室编号+日期+节次 GROUP BY,筛选 COUNT(DISTINCT 课次) > 1 * 内层:按教室编号+日期+节次 GROUP BY,筛选 COUNT(DISTINCT 课次) > 1
* *
* 关键修正: * 关键修正:
* 1. 三层关联 JOIN:SSKCB → 实施_课程 → 课表;CAST(ss.编号 AS CHAR(36)) * 1. 三层关联 JOIN:SSKCB → 实施_课程 → 课表;VARBINARY 主键用 RAWTOHEX 对齐 CHAR 外键
* 2. 日期节次统一取主表 s * 2. 日期节次统一取主表 s
* *
* 新增 JOIN(同其他维度):聚合该冲突课次的班次/教员/场地所有明细信息 * 新增 JOIN(同其他维度):聚合该冲突课次的班次/教员/场地所有明细信息
@@ -88,7 +88,7 @@
/* 维度过滤主关联:教室维度分组 */ /* 维度过滤主关联:教室维度分组 */
INNER JOIN "实施_课程表_教室" c ON c."实施_课程表编号" = s."编号" INNER JOIN "实施_课程表_教室" c ON c."实施_课程表编号" = s."编号"
/* 课表信息三层关联(ss=实施_课程别名,避免与聚合JOIN的别名冲突)*/ /* 课表信息三层关联(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."科目编号" LEFT JOIN "课表" k ON k."科目代码" = ss."科目编号"
/* ========== 新增 JOIN:聚合该冲突课次的班次/教员/场地所有信息 ========== */ /* ========== 新增 JOIN:聚合该冲突课次的班次/教员/场地所有信息 ========== */
LEFT JOIN "实施_课程表_学员队" xyd_link ON xyd_link."实施_课程表编号" = s."编号" LEFT JOIN "实施_课程表_学员队" xyd_link ON xyd_link."实施_课程表编号" = s."编号"
@@ -60,7 +60,7 @@
b.创建时间, b.创建时间,
b.变动时间 b.变动时间
FROM "实施_课程表" 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.课编号 LEFT JOIN "课程表" c ON a.课编号 = c.课编号
WHERE b.删除状态 = 0 WHERE b.删除状态 = 0
<if test="sskcbh != null and sskcbh != ''"> <if test="sskcbh != null and sskcbh != ''">
@@ -116,7 +116,7 @@
b.创建时间, b.创建时间,
b.变动时间 b.变动时间
FROM "实施_课程表" 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.课编号 LEFT JOIN "课程表" c ON a.课编号 = c.课编号
WHERE b.编号 = #{sskcbbh} WHERE b.编号 = #{sskcbbh}
AND b.删除状态 = 0 AND b.删除状态 = 0
@@ -32,10 +32,11 @@
JOIN "实施_课程表_学员队" t ON t."实施_课程表编号" = s."编号" JOIN "实施_课程表_学员队" t ON t."实施_课程表编号" = s."编号"
<!-- <!--
修正:三层关联 SSKCB → 实施_课程 → 课表 修正:三层关联 SSKCB → 实施_课程 → 课表
- 类型转换:c."编号" 是 VARBINARY,s."实施_课程编号" 是 CHAR,必须 CAST 才能匹配上 - 类型转换:c."编号" 是 VARBINARY(16),s."实施_课程编号" 是 CHAR(36);
禁止直接等号(-6147),须 RAWTOHEX 去横线后比较
- 科目关联:KB.科目代码 = SSKC.科目编号 - 科目关联:KB.科目代码 = SSKC.科目编号
--> -->
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."科目编号" LEFT JOIN "课表" k ON k."科目代码" = c."科目编号"
WHERE t."学员队编号" = #{xydbh} WHERE t."学员队编号" = #{xydbh}
<if test="nd != null"> <if test="nd != null">
@@ -126,7 +127,7 @@
/* 维度过滤用:主关联"教学班(学员队)" —— 教学班冲突就是按这个维度 GROUP BY */ /* 维度过滤用:主关联"教学班(学员队)" —— 教学班冲突就是按这个维度 GROUP BY */
INNER JOIN "实施_课程表_学员队" t ON t."实施_课程表编号" = 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."科目编号" LEFT JOIN "课表" k ON k."科目代码" = c."科目编号"
/* ========== 新增 JOIN:聚合该冲突课次涉及的所有班次/教员/教室信息 ========== */ /* ========== 新增 JOIN:聚合该冲突课次涉及的所有班次/教员/教室信息 ========== */
LEFT JOIN "实施_课程表_学员队" xyd_link ON xyd_link."实施_课程表编号" = s."编号" LEFT JOIN "实施_课程表_学员队" xyd_link ON xyd_link."实施_课程表编号" = s."编号"
@@ -206,7 +207,7 @@
WM_CONCAT(DISTINCT xyd_info."所属单位") AS responsible_dept_list WM_CONCAT(DISTINCT xyd_info."所属单位") AS responsible_dept_list
FROM "实施_课程表" s FROM "实施_课程表" s
INNER JOIN "实施_课程表_学员队" t ON t."实施_课程表编号" = 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."科目编号" LEFT JOIN "课表" k ON k."科目代码" = c."科目编号"
/* ========== 新增 JOIN:三类业务信息 ========== */ /* ========== 新增 JOIN:三类业务信息 ========== */
LEFT JOIN "实施_课程表_学员队" xyd_link ON xyd_link."实施_课程表编号" = s."编号" LEFT JOIN "实施_课程表_学员队" xyd_link ON xyd_link."实施_课程表编号" = s."编号"
@@ -2,20 +2,32 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.roomroot.jwgl.mapper.SSKCMapper"> <mapper namespace="com.roomroot.jwgl.mapper.SSKCMapper">
<select id="selectBySemester" resultType="com.roomroot.jwgl.entity.SSKC"> <resultMap id="SSKCMap" type="com.roomroot.jwgl.entity.SSKC" autoMapping="true">
<id column="编号" property="bh" jdbcType="VARBINARY"
typeHandler="com.roomroot.jwgl.handler.DmGuidTypeHandler"/>
</resultMap>
<select id="selectBySemester" resultMap="SSKCMap">
SELECT * FROM 实施_课程 SELECT * FROM 实施_课程
WHERE 年度 = #{nd} AND 学期第次 = #{xqdc} AND 停用 = 0 WHERE 年度 = #{nd} AND 学期第次 = #{xqdc} AND 停用 = 0
</select> </select>
<select id="selectByClassBh" resultType="com.roomroot.jwgl.entity.SSKC"> <!--
达梦:实施_课程.编号 为 VARBINARY(16),实施_课程学员队.实施_课程编号 为 CHAR(36)。
禁止 编号 = 实施_课程编号:达梦会把 CHAR 当十六进制转 VARBINARY,UUID 中的 '-' 报 -6147。
统一:LOWER(RAWTOHEX(主键)) = LOWER(REPLACE(TRIM(外键), '-', ''))
-->
<select id="selectByClassBh" resultMap="SSKCMap">
SELECT DISTINCT sc.* FROM 实施_课程 sc 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 WHERE scxd.学员队编号 = #{classBh} AND sc.年度 = #{nd} AND sc.停用 = 0
</select> </select>
<select id="selectByCourseBhAndClassBh" resultType="com.roomroot.jwgl.entity.SSKC"> <select id="selectByCourseBhAndClassBh" resultMap="SSKCMap">
SELECT sc.* FROM 实施_课程 sc 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 WHERE sc.课编号 = #{courseBh} AND scxd.学员队编号 = #{classBh} AND sc.年度 = #{nd} AND sc.停用 = 0
</select> </select>
@@ -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。
* <p>
* 提供调课策略的基础 CRUD 能力,时间段限制与免审批策略的匹配查询
* 由 Service 层调用 BaseMapper 方法完成。
* </p>
*/
@Mapper
public interface ScheduleAdjustStrategyMapper extends BaseMapper<ScheduleAdjustStrategy> {
}
@@ -2,10 +2,17 @@ package com.roomroot.jwgl.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.roomroot.jwgl.dto.jys.TeachingTaskQuery;
import com.roomroot.jwgl.entity.JXRW; import com.roomroot.jwgl.entity.JXRW;
import com.roomroot.jwgl.vo.jys.TeachingOfficeBookCourseVO;
import com.roomroot.jwgl.vo.jys.TeachingOfficeBookVO;
import com.roomroot.jwgl.vo.jys.TeachingTaskSquadVO;
import com.roomroot.jwgl.vo.jys.TeachingTaskVO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.List;
/** /**
* 教学任务 Mapper * 教学任务 Mapper
*/ */
@@ -20,4 +27,31 @@ public interface TeachingTaskMapper extends BaseMapper<JXRW> {
* @return 分页结果 * @return 分页结果
*/ */
Page<JXRW> selectPageList(Page<JXRW> page, @Param("jxrw") JXRW jxrw); Page<JXRW> selectPageList(Page<JXRW> page, @Param("jxrw") JXRW jxrw);
}
/**
* 教学任务管理分页(含教研室数、学员队数、合班前后课程任务/学时)。
*/
Page<TeachingTaskVO> selectManagePage(Page<TeachingTaskVO> page, @Param("cond") TeachingTaskQuery cond);
/**
* 教研室任务书列表(含课程任务数、完成指定数)。
*/
List<TeachingOfficeBookVO> selectOfficeBooks(@Param("jxrwbh") String jxrwbh);
/**
* 教学任务已关联学员队。
*/
List<TeachingTaskSquadVO> selectBoundSquads(@Param("jxrwbh") String jxrwbh);
/**
* 某学期可选学员队(未占用或占用当前任务)。
*/
List<TeachingTaskSquadVO> selectAvailableSquads(@Param("nd") Integer nd,
@Param("xqdc") Integer xqdc, @Param("jxrwbh") String jxrwbh);
/**
* 教研室任务书课程明细。
*/
List<TeachingOfficeBookCourseVO> selectOfficeBookCourses(@Param("jxrwbh") String jxrwbh,
@Param("jysdh") String jysdh);
}
@@ -38,4 +38,194 @@
ORDER BY 编号 ORDER BY 编号
</select> </select>
<sql id="CourseTaskJoin">
FROM "学员队任务表" r
INNER JOIN "学员队年度学期基本信息表" s ON s."编号" = r."学员队学期编号"
WHERE s."教学任务编号" = t."编号"
AND NVL(r.DEL_FLAG, 0) = 0
AND NVL(s.DEL_FLAG, 0) = 0
</sql>
<select id="selectManagePage" resultType="com.roomroot.jwgl.vo.jys.TeachingTaskVO">
SELECT
t."编号" AS bh,
t."任务名称" AS rwmc,
t."年度" AS nd,
(SELECT MAX(s0."学期第次")
FROM "学员队年度学期基本信息表" s0
WHERE s0."教学任务编号" = t."编号"
AND NVL(s0.DEL_FLAG, 0) = 0) AS xqdc,
t."状态" AS zt,
t."发布时间" AS fbsj,
t."创建时间" AS cjsj,
t."结束时间" AS jssj,
(SELECT COUNT(1)
FROM "教研室任务书" b
WHERE b."教学任务编号" = t."编号"
AND NVL(b.DEL_FLAG, 0) = 0) AS jyss,
(SELECT COUNT(1)
FROM "学员队年度学期基本信息表" s
WHERE s."教学任务编号" = t."编号"
AND NVL(s.DEL_FLAG, 0) = 0) AS xyds,
(SELECT COUNT(1)
FROM "学员队任务表" r
INNER JOIN "学员队年度学期基本信息表" s ON s."编号" = r."学员队学期编号"
WHERE s."教学任务编号" = t."编号"
AND NVL(r.DEL_FLAG, 0) = 0
AND NVL(s.DEL_FLAG, 0) = 0) AS hbqkcrws,
(SELECT NVL(SUM(r."学时"), 0)
FROM "学员队任务表" r
INNER JOIN "学员队年度学期基本信息表" s ON s."编号" = r."学员队学期编号"
WHERE s."教学任务编号" = t."编号"
AND NVL(r.DEL_FLAG, 0) = 0
AND NVL(s.DEL_FLAG, 0) = 0) AS hbqzxs,
(SELECT COUNT(1) FROM (
SELECT CASE
WHEN r."课次序号" IS NULL THEN r."编号"
ELSE TO_CHAR(r."课次序号")
END AS gid
FROM "学员队任务表" r
INNER JOIN "学员队年度学期基本信息表" s ON s."编号" = r."学员队学期编号"
WHERE s."教学任务编号" = t."编号"
AND NVL(r.DEL_FLAG, 0) = 0
AND NVL(s.DEL_FLAG, 0) = 0
GROUP BY CASE
WHEN r."课次序号" IS NULL THEN r."编号"
ELSE TO_CHAR(r."课次序号")
END
) g) AS hbhkcrws,
(SELECT NVL(SUM(g.mx), 0) FROM (
SELECT MAX(r."学时") AS mx
FROM "学员队任务表" r
INNER JOIN "学员队年度学期基本信息表" s ON s."编号" = r."学员队学期编号"
WHERE s."教学任务编号" = t."编号"
AND NVL(r.DEL_FLAG, 0) = 0
AND NVL(s.DEL_FLAG, 0) = 0
GROUP BY CASE
WHEN r."课次序号" IS NULL THEN r."编号"
ELSE TO_CHAR(r."课次序号")
END
) g) AS hbhzxs
FROM "教学任务" t
<where>
AND NVL(t.DEL_FLAG, 0) = 0
<if test="cond != null and cond.rwmc != null and cond.rwmc != ''">
AND t."任务名称" LIKE CONCAT('%', #{cond.rwmc}, '%')
</if>
<if test="cond != null and cond.nd != null">
AND t."年度" = #{cond.nd}
</if>
<if test="cond != null and cond.zt != null and cond.zt != ''">
AND t."状态" = #{cond.zt}
</if>
<if test="cond != null and cond.xqdc != null">
AND EXISTS (
SELECT 1 FROM "学员队年度学期基本信息表" sx
WHERE sx."教学任务编号" = t."编号"
AND sx."学期第次" = #{cond.xqdc}
AND NVL(sx.DEL_FLAG, 0) = 0
)
</if>
</where>
ORDER BY t."创建时间" DESC, t."编号" DESC
</select>
<select id="selectOfficeBooks" resultType="com.roomroot.jwgl.vo.jys.TeachingOfficeBookVO">
SELECT
b."编号" AS bh,
b."教学任务编号" AS jxrwbh,
b."教研室代号" AS jysdh,
j."教研室名称" AS jysmc,
b."状态" AS zt,
b."上报时间" AS sbsj,
(SELECT COUNT(1)
FROM "学员队任务表" r
INNER JOIN "学员队年度学期基本信息表" s ON s."编号" = r."学员队学期编号"
WHERE s."教学任务编号" = b."教学任务编号"
AND r."教研室代号" = b."教研室代号"
AND NVL(r.DEL_FLAG, 0) = 0
AND NVL(s.DEL_FLAG, 0) = 0) AS xydkcrws,
(SELECT COUNT(1)
FROM "学员队任务表" r
INNER JOIN "学员队年度学期基本信息表" s ON s."编号" = r."学员队学期编号"
WHERE s."教学任务编号" = b."教学任务编号"
AND r."教研室代号" = b."教研室代号"
AND r."教研室计划教员编号" IS NOT NULL
AND r."教研室计划教员编号" != ''
AND NVL(r.DEL_FLAG, 0) = 0
AND NVL(s.DEL_FLAG, 0) = 0) AS wczds
FROM "教研室任务书" b
LEFT JOIN "教研室表" j ON j."教研室代号" = b."教研室代号"
WHERE b."教学任务编号" = #{jxrwbh}
AND NVL(b.DEL_FLAG, 0) = 0
ORDER BY b."教研室代号"
</select>
<sql id="SquadSelect">
SELECT
s."编号" AS xydxqbh,
s."学员队编号" AS xydbh,
d."学员队名称" AS xydmc,
d."年级" AS nj,
d."专业代号" AS zydh,
z."专业名称" AS zymc,
d."学员队人数" AS xydrs,
s."年度" AS nd,
s."学期第次" AS xqdc,
s."开学日期" AS kxrq,
s."结束日期" AS jsrq,
CASE
WHEN s."教学任务编号" IS NOT NULL
AND s."教学任务编号" != ''
AND (#{jxrwbh} IS NULL OR s."教学任务编号" != #{jxrwbh})
THEN 1 ELSE 0
END AS occupied
FROM "学员队年度学期基本信息表" s
LEFT JOIN "学员队表" d ON d."学员队编号" = s."学员队编号"
LEFT JOIN "专业表" z ON z."专业代号" = d."专业代号"
</sql>
<select id="selectBoundSquads" resultType="com.roomroot.jwgl.vo.jys.TeachingTaskSquadVO">
<include refid="SquadSelect"/>
WHERE s."教学任务编号" = #{jxrwbh}
AND NVL(s.DEL_FLAG, 0) = 0
ORDER BY d."年级", d."学员队名称"
</select>
<select id="selectAvailableSquads" resultType="com.roomroot.jwgl.vo.jys.TeachingTaskSquadVO">
<include refid="SquadSelect"/>
WHERE NVL(s.DEL_FLAG, 0) = 0
AND s."年度" = #{nd}
AND s."学期第次" = #{xqdc}
AND (
s."教学任务编号" IS NULL
OR s."教学任务编号" = ''
OR (#{jxrwbh} IS NOT NULL AND s."教学任务编号" = #{jxrwbh})
)
ORDER BY d."年级", d."学员队名称"
</select>
<select id="selectOfficeBookCourses" resultType="com.roomroot.jwgl.vo.jys.TeachingOfficeBookCourseVO">
SELECT
r."编号" AS rwbh,
r."课编号" AS kbh,
k."课名称" AS kmc,
r."课类型" AS klx,
r."学时" AS xs,
r."学员队编号" AS xydbh,
d."学员队名称" AS xydmc,
r."教研室计划教员编号" AS jysjhjybh,
jy."教员姓名" AS jysjhjyxm
FROM "学员队任务表" r
INNER JOIN "学员队年度学期基本信息表" s ON s."编号" = r."学员队学期编号"
LEFT JOIN "课表" k ON k."课编号" = r."课编号"
LEFT JOIN "学员队表" d ON d."学员队编号" = r."学员队编号"
LEFT JOIN "教员表" jy ON jy."教员编号" = r."教研室计划教员编号"
WHERE s."教学任务编号" = #{jxrwbh}
AND r."教研室代号" = #{jysdh}
AND NVL(r.DEL_FLAG, 0) = 0
AND NVL(s.DEL_FLAG, 0) = 0
ORDER BY d."学员队名称", k."课名称"
</select>
</mapper> </mapper>
@@ -0,0 +1,40 @@
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 {
/**
* 查询全部排课记录,无筛选条件。
*/
List<PkLessonVO> selectAll();
/**
* 同一班次同一日期节次被安排多门课程。
*/
List<PkConflictVO> selectClassTimeConflict(@Param("nd") Integer nd);
/**
* 同一班次同一日期节次必修课与选修课重叠。
*/
List<PkConflictVO> selectElectiveRequiredConflict(@Param("nd") Integer nd);
/**
* 同一教员同一日期节次被安排多门课程。
*/
List<PkConflictVO> selectTeacherTimeConflict(@Param("nd") Integer nd);
/**
* 同一教室同一日期节次被多门课程占用。
*/
List<PkConflictVO> selectClassroomTimeConflict(@Param("nd") Integer nd);
}
@@ -0,0 +1,310 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.roomroot.jwgl.mapper.TestPkMapper">
<!--
排课全查:实施_课程表为课次主表,关联课表、教员表、学员队表、教室表。
开始/结束时间取节次时间表;学年学期、周次按学期开学日期推算;应到人数优先课次绑定人数。
不做业务筛选。
-->
<select id="selectAll" resultType="com.roomroot.jwgl.vo.jwtest.PkLessonVO">
SELECT
COALESCE(NULLIF(TRIM(MAX(s."教学内容")), ''), MAX(k."课名称")) AS ktmc,
MAX(k."课名称") AS kcmc,
MAX(jy."教员姓名") AS jymc,
WM_CONCAT(DISTINCT xyd."学员队名称") AS bjmc,
MAX(js."教室名称") AS jsmc,MAX(jxlb.教学楼名称) AS jxlmc,
TO_CHAR(s."日期", 'YYYY-MM-DD') AS skrq,
MAX(jc."开始时间") AS kssj,
MAX(jc."结束时间") AS jssj,
CASE
WHEN EXTRACT(MONTH FROM MAX(xq."开学日期")) >= 8
THEN EXTRACT(YEAR FROM MAX(xq."开学日期")) || '-' || (EXTRACT(YEAR FROM MAX(xq."开学日期")) + 1)
ELSE (EXTRACT(YEAR FROM MAX(xq."开学日期")) - 1) || '-' || EXTRACT(YEAR FROM MAX(xq."开学日期"))
END
|| '学年第'
|| TRIM(LEADING '0' FROM SUBSTR(CAST(MAX(xq."年度") AS VARCHAR), 5, 2))
|| '学期' AS xnxq,
CAST(
FLOOR((TRUNC(s."日期") - TRUNC(MAX(xq."开学日期"))) / 7) + 1
AS INT
) AS zc,
(
SELECT SUM(t2."人数")
FROM "实施_课程表_学员队" t2
WHERE TRIM(t2."实施_课程表编号") = TRIM(s."编号")
) AS ydrs,
MAX(NVL(TO_CHAR(kcb."备注"), NVL(s."教务备注", s."教员备注"))) AS bz
FROM "实施_课程表_学员队" tx
INNER JOIN "实施_课程表" s
ON TRIM(s."编号") = TRIM(tx."实施_课程表编号")
LEFT JOIN "实施_课程" c
ON LOWER(RAWTOHEX(c."编号"))
= LOWER(REPLACE(TRIM(s."实施_课程编号"), '-', ''))
LEFT JOIN "课表" k
ON 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 "学员队表" xyd
ON xyd."学员队编号" = tx."学员队编号"
LEFT JOIN "教室表" js
ON js."教室编号" = NVL(kcb."教室编号", c."教室编号")
LEFT JOIN "节次时间表" jc
ON TRIM(jc."节次索引") = TRIM(CAST(s."节次" AS VARCHAR))
LEFT JOIN "学期" xq
ON CAST(xq."年度" AS VARCHAR) = CAST(s."年度" AS VARCHAR)
AND TRUNC(s."日期") &gt;= TRUNC(xq."开学日期")
AND TRUNC(s."日期") &lt;= TRUNC(xq."结束日期")
LEFT JOIN "教学楼表" jxlb on jxlb.教学楼代号=js.教学楼代号
WHERE NVL(s."删除状态", 0) = 0
GROUP BY s."编号", s."日期", s."节次", s."年度"
</select>
<!-- 课次明细:一门课一行,含课程/教员/班次/教室/节次时间 -->
<sql id="pkLessonRows">
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
<if test="nd != null">
AND s."年度" = #{nd}
</if>
GROUP BY s."编号", s."日期", s."节次"
</sql>
<!-- 1. 同一班次同一时间被安排多门课程 -->
<select id="selectClassTimeConflict" resultType="com.roomroot.jwgl.vo.jwtest.PkConflictVO">
WITH lesson AS (
<include refid="pkLessonRows"/>
)
SELECT
'教学班时间冲突' AS ctlx,
COALESCE(xyd."学员队名称", ta."学员队编号") AS ctzy,
a.skrq AS skrq,
a.jc AS jc,
a.kssj AS kssj,
a.jssj AS jssj,
'【' || COALESCE(xyd."学员队名称", ta."学员队编号") || '】在 '
|| a.skrq || ' 第' || CAST(a.jc AS VARCHAR) || '节('
|| NVL(a.kssj, '-') || '~' || NVL(a.jssj, '-') || ')同时安排了《'
|| NVL(a.kcmc, NVL(a.ktmc, '未命名')) || '》(教员:' || NVL(a.jymc, '无')
|| ',教室:' || NVL(a.jsmc, '无') || ')与《'
|| NVL(b.kcmc, NVL(b.ktmc, '未命名')) || '》(教员:' || NVL(b.jymc, '无')
|| ',教室:' || NVL(b.jsmc, '无') || ')' AS sm,
a.kcbh AS aKcBh, a.ktmc AS aKtmc, a.kcmc AS aKcmc, a.kclx AS aKclx,
a.jymc AS aJymc, a.bjmc AS aBjmc, a.jsmc AS aJsmc,
b.kcbh AS bKcBh, b.ktmc AS bKtmc, b.kcmc AS bKcmc, b.kclx AS bKclx,
b.jymc AS bJymc, b.bjmc AS bBjmc, b.jsmc AS bJsmc
FROM lesson a
INNER JOIN lesson b
ON TRUNC(a.rq) = TRUNC(b.rq)
AND a.jc = b.jc
AND TRIM(a.kcbh) &lt; TRIM(b.kcbh)
INNER JOIN "实施_课程表_学员队" ta ON TRIM(ta."实施_课程表编号") = TRIM(a.kcbh)
INNER JOIN "实施_课程表_学员队" tb
ON TRIM(tb."实施_课程表编号") = TRIM(b.kcbh)
AND ta."学员队编号" = tb."学员队编号"
LEFT JOIN "学员队表" xyd ON xyd."学员队编号" = ta."学员队编号"
ORDER BY a.rq, a.jc, ta."学员队编号"
</select>
<!-- 2. 同一班次同一时间必修课与选修课冲突 -->
<select id="selectElectiveRequiredConflict" resultType="com.roomroot.jwgl.vo.jwtest.PkConflictVO">
WITH lesson AS (
<include refid="pkLessonRows"/>
)
SELECT
'必修选修时间冲突' AS ctlx,
COALESCE(xyd."学员队名称", ta."学员队编号") AS ctzy,
a.skrq AS skrq,
a.jc AS jc,
a.kssj AS kssj,
a.jssj AS jssj,
'【' || COALESCE(xyd."学员队名称", ta."学员队编号") || '】在 '
|| a.skrq || ' 第' || CAST(a.jc AS VARCHAR) || '节('
|| NVL(a.kssj, '-') || '~' || NVL(a.jssj, '-') || ')必修课《'
|| NVL(CASE WHEN INSTR(NVL(a.kclx, ''), '选修') &gt; 0 THEN b.kcmc ELSE a.kcmc END,
'未命名')
|| '》与选修课《'
|| NVL(CASE WHEN INSTR(NVL(a.kclx, ''), '选修') &gt; 0 THEN a.kcmc ELSE b.kcmc END,
'未命名')
|| '》时间重叠;教员分别为 '
|| NVL(a.jymc, '无') || ' / ' || NVL(b.jymc, '无')
|| ',教室分别为 '
|| NVL(a.jsmc, '无') || ' / ' || NVL(b.jsmc, '无') AS sm,
a.kcbh AS aKcBh, a.ktmc AS aKtmc, a.kcmc AS aKcmc, a.kclx AS aKclx,
a.jymc AS aJymc, a.bjmc AS aBjmc, a.jsmc AS aJsmc,
b.kcbh AS bKcBh, b.ktmc AS bKtmc, b.kcmc AS bKcmc, b.kclx AS bKclx,
b.jymc AS bJymc, b.bjmc AS bBjmc, b.jsmc AS bJsmc
FROM lesson a
INNER JOIN lesson b
ON TRUNC(a.rq) = TRUNC(b.rq)
AND a.jc = b.jc
AND TRIM(a.kcbh) &lt; TRIM(b.kcbh)
INNER JOIN "实施_课程表_学员队" ta ON TRIM(ta."实施_课程表编号") = TRIM(a.kcbh)
INNER JOIN "实施_课程表_学员队" tb
ON TRIM(tb."实施_课程表编号") = TRIM(b.kcbh)
AND ta."学员队编号" = tb."学员队编号"
LEFT JOIN "学员队表" xyd ON xyd."学员队编号" = ta."学员队编号"
WHERE (
INSTR(NVL(a.kclx, ''), '必修') &gt; 0 AND INSTR(NVL(b.kclx, ''), '选修') &gt; 0
) OR (
INSTR(NVL(a.kclx, ''), '选修') &gt; 0 AND INSTR(NVL(b.kclx, ''), '必修') &gt; 0
)
ORDER BY a.rq, a.jc, ta."学员队编号"
</select>
<!-- 3. 同一教员同一时间被安排多门课程 -->
<select id="selectTeacherTimeConflict" resultType="com.roomroot.jwgl.vo.jwtest.PkConflictVO">
WITH lesson AS (
<include refid="pkLessonRows"/>
),
teacher_link AS (
SELECT TRIM(s."编号") AS kcbh, TRIM(NVL(kcb."授课教员编号", c."教员编号")) AS jybh
FROM "实施_课程表" s
LEFT JOIN "实施_课程" c
ON LOWER(RAWTOHEX(c."编号")) = LOWER(REPLACE(TRIM(s."实施_课程编号"), '-', ''))
LEFT JOIN "课程表" kcb
ON kcb."年度" = s."年度"
AND kcb."安排时间" = s."计划时间安排"
AND TRIM(kcb."教员编号") = TRIM(c."教员编号")
WHERE NVL(s."删除状态", 0) = 0
AND TRIM(NVL(kcb."授课教员编号", c."教员编号")) IS NOT NULL
AND TRIM(NVL(kcb."授课教员编号", c."教员编号")) &lt;&gt; ''
<if test="nd != null">
AND s."年度" = #{nd}
</if>
UNION
SELECT TRIM(fz."实施_课程表编号") AS kcbh, TRIM(fz."辅助教员编号") AS jybh
FROM "实施_课程表_辅助教员" fz
INNER JOIN "实施_课程表" s ON TRIM(s."编号") = TRIM(fz."实施_课程表编号")
WHERE NVL(s."删除状态", 0) = 0
AND TRIM(fz."辅助教员编号") IS NOT NULL
AND TRIM(fz."辅助教员编号") &lt;&gt; ''
<if test="nd != null">
AND s."年度" = #{nd}
</if>
)
SELECT
'教员时间冲突' AS ctlx,
COALESCE(jy."教员姓名", ta.jybh) AS ctzy,
a.skrq AS skrq,
a.jc AS jc,
a.kssj AS kssj,
a.jssj AS jssj,
'教员【' || COALESCE(jy."教员姓名", ta.jybh) || '】在 '
|| a.skrq || ' 第' || CAST(a.jc AS VARCHAR) || '节('
|| NVL(a.kssj, '-') || '~' || NVL(a.jssj, '-') || ')同时被安排《'
|| NVL(a.kcmc, NVL(a.ktmc, '未命名')) || '》(班级:' || NVL(a.bjmc, '无')
|| ',教室:' || NVL(a.jsmc, '无') || ')与《'
|| NVL(b.kcmc, NVL(b.ktmc, '未命名')) || '》(班级:' || NVL(b.bjmc, '无')
|| ',教室:' || NVL(b.jsmc, '无') || ')' AS sm,
a.kcbh AS aKcBh, a.ktmc AS aKtmc, a.kcmc AS aKcmc, a.kclx AS aKclx,
a.jymc AS aJymc, a.bjmc AS aBjmc, a.jsmc AS aJsmc,
b.kcbh AS bKcBh, b.ktmc AS bKtmc, b.kcmc AS bKcmc, b.kclx AS bKclx,
b.jymc AS bJymc, b.bjmc AS bBjmc, b.jsmc AS bJsmc
FROM lesson a
INNER JOIN lesson b
ON TRUNC(a.rq) = TRUNC(b.rq)
AND a.jc = b.jc
AND TRIM(a.kcbh) &lt; TRIM(b.kcbh)
INNER JOIN teacher_link ta ON TRIM(ta.kcbh) = TRIM(a.kcbh)
INNER JOIN teacher_link tb ON TRIM(tb.kcbh) = TRIM(b.kcbh) AND ta.jybh = tb.jybh
LEFT JOIN "教员表" jy ON TRIM(jy."教员编号") = ta.jybh
ORDER BY a.rq, a.jc, ta.jybh
</select>
<!-- 4. 同一教室同一时间被多门课程占用 -->
<select id="selectClassroomTimeConflict" resultType="com.roomroot.jwgl.vo.jwtest.PkConflictVO">
WITH lesson AS (
<include refid="pkLessonRows"/>
),
room_link AS (
SELECT TRIM(s."编号") AS kcbh, TRIM(NVL(kcb."教室编号", c."教室编号")) AS jsbh
FROM "实施_课程表" s
LEFT JOIN "实施_课程" c
ON LOWER(RAWTOHEX(c."编号")) = LOWER(REPLACE(TRIM(s."实施_课程编号"), '-', ''))
LEFT JOIN "课程表" kcb
ON kcb."年度" = s."年度"
AND kcb."安排时间" = s."计划时间安排"
AND TRIM(kcb."教员编号") = TRIM(c."教员编号")
WHERE NVL(s."删除状态", 0) = 0
AND TRIM(NVL(kcb."教室编号", c."教室编号")) IS NOT NULL
AND TRIM(NVL(kcb."教室编号", c."教室编号")) &lt;&gt; ''
<if test="nd != null">
AND s."年度" = #{nd}
</if>
UNION
SELECT TRIM(jsx."实施_课程表编号") AS kcbh, TRIM(jsx."教室编号") AS jsbh
FROM "实施_课程表_教室" jsx
INNER JOIN "实施_课程表" s ON TRIM(s."编号") = TRIM(jsx."实施_课程表编号")
WHERE NVL(s."删除状态", 0) = 0
AND TRIM(jsx."教室编号") IS NOT NULL
AND TRIM(jsx."教室编号") &lt;&gt; ''
<if test="nd != null">
AND s."年度" = #{nd}
</if>
)
SELECT
'教室时间冲突' AS ctlx,
COALESCE(js."教室名称", ta.jsbh) AS ctzy,
a.skrq AS skrq,
a.jc AS jc,
a.kssj AS kssj,
a.jssj AS jssj,
'教室【' || COALESCE(js."教室名称", ta.jsbh) || '】在 '
|| a.skrq || ' 第' || CAST(a.jc AS VARCHAR) || '节('
|| NVL(a.kssj, '-') || '~' || NVL(a.jssj, '-') || ')同时被《'
|| NVL(a.kcmc, NVL(a.ktmc, '未命名')) || '》(教员:' || NVL(a.jymc, '无')
|| ',班级:' || NVL(a.bjmc, '无') || ')与《'
|| NVL(b.kcmc, NVL(b.ktmc, '未命名')) || '》(教员:' || NVL(b.jymc, '无')
|| ',班级:' || NVL(b.bjmc, '无') || ')占用' AS sm,
a.kcbh AS aKcBh, a.ktmc AS aKtmc, a.kcmc AS aKcmc, a.kclx AS aKclx,
a.jymc AS aJymc, a.bjmc AS aBjmc, a.jsmc AS aJsmc,
b.kcbh AS bKcBh, b.ktmc AS bKtmc, b.kcmc AS bKcmc, b.kclx AS bKclx,
b.jymc AS bJymc, b.bjmc AS bBjmc, b.jsmc AS bJsmc
FROM lesson a
INNER JOIN lesson b
ON TRUNC(a.rq) = TRUNC(b.rq)
AND a.jc = b.jc
AND TRIM(a.kcbh) &lt; TRIM(b.kcbh)
INNER JOIN room_link ta ON TRIM(ta.kcbh) = TRIM(a.kcbh)
INNER JOIN room_link tb ON TRIM(tb.kcbh) = TRIM(b.kcbh) AND ta.jsbh = tb.jsbh
LEFT JOIN "教室表" js ON TRIM(js."教室编号") = ta.jsbh
ORDER BY a.rq, a.jc, ta.jsbh
</select>
</mapper>
@@ -216,7 +216,7 @@
LEFT JOIN "教员表" j ON j."教员编号" = sp."审批人编号" LEFT JOIN "教员表" j ON j."教员编号" = sp."审批人编号"
LEFT JOIN "请假课堂表" qjkt ON qjkt."请假编号" = q."编号" LEFT JOIN "请假课堂表" qjkt ON qjkt."请假编号" = q."编号"
LEFT JOIN "实施_课程表" sskcb ON sskcb."编号" = qjkt."实施_课堂编号" 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."课编号" LEFT JOIN "课表" kb ON sskc."科目编号" = kb."课编号"
WHERE q."编号" = #{qjbh} WHERE q."编号" = #{qjbh}
</select> </select>
@@ -18,8 +18,8 @@ import com.roomroot.jwgl.dto.courserunning.StudentLeaveApplyDTO;
import com.roomroot.jwgl.dto.courserunning.StudentLeaveAuditDTO; import com.roomroot.jwgl.dto.courserunning.StudentLeaveAuditDTO;
import com.roomroot.jwgl.dto.courserunning.StudentLeaveQueryDTO; import com.roomroot.jwgl.dto.courserunning.StudentLeaveQueryDTO;
import com.roomroot.jwgl.dto.courserunning.OperationAnalysisQueryDTO; import com.roomroot.jwgl.dto.courserunning.OperationAnalysisQueryDTO;
import com.roomroot.jwgl.dto.courserunning.ScheduleAdjustStrategyDTO;
import com.roomroot.jwgl.unit.PageResult; import com.roomroot.jwgl.unit.PageResult;
import com.roomroot.jwgl.vo.courserunning.AdjustableLessonVO;
import com.roomroot.jwgl.vo.courserunning.CourseRunningImportResultVO; import com.roomroot.jwgl.vo.courserunning.CourseRunningImportResultVO;
import com.roomroot.jwgl.vo.courserunning.ScheduleAdjustApplyVO; import com.roomroot.jwgl.vo.courserunning.ScheduleAdjustApplyVO;
import com.roomroot.jwgl.vo.courserunning.ScheduleAdjustDetailVO; import com.roomroot.jwgl.vo.courserunning.ScheduleAdjustDetailVO;
@@ -36,7 +36,6 @@ import com.roomroot.jwgl.vo.courserunning.OperationOverviewVO;
import com.roomroot.jwgl.vo.courserunning.CourseCompletionByDeptVO; import com.roomroot.jwgl.vo.courserunning.CourseCompletionByDeptVO;
import com.roomroot.jwgl.vo.courserunning.VenueUsageRateVO; import com.roomroot.jwgl.vo.courserunning.VenueUsageRateVO;
import com.roomroot.jwgl.vo.courserunning.ScheduleAdjustTrendVO; import com.roomroot.jwgl.vo.courserunning.ScheduleAdjustTrendVO;
import com.roomroot.jwgl.vo.courserunning.ScheduleAdjustStrategyVO;
import java.util.List; import java.util.List;
@@ -178,6 +177,14 @@ public interface CourseRunningImportService {
*/ */
PageResult<ScheduleAdjustApplyVO> queryScheduleAdjustList(ScheduleAdjustQueryDTO query); PageResult<ScheduleAdjustApplyVO> queryScheduleAdjustList(ScheduleAdjustQueryDTO query);
/**
* 查询当前教员可供调课的课次,关联学员队任务表、课表、课程表。
*
* @param nd 年度,可空
* @return 可选课次列表
*/
List<AdjustableLessonVO> listAdjustableLessons(Integer nd);
/** /**
* 查询单个调课申请的完整详情。 * 查询单个调课申请的完整详情。
* <p> * <p>
@@ -206,101 +213,23 @@ public interface CourseRunningImportService {
/** /**
* 教研室审批调课申请。 * 教研室审批调课申请。
* <p> * <p>
* 教研室审批教员提交的调课申请,包含同意、发回、拒绝三种处理方式。 * 教研室审批调课申请。点审批即已查收,审批人同时记为查收人。
* 同意时同步更新课次数据,发回时记录发回原因,拒绝时标记申请为已拒绝。 * 同意后写回课次;发回、拒绝也记为已查收。
* </p> * </p>
* *
* @param dto 教研室审批参数 * @param dto 教研室审批参数
*/ */
void auditByTeachingOffice(ScheduleAdjustAuditDTO dto); void auditByTeachingOffice(ScheduleAdjustAuditDTO dto);
/**
* 教研室查收调课申请。
* <p>
* 教研室查收调课申请,标记查收状态和查收时间。
* </p>
*
* @param ssdksqbh 调课申请编号
* @param csrbh 查收人编号
*/
void receiveByTeachingOffice(String ssdksqbh, String csrbh);
/**
* 教学系审批调课申请。
* <p>
* 教学系审批教研室已同意的调课申请,包含同意、发回、拒绝三种处理方式。
* 教学系同意后流转到机关审批。
* </p>
*
* @param dto 教学系审批参数
*/
void auditByTeachingDepartment(ScheduleAdjustAuditDTO dto);
/**
* 机关审批调课申请。
* <p>
* 机关审批教研室已同意的调课申请,包含同意、发回、拒绝三种处理方式。
* 机关全部同意后调课申请最终生效,课次按调整后的内容执行。
* </p>
*
* @param dto 机关审批参数
*/
void auditByAuthority(ScheduleAdjustAuditDTO dto);
/**
* 机关查收调课申请。
* <p>
* 机关查收调课申请,标记查收状态和查收时间。
* </p>
*
* @param ssdksqbh 调课申请编号
* @param jgbh 机关编号
* @param csrbh 查收人编号
*/
void receiveByAuthority(String ssdksqbh, String jgbh, String csrbh);
/** /**
* 撤销调课申请。 * 撤销调课申请。
* <p> * <p>
* 教员撤销自己提交的、未经过审批的调课申请。 * 教员撤销未经过审批的调课申请。列表已按登录教员过滤。
* 软删除调课申请,标记删除状态为1。
* </p> * </p>
* *
* @param ssdksqbh 调课申请编号 * @param ssdksqbh 调课申请编号
* @param sqjybh 申请教员编号(用于权限校验)
*/ */
void cancelScheduleAdjust(String ssdksqbh, String sqjybh); void cancelScheduleAdjust(String ssdksqbh);
// ==================== 调课策略管理 ====================
/**
* 查询调课策略列表。
* <p>
* 支持按策略类型筛选,返回所有策略(含停用)。
* </p>
*
* @param cllx 策略类型:TIME_LIMIT/AUTO_APPROVE,为空时查询全部
* @return 调课策略列表
*/
List<ScheduleAdjustStrategyVO> listScheduleAdjustStrategies(String cllx);
/**
* 保存调课策略(新增或修改)。
* <p>
* 策略编号为空时执行新增,非空时执行修改。
* </p>
*
* @param dto 策略参数
* @return 策略编号
*/
String saveScheduleAdjustStrategy(ScheduleAdjustStrategyDTO dto);
/**
* 删除调课策略。
*
* @param clbh 策略编号
*/
void deleteScheduleAdjustStrategy(String clbh);
// ==================== 教学保障管理 ==================== // ==================== 教学保障管理 ====================
@@ -0,0 +1,40 @@
package com.roomroot.jwgl.service;
import com.roomroot.jwgl.dto.jys.ElectiveCourseQuery;
import com.roomroot.jwgl.dto.jys.ElectiveCourseSaveDTO;
import com.roomroot.jwgl.unit.PageQuery;
import com.roomroot.jwgl.unit.PageResult;
import com.roomroot.jwgl.vo.jys.ElectiveCourseVO;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/**
* 选修课管理。
* <p>数据来源:学员队任务表 INNER JOIN 学员队表 INNER JOIN 课表,课类型含“选修”。</p>
*/
public interface ElectiveCourseService {
PageResult<ElectiveCourseVO> pageList(PageQuery query, ElectiveCourseQuery cond);
void add(ElectiveCourseSaveDTO dto);
int batchOpenRegistration(List<String> bhList);
int batchCancelOpenRegistration(List<String> bhList);
int batchStopRegistration(List<String> bhList);
int batchReverseClassRange(List<String> bhList);
void exportClassList(ElectiveCourseQuery cond, HttpServletResponse response);
void exportStudentExcel(List<String> bhList, HttpServletResponse response);
void exportStudentWord(List<String> bhList, HttpServletResponse response);
void downloadTemplate(HttpServletResponse response);
int importData(MultipartFile file) throws Exception;
}
@@ -76,6 +76,16 @@ public interface FacultyService {
*/ */
JYB getJYBById(String jYBH); JYB getJYBById(String jYBH);
/**
* 从 Excel(.xls/.xlsx) 导入教员
*/
int importJYBFromExcel(MultipartFile file) throws Exception;
/**
* 导出教员到 Excel(.xlsx)
*/
void exportJYBExcel(jakarta.servlet.http.HttpServletResponse response) throws Exception;
/** /**
* 分页条件查询教员列表 * 分页条件查询教员列表
*/ */
@@ -1,6 +1,12 @@
package com.roomroot.jwgl.service; package com.roomroot.jwgl.service;
import com.roomroot.jwgl.dto.kcb.TimetableQuery;
import com.roomroot.jwgl.entity.KCB; import com.roomroot.jwgl.entity.KCB;
import com.roomroot.jwgl.unit.PageQuery;
import com.roomroot.jwgl.unit.PageResult;
import com.roomroot.jwgl.vo.kcb.ClassTimetableVO;
import com.roomroot.jwgl.vo.kcb.DailyWeeklyTimetableVO;
import jakarta.servlet.http.HttpServletResponse;
import java.util.List; import java.util.List;
@@ -51,4 +57,34 @@ public interface KCBService {
* @return 所有数据列表 * @return 所有数据列表
*/ */
List<KCB> list(); List<KCB> list();
/**
* 今日课表。未传年度时按当前时间取本学期。
*/
List<DailyWeeklyTimetableVO> listToday(TimetableQuery cond);
/**
* 本周课表(周一至周日)。未传年度时按当前时间取本学期。
*/
List<DailyWeeklyTimetableVO> listWeek(TimetableQuery cond);
/**
* 班次课表。未传年度时按当前时间取本学期,可按队别班次筛选。
*/
PageResult<ClassTimetableVO> pageClassTimetable(PageQuery query, TimetableQuery cond);
/**
* 下载学期教学实施计划(课次明细 Excel)。
*/
void exportSemesterPlan(TimetableQuery cond, HttpServletResponse response);
/**
* 下载学期成绩表 Excel。
*/
void exportSemesterGrades(TimetableQuery cond, HttpServletResponse response);
/**
* 班次课程列表另存 Excel。
*/
void exportClassCourses(TimetableQuery cond, HttpServletResponse response);
} }
@@ -0,0 +1,39 @@
package com.roomroot.jwgl.service;
import com.roomroot.jwgl.dto.jys.TeachingTaskQuery;
import com.roomroot.jwgl.dto.jys.TeachingTaskSaveDTO;
import com.roomroot.jwgl.unit.PageQuery;
import com.roomroot.jwgl.unit.PageResult;
import com.roomroot.jwgl.vo.jys.TeachingOfficeBookCourseVO;
import com.roomroot.jwgl.vo.jys.TeachingOfficeBookVO;
import com.roomroot.jwgl.vo.jys.TeachingTaskDetailVO;
import com.roomroot.jwgl.vo.jys.TeachingTaskSquadVO;
import com.roomroot.jwgl.vo.jys.TeachingTaskVO;
import java.util.List;
/**
* 教学任务管理(机关发布,关联教研室任务书与学员队,供排课使用)。
*/
public interface TeachingTaskManageService {
PageResult<TeachingTaskVO> pageList(PageQuery query, TeachingTaskQuery cond);
TeachingTaskDetailVO getDetail(String bh);
void add(TeachingTaskSaveDTO dto);
void update(TeachingTaskSaveDTO dto);
void delete(String bh);
int publish(String bh);
List<TeachingOfficeBookVO> listOfficeBooks(String jxrwbh);
List<TeachingOfficeBookCourseVO> listOfficeBookCourses(String jxrwbh, String jysdh);
List<TeachingTaskSquadVO> listBoundSquads(String jxrwbh);
List<TeachingTaskSquadVO> listAvailableSquads(Integer nd, Integer xqdc, String jxrwbh);
}
@@ -53,4 +53,10 @@ public interface TrainingProgramService {
* @return 分页结果 * @return 分页结果
*/ */
PageResult<ZYB> pageList(PageQuery query, ZYB cond); PageResult<ZYB> 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;
} }
@@ -60,4 +60,10 @@ public interface ZYJXJHBService {
* @return 列表 * @return 列表
*/ */
List<ZYJXJHB> listByZydhAndTy(String zydh, Integer ty); List<ZYJXJHB> 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;
} }
@@ -0,0 +1,534 @@
package com.roomroot.jwgl.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.roomroot.common.exception.ServiceException;
import com.roomroot.common.utils.StringUtils;
import com.roomroot.common.utils.file.FileUtils;
import com.roomroot.common.utils.poi.ExcelUtil;
import com.roomroot.jwgl.dto.jys.ElectiveCourseImportDTO;
import com.roomroot.jwgl.dto.jys.ElectiveCourseQuery;
import com.roomroot.jwgl.dto.jys.ElectiveCourseSaveDTO;
import com.roomroot.jwgl.entity.JSB;
import com.roomroot.jwgl.entity.JYB;
import com.roomroot.jwgl.entity.KB;
import com.roomroot.jwgl.entity.XYDB;
import com.roomroot.jwgl.entity.XYDQB;
import com.roomroot.jwgl.entity.XYDRWB;
import com.roomroot.jwgl.mapper.ClassRoomMapper;
import com.roomroot.jwgl.mapper.ElectiveCourseMapper;
import com.roomroot.jwgl.mapper.JYBMapper;
import com.roomroot.jwgl.mapper.KBMapper;
import com.roomroot.jwgl.mapper.StudentTeamTaskMapper;
import com.roomroot.jwgl.mapper.XYDBMapper;
import com.roomroot.jwgl.mapper.XYDQBMapper;
import com.roomroot.jwgl.service.ElectiveCourseService;
import com.roomroot.jwgl.unit.PageQuery;
import com.roomroot.jwgl.unit.PageResult;
import com.roomroot.jwgl.utils.UuidUtil;
import com.roomroot.jwgl.vo.jys.ElectiveCourseStudentVO;
import com.roomroot.jwgl.vo.jys.ElectiveCourseVO;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.OutputStream;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import static com.roomroot.jwgl.utils.BasicManagementConstants.BAD_REQUEST;
import static com.roomroot.jwgl.utils.BasicManagementConstants.NOT_FOUND;
/**
* 选修课管理实现。
*/
@Service
public class ElectiveCourseServiceImpl implements ElectiveCourseService {
private static final String COURSE_TYPE_ELECTIVE = "选修";
private static final String STATUS_DRAFT = "拟制";
private static final String STATUS_OPEN = "开放报名";
private static final String STATUS_STOP = "停止报名";
private static final String CLASS_TYPE_ELECTIVE = "选修班";
private static final int FLAG_DRAFT = 0;
private static final int FLAG_OPEN = 1;
private static final int FLAG_STOP = 2;
@Resource
private ElectiveCourseMapper electiveCourseMapper;
@Resource
private StudentTeamTaskMapper studentTeamTaskMapper;
@Resource
private XYDBMapper xydbMapper;
@Resource
private KBMapper kbMapper;
@Resource
private JYBMapper jybMapper;
@Resource
private ClassRoomMapper classRoomMapper;
@Resource
private XYDQBMapper xydqbMapper;
@Override
public PageResult<ElectiveCourseVO> pageList(PageQuery query, ElectiveCourseQuery cond) {
ElectiveCourseQuery filter = normalizeQuery(cond);
int pageNum = query.getPageNum() == null ? 1 : query.getPageNum();
int pageSize = query.getPageSize() == null ? 20 : query.getPageSize();
Page<ElectiveCourseVO> page = new Page<>(pageNum, pageSize);
Page<ElectiveCourseVO> result = electiveCourseMapper.selectElectiveCoursePage(page, filter);
fillSerial(result.getRecords(), pageNum, pageSize);
return new PageResult<>(result.getRecords(), result.getTotal(), pageNum, pageSize);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void add(ElectiveCourseSaveDTO dto) {
if (dto == null) {
throw new ServiceException("选修课信息不能为空", BAD_REQUEST);
}
KB course = requireCourse(dto.getKbh());
String xydbh = resolveTeamId(dto.getXydbh(), dto.getXxbmc(), dto.getNj(), true);
XYDRWB task = new XYDRWB();
task.setBh(UuidUtil.getOriginalUUID());
task.setDelFlag(0);
task.setNd(dto.getNd() != null ? dto.getNd() : LocalDate.now().getYear());
task.setXydbh(xydbh);
task.setKbh(course.getKbh());
task.setXqdc(dto.getXqdc());
task.setJybh(blankToNull(dto.getJybh()));
task.setJsbh(blankToNull(dto.getJsbh()));
task.setRs(dto.getRs());
task.setXs(dto.getXs() != null ? dto.getXs() : course.getXs());
task.setXf(dto.getXf() != null ? dto.getXf() : course.getXf());
task.setKlx(ensureElectiveType(null));
task.setJysdh(StringUtils.isNotEmpty(dto.getJysdh()) ? dto.getJysdh() : course.getJysdh());
task.setBz(dto.getBz());
task.setXz(FLAG_DRAFT);
studentTeamTaskMapper.insert(task);
upsertPinban(xydbh, STATUS_DRAFT, dto.getBmkssj(), dto.getBmjssj(),
dto.getYqsm(), dto.getKxdlbc(), dto.getBz());
updateTeamDates(xydbh, dto.getKkrq(), dto.getJkrq());
}
@Override
@Transactional(rollbackFor = Exception.class)
public int batchOpenRegistration(List<String> bhList) {
return changeStatus(bhList, STATUS_OPEN, FLAG_OPEN, 1, false);
}
@Override
@Transactional(rollbackFor = Exception.class)
public int batchCancelOpenRegistration(List<String> bhList) {
return changeStatus(bhList, STATUS_DRAFT, FLAG_DRAFT, 0, false);
}
@Override
@Transactional(rollbackFor = Exception.class)
public int batchStopRegistration(List<String> bhList) {
return changeStatus(bhList, STATUS_STOP, FLAG_STOP, 0, true);
}
@Override
@Transactional(rollbackFor = Exception.class)
public int batchReverseClassRange(List<String> bhList) {
List<XYDRWB> tasks = loadElectiveTasks(bhList);
int count = 0;
Set<String> handled = new LinkedHashSet<>();
for (XYDRWB task : tasks) {
if (!handled.add(task.getXydbh())) {
continue;
}
List<String> names = electiveCourseMapper.selectOriginalClassNames(task.getXydbh());
if (names == null || names.isEmpty()) {
throw new ServiceException("选修班暂无学员,无法根据学员反向确定班次范围", BAD_REQUEST);
}
upsertPinban(task.getXydbh(), null, null, null, null, String.join(";", names), null);
count++;
}
return count;
}
@Override
public void exportClassList(ElectiveCourseQuery cond, HttpServletResponse response) {
List<ElectiveCourseVO> list = electiveCourseMapper.selectElectiveCourseList(normalizeQuery(cond));
fillSerial(list, 1, list == null ? 0 : list.size());
ExcelUtil<ElectiveCourseVO> util = new ExcelUtil<>(ElectiveCourseVO.class);
util.exportExcel(response, list, "选修班列表");
}
@Override
public void exportStudentExcel(List<String> bhList, HttpServletResponse response) {
List<ElectiveCourseStudentVO> students = loadStudents(bhList);
ExcelUtil<ElectiveCourseStudentVO> util = new ExcelUtil<>(ElectiveCourseStudentVO.class);
util.exportExcel(response, students, "选修班学员名单");
}
@Override
public void exportStudentWord(List<String> bhList, HttpServletResponse response) {
List<ElectiveCourseStudentVO> students = loadStudents(bhList);
try (XWPFDocument doc = new XWPFDocument(); OutputStream os = response.getOutputStream()) {
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
FileUtils.setAttachmentResponseHeader(response, "选修班学员名单.docx");
writeStudentWord(doc, students);
doc.write(os);
os.flush();
} catch (ServiceException e) {
throw e;
} catch (Exception e) {
throw new ServiceException("导出Word失败:" + e.getMessage(), BAD_REQUEST);
}
}
@Override
public void downloadTemplate(HttpServletResponse response) {
ExcelUtil<ElectiveCourseImportDTO> util = new ExcelUtil<>(ElectiveCourseImportDTO.class);
util.importTemplateExcel(response, "选修课数据");
}
@Override
@Transactional(rollbackFor = Exception.class)
public int importData(MultipartFile file) throws Exception {
if (file == null || file.isEmpty()) {
throw new ServiceException("导入文件不能为空", BAD_REQUEST);
}
ExcelUtil<ElectiveCourseImportDTO> util = new ExcelUtil<>(ElectiveCourseImportDTO.class);
List<ElectiveCourseImportDTO> rows = util.importExcel(file.getInputStream());
if (rows == null || rows.isEmpty()) {
throw new ServiceException("Excel中无有效数据", BAD_REQUEST);
}
int count = 0;
for (int i = 0; i < rows.size(); i++) {
ElectiveCourseImportDTO row = rows.get(i);
int line = i + 2;
if (StringUtils.isEmpty(row.getKmc())) {
throw new ServiceException("第" + line + "行课程科目名称未填写", BAD_REQUEST);
}
if (StringUtils.isEmpty(row.getXxbmc())) {
throw new ServiceException("第" + line + "行选修班名称未填写", BAD_REQUEST);
}
KB course = kbMapper.selectOne(new LambdaQueryWrapper<KB>().eq(KB::getKmc, row.getKmc().trim()));
if (course == null) {
throw new ServiceException("第" + line + "行课程科目名称不存在:" + row.getKmc(), NOT_FOUND);
}
String jybh = null;
if (StringUtils.isNotEmpty(row.getJyxm())) {
JYB teacher = jybMapper.selectOne(new LambdaQueryWrapper<JYB>().eq(JYB::getJyxm, row.getJyxm().trim()));
if (teacher == null) {
throw new ServiceException("第" + line + "行授课教员不存在:" + row.getJyxm(), NOT_FOUND);
}
jybh = teacher.getJybh();
}
String jsbh = null;
if (StringUtils.isNotEmpty(row.getJsmc())) {
JSB room = classRoomMapper.selectOne(new LambdaQueryWrapper<JSB>().eq(JSB::getJsmc, row.getJsmc().trim()));
if (room == null) {
throw new ServiceException("第" + line + "行教学场地不存在:" + row.getJsmc(), NOT_FOUND);
}
jsbh = room.getJsbh();
}
String xydbh = resolveTeamId(null, row.getXxbmc().trim(), row.getKxbcnj(), true);
XYDRWB task = new XYDRWB();
task.setBh(UuidUtil.getOriginalUUID());
task.setDelFlag(0);
task.setNd(LocalDate.now().getYear());
task.setXydbh(xydbh);
task.setKbh(course.getKbh());
task.setJybh(jybh);
task.setJsbh(jsbh);
task.setRs(row.getJhrs());
task.setXs(row.getJhxs() != null ? row.getJhxs() : course.getXs());
task.setXf(row.getXf() != null ? row.getXf() : course.getXf());
task.setKlx(ensureElectiveType(null));
task.setJysdh(course.getJysdh());
task.setXz(statusToFlag(row.getXkzt()));
studentTeamTaskMapper.insert(task);
upsertPinban(xydbh, normalizeStatus(row.getXkzt()), parseDateTime(row.getBmkssj()),
parseDateTime(row.getBmjssj()), row.getYqsm(), row.getKxbcnj(), null);
updateTeamDates(xydbh, parseDateTime(row.getKkrq()), parseDateTime(row.getJkrq()));
count++;
}
return count;
}
private int changeStatus(List<String> bhList, String status, int xzFlag, Integer allowRegister, boolean stopNow) {
List<XYDRWB> tasks = loadElectiveTasks(bhList);
Set<String> teams = new LinkedHashSet<>();
for (XYDRWB task : tasks) {
task.setXz(xzFlag);
studentTeamTaskMapper.updateById(task);
teams.add(task.getXydbh());
}
for (String xydbh : teams) {
upsertPinban(xydbh, status, null, null, null, null, null);
updatePeriodRegister(xydbh, allowRegister, stopNow);
}
return tasks.size();
}
private List<XYDRWB> loadElectiveTasks(List<String> bhList) {
if (bhList == null || bhList.isEmpty()) {
throw new ServiceException("请先选择选修课", BAD_REQUEST);
}
List<XYDRWB> tasks = new ArrayList<>();
for (String bh : bhList) {
if (StringUtils.isEmpty(bh)) {
continue;
}
XYDRWB task = studentTeamTaskMapper.selectById(bh);
if (task == null) {
throw new ServiceException("选修课不存在", NOT_FOUND);
}
if (task.getKlx() == null || !task.getKlx().contains(COURSE_TYPE_ELECTIVE)) {
throw new ServiceException("所选记录不是选修课", BAD_REQUEST);
}
tasks.add(task);
}
if (tasks.isEmpty()) {
throw new ServiceException("请先选择选修课", BAD_REQUEST);
}
return tasks;
}
private List<ElectiveCourseStudentVO> loadStudents(List<String> bhList) {
List<XYDRWB> tasks = loadElectiveTasks(bhList);
List<ElectiveCourseStudentVO> students = new ArrayList<>();
Set<String> teams = new LinkedHashSet<>();
for (XYDRWB task : tasks) {
if (!teams.add(task.getXydbh())) {
continue;
}
KB course = kbMapper.selectById(task.getKbh());
List<ElectiveCourseStudentVO> rows = electiveCourseMapper.selectElectiveStudents(task.getXydbh());
if (rows == null) {
continue;
}
for (ElectiveCourseStudentVO row : rows) {
if (course != null) {
row.setKmc(course.getKmc());
}
students.add(row);
}
}
if (students.isEmpty()) {
throw new ServiceException("所选选修班暂无学员", BAD_REQUEST);
}
for (int i = 0; i < students.size(); i++) {
students.get(i).setXh(i + 1);
}
return students;
}
private void upsertPinban(String xydbh, String zt, LocalDateTime start, LocalDateTime end,
String yqsm, String bmdtj, String bz) {
Integer exists = electiveCourseMapper.countPinban(xydbh);
if (exists == null || exists == 0) {
electiveCourseMapper.insertPinban(xydbh, zt == null ? STATUS_DRAFT : zt, start, end, yqsm, bmdtj, bz);
} else {
electiveCourseMapper.updatePinban(xydbh, zt, start, end, yqsm, bmdtj);
}
}
private String resolveTeamId(String xydbh, String xxbmc, String nj, boolean createIfMissing) {
if (StringUtils.isNotEmpty(xydbh)) {
XYDB exists = xydbMapper.selectById(xydbh);
if (exists == null) {
throw new ServiceException("选修班不存在", NOT_FOUND);
}
return xydbh;
}
if (StringUtils.isEmpty(xxbmc)) {
throw new ServiceException("选修班编号或名称不能为空", BAD_REQUEST);
}
XYDB team = xydbMapper.selectOne(new LambdaQueryWrapper<XYDB>().eq(XYDB::getXydmc, xxbmc.trim()));
if (team != null) {
return team.getXydbh();
}
if (!createIfMissing) {
throw new ServiceException("选修班不存在:" + xxbmc, NOT_FOUND);
}
XYDB created = new XYDB();
created.setXydbh(UuidUtil.getOriginalUUID());
created.setXydmc(xxbmc.trim());
created.setNj(blankToNull(nj));
created.setXydlx(CLASS_TYPE_ELECTIVE);
created.setXslx(0);
xydbMapper.insert(created);
return created.getXydbh();
}
private KB requireCourse(String kbh) {
if (StringUtils.isEmpty(kbh)) {
throw new ServiceException("课程科目不能为空", BAD_REQUEST);
}
KB course = kbMapper.selectById(kbh);
if (course == null) {
throw new ServiceException("课程科目不存在", NOT_FOUND);
}
return course;
}
private void updateTeamDates(String xydbh, LocalDateTime start, LocalDateTime end) {
if (start == null && end == null) {
return;
}
XYDB patch = new XYDB();
patch.setXydbh(xydbh);
if (start != null) {
patch.setRxrq(start);
}
if (end != null) {
patch.setByrq(end);
}
xydbMapper.updateById(patch);
}
private void updatePeriodRegister(String xydbh, Integer allowRegister, boolean stopNow) {
List<XYDQB> periods = xydqbMapper.selectList(new LambdaQueryWrapper<XYDQB>().eq(XYDQB::getXydbh, xydbh));
if (periods == null || periods.isEmpty()) {
return;
}
for (XYDQB period : periods) {
LambdaUpdateWrapper<XYDQB> update = new LambdaUpdateWrapper<XYDQB>()
.eq(XYDQB::getBh, period.getBh())
.set(XYDQB::getYxzc, allowRegister);
if (stopNow) {
update.set(XYDQB::getJszcsj, LocalDateTime.now());
}
xydqbMapper.update(null, update);
}
}
private ElectiveCourseQuery normalizeQuery(ElectiveCourseQuery cond) {
ElectiveCourseQuery filter = cond == null ? new ElectiveCourseQuery() : cond;
filter.setJyxm(blankToNull(filter.getJyxm()));
filter.setKmc(blankToNull(filter.getKmc()));
filter.setKxbcnj(blankToNull(filter.getKxbcnj()));
filter.setXxbmc(blankToNull(filter.getXxbmc()));
filter.setPxcc(normalizeNone(filter.getPxcc()));
filter.setXkzt(normalizeNone(filter.getXkzt()));
return filter;
}
private void fillSerial(List<ElectiveCourseVO> records, int pageNum, int pageSize) {
if (records == null) {
return;
}
int start = Math.max(pageNum - 1, 0) * Math.max(pageSize, 0);
for (int i = 0; i < records.size(); i++) {
records.get(i).setXh(start + i + 1);
}
}
private void writeStudentWord(XWPFDocument doc, List<ElectiveCourseStudentVO> students) {
XWPFParagraph title = doc.createParagraph();
title.setAlignment(ParagraphAlignment.CENTER);
XWPFRun titleRun = title.createRun();
titleRun.setText("选修班学员名单");
titleRun.setBold(true);
titleRun.setFontSize(16);
XWPFTable table = doc.createTable(students.size() + 1, 7);
String[] headers = {"序号", "选修班名称", "课程", "学号", "姓名", "性别", "联系电话"};
XWPFTableRow headerRow = table.getRow(0);
for (int i = 0; i < headers.length; i++) {
setCellText(headerRow.getCell(i), headers[i]);
}
for (int i = 0; i < students.size(); i++) {
ElectiveCourseStudentVO row = students.get(i);
XWPFTableRow tableRow = table.getRow(i + 1);
setCellText(tableRow.getCell(0), String.valueOf(row.getXh()));
setCellText(tableRow.getCell(1), nvl(row.getXxbmc()));
setCellText(tableRow.getCell(2), nvl(row.getKmc()));
setCellText(tableRow.getCell(3), nvl(row.getXhNo()));
setCellText(tableRow.getCell(4), nvl(row.getXm()));
setCellText(tableRow.getCell(5), nvl(row.getXb()));
setCellText(tableRow.getCell(6), nvl(row.getLxdh()));
}
}
private void setCellText(XWPFTableCell cell, String text) {
cell.removeParagraph(0);
XWPFParagraph paragraph = cell.addParagraph();
XWPFRun run = paragraph.createRun();
run.setText(text == null ? "" : text);
run.setFontSize(11);
}
private String ensureElectiveType(String klx) {
if (StringUtils.isEmpty(klx)) {
return COURSE_TYPE_ELECTIVE;
}
return klx.contains(COURSE_TYPE_ELECTIVE) ? klx : klx + COURSE_TYPE_ELECTIVE;
}
private String normalizeStatus(String status) {
String value = normalizeNone(status);
if (value == null) {
return STATUS_DRAFT;
}
if (STATUS_OPEN.equals(value) || STATUS_STOP.equals(value) || STATUS_DRAFT.equals(value)) {
return value;
}
return STATUS_DRAFT;
}
private int statusToFlag(String status) {
String value = normalizeStatus(status);
if (STATUS_OPEN.equals(value)) {
return FLAG_OPEN;
}
if (STATUS_STOP.equals(value)) {
return FLAG_STOP;
}
return FLAG_DRAFT;
}
private String normalizeNone(String value) {
String trimmed = blankToNull(value);
if (trimmed == null || "无".equals(trimmed)) {
return null;
}
return trimmed;
}
private String blankToNull(String value) {
return StringUtils.isEmpty(value) ? null : value.trim();
}
private String nvl(String value) {
return value == null ? "" : value;
}
private LocalDateTime parseDateTime(String text) {
String value = blankToNull(text);
if (value == null) {
return null;
}
try {
if (value.length() <= 10) {
if (value.contains("/")) {
return LocalDate.parse(value, DateTimeFormatter.ofPattern("yyyy/MM/dd")).atStartOfDay();
}
return LocalDate.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atStartOfDay();
}
return LocalDateTime.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
} catch (Exception e) {
throw new ServiceException("日期格式不正确:" + text, BAD_REQUEST);
}
}
}
@@ -9,6 +9,7 @@ import com.roomroot.jwgl.service.FacultyService;
import com.roomroot.jwgl.unit.BusinessException; import com.roomroot.jwgl.unit.BusinessException;
import com.roomroot.jwgl.unit.PageQuery; import com.roomroot.jwgl.unit.PageQuery;
import com.roomroot.jwgl.unit.PageResult; import com.roomroot.jwgl.unit.PageResult;
import com.roomroot.jwgl.utils.ExcelExportUtil;
import com.roomroot.jwgl.utils.ExcelParseUtil; import com.roomroot.jwgl.utils.ExcelParseUtil;
import com.roomroot.jwgl.utils.UuidUtil; import com.roomroot.jwgl.utils.UuidUtil;
import com.roomroot.jwgl.vo.faculty.TeacherListVO; import com.roomroot.jwgl.vo.faculty.TeacherListVO;
@@ -160,6 +161,52 @@ public class FacultyServiceImpl implements FacultyService {
return jybMapper.selectById(jybh); 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<JYB> 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<JYB> w =
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<>();
w.eq(JYB::getLzzt, 0).orderByAsc(JYB::getXh);
List<JYB> 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 @Override
public PageResult<TeacherListVO> pageJYBDetail(PageQuery query, JYB cond) { public PageResult<TeacherListVO> pageJYBDetail(PageQuery query, JYB cond) {
Page<TeacherListVO> page = new Page<>(query.getPageNum(), query.getPageSize()); Page<TeacherListVO> page = new Page<>(query.getPageNum(), query.getPageSize());
@@ -1,13 +1,30 @@
package com.roomroot.jwgl.service.impl; package com.roomroot.jwgl.service.impl;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.roomroot.common.utils.StringUtils;
import com.roomroot.common.utils.poi.ExcelUtil;
import com.roomroot.jwgl.dto.kcb.TimetableQuery;
import com.roomroot.jwgl.entity.KCB; import com.roomroot.jwgl.entity.KCB;
import com.roomroot.jwgl.mapper.KCBMapper; import com.roomroot.jwgl.mapper.KCBMapper;
import com.roomroot.jwgl.mapper.KcbTimetableMapper;
import com.roomroot.jwgl.mapper.SystemInitializationMapper;
import com.roomroot.jwgl.service.KCBService; import com.roomroot.jwgl.service.KCBService;
import com.roomroot.jwgl.unit.PageQuery;
import com.roomroot.jwgl.unit.PageResult;
import com.roomroot.jwgl.utils.UuidUtil; import com.roomroot.jwgl.utils.UuidUtil;
import com.roomroot.jwgl.utils.JwglRoleHelper;
import com.roomroot.jwgl.vo.kcb.ClassTimetableVO;
import com.roomroot.jwgl.vo.kcb.DailyWeeklyTimetableVO;
import com.roomroot.jwgl.vo.kcb.SemesterGradeExportVO;
import com.roomroot.jwgl.vo.systeminitialization.SemesterInitializationVO;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List; import java.util.List;
/** /**
@@ -16,9 +33,20 @@ import java.util.List;
@Service @Service
public class KCBServiceImpl implements KCBService { public class KCBServiceImpl implements KCBService {
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@Resource @Resource
private KCBMapper kcbMapper; private KCBMapper kcbMapper;
@Resource
private KcbTimetableMapper kcbTimetableMapper;
@Resource
private SystemInitializationMapper systemInitializationMapper;
@Resource
private JwglRoleHelper jwglRoleHelper;
@Override @Override
public void add(KCB kcb) { public void add(KCB kcb) {
if (kcb.getCjsj() == null) { if (kcb.getCjsj() == null) {
@@ -56,4 +84,108 @@ public class KCBServiceImpl implements KCBService {
return kcbMapper.selectList(null); return kcbMapper.selectList(null);
} }
@Override
public List<DailyWeeklyTimetableVO> listToday(TimetableQuery cond) {
TimetableQuery filter = prepareFilter(cond);
String today = LocalDate.now().format(DATE_FORMAT);
return kcbTimetableMapper.selectLessonTimetable(filter, today, today);
}
@Override
public List<DailyWeeklyTimetableVO> listWeek(TimetableQuery cond) {
TimetableQuery filter = prepareFilter(cond);
LocalDate today = LocalDate.now();
LocalDate monday = today.with(DayOfWeek.MONDAY);
LocalDate sunday = today.with(DayOfWeek.SUNDAY);
return kcbTimetableMapper.selectLessonTimetable(filter,
monday.format(DATE_FORMAT), sunday.format(DATE_FORMAT));
}
@Override
public PageResult<ClassTimetableVO> pageClassTimetable(PageQuery query, TimetableQuery cond) {
TimetableQuery filter = prepareFilter(cond);
int pageNum = (query == null || query.getPageNum() == null) ? 1 : query.getPageNum();
int pageSize = (query == null || query.getPageSize() == null) ? 20 : query.getPageSize();
Page<ClassTimetableVO> page = new Page<>(pageNum, pageSize);
Page<ClassTimetableVO> result = kcbTimetableMapper.selectClassTimetable(page, filter);
return new PageResult<>(result.getRecords(), result.getTotal(), pageNum, pageSize);
}
@Override
public void exportSemesterPlan(TimetableQuery cond, HttpServletResponse response) {
TimetableQuery filter = prepareFilter(cond);
String[] range = resolveSemesterDateRange(filter);
List<DailyWeeklyTimetableVO> list = kcbTimetableMapper.selectLessonTimetable(filter, range[0], range[1]);
ExcelUtil<DailyWeeklyTimetableVO> util = new ExcelUtil<>(DailyWeeklyTimetableVO.class);
util.exportExcel(response, list, "学期教学实施计划");
}
@Override
public void exportSemesterGrades(TimetableQuery cond, HttpServletResponse response) {
TimetableQuery filter = prepareFilter(cond);
List<SemesterGradeExportVO> list = kcbTimetableMapper.selectSemesterGrades(filter);
ExcelUtil<SemesterGradeExportVO> util = new ExcelUtil<>(SemesterGradeExportVO.class);
util.exportExcel(response, list, "学期成绩表");
}
@Override
public void exportClassCourses(TimetableQuery cond, HttpServletResponse response) {
TimetableQuery filter = prepareFilter(cond);
List<ClassTimetableVO> list = kcbTimetableMapper.selectClassTimetableList(filter);
ExcelUtil<ClassTimetableVO> util = new ExcelUtil<>(ClassTimetableVO.class);
util.exportExcel(response, list, "班次课程列表");
}
/**
* 未传年度时按当前学期填充;队别班次名称去空白。
* 学员强制只看本人所在学员队课表。
*/
private TimetableQuery prepareFilter(TimetableQuery cond) {
TimetableQuery filter = cond == null ? new TimetableQuery() : cond;
if (jwglRoleHelper.isStudent()) {
filter.setXydbh(jwglRoleHelper.requireStudentTeamId());
filter.setXydmc(null);
} else {
if (StringUtils.isNotEmpty(filter.getXydbh())) {
filter.setXydbh(filter.getXydbh().trim());
}
if (StringUtils.isNotEmpty(filter.getXydmc())) {
filter.setXydmc(filter.getXydmc().trim());
}
}
if (filter.getNd() != null) {
return filter;
}
SemesterInitializationVO semester = systemInitializationMapper.selectCurrentSemester();
if (semester != null && semester.getYear() != null) {
filter.setNd(semester.getYear());
if (filter.getXqdc() == null) {
filter.setXqdc(semester.getSequenceNumber());
}
} else {
filter.setNd(LocalDate.now().getYear());
}
return filter;
}
/**
* 本学期用开学/结束日期;查其他年度时退回该年 1 月 1 日至 12 月 31 日。
*/
private String[] resolveSemesterDateRange(TimetableQuery filter) {
SemesterInitializationVO semester = systemInitializationMapper.selectCurrentSemester();
if (semester != null && semester.getStartAt() != null && semester.getEndAt() != null
&& filter.getNd() != null && filter.getNd().equals(semester.getYear())
&& (filter.getXqdc() == null || filter.getXqdc().equals(semester.getSequenceNumber()))) {
return new String[]{
semester.getStartAt().toLocalDate().format(DATE_FORMAT),
semester.getEndAt().toLocalDate().format(DATE_FORMAT)
};
}
LocalDate now = LocalDate.now();
int year = filter.getNd() == null ? now.getYear() : filter.getNd();
LocalDate start = LocalDate.of(year, 1, 1);
LocalDate end = LocalDate.of(year, 12, 31);
return new String[]{start.format(DATE_FORMAT), end.format(DATE_FORMAT)};
}
} }
@@ -39,10 +39,8 @@ public class SemesterServiceImpl extends ServiceImpl<SemesterMapper, XQ> impleme
if(insert > 0){ if(insert > 0){
LocalDateTime kxrq = xq.getKxrq(); LocalDateTime kxrq = xq.getKxrq();
LocalDateTime jsrq = xq.getJsrq(); LocalDateTime jsrq = xq.getJsrq();
String startDate = localDateTimeToDateStr(kxrq); String startDate = localDateTimeToDateStr(kxrq);
String endDate = localDateTimeToDateStr(jsrq); String endDate = localDateTimeToDateStr(jsrq);
semesterCalendarService.add(startDate, endDate); semesterCalendarService.add(startDate, endDate);
} }
} }
@@ -0,0 +1,412 @@
package com.roomroot.jwgl.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.roomroot.common.exception.ServiceException;
import com.roomroot.common.utils.StringUtils;
import com.roomroot.jwgl.dto.jys.TeachingTaskQuery;
import com.roomroot.jwgl.dto.jys.TeachingTaskSaveDTO;
import com.roomroot.jwgl.entity.JXRW;
import com.roomroot.jwgl.entity.JYSRWS;
import com.roomroot.jwgl.entity.KB;
import com.roomroot.jwgl.entity.XYDB;
import com.roomroot.jwgl.entity.XYDNDXQJBXXB;
import com.roomroot.jwgl.entity.XYDRWB;
import com.roomroot.jwgl.entity.ZYJXJHB;
import com.roomroot.jwgl.mapper.ClassSemesterMapper;
import com.roomroot.jwgl.mapper.KBMapper;
import com.roomroot.jwgl.mapper.OfficeTaskBookMapper;
import com.roomroot.jwgl.mapper.StudentTeamTaskMapper;
import com.roomroot.jwgl.mapper.SystemInitializationMapper;
import com.roomroot.jwgl.mapper.TeachingTaskMapper;
import com.roomroot.jwgl.mapper.XYDBMapper;
import com.roomroot.jwgl.mapper.ZYJXJHBMapper;
import com.roomroot.jwgl.service.StudentTeamTaskService;
import com.roomroot.jwgl.service.TeachingTaskManageService;
import com.roomroot.jwgl.unit.PageQuery;
import com.roomroot.jwgl.unit.PageResult;
import com.roomroot.jwgl.utils.UuidUtil;
import com.roomroot.jwgl.vo.jys.TeachingOfficeBookCourseVO;
import com.roomroot.jwgl.vo.jys.TeachingOfficeBookVO;
import com.roomroot.jwgl.vo.jys.TeachingTaskDetailVO;
import com.roomroot.jwgl.vo.jys.TeachingTaskSquadVO;
import com.roomroot.jwgl.vo.jys.TeachingTaskVO;
import com.roomroot.jwgl.vo.systeminitialization.SemesterInitializationVO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import jakarta.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import static com.roomroot.jwgl.utils.BasicManagementConstants.BAD_REQUEST;
import static com.roomroot.jwgl.utils.BasicManagementConstants.CONFLICT;
import static com.roomroot.jwgl.utils.BasicManagementConstants.NOT_FOUND;
/**
* 教学任务管理实现。
*/
@Service
public class TeachingTaskManageServiceImpl implements TeachingTaskManageService {
private static final String STATUS_DRAFT = "上报";
private static final String STATUS_PUBLISHED = "发布";
private static final String OFFICE_STATUS_UNREPORTED = "未上报";
private static final String OFFICE_STATUS_PUBLISHED = "发布";
@Resource
private TeachingTaskMapper teachingTaskMapper;
@Resource
private OfficeTaskBookMapper officeTaskBookMapper;
@Resource
private ClassSemesterMapper classSemesterMapper;
@Resource
private XYDBMapper xydbMapper;
@Resource
private KBMapper kbMapper;
@Resource
private ZYJXJHBMapper zyjxjhbMapper;
@Resource
private StudentTeamTaskMapper studentTeamTaskMapper;
@Resource
private StudentTeamTaskService studentTeamTaskService;
@Resource
private SystemInitializationMapper systemInitializationMapper;
@Override
public PageResult<TeachingTaskVO> pageList(PageQuery query, TeachingTaskQuery cond) {
TeachingTaskQuery filter = cond == null ? new TeachingTaskQuery() : cond;
if (StringUtils.isNotEmpty(filter.getRwmc())) {
filter.setRwmc(filter.getRwmc().trim());
}
int pageNum = query == null || query.getPageNum() == null ? 1 : query.getPageNum();
int pageSize = query == null || query.getPageSize() == null ? 20 : query.getPageSize();
Page<TeachingTaskVO> result = teachingTaskMapper.selectManagePage(new Page<>(pageNum, pageSize), filter);
if (result.getRecords() != null) {
for (TeachingTaskVO row : result.getRecords()) {
fillSemesterName(row);
}
}
return new PageResult<>(result.getRecords(), result.getTotal(), pageNum, pageSize);
}
@Override
public TeachingTaskDetailVO getDetail(String bh) {
if (StringUtils.isEmpty(bh)) {
throw new ServiceException("教学任务编号不能为空", BAD_REQUEST);
}
JXRW entity = teachingTaskMapper.selectById(bh);
if (entity == null || Integer.valueOf(1).equals(entity.getDelFlag())) {
throw new ServiceException("教学任务不存在", NOT_FOUND);
}
TeachingTaskVO vo = toSimpleVo(entity);
List<TeachingTaskSquadVO> squads = listBoundSquads(bh);
if (!squads.isEmpty() && squads.get(0).getXqdc() != null) {
vo.setXqdc(squads.get(0).getXqdc());
}
fillSemesterName(vo);
TeachingTaskDetailVO detail = new TeachingTaskDetailVO();
detail.setTask(vo);
detail.setOfficeBooks(listOfficeBooks(bh));
detail.setSquads(squads);
return detail;
}
@Transactional(rollbackFor = Exception.class)
@Override
public void add(TeachingTaskSaveDTO dto) {
if (dto == null || StringUtils.isEmpty(dto.getRwmc())) {
throw new ServiceException("任务名称不能为空", BAD_REQUEST);
}
fillSemester(dto);
JXRW entity = new JXRW();
entity.setBh(UuidUtil.getOriginalUUID());
entity.setRwmc(dto.getRwmc().trim());
entity.setNd(dto.getNd());
entity.setZt(STATUS_DRAFT);
entity.setCjsj(LocalDateTime.now());
entity.setJssj(dto.getJssj() != null ? dto.getJssj() : defaultEndTime(dto.getNd(), dto.getXqdc()));
entity.setDelFlag(0);
teachingTaskMapper.insert(entity);
bindSquads(entity.getBh(), dto.getNd(), dto.getXqdc(), dto.getXydxqbhList());
generateCourseTasksAndOfficeBooks(entity.getBh());
}
@Transactional(rollbackFor = Exception.class)
@Override
public void update(TeachingTaskSaveDTO dto) {
if (dto == null || StringUtils.isEmpty(dto.getBh())) {
throw new ServiceException("教学任务编号不能为空", BAD_REQUEST);
}
if (StringUtils.isEmpty(dto.getRwmc())) {
throw new ServiceException("任务名称不能为空", BAD_REQUEST);
}
JXRW entity = teachingTaskMapper.selectById(dto.getBh());
if (entity == null || Integer.valueOf(1).equals(entity.getDelFlag())) {
throw new ServiceException("教学任务不存在", NOT_FOUND);
}
fillSemester(dto);
entity.setRwmc(dto.getRwmc().trim());
if (dto.getJssj() != null) {
entity.setJssj(dto.getJssj());
}
teachingTaskMapper.updateById(entity);
bindSquads(entity.getBh(), dto.getNd(), dto.getXqdc(), dto.getXydxqbhList());
generateCourseTasksAndOfficeBooks(entity.getBh());
}
@Transactional(rollbackFor = Exception.class)
@Override
public void delete(String bh) {
if (StringUtils.isEmpty(bh)) {
throw new ServiceException("教学任务编号不能为空", BAD_REQUEST);
}
JXRW entity = teachingTaskMapper.selectById(bh);
if (entity == null || Integer.valueOf(1).equals(entity.getDelFlag())) {
return;
}
entity.setDelFlag(1);
teachingTaskMapper.updateById(entity);
List<JYSRWS> books = officeTaskBookMapper.selectList(new LambdaQueryWrapper<JYSRWS>()
.eq(JYSRWS::getJxrwbh, bh)
.eq(JYSRWS::getDelFlag, 0));
for (JYSRWS book : books) {
book.setDelFlag(1);
officeTaskBookMapper.updateById(book);
}
classSemesterMapper.update(null, new LambdaUpdateWrapper<XYDNDXQJBXXB>()
.eq(XYDNDXQJBXXB::getJxrwbh, bh)
.set(XYDNDXQJBXXB::getJxrwbh, null));
}
@Transactional(rollbackFor = Exception.class)
@Override
public int publish(String bh) {
if (StringUtils.isEmpty(bh)) {
throw new ServiceException("教学任务编号不能为空", BAD_REQUEST);
}
JXRW entity = teachingTaskMapper.selectById(bh);
if (entity == null || Integer.valueOf(1).equals(entity.getDelFlag())) {
throw new ServiceException("教学任务不存在", NOT_FOUND);
}
if (STATUS_PUBLISHED.equals(entity.getZt())) {
throw new ServiceException("该教学任务已发布", CONFLICT);
}
generateCourseTasksAndOfficeBooks(bh);
entity.setZt(STATUS_PUBLISHED);
entity.setFbsj(LocalDateTime.now());
teachingTaskMapper.updateById(entity);
List<JYSRWS> books = officeTaskBookMapper.selectList(new LambdaQueryWrapper<JYSRWS>()
.eq(JYSRWS::getJxrwbh, bh)
.eq(JYSRWS::getDelFlag, 0));
for (JYSRWS book : books) {
book.setZt(OFFICE_STATUS_PUBLISHED);
officeTaskBookMapper.updateById(book);
}
return books.size();
}
@Override
public List<TeachingOfficeBookVO> listOfficeBooks(String jxrwbh) {
if (StringUtils.isEmpty(jxrwbh)) {
return new ArrayList<>();
}
List<TeachingOfficeBookVO> list = teachingTaskMapper.selectOfficeBooks(jxrwbh);
return list == null ? new ArrayList<>() : list;
}
@Override
public List<TeachingOfficeBookCourseVO> listOfficeBookCourses(String jxrwbh, String jysdh) {
if (StringUtils.isEmpty(jxrwbh) || StringUtils.isEmpty(jysdh)) {
throw new ServiceException("教学任务编号和教研室代号不能为空", BAD_REQUEST);
}
List<TeachingOfficeBookCourseVO> list = teachingTaskMapper.selectOfficeBookCourses(jxrwbh, jysdh);
return list == null ? new ArrayList<>() : list;
}
@Override
public List<TeachingTaskSquadVO> listBoundSquads(String jxrwbh) {
if (StringUtils.isEmpty(jxrwbh)) {
return new ArrayList<>();
}
List<TeachingTaskSquadVO> list = teachingTaskMapper.selectBoundSquads(jxrwbh);
return list == null ? new ArrayList<>() : list;
}
@Override
public List<TeachingTaskSquadVO> listAvailableSquads(Integer nd, Integer xqdc, String jxrwbh) {
if (nd == null || xqdc == null) {
SemesterInitializationVO semester = systemInitializationMapper.selectCurrentSemester();
if (semester == null || semester.getYear() == null || semester.getSequenceNumber() == null) {
throw new ServiceException("未设置当前学期,请指定年度和学期第次", BAD_REQUEST);
}
nd = semester.getYear();
xqdc = semester.getSequenceNumber();
}
List<TeachingTaskSquadVO> list = teachingTaskMapper.selectAvailableSquads(nd, xqdc, jxrwbh);
return list == null ? new ArrayList<>() : list;
}
private void fillSemester(TeachingTaskSaveDTO dto) {
if (dto.getNd() != null && dto.getXqdc() != null) {
return;
}
SemesterInitializationVO semester = systemInitializationMapper.selectCurrentSemester();
if (semester == null || semester.getYear() == null) {
if (dto.getNd() == null) {
throw new ServiceException("请选择学期", BAD_REQUEST);
}
return;
}
if (dto.getNd() == null) {
dto.setNd(semester.getYear());
}
if (dto.getXqdc() == null) {
dto.setXqdc(semester.getSequenceNumber());
}
}
private LocalDateTime defaultEndTime(Integer nd, Integer xqdc) {
if (nd == null || xqdc == null) {
return null;
}
SemesterInitializationVO semester = systemInitializationMapper.selectSemesterByKey(nd, xqdc);
return semester == null ? null : semester.getEndAt();
}
private void fillSemesterName(TeachingTaskVO row) {
if (row == null || row.getNd() == null || row.getXqdc() == null) {
return;
}
SemesterInitializationVO semester = systemInitializationMapper.selectSemesterByKey(row.getNd(), row.getXqdc());
if (semester != null && StringUtils.isNotEmpty(semester.getSemesterName())) {
row.setXqmc(semester.getSemesterName());
} else {
row.setXqmc(row.getNd() + "年第" + row.getXqdc() + "学期");
}
}
private TeachingTaskVO toSimpleVo(JXRW entity) {
TeachingTaskVO vo = new TeachingTaskVO();
vo.setBh(entity.getBh());
vo.setRwmc(entity.getRwmc());
vo.setNd(entity.getNd());
vo.setZt(entity.getZt());
vo.setFbsj(entity.getFbsj());
vo.setCjsj(entity.getCjsj());
vo.setJssj(entity.getJssj());
return vo;
}
private void bindSquads(String jxrwbh, Integer nd, Integer xqdc, List<String> xydxqbhList) {
classSemesterMapper.update(null, new LambdaUpdateWrapper<XYDNDXQJBXXB>()
.eq(XYDNDXQJBXXB::getJxrwbh, jxrwbh)
.set(XYDNDXQJBXXB::getJxrwbh, null));
if (CollectionUtils.isEmpty(xydxqbhList)) {
return;
}
for (String xydxqbh : xydxqbhList) {
if (StringUtils.isEmpty(xydxqbh)) {
continue;
}
XYDNDXQJBXXB semester = classSemesterMapper.selectById(xydxqbh);
if (semester == null || Integer.valueOf(1).equals(semester.getDelFlag())) {
throw new ServiceException("学员队学期信息不存在:" + xydxqbh, NOT_FOUND);
}
if (nd != null && semester.getNd() != null && !nd.equals(semester.getNd())) {
throw new ServiceException("所选学员队不属于该学期", BAD_REQUEST);
}
if (xqdc != null && semester.getXqdc() != null && !xqdc.equals(semester.getXqdc())) {
throw new ServiceException("所选学员队不属于该学期", BAD_REQUEST);
}
if (StringUtils.isNotEmpty(semester.getJxrwbh()) && !jxrwbh.equals(semester.getJxrwbh())) {
throw new ServiceException("学员队已被其他教学任务占用", CONFLICT);
}
semester.setJxrwbh(jxrwbh);
classSemesterMapper.updateById(semester);
}
}
private void generateCourseTasksAndOfficeBooks(String jxrwbh) {
List<XYDNDXQJBXXB> squads = classSemesterMapper.selectList(new LambdaQueryWrapper<XYDNDXQJBXXB>()
.eq(XYDNDXQJBXXB::getJxrwbh, jxrwbh)
.eq(XYDNDXQJBXXB::getDelFlag, 0));
for (XYDNDXQJBXXB squad : squads) {
if (StringUtils.isEmpty(squad.getXydbh()) || squad.getXqdc() == null) {
continue;
}
try {
studentTeamTaskService.autoGenerateRequiredCourses(squad.getXydbh(), squad.getXqdc());
} catch (Exception ignored) {
// 课程任务已存在或计划缺失时,仍继续生成教研室任务书
}
}
Set<String> jysdhSet = collectOfficeCodes(squads);
for (String jysdh : jysdhSet) {
Long exist = officeTaskBookMapper.selectCount(new LambdaQueryWrapper<JYSRWS>()
.eq(JYSRWS::getJxrwbh, jxrwbh)
.eq(JYSRWS::getJysdh, jysdh)
.eq(JYSRWS::getDelFlag, 0));
if (exist != null && exist > 0) {
continue;
}
JYSRWS book = new JYSRWS();
book.setBh(UuidUtil.getOriginalUUID());
book.setJxrwbh(jxrwbh);
book.setJysdh(jysdh);
book.setZt(OFFICE_STATUS_UNREPORTED);
book.setDelFlag(0);
officeTaskBookMapper.insert(book);
}
}
private Set<String> collectOfficeCodes(List<XYDNDXQJBXXB> squads) {
Set<String> codes = new LinkedHashSet<>();
List<String> xydxqbhList = new ArrayList<>();
for (XYDNDXQJBXXB squad : squads) {
xydxqbhList.add(squad.getBh());
if (StringUtils.isEmpty(squad.getXydbh()) || squad.getXqdc() == null) {
continue;
}
XYDB xyd = xydbMapper.selectById(squad.getXydbh());
if (xyd == null || StringUtils.isEmpty(xyd.getZydh())) {
continue;
}
List<ZYJXJHB> plans = zyjxjhbMapper.selectList(new LambdaQueryWrapper<ZYJXJHB>()
.eq(ZYJXJHB::getZydh, xyd.getZydh())
.eq(ZYJXJHB::getXqdc, squad.getXqdc())
.eq(ZYJXJHB::getTy, 0));
for (ZYJXJHB plan : plans) {
if (StringUtils.isNotEmpty(plan.getJysdh())) {
codes.add(plan.getJysdh());
continue;
}
if (StringUtils.isEmpty(plan.getKbh())) {
continue;
}
KB kb = kbMapper.selectById(plan.getKbh());
if (kb != null && StringUtils.isNotEmpty(kb.getJysdh())) {
codes.add(kb.getJysdh());
}
}
}
if (!xydxqbhList.isEmpty()) {
List<XYDRWB> tasks = studentTeamTaskMapper.selectList(new LambdaQueryWrapper<XYDRWB>()
.in(XYDRWB::getXydxqbh, xydxqbhList)
.eq(XYDRWB::getDelFlag, 0));
for (XYDRWB task : tasks) {
if (StringUtils.isNotEmpty(task.getJysdh())) {
codes.add(task.getJysdh());
}
}
}
return codes;
}
}
@@ -73,4 +73,45 @@ public class TrainingProgramServiceImpl implements TrainingProgramService {
return new PageResult<>(result.getRecords(), result.getTotal(), return new PageResult<>(result.getRecords(), result.getTotal(),
query.getPageNum(), query.getPageSize()); 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<ZYB> 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<ZYB> list = zybMapper.selectList(
new LambdaQueryWrapper<ZYB>().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();
}
} }
@@ -71,4 +71,43 @@ public class ZYJXJHBServiceImpl implements ZYJXJHBService {
.eq(ZYJXJHB::getTy, ty); .eq(ZYJXJHB::getTy, ty);
return zyjxjhbMapper.selectList(wrapper); 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<ZYJXJHB> 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<ZYJXJHB> list = zyjxjhbMapper.selectList(
new LambdaQueryWrapper<ZYJXJHB>().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();
}
} }
@@ -5,10 +5,13 @@ import com.roomroot.jwgl.dto.courserunning.SemesterImportDTO;
import com.roomroot.jwgl.dto.courserunning.SingleCourseImportDTO; import com.roomroot.jwgl.dto.courserunning.SingleCourseImportDTO;
import com.roomroot.jwgl.dto.departmentpersonnel.DepartmentPersonnelCreateDTO; import com.roomroot.jwgl.dto.departmentpersonnel.DepartmentPersonnelCreateDTO;
import com.roomroot.jwgl.entity.JXSSJH; import com.roomroot.jwgl.entity.JXSSJH;
import com.roomroot.jwgl.entity.JYB;
import com.roomroot.jwgl.entity.JYSB; import com.roomroot.jwgl.entity.JYSB;
import com.roomroot.jwgl.entity.KSBZXM; import com.roomroot.jwgl.entity.KSBZXM;
import com.roomroot.jwgl.entity.XYDB; import com.roomroot.jwgl.entity.XYDB;
import com.roomroot.jwgl.entity.XYXX; 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.Cell;
import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Row;
@@ -386,6 +389,144 @@ public class ExcelParseUtil {
return result; return result;
} }
/**
* 解析教员管理导入 Excel 文件(支持 .xls / .xlsx)。
* <p>表头顺序:</p>
* 教员编号, 教员姓名, 教研室代号, 职称, 专业技术职务等级, 教员类别,
* 性别, 手机号, 身份证号, 备注, 序号, 虚实类型
*
* @return 教员实体列表(离职状态由业务层默认填充)
* @throws Exception 解析异常
*/
public static List<JYB> parseJYBExcel(InputStream inputStream, String fileName) throws Exception {
List<JYB> 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)。
* <p>表头顺序:</p>
* 专业代号, 专业名称, 专业代码, 专业版本, 培训类型, 培训层次, 学年制, 学期数,
* 专业方向, 专业备注, 学员类别, 简称, 培训类型2
*
* @return 专业实体列表
* @throws Exception 解析异常
*/
public static List<ZYB> parseZYBExcel(InputStream inputStream, String fileName) throws Exception {
List<ZYB> 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)。
* <p>表头顺序:</p>
* 专业代号, 课编号, 学期第次, 课类型, 学时, 学分, 周课时, 课程定位,
* 模块, 成绩分制, 理论学时, 实践学时, 考试课时, 教研室代号, 简称
*
* @return 专业教学计划实体列表
* @throws Exception 解析异常
*/
public static List<ZYJXJHB> parseZYJXJHBExcel(InputStream inputStream, String fileName) throws Exception {
List<ZYJXJHB> 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 { private static Workbook createWorkbook(InputStream inputStream, String fileName) throws Exception {
String name = fileName == null ? "" : fileName.toLowerCase(); String name = fileName == null ? "" : fileName.toLowerCase();
if (!name.endsWith(".xlsx") && !name.endsWith(".xls")) { 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) { private static String getCellStringValue(Cell cell) {
if (cell == null) { if (cell == null) {
return null; return null;
@@ -9,9 +9,11 @@ import com.roomroot.common.exception.ServiceException;
import com.roomroot.common.utils.SecurityUtils; import com.roomroot.common.utils.SecurityUtils;
import com.roomroot.common.utils.StringUtils; import com.roomroot.common.utils.StringUtils;
import com.roomroot.jwgl.entity.JYB; import com.roomroot.jwgl.entity.JYB;
import com.roomroot.jwgl.entity.XYDQB;
import com.roomroot.jwgl.entity.XYXX; import com.roomroot.jwgl.entity.XYXX;
import com.roomroot.jwgl.mapper.JYBMapper; import com.roomroot.jwgl.mapper.JYBMapper;
import com.roomroot.jwgl.mapper.JYSDLBMapper; import com.roomroot.jwgl.mapper.JYSDLBMapper;
import com.roomroot.jwgl.mapper.XYDQBMapper;
import com.roomroot.jwgl.mapper.XYXXMapper; import com.roomroot.jwgl.mapper.XYXXMapper;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -40,6 +42,9 @@ public class JwglRoleHelper {
@Resource @Resource
private JYSDLBMapper jysdlbMapper; private JYSDLBMapper jysdlbMapper;
@Resource
private XYDQBMapper xydqbMapper;
public boolean isAdmin() { public boolean isAdmin() {
try { try {
if (SecurityUtils.isAdmin()) { if (SecurityUtils.isAdmin()) {
@@ -112,6 +117,34 @@ public class JwglRoleHelper {
return studentId; return studentId;
} }
/**
* 当前学员所属学员队编号。课表按班次过滤时使用。
*/
public String requireStudentTeamId() {
String teamId = findStudentTeamId();
if (StringUtils.isEmpty(teamId)) {
throw new ServiceException("当前学员未分班,无法查看课表", FORBIDDEN);
}
return teamId;
}
public String findStudentTeamId() {
try {
String studentId = findStudentId();
if (StringUtils.isEmpty(studentId)) {
return null;
}
XYXX student = xyxxMapper.selectById(studentId);
if (student == null || StringUtils.isEmpty(student.getXydqbh())) {
return null;
}
XYDQB period = xydqbMapper.selectById(student.getXydqbh());
return period == null ? null : period.getXydbh();
} catch (Exception ignored) {
return null;
}
}
public String findTeacherId() { public String findTeacherId() {
try { try {
String username = SecurityUtils.getUsername(); String username = SecurityUtils.getUsername();
@@ -37,4 +37,42 @@ public class UuidUtil {
public static String getUuidWithPrefix(String prefix) { public static String getUuidWithPrefix(String prefix) {
return prefix + getUUID(); 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);
}
} }
@@ -24,8 +24,29 @@ public class ScheduleAdjustApplyVO {
*/ */
private String sskcbbh; private String sskcbbh;
/** 学员队任务表编号 */
private String xydrwbh;
/** 课编号 */
private String kbh;
/** 课程表编号 */
private String kcbbh;
/** 学员队名称 */
private String xydmc;
/** 教研室代号 */
private String jysdh;
/** 原教室编号 */
private String yjsbh;
/** 拟调整教室编号 */
private String xjsbh;
/** /**
* 课程名称,关联课程表获取。 * 课程名称,关联课表获取。
*/ */
private String kcmc; private String kcmc;
@@ -79,11 +100,6 @@ public class ScheduleAdjustApplyVO {
*/ */
private Integer jyscszt; private Integer jyscszt;
/**
* 机关审批状态:0-未审批,1-已同意,2-已发回,3-已拒绝。
*/
private Integer jgspzt;
/** /**
* 发回意见。 * 发回意见。
*/ */
@@ -1,81 +0,0 @@
package com.roomroot.jwgl.vo.courserunning;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 调课申请审批记录展示数据。
* <p>
* 用于展示调课申请中机关审批环节的审批记录,包括机关编号、审批时间、
* 审批状态、审批人、查收状态、发回意见等信息。
* </p>
*/
@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;
}
@@ -10,7 +10,7 @@ import java.util.List;
* <p> * <p>
* 用于展示调课申请的完整信息,包括课次信息、申请教员、调整后的日期节次、 * 用于展示调课申请的完整信息,包括课次信息、申请教员、调整后的日期节次、
* 教学内容变更、新的教室列表、辅助教员列表、学员队列表、保障明细列表, * 教学内容变更、新的教室列表、辅助教员列表、学员队列表、保障明细列表,
* 以及教研室和机关的审批记录。 * 以及教研室审批与查收信息。
* </p> * </p>
*/ */
@Data @Data
@@ -26,6 +26,27 @@ public class ScheduleAdjustDetailVO {
*/ */
private String sskcbbh; private String sskcbbh;
/** 学员队任务表编号 */
private String xydrwbh;
/** 课编号 */
private String kbh;
/** 课程表编号 */
private String kcbbh;
/** 学员队名称 */
private String xydmc;
/** 教研室代号 */
private String jysdh;
/** 原教室编号 */
private String yjsbh;
/** 拟调整教室编号 */
private String xjsbh;
/** /**
* 课程名称。 * 课程名称。
*/ */
@@ -127,14 +148,14 @@ public class ScheduleAdjustDetailVO {
private LocalDateTime jyscssj; private LocalDateTime jyscssj;
/** /**
* 教研室查收人编号。 * 教研室查收人编号,与审批人为同一人。
*/ */
private String jyscsrbh; private String jyscsrbh;
/** /**
* 机关审批列表,记录所有机关审批结果。 * 发回意见。
*/ */
private List<ScheduleAdjustAuditVO> auditList; private String fhyj;
/** /**
* 新教室列表。 * 新教室列表。
@@ -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;
}
@@ -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<PkConflictVO> mx;
}
@@ -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;
}
@@ -0,0 +1,45 @@
package com.roomroot.jwgl.vo.jwtest;
import lombok.Data;
/**
* 排课全查明细(课堂、课程、教师、班级、教室及上课时间)。
*/
@Data
public class PkLessonVO {
/** 课堂名称 */
private String ktmc;
/** 课程名称 */
private String kcmc;
/** 教学楼名称 */
private String jxlmc;
/** 教师名称 */
private String jymc;
/** 班级名称 */
private String bjmc;
/** 教室名称 */
private String jsmc;
/** 上课日期 */
private String skrq;
/** 开始时间 */
private String kssj;
/** 结束时间 */
private String jssj;
/** 学年学期 */
private String xnxq;
/** 周次 */
private Integer zc;
/** 应到人数 */
private Integer ydrs;
}
@@ -0,0 +1,32 @@
package com.roomroot.jwgl.vo.jys;
import com.roomroot.common.annotation.Excel;
import lombok.Data;
/**
* 选修班学员名单行。
*/
@Data
public class ElectiveCourseStudentVO {
@Excel(name = "序号", sort = 1)
private Integer xh;
@Excel(name = "选修班名称", sort = 2)
private String xxbmc;
@Excel(name = "课程", sort = 3)
private String kmc;
@Excel(name = "学号", sort = 4)
private String xhNo;
@Excel(name = "姓名", sort = 5)
private String xm;
@Excel(name = "性别", sort = 6)
private String xb;
@Excel(name = "联系电话", sort = 7)
private String lxdh;
}
@@ -0,0 +1,80 @@
package com.roomroot.jwgl.vo.jys;
import com.roomroot.common.annotation.Excel;
import lombok.Data;
/**
* 选修课管理列表行。
*/
@Data
public class ElectiveCourseVO {
/** 学员队任务编号 */
private String bh;
/** 年度 */
private Integer nd;
/** 学员队编号 */
private String xydbh;
/** 课编号 */
private String kbh;
/** 教员编号 */
private String jybh;
/** 教室编号 */
private String jsbh;
@Excel(name = "序号", sort = 1)
private Integer xh;
@Excel(name = "课程", sort = 2)
private String kmc;
@Excel(name = "教材", sort = 3)
private String jcmc;
@Excel(name = "实施教员", sort = 4)
private String jyxm;
@Excel(name = "教学场地", sort = 5)
private String jsmc;
@Excel(name = "显示可选队列班次", sort = 6, width = 28)
private String kxdlbc;
/** 计划学时 */
private Integer jhxs;
/** 运行学时 */
private Integer yxxs;
/** 学分 */
private Float xf;
@Excel(name = "计划学时/运行学时/学分", sort = 7, width = 24)
private String jhyxxsxf;
@Excel(name = "开课结课日期", sort = 8, width = 24)
private String kkjkrq;
@Excel(name = "报名日期", sort = 9, width = 24)
private String bmrq;
@Excel(name = "计划人数", sort = 10)
private Integer jhrs;
@Excel(name = "要求说明", sort = 11, width = 24)
private String yqsm;
@Excel(name = "选修班名称", sort = 12)
private String xxbmc;
@Excel(name = "选择课状态", sort = 13)
private String xkzt;
@Excel(name = "培训层次", sort = 14)
private String pxcc;
}
@@ -0,0 +1,28 @@
package com.roomroot.jwgl.vo.jys;
import lombok.Data;
/**
* 教研室任务书课程明细。
*/
@Data
public class TeachingOfficeBookCourseVO {
private String rwbh;
private String kbh;
private String kmc;
private String klx;
private Integer xs;
private String xydbh;
private String xydmc;
private String jysjhjybh;
private String jysjhjyxm;
}
@@ -0,0 +1,32 @@
package com.roomroot.jwgl.vo.jys;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 教研室任务书列表行。
*/
@Data
public class TeachingOfficeBookVO {
private String bh;
private String jxrwbh;
private String jysdh;
private String jysmc;
private String zt;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime sbsj;
/** 学员队课程任务数 */
private Integer xydkcrws;
/** 完成指定数(已指定计划教员的课程任务数) */
private Integer wczds;
}
@@ -0,0 +1,19 @@
package com.roomroot.jwgl.vo.jys;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 教学任务详情(含教研室任务书、学员队两个页签数据)。
*/
@Data
public class TeachingTaskDetailVO {
private TeachingTaskVO task;
private List<TeachingOfficeBookVO> officeBooks = new ArrayList<>();
private List<TeachingTaskSquadVO> squads = new ArrayList<>();
}
@@ -0,0 +1,41 @@
package com.roomroot.jwgl.vo.jys;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.time.LocalDate;
/**
* 教学任务关联的学员队(班次)行。
*/
@Data
public class TeachingTaskSquadVO {
/** 学员队年度学期基本信息编号 */
private String xydxqbh;
private String xydbh;
private String xydmc;
private String nj;
private String zydh;
private String zymc;
private Integer xydrs;
private Integer nd;
private Integer xqdc;
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private LocalDate kxrq;
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private LocalDate jsrq;
/** 是否已被其他教学任务占用 */
private Integer occupied;
}
@@ -0,0 +1,53 @@
package com.roomroot.jwgl.vo.jys;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 教学任务列表行。
*/
@Data
public class TeachingTaskVO {
private String bh;
private String rwmc;
private Integer nd;
private Integer xqdc;
/** 学期名称,如 2026年春季学期 */
private String xqmc;
private String zt;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime fbsj;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime cjsj;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime jssj;
/** 教研室数 */
private Integer jyss;
/** 学员队数 */
private Integer xyds;
/** 合班前课程任务数 */
private Integer hbqkcrws;
/** 合班前总学时数 */
private Integer hbqzxs;
/** 合班后课程任务数 */
private Integer hbhkcrws;
/** 合班后总学时数 */
private Integer hbhzxs;
}
@@ -0,0 +1,38 @@
package com.roomroot.jwgl.vo.kcb;
import com.roomroot.common.annotation.Excel;
import lombok.Data;
/**
* 班次课表行。
*/
@Data
public class ClassTimetableVO {
/** 实施_课程编号 */
private String sskcbh;
@Excel(name = "课程名称/课时系数", sort = 1, width = 28)
private String kcmcksxs;
@Excel(name = "责任教员/课次", sort = 2, width = 20)
private String zrjykc;
@Excel(name = "计划学时", sort = 3)
private Integer jhxs;
@Excel(name = "运行学时", sort = 4)
private Integer yxxs;
@Excel(name = "考核类型方式", sort = 5, width = 16)
private String khlxfs;
@Excel(name = "实施教员", sort = 6, width = 16)
private String ssjy;
@Excel(name = "人数/场地", sort = 7, width = 22)
private String rscd;
@Excel(name = "实施计划变更", sort = 8, width = 28)
private String ssjhbg;
}
@@ -0,0 +1,38 @@
package com.roomroot.jwgl.vo.kcb;
import com.roomroot.common.annotation.Excel;
import lombok.Data;
/**
* 今日课表、本周课表、学期教学实施计划行。
*/
@Data
public class DailyWeeklyTimetableVO {
/** 实施_课程表编号 */
private String bh;
@Excel(name = "上课时间", sort = 1, width = 22)
private String sksj;
@Excel(name = "课程", sort = 2, width = 20)
private String kc;
@Excel(name = "责任单位", sort = 3, width = 18)
private String zrdw;
@Excel(name = "班次", sort = 4, width = 24)
private String bc;
@Excel(name = "教员", sort = 5, width = 16)
private String jy;
@Excel(name = "场地", sort = 6, width = 18)
private String cd;
@Excel(name = "教学内容方法", sort = 7, width = 28)
private String jxnrxff;
@Excel(name = "备注", sort = 8, width = 20)
private String bz;
}
@@ -0,0 +1,35 @@
package com.roomroot.jwgl.vo.kcb;
import com.roomroot.common.annotation.Excel;
import lombok.Data;
/**
* 学期成绩表导出行。
*/
@Data
public class SemesterGradeExportVO {
@Excel(name = "学号", sort = 1)
private String xh;
@Excel(name = "姓名", sort = 2)
private String xm;
@Excel(name = "课程", sort = 3, width = 20)
private String kcmc;
@Excel(name = "平时成绩", sort = 4)
private Float pscj;
@Excel(name = "考试成绩", sort = 5)
private Float kscj;
@Excel(name = "最终成绩", sort = 6)
private Float zzcj;
@Excel(name = "考试情况", sort = 7)
private String ksqk;
@Excel(name = "年度", sort = 8)
private Integer nd;
}
+18813
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -8,7 +8,8 @@
"dev": "vue-cli-service serve", "dev": "vue-cli-service serve",
"build:prod": "vue-cli-service build", "build:prod": "vue-cli-service build",
"build:stage": "vue-cli-service build --mode staging", "build:stage": "vue-cli-service build --mode staging",
"preview": "node build/index.js --preview" "preview": "node build/index.js --preview",
"dict:init-course-type": "node scripts/init-course-type-dict.js"
}, },
"keywords": [ "keywords": [
"vue", "vue",
@@ -67,3 +67,13 @@ export function exportJointTraining() {
responseType: 'blob' responseType: 'blob'
}) })
} }
// 下载联教联训导入模板(返回 Blob)
// GET /download/joint-training
export function downloadJointTrainingTemplate() {
return request({
url: '/download/joint-training',
method: 'get',
responseType: 'blob'
})
}
+22 -3
View File
@@ -85,6 +85,24 @@ export function rejectTeachingLog(bh, thyj) {
}) })
} }
// 批量上报教学日志,接口形态:POST /log/teaching-log/batch-report(body 为裸数组 ["",""])
export function batchReportTeachingLog(bhList) {
return request({
url: '/log/teaching-log/batch-report',
method: 'post',
data: bhList
})
}
// 批量审核教学日志,接口形态:POST /log/teaching-log/batch-audit(body 为裸数组 ["",""])
export function batchAuditTeachingLog(bhList) {
return request({
url: '/log/teaching-log/batch-audit',
method: 'post',
data: bhList
})
}
// 根据课表自动创建教学日志,接口形态:POST /log/teaching-log/auto-create?tybh= // 根据课表自动创建教学日志,接口形态:POST /log/teaching-log/auto-create?tybh=
export function autoCreateTeachingLog(tybh) { export function autoCreateTeachingLog(tybh) {
return request({ return request({
@@ -101,7 +119,8 @@ export function importTeachingLogExcel(file) {
return request({ return request({
url: '/log/teaching-log/import-excel', url: '/log/teaching-log/import-excel',
method: 'post', method: 'post',
data: formData data: formData,
headers: { 'Content-Type': 'multipart/form-data' }
}) })
} }
@@ -115,10 +134,10 @@ export function exportTeachingLog(query) {
}) })
} }
// 下载导入模板(返回 Blob),接口形态:GET /log/teaching-log/download-template // 下载导入模板(返回 Blob),接口形态:GET /download/teaching-log
export function downloadTeachingLogTemplate() { export function downloadTeachingLogTemplate() {
return request({ return request({
url: '/log/teaching-log/download-template', url: '/download/teaching-log',
method: 'get', method: 'get',
responseType: 'blob' responseType: 'blob'
}) })
-9
View File
@@ -39,15 +39,6 @@ export function getInfo() {
}) })
} }
// 解锁屏幕
export function unlockScreen(password) {
return request({
url: '/unlockscreen',
method: 'post',
data: { password }
})
}
// 退出方法 // 退出方法
export function logout() { export function logout() {
return request({ return request({
@@ -21,7 +21,7 @@ export function getApplication(bh) {
}) })
} }
// 新增学籍异动申请(请求体 XYXJYDSQB 实体;bh 需前端传入,创建/修改时间后端自动维护) // 新增学籍异动申请(编号、状态和创建/修改时间由后端维护)
export function addApplication(data) { export function addApplication(data) {
return request({ return request({
url: '/student-records/application/add', url: '/student-records/application/add',

Some files were not shown because too many files have changed in this diff Show More