From 094245231cac7fe3ff2c820c499dc3fbc71d68bb Mon Sep 17 00:00:00 2001 From: liweijie Date: Thu, 24 Sep 2026 11:13:23 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E6=96=B0=E5=A2=9E=E6=95=99=E5=AD=A6?= =?UTF-8?q?=E5=A4=A7=E7=BA=B2=EF=BC=8C=E6=8A=A5=E4=B8=93=E4=B8=9A=E5=90=8D?= =?UTF-8?q?=E7=A7=B0=E9=87=8D=E5=A4=8D=EF=BC=8C=E5=85=B6=E5=AE=9E=E6=98=AF?= =?UTF-8?q?code=E9=87=8D=E5=A4=8D=EF=BC=8C=E4=BF=AE=E5=A4=8D=EF=BC=9B=202?= =?UTF-8?q?=E3=80=81=E5=B7=B2=E7=BB=8F=E9=99=90=E5=88=B6=E6=95=99=E5=91=98?= =?UTF-8?q?=E6=8E=92=E8=AF=BE=E7=9A=84=EF=BC=8C=E6=95=99=E5=91=98=E4=BB=8D?= =?UTF-8?q?=E7=84=B6=E5=8F=AF=E4=BB=A5=E6=8E=92=E8=AF=BE=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jwgl/TrainingProgramController.java | 4 ++- .../impl/ClassSemesterServiceImpl.java | 30 ++++++++++++++----- .../impl/SchedulingWindowServiceImpl.java | 22 +++++++++++++- .../impl/TrainingProgramServiceImpl.java | 22 ++++++++++---- .../teachBusiness/schedulingWindow/index.vue | 1 + .../teachBusiness/trainingPlan/index.vue | 3 +- 6 files changed, 66 insertions(+), 16 deletions(-) diff --git a/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwgl/TrainingProgramController.java b/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwgl/TrainingProgramController.java index bd5f6047..ccc85eda 100644 --- a/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwgl/TrainingProgramController.java +++ b/backend/roomroot-admin/src/main/java/com/roomroot/web/controller/jwgl/TrainingProgramController.java @@ -108,7 +108,8 @@ public class TrainingProgramController { @RequestParam(required = false) String zymc, @RequestParam(required = false) String zydm, @RequestParam(required = false) String pxlx, - @RequestParam(required = false) String pxcc) { + @RequestParam(required = false) String pxcc, + @RequestParam(required = false) Boolean ty) { PageQuery query = new PageQuery(); query.setPageNum(pageNum); query.setPageSize(pageSize); @@ -117,6 +118,7 @@ public class TrainingProgramController { cond.setZydm(zydm); cond.setPxlx(pxlx); cond.setPxcc(pxcc); + cond.setTy(ty); PageResult pageResult = trainingProgramService.pageList(query, cond); return Result.success(pageResult); } diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/ClassSemesterServiceImpl.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/ClassSemesterServiceImpl.java index da2678b2..b992c7cb 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/ClassSemesterServiceImpl.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/ClassSemesterServiceImpl.java @@ -80,13 +80,7 @@ public class ClassSemesterServiceImpl implements ClassSemesterService { if (xydndxqjbxxb.getZyjsbh() == null || xydndxqjbxxb.getZyjsbh().isEmpty()) { xydndxqjbxxb.setZyjsbh(init.getZyjsbh()); } - XYDNDXQJBXXB existing = classSemesterMapper.selectByBcAndNdAndXqdc( - xydndxqjbxxb.getXydbh(), - String.valueOf(xydndxqjbxxb.getNd()), - String.valueOf(xydndxqjbxxb.getXqdc())); - if (existing != null) { - throw new ServiceException("该班次本年度学期已存在,不能重复新建", CONFLICT); - } + assertNoDuplicateSemester(xydndxqjbxxb.getXydbh(), xydndxqjbxxb.getNd(), xydndxqjbxxb.getXqdc(), null); if (xydndxqjbxxb.getYsbbh() == null) { xydndxqjbxxb.setYsbbh(0); } @@ -134,6 +128,10 @@ public class ClassSemesterServiceImpl implements ClassSemesterService { @Override public void update(XYDNDXQJBXXB xydndxqjbxxb) { + if (xydndxqjbxxb != null && xydndxqjbxxb.getBh() != null) { + assertNoDuplicateSemester(xydndxqjbxxb.getXydbh(), xydndxqjbxxb.getNd(), + xydndxqjbxxb.getXqdc(), xydndxqjbxxb.getBh()); + } if (xydndxqjbxxb != null && xydndxqjbxxb.getXydbh() != null && xydndxqjbxxb.getXqdc() != null) { com.roomroot.jwgl.vo.elective.SemesterInitVO init = electiveService.getSemesterInit( xydndxqjbxxb.getXydbh(), null, null, xydndxqjbxxb.getXqdc()); @@ -212,6 +210,7 @@ public class ClassSemesterServiceImpl implements ClassSemesterService { for (String bh : bhList) { XYDNDXQJBXXB xydndxqjbxxb = classSemesterMapper.selectById(bh); if (xydndxqjbxxb != null) { + assertNoDuplicateSemester(xydndxqjbxxb.getXydbh(), xydndxqjbxxb.getNd(), xqdc, bh); xydndxqjbxxb.setXqdc(xqdc); classSemesterMapper.updateById(xydndxqjbxxb); count++; @@ -220,6 +219,23 @@ public class ClassSemesterServiceImpl implements ClassSemesterService { return count; } + /** 同班次不允许存在同学期代号的多行(兼容年度存 4 位年份/6 位学期代号两种口径) */ + private void assertNoDuplicateSemester(String xydbh, Integer nd, Integer xqdc, String excludeBh) { + Integer code = SemesterCodeUtil.resolve(nd, xqdc); + if (xydbh == null || code == null) { + return; + } + List siblings = classSemesterMapper.selectList(new LambdaQueryWrapper() + .eq(XYDNDXQJBXXB::getXydbh, xydbh) + .eq(XYDNDXQJBXXB::getDelFlag, 0)); + for (XYDNDXQJBXXB s : siblings) { + if (excludeBh != null && excludeBh.equals(s.getBh())) continue; + if (code.equals(SemesterCodeUtil.resolve(s.getNd(), s.getXqdc()))) { + throw new ServiceException("该班次已存在同学期记录(学期代号 " + code + "),不能重复", CONFLICT); + } + } + } + @Override public int batchUpdateJxrwbh(List bhList, String jxrwbh) { if (CollectionUtils.isEmpty(bhList)) { diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/SchedulingWindowServiceImpl.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/SchedulingWindowServiceImpl.java index a17d6414..24d4d209 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/SchedulingWindowServiceImpl.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/SchedulingWindowServiceImpl.java @@ -1221,6 +1221,8 @@ public class SchedulingWindowServiceImpl implements SchedulingWindowService { Integer ndCode; List links; List lessons; + /** 该学员队所有未开放教员排课的学期代号(同代号多行任一行未开放即视为关闭) */ + Set closedSemCodes = new HashSet<>(); } private Ctx loadCtx(String xydxqbh) { @@ -1235,6 +1237,9 @@ public class SchedulingWindowServiceImpl implements SchedulingWindowService { ctx.semester = semester; ctx.team = xydbMapper.selectById(semester.getXydbh()); ctx.ndCode = SemesterCodeUtil.resolve(semester.getNd(), semester.getXqdc()); + markClosedSemesterCodes(ctx, classSemesterMapper.selectList(new LambdaQueryWrapper() + .eq(XYDNDXQJBXXB::getXydbh, semester.getXydbh()) + .eq(XYDNDXQJBXXB::getDelFlag, 0))); ctx.links = sskcxydMapper.selectList(new LambdaQueryWrapper() .eq(SSKCXYD::getXydbh, semester.getXydbh()) .eq(SSKCXYD::getNd, ctx.ndCode)); @@ -1279,9 +1284,24 @@ public class SchedulingWindowServiceImpl implements SchedulingWindowService { ctx.semester = match; ctx.team = xydbMapper.selectById(xydbh); ctx.ndCode = ndCode; + markClosedSemesterCodes(ctx, semesters); return ctx; } + /** 收集未开放教员排课的学期代号:kfjypk 非 1(含 null)一律按未开放计 */ + private void markClosedSemesterCodes(Ctx ctx, List teamSemesters) { + for (XYDNDXQJBXXB s : teamSemesters) { + if (s.getKfjypk() != null && s.getKfjypk() == 1) continue; + Integer code = SemesterCodeUtil.resolve(s.getNd(), s.getXqdc()); + if (code != null) ctx.closedSemCodes.add(code); + } + } + + /** 教员排课是否开放:同学期代号下存在未开放行即关闭;学期缺失按关闭处理 */ + private boolean teacherSchedulingOpen(Ctx ctx) { + return ctx.semester != null && ctx.ndCode != null && !ctx.closedSemCodes.contains(ctx.ndCode); + } + private SSKC requireCourse(String sskcbh) { if (sskcbh == null || sskcbh.isEmpty()) { throw new ServiceException("请指定运行课程", BAD_REQUEST); @@ -1306,7 +1326,7 @@ public class SchedulingWindowServiceImpl implements SchedulingWindowService { return course.getJybh() != null && jyInOffice(course.getJybh(), officeId); } if (roleHelper.isTeacher()) { - if (ctx.semester != null && ctx.semester.getKfjypk() != null && ctx.semester.getKfjypk() == 0) { + if (!teacherSchedulingOpen(ctx)) { return false; // 开放教员排课关闭,教员只读 } String teacherId = roleHelper.findTeacherId(); diff --git a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/TrainingProgramServiceImpl.java b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/TrainingProgramServiceImpl.java index 718f623d..dd16f2cb 100644 --- a/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/TrainingProgramServiceImpl.java +++ b/backend/roomroot-jwgl/src/main/java/com/roomroot/jwgl/service/impl/TrainingProgramServiceImpl.java @@ -45,10 +45,24 @@ public class TrainingProgramServiceImpl implements TrainingProgramService { entity.setTy(false); entity.setQysj(LocalDateTime.now()); com.roomroot.jwgl.handler.ZybInsertFillHandler.fillNotNullColumns(entity); - if (findExistingMajor(entity) != null) { + checkDuplicateForAdd(entity); + zybMapper.insert(entity); + } + + /** + * 新增前的重复校验:代号撞主键与名称重复分开提示; + * 名称重复只在启用中的记录里判定(已停用记录不占用名称)。 + */ + private void checkDuplicateForAdd(ZYB entity) { + if (zybMapper.selectById(entity.getZydh()) != null) { + throw new RuntimeException("专业代号已存在:" + entity.getZydh()); + } + java.util.List sameName = zybMapper.selectList(new LambdaQueryWrapper() + .eq(ZYB::getZymc, entity.getZymc()) + .eq(ZYB::getTy, false)); + if (sameName != null && !sameName.isEmpty()) { throw new RuntimeException("专业名称已存在,请勿重复新增"); } - zybMapper.insert(entity); } @Override @@ -77,10 +91,6 @@ public class TrainingProgramServiceImpl implements TrainingProgramService { @Override public PageResult pageList(PageQuery query, ZYB cond) { - // 未显式指定停用条件时,默认只展示启用中的培养方案(停用的不展示) - if (cond != null && cond.getTy() == null) { - cond.setTy(false); - } Page page = new Page<>(query.getPageNum(), query.getPageSize()); Page result = zybMapper.selectPageList(page, cond); return new PageResult<>(result.getRecords(), result.getTotal(), diff --git a/frontend/src/views/teachBusiness/schedulingWindow/index.vue b/frontend/src/views/teachBusiness/schedulingWindow/index.vue index 3abf1feb..9c6ed4c1 100644 --- a/frontend/src/views/teachBusiness/schedulingWindow/index.vue +++ b/frontend/src/views/teachBusiness/schedulingWindow/index.vue @@ -877,6 +877,7 @@ export default { const ext = [] if (s.nd) ext.push(s.nd + '年') if (s.xqdc) ext.push('第' + s.xqdc + '学期') + if (Number(s.kfjypk) !== 1) ext.push('未开放教员排课') return ext.length ? `${name}(${ext.join(' ')})` : name }, remainHours(c) { diff --git a/frontend/src/views/teachBusiness/trainingPlan/index.vue b/frontend/src/views/teachBusiness/trainingPlan/index.vue index 3271fd0b..269cf830 100644 --- a/frontend/src/views/teachBusiness/trainingPlan/index.vue +++ b/frontend/src/views/teachBusiness/trainingPlan/index.vue @@ -513,7 +513,8 @@ export default { this.loading = true const params = { pageNum: this.pageNum, - pageSize: this.pageSize + pageSize: this.pageSize, + ty: false } Object.keys(this.searchForm).forEach(key => { const value = this.searchForm[key]