From 6d2ad20723439b7a069edb7f9cec588ff4d871b6 Mon Sep 17 00:00:00 2001 From: buliping Date: Thu, 12 Nov 2026 08:52:21 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AF=BE=E7=A8=8B=E8=B5=84=E6=BA=90=E5=88=86?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E8=B5=84=E6=BA=90=E3=80=81=E9=80=89=E5=8F=96?= =?UTF-8?q?=E5=B7=B2=E6=9C=89=E8=B5=84=E6=BA=90=EF=BC=9B=E8=AF=BE=E7=A8=8B?= =?UTF-8?q?=E8=A2=AB=E7=8F=AD=E7=BA=A7=E5=85=B3=E8=81=94=E6=97=B6=E4=B8=8D?= =?UTF-8?q?=E5=8F=AF=E5=81=9C=E7=94=A8=EF=BC=88=E5=88=A0=E9=99=A4=EF=BC=89?= =?UTF-8?q?=E8=AF=BE=E7=A8=8B=EF=BC=9B=E5=AD=A6=E5=91=98=E5=AF=BC=E5=85=A5?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/ruoyi/train/domain/TrainResource.java | 12 + .../ruoyi/train/domain/vo/CourseDetailVO.java | 13 + .../train/mapper/TrainPlanCourseMapper.java | 10 + .../service/impl/TrainCourseServiceImpl.java | 33 + .../impl/TrainResourceServiceImpl.java | 36 +- .../mapper/train/TrainPlanCourseMapper.xml | 9 + .../impl/SchedulingAppServiceTest.java | 5 + ...SchedulingTeacherStatusAndOptionsTest.java | 5 + .../template/enrollment_import_template.xlsx | Bin 0 -> 10683 bytes .../class/components/EnrollmentPanel.vue | 29 +- build_ui/src/views/train/class/index.vue | 3 +- build_ui/src/views/train/course/index.vue | 746 ++++++++++++++---- build_ui/src/views/train/plan/index.vue | 68 +- build_ui/src/views/train/resource/index.vue | 348 +++++++- 14 files changed, 1098 insertions(+), 219 deletions(-) create mode 100644 build_ui/public/files/template/enrollment_import_template.xlsx diff --git a/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/domain/TrainResource.java b/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/domain/TrainResource.java index 0efe22cf6..0a4e3c278 100644 --- a/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/domain/TrainResource.java +++ b/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/domain/TrainResource.java @@ -61,6 +61,9 @@ public class TrainResource extends BaseEntity /** 自增编号(数据库递增序号)。 */ private Long id; + /** 课程引用数(派生字段,非数据库列,仅详情查询时回填)。 */ + private Integer courseReferenceCount; + public void setResourceId(String resourceId) { this.resourceId = resourceId; @@ -139,6 +142,14 @@ public class TrainResource extends BaseEntity this.id = id; } + public Integer getCourseReferenceCount() { + return courseReferenceCount; + } + + public void setCourseReferenceCount(Integer courseReferenceCount) { + this.courseReferenceCount = courseReferenceCount; + } + @Override public String toString() { @@ -151,6 +162,7 @@ public class TrainResource extends BaseEntity .append("status", getStatus()) .append("delFlag", getDelFlag()) .append("id", getId()) + .append("courseReferenceCount", getCourseReferenceCount()) .append("createBy", getCreateBy()) .append("createTime", getCreateTime()) .append("updateBy", getUpdateBy()) diff --git a/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/domain/vo/CourseDetailVO.java b/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/domain/vo/CourseDetailVO.java index d61536b7d..430190f81 100644 --- a/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/domain/vo/CourseDetailVO.java +++ b/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/domain/vo/CourseDetailVO.java @@ -71,6 +71,9 @@ public class CourseDetailVO implements Serializable */ private List resources; + /** 未完成状态开班计划的引用数(派生字段,供课程停用校验使用)。 */ + private Integer unfinishedPlanRefCount; + public CourseDetailVO() { } @@ -317,4 +320,14 @@ public class CourseDetailVO implements Serializable { this.resources = resources; } + + public Integer getUnfinishedPlanRefCount() + { + return unfinishedPlanRefCount; + } + + public void setUnfinishedPlanRefCount(Integer unfinishedPlanRefCount) + { + this.unfinishedPlanRefCount = unfinishedPlanRefCount; + } } diff --git a/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/mapper/TrainPlanCourseMapper.java b/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/mapper/TrainPlanCourseMapper.java index 97612ad02..ccfc7a462 100644 --- a/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/mapper/TrainPlanCourseMapper.java +++ b/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/mapper/TrainPlanCourseMapper.java @@ -91,6 +91,16 @@ public interface TrainPlanCourseMapper */ int countPlanCoursesByCId(@Param("cId") String cId); + /** + * 统计指定公共课程被「未完成」状态开班计划引用的数量。 + * 未完成 = plan_status in (0 未开始, 1 进行中) 且计划未删除; + * 已结束(2)、已取消(3)及已移除的本期课程配置不计入。 + * + * @param cId 公共课程ID + * @return 未完成计划引用数 + */ + int countUnfinishedPlanCoursesByCId(@Param("cId") String cId); + /** * 统计指定公共课程关联的当前有效V2成绩明细数(train_student_score + train_score_item 下游引用)。 * diff --git a/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/service/impl/TrainCourseServiceImpl.java b/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/service/impl/TrainCourseServiceImpl.java index c5f6ac631..29d4821da 100644 --- a/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/service/impl/TrainCourseServiceImpl.java +++ b/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/service/impl/TrainCourseServiceImpl.java @@ -49,6 +49,8 @@ public class TrainCourseServiceImpl implements ITrainCourseService { List resources = trainCourseResourceMapper.selectResourcesByCourseId(cId); detail.setResources(resources); + // 派生字段:供编辑窗口与表格停用开关判断,不参与新增/修改入库 + detail.setUnfinishedPlanRefCount(trainPlanCourseMapper.countUnfinishedPlanCoursesByCId(cId)); // 规范权威读取:优先以 train_course_resource 规范关联表首个资源派生兼容 resourceId; // 若关系表暂无关联但实体存在历史 resourceId,则保留历史 resourceId if (resources != null && !resources.isEmpty()) @@ -181,6 +183,9 @@ public class TrainCourseServiceImpl implements ITrainCourseService throw new ServiceException("课程学时必须为正整数", 400); } + // 4. 由正常改为停用时,校验未完成开班计划的引用 + assertUnfinishedPlanCourseNotDisabled(trainCourse); + trainCourse.setUpdateTime(DateUtils.getNowDate()); try { @@ -193,6 +198,34 @@ public class TrainCourseServiceImpl implements ITrainCourseService return trainCourseMapper.updateTrainCourse(trainCourse); } + /** + * 已被「未完成」状态开班计划引用的课程禁止由「正常」改为「停用」。 + * + *

触发条件:本次提交状态为停用(0)、库中原有状态为正常(1),且该课程仍被 + * 未开始(0)/进行中(1)且未删除的开班计划引用(train_plan_course)。 + * 计划已结束(2)、已取消(3)、或本期课程配置已被移除时不再拦截。 + * 作用范围:仅 PUT /train/course/{cId} 的状态变更;新增、状态未变、停用改正常、 + * 原本已停用、以及只修改其它字段均不受影响。 + * 预期行为:抛出 ServiceException,不执行更新,前端提示先等待班级完成或取消课程配置。

+ */ + private void assertUnfinishedPlanCourseNotDisabled(TrainCourse trainCourse) + { + Integer newStatus = trainCourse.getStatus(); + if (newStatus == null || newStatus != 0) + { + return; + } + TrainCourse existing = trainCourseMapper.selectTrainCourseByCId(trainCourse.getcId()); + if (existing == null || existing.getStatus() == null || existing.getStatus() != 1) + { + return; + } + if (trainPlanCourseMapper.countUnfinishedPlanCoursesByCId(trainCourse.getcId()) > 0) + { + throw new ServiceException("课程已被未完成状态的班级引用,不可停用;请等待班级完成、取消计划或移除本期课程配置后再试"); + } + } + /** * 校验课程统一业务编码是否唯一 * diff --git a/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/service/impl/TrainResourceServiceImpl.java b/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/service/impl/TrainResourceServiceImpl.java index 03104f25f..b7ca04906 100644 --- a/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/service/impl/TrainResourceServiceImpl.java +++ b/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/service/impl/TrainResourceServiceImpl.java @@ -38,7 +38,13 @@ public class TrainResourceServiceImpl implements ITrainResourceService @Override public TrainResource selectTrainResourceByResourceId(String resourceId) { - return trainResourceMapper.selectTrainResourceByResourceId(resourceId); + TrainResource resource = trainResourceMapper.selectTrainResourceByResourceId(resourceId); + if (resource != null) + { + // 派生字段:供编辑窗口判断该资源是否允许停用,不参与新增/修改入库 + resource.setCourseReferenceCount(trainCourseResourceMapper.countCourseReferencesByResourceId(resourceId)); + } + return resource; } /** @@ -99,6 +105,7 @@ public class TrainResourceServiceImpl implements ITrainResourceService { validateStatus(trainResource.getStatus()); } + assertBoundResourceNotDisabled(trainResource); trainResource.setUpdateTime(DateUtils.getNowDate()); return trainResourceMapper.updateTrainResource(trainResource); } @@ -148,6 +155,33 @@ public class TrainResourceServiceImpl implements ITrainResourceService } } + /** + * 已绑定课程的教学资源禁止由「正常」改为「停用」。 + * + *

触发条件:本次提交状态为停用(0)、库中原有状态为正常(1),且 train_course_resource + * 中仍存在该资源的关联记录,三个条件同时满足才拦截。 + * 作用范围:仅 PUT /train/resource/{resourceId} 修改;新增资源、状态未变更、停用改正常、 + * 原本已停用、以及未被课程引用的资源均不受影响。 + * 预期行为:抛出 ServiceException,不执行更新,前端提示先解除课程关联。

+ */ + private void assertBoundResourceNotDisabled(TrainResource trainResource) + { + Integer newStatus = trainResource.getStatus(); + if (newStatus == null || newStatus != 0) + { + return; + } + TrainResource existing = trainResourceMapper.selectTrainResourceByResourceId(trainResource.getResourceId()); + if (existing == null || existing.getStatus() == null || existing.getStatus() != 1) + { + return; + } + if (trainCourseResourceMapper.countCourseReferencesByResourceId(trainResource.getResourceId()) > 0) + { + throw new ServiceException("教学资源已被课程引用,不可停用,请先解除课程关联"); + } + } + /** * 校验资源类型取值:1文档 2视频 33D模型 4VR资源 5外部教学链接 6其他 */ diff --git a/RuoYi-Vue/ruoyi-train/src/main/resources/mapper/train/TrainPlanCourseMapper.xml b/RuoYi-Vue/ruoyi-train/src/main/resources/mapper/train/TrainPlanCourseMapper.xml index 26332769e..f85bd3916 100644 --- a/RuoYi-Vue/ruoyi-train/src/main/resources/mapper/train/TrainPlanCourseMapper.xml +++ b/RuoYi-Vue/ruoyi-train/src/main/resources/mapper/train/TrainPlanCourseMapper.xml @@ -122,6 +122,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" select count(*) from train_plan_course where c_id = #{cId} + +