后端代码

This commit is contained in:
liumengyu
2026-08-24 17:48:55 +08:00
parent e0485f7366
commit aa4145fd33
419 changed files with 3684 additions and 53031 deletions
@@ -17,7 +17,7 @@ import java.util.List;
public class ScheduleAdjustApplyDTO {
/**
* 实施_课程表编号,必填,标识需要调整的课次。
* 实施_课程表编号,必填,对应实施_调课申请.实施_课程表编号。
*/
@NotBlank(message = "课次编号不能为空")
@Size(max = 50, message = "课次编号不能超过50个字符")
@@ -49,12 +49,35 @@ public class ScheduleAdjustApplyDTO {
private Integer nd;
/**
* 申请教员编号,必填,标识提交申请的教员。
* 申请教员编号,教员提交时由服务端按登录人写入,前端可不传。
*/
@NotBlank(message = "申请教员编号不能为空")
@Size(max = 50, message = "申请教员编号不能超过50个字符")
private String sqjybh;
/**
* 学员队任务表编号,由服务端根据实施课次反查,前端可不传。
*/
@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;
/**
* 调课后教学内容,调整后的本节课教学主题或知识点。
*/
@@ -41,14 +41,13 @@ public class ScheduleAdjustAuditDTO {
private String fhyj;
/**
* 审批人编号,必填,标识审批人身份。
* 审批人编号,不传时由服务端取当前登录人。
*/
@NotBlank(message = "审批人编号不能为空")
@Size(max = 50, message = "审批人编号不能超过50个字符")
private String sprbh;
/**
* 机关编号,机关审批时必填,标识审批机关。
* 机关编号,机关审批时可选。
*/
@Size(max = 50, message = "机关编号不能超过50个字符")
private String jgbh;
@@ -52,6 +52,21 @@ public class ScheduleAdjustQueryDTO extends PageQuery {
*/
private Integer sczt;
/**
* 教研室代号,教研室角色由服务端强制写入,前端不必传。
*/
private String jysdh;
/**
* 学员队任务表编号。
*/
private String xydrwbh;
/**
* 课编号。
*/
private String kbh;
/**
* 申请日期范围开始。
*/
@@ -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;
}
@@ -3,6 +3,7 @@ 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.baomidou.mybatisplus.annotation.TableField;
import com.roomroot.jwgl.unit.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -24,163 +25,144 @@ public class SSDKSQ extends BaseEntity {
private String bh;
/**
* 实施_课程表编号
* 实施_课程表编号(原表主关联:指向实施_课程表)
*/
@TableField("实施_课程表编号")
private String sskcbbh;
/**
* 预调时间安排
*/
@TableField("预调时间安排")
private Integer ydsjap;
/**
* 创建时间
*/
@TableField("创建时间")
private LocalDateTime cjsj;
/**
* 执行人编号
*/
@TableField("执行人编号")
private String zxrbh;
/**
* 执行时间
*/
@TableField("执行时间")
private LocalDateTime zxsj;
/**
* 执行状态
*/
@TableField("执行状态")
private Integer zxzt;
/**
* 事由
*/
@TableField("事由")
private String sy;
/**
* 教研室批准状态
*/
@TableField("教研室批准状态")
private Integer jyspzzt;
/**
* 教研室批准时间
*/
@TableField("教研室批准时间")
private LocalDateTime jyspzsj;
/**
* 教研室批准人编号
*/
@TableField("教研室批准人编号")
private String jyspzrbh;
/**
* 教学系批准状态
*/
/** 原表无此列 */
@TableField(exist = false)
private Integer jxxspzzt;
/**
* 教学系批准时间
*/
@TableField(exist = false)
private LocalDateTime jxxspzsj;
/**
* 教学系批准人编号
*/
@TableField(exist = false)
private String jxxspzrbh;
/**
* 备注
*/
@TableField("备注")
private String bz;
/**
* 申请教员编号
*/
@TableField("申请教员编号")
private String sqjybh;
/**
* 延时状态
*/
@TableField("延时状态")
private Integer yszt;
/**
* 教研室查收状态
*/
@TableField("教研室查收状态")
private Integer jyscszt;
/**
* 教研室查收时间
*/
@TableField("教研室查收时间")
private LocalDateTime jyscssj;
/**
* 教研室查收人编号
*/
@TableField("教研室查收人编号")
private String jyscsrbh;
/**
* 删除状态
*/
@TableField("删除状态")
private Integer sczt;
/**
* 发回状态
*/
@TableField("发回状态")
private Integer fhzt;
/**
* 发回意见
*/
@TableField("发回意见")
private String fhyj;
/**
* 发回时间
*/
@TableField("发回时间")
private LocalDateTime fhsj;
/**
* 调课
*/
@TableField("调课")
private Integer dk;
/**
* 年度
*/
@TableField("年度")
private Integer nd;
/**
* 日期
*/
/** 拟调整后的上课日期 */
@TableField("日期")
private LocalDateTime rq;
/**
* 节次
*/
/** 拟调整后的节次 */
@TableField("节次")
private Integer jc;
/**
* 教学内容
*/
@TableField("教学内容")
private String jxnr;
/**
* 教学要点
*/
@TableField("教学要点")
private String jxyd;
/**
* 教学方法
*/
@TableField("教学方法")
private String jxff;
/**
* 教学保障备注
*/
@TableField("教学保障备注")
private String jxbzbz;
/**
* 用车信息
*/
@TableField("用车信息")
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;
}
@@ -92,22 +92,38 @@ public class JwglApiAccessInterceptor implements HandlerInterceptor {
if (startsWithAny(path, "/getInfo", "/getRouters", "/logout", "/system/user/profile")) {
return true;
}
// 通知已读属于个人操作,布局顶栏会调用。
if (HttpMethod.POST.matches(method)
&& startsWithAny(path, "/system/notice/markRead", "/system/notice/markReadAll")) {
return true;
}
if (!HttpMethod.GET.matches(method)) {
return false;
}
return startsWithAny(path,
// 学期列表 / 学期校历(只读;新增、修改、删除仍拦截)
"/semester",
"/xqxlb",
"/kcb",
"/studentTeamOutputSchedule",
"/classCalendar",
// 本人课表
"/kcb/today",
"/kcb/week",
"/kcb/class",
"/studentTeamOutputSchedule",
"/course-running/plan/list",
"/course-running/plan/detail",
"/course-running/plan/export",
// 本人学籍预警、个人信息、成绩
"/student-records/warning-result",
"/student-records/warning-condition",
"/student-records/get",
"/student-records/list",
"/student-records/grades",
"/student-records/process-grades");
"/student-records/process-grades",
// 布局公共只读:字典、参数、通知
"/system/dict/data/type",
"/system/config/configKey",
"/system/notice");
}
private boolean isTeacherAdjustDenied(String path, String method) {
@@ -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 org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
* 教员表 Mapper
@@ -27,4 +28,9 @@ public interface JYBMapper extends BaseMapper<JYB> {
/** 教员列表双表联查分页 */
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,170 @@
<?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 cxf."实施_课程编号" = c."编号"
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 CAST(c."编号" AS CHAR(36)) = 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
c."编号" 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 CAST(c."编号" AS CHAR(36)) = 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 CAST(c."编号" AS CHAR(36)) = TRIM(t."实施_课程编号")
AND COALESCE(t."删除状态", 0) = 0),
NVL(js0."教室名称", '-'))) AS rscd,
(SELECT WM_CONCAT(DISTINCT sq."事由")
FROM "实施_调课申请" sq
INNER JOIN "实施_课程表" t ON t."编号" = sq."实施_课程表编号"
WHERE CAST(c."编号" AS CHAR(36)) = TRIM(t."实施_课程编号")) AS ssjhbg
FROM "实施_课程" c
INNER JOIN "实施_课程学员队" cx ON cx."实施_课程编号" = c."编号"
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,58 +3,45 @@ package com.roomroot.jwgl.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.ScheduleAdjustDetailVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 实施_调课申请 Mapper。
* <p>
* 提供调课申请数据的CRUD操作及调课管理相关的自定义查询。
* </p>
*/
@Mapper
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> page,
@Param("sskcbbh") String sskcbbh,
@Param("xydrwbh") String xydrwbh,
@Param("kbh") String kbh,
@Param("sqjybh") String sqjybh,
@Param("jysdh") String jysdh,
@Param("nd") Integer nd,
@Param("jyspzzt") Integer jyspzzt,
@Param("jyscszt") Integer jyscszt,
@Param("jgspzt") Integer jgspzt,
@Param("sczt") Integer sczt,
@Param("cjsjStart") String cjsjStart,
@Param("cjsjEnd") String cjsjEnd);
/**
* 根据调课申请编号查询调课申请详情。
* <p>
* 关联课程表获取课程名称,关联教员表获取申请教员姓名。
* </p>
*
* @param ssdksqbh 调课申请编号
* @return 调课申请详情
* 根据调课申请编号查询详情。
*/
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">
<mapper namespace="com.roomroot.jwgl.mapper.SSDKSQMapper">
<!-- 调课申请列表结果映射 -->
<resultMap id="ScheduleAdjustApplyMap" type="com.roomroot.jwgl.vo.courserunning.ScheduleAdjustApplyVO">
<result column="调课申请编号" property="ssdksqbh"/>
<result column="实施_课程表编号" property="sskcbbh"/>
<result column="学员队任务编号" property="xydrwbh"/>
<result column="课编号" property="kbh"/>
<result column="课程表编号" property="kcbbh"/>
<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="sqjyxm"/>
<result column="事由" property="sy"/>
@@ -16,15 +22,22 @@
<result column="创建时间" property="cjsj"/>
<result column="教研室批准状态" property="jyspzzt"/>
<result column="教研室查收状态" property="jyscszt"/>
<result column="机关审批状态" property="jgspzt"/>
<result column="发回意见" property="fhyj"/>
<result column="删除状态" property="sczt"/>
</resultMap>
<!-- 调课申请详情结果映射 -->
<resultMap id="ScheduleAdjustDetailMap" type="com.roomroot.jwgl.vo.courserunning.ScheduleAdjustDetailVO">
<result column="调课申请编号" property="ssdksqbh"/>
<result column="实施_课程表编号" property="sskcbbh"/>
<result column="学员队任务编号" property="xydrwbh"/>
<result column="课编号" property="kbh"/>
<result column="课程表编号" property="kcbbh"/>
<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="sqjyxm"/>
<result column="事由" property="sy"/>
@@ -46,14 +59,43 @@
<result column="教研室查收人编号" property="jyscsrbh"/>
</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
a.编号 AS 调课申请编号,
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.申请教员编号,
j.姓名 AS 申请教员姓名,
j.教员姓名 AS 申请教员姓名,
a.事由,
a.日期 AS 新日期,
a.节次 AS 新节次,
@@ -61,20 +103,36 @@
a.创建时间,
a.教研室批准状态,
a.教研室查收状态,
(
SELECT MAX(sp.审批状态)
FROM "实施_调课申请审批" sp
WHERE sp.实施_调课申请编号 = a.编号
AND sp.审批状态 IS NOT NULL
) AS 机关审批状态,
a.发回意见,
a.删除状态
FROM "实施_调课申请" a
LEFT JOIN "实施_课程表" b ON a.实施_课程表编号 = b.编号
LEFT JOIN "实施_课程" e ON b.实施_课程编号 = e.编号
LEFT JOIN "课程表" c ON e.课编号 = c.课编号
LEFT JOIN "教员表" j ON a.申请教员编号 = j.编号
LEFT JOIN "实施_课程表" b ON TRIM(b.编号) = TRIM(a.实施_课程表编号)
LEFT JOIN "实施_课程" e ON CAST(e.编号 AS CHAR(36)) = TRIM(b.实施_课程编号)
LEFT JOIN "课表" kb ON kb.课编号 = COALESCE(NULLIF(TRIM(a.课编号), ''), e.科目编号)
LEFT JOIN "教员表" j ON TRIM(j.教员编号) = TRIM(a.申请教员编号)
LEFT JOIN "课程表" kcb ON TRIM(kcb.编号) = TRIM(a.课程表编号)
WHERE 1=1
<if test="sskcbbh != null and sskcbbh != ''">
AND a.实施_课程表编号 = #{sskcbbh}
</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 != ''">
AND a.申请教员编号 = #{sqjybh}
</if>
<if test="jysdh != null and jysdh != ''">
AND COALESCE(a.教研室代号, j.教研室代号) = #{jysdh}
</if>
<if test="nd != null">
AND a.年度 = #{nd}
</if>
@@ -84,6 +142,14 @@
<if test="jyscszt != null">
AND a.教研室查收状态 = #{jyscszt}
</if>
<if test="jgspzt != null">
AND (
SELECT MAX(sp.审批状态)
FROM "实施_调课申请审批" sp
WHERE sp.实施_调课申请编号 = a.编号
AND sp.审批状态 IS NOT NULL
) = #{jgspzt}
</if>
<if test="sczt != null">
AND a.删除状态 = #{sczt}
</if>
@@ -96,14 +162,25 @@
ORDER BY a.创建时间 DESC
</select>
<!-- 根据调课申请编号查询详情 -->
<select id="selectDetailById" resultMap="ScheduleAdjustDetailMap">
SELECT
a.编号 AS 调课申请编号,
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.申请教员编号,
j.姓名 AS 申请教员姓名,
j.教员姓名 AS 申请教员姓名,
a.事由,
a.日期 AS 新日期,
a.节次 AS 新节次,
@@ -122,11 +199,70 @@
a.教研室查收时间,
a.教研室查收人编号
FROM "实施_调课申请" a
LEFT JOIN "实施_课程表" b ON a.实施_课程表编号 = b.编号
LEFT JOIN "实施_课程" e ON b.实施_课程编号 = e.编号
LEFT JOIN "课程表" c ON e.课编号 = c.课编号
LEFT JOIN "教员表" j ON a.申请教员编号 = j.编号
LEFT JOIN "实施_课程表" b ON TRIM(b.编号) = TRIM(a.实施_课程表编号)
LEFT JOIN "实施_课程" e ON CAST(e.编号 AS CHAR(36)) = TRIM(b.实施_课程编号)
LEFT JOIN "课表" kb ON kb.课编号 = COALESCE(NULLIF(TRIM(a.课编号), ''), e.科目编号)
LEFT JOIN "教员表" j ON TRIM(j.教员编号) = TRIM(a.申请教员编号)
WHERE a.编号 = #{ssdksqbh}
</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 CAST(e.编号 AS CHAR(36)) = 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>
@@ -2,10 +2,17 @@ package com.roomroot.jwgl.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
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.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.Param;
import java.util.List;
/**
* 教学任务 Mapper
*/
@@ -20,4 +27,31 @@ public interface TeachingTaskMapper extends BaseMapper<JXRW> {
* @return 分页结果
*/
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 编号
</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>
@@ -0,0 +1,18 @@
package com.roomroot.jwgl.mapper;
import com.roomroot.jwgl.vo.jwtest.PkLessonVO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 排课测试全查。
*/
@Mapper
public interface TestPkMapper {
/**
* 查询全部排课记录,无筛选条件。
*/
List<PkLessonVO> selectAll();
}
@@ -0,0 +1,67 @@
<?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 REPLACE(LOWER(TRIM(CAST(c."编号" AS CHAR(36)))), '-', '')
= REPLACE(LOWER(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>
</mapper>
@@ -20,6 +20,7 @@ import com.roomroot.jwgl.dto.courserunning.StudentLeaveQueryDTO;
import com.roomroot.jwgl.dto.courserunning.OperationAnalysisQueryDTO;
import com.roomroot.jwgl.dto.courserunning.ScheduleAdjustStrategyDTO;
import com.roomroot.jwgl.unit.PageResult;
import com.roomroot.jwgl.vo.courserunning.AdjustableLessonVO;
import com.roomroot.jwgl.vo.courserunning.CourseRunningImportResultVO;
import com.roomroot.jwgl.vo.courserunning.ScheduleAdjustApplyVO;
import com.roomroot.jwgl.vo.courserunning.ScheduleAdjustDetailVO;
@@ -178,6 +179,14 @@ public interface CourseRunningImportService {
*/
PageResult<ScheduleAdjustApplyVO> queryScheduleAdjustList(ScheduleAdjustQueryDTO query);
/**
* 查询当前教员可供调课的课次,关联学员队任务表、课表、课程表。
*
* @param nd 年度,可空
* @return 可选课次列表
*/
List<AdjustableLessonVO> listAdjustableLessons(Integer nd);
/**
* 查询单个调课申请的完整详情。
* <p>
@@ -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;
}
@@ -1,6 +1,12 @@
package com.roomroot.jwgl.service;
import com.roomroot.jwgl.dto.kcb.TimetableQuery;
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;
@@ -51,4 +57,34 @@ public interface KCBService {
* @return 所有数据列表
*/
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);
}
@@ -2,17 +2,18 @@ package com.roomroot.jwgl.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.roomroot.common.utils.SecurityUtils;
import com.roomroot.common.utils.StringUtils;
import com.roomroot.jwgl.dto.courserunning.*;
import com.roomroot.jwgl.entity.*;
import com.roomroot.jwgl.mapper.*;
import com.roomroot.jwgl.vo.courserunning.*;
import com.roomroot.jwgl.mapper.*;
import com.roomroot.jwgl.service.CourseRunningImportService;
import com.roomroot.jwgl.service.DictDataService;
import com.roomroot.jwgl.unit.BusinessException;
import com.roomroot.jwgl.unit.PageResult;
import com.roomroot.jwgl.utils.BasicManagementConstants;
import com.roomroot.jwgl.utils.ExcelExportUtil;
import com.roomroot.jwgl.utils.JwglRoleHelper;
import com.roomroot.jwgl.utils.UuidUtil;
import com.roomroot.jwgl.validator.StudentLeaveValidator;
import com.roomroot.jwgl.vo.courserunning.*;
@@ -102,6 +103,15 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic
@Resource
private SSDKSQMapper ssdksqMapper;
@Resource
private JwglRoleHelper jwglRoleHelper;
@Resource
private StudentTeamTaskMapper studentTeamTaskMapper;
@Resource
private KCBMapper kcbMapper;
/**
* 实施_调课申请审批 Mapper,用于调课申请审批记录的数据访问。
*/
@@ -863,35 +873,33 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic
*/
@Override
public PageResult<ScheduleAdjustApplyVO> queryScheduleAdjustList(ScheduleAdjustQueryDTO query) {
// 步骤1:校验查询参数,确保分页参数有效
if (query == null) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "查询参数不能为空");
}
if (query.getPageNum() == null || query.getPageSize() == null) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "分页参数不能为空");
}
applyScheduleAdjustDataScope(query);
// 步骤2:构建MyBatis-Plus分页对象
Page<ScheduleAdjustApplyVO> page = new Page<>(query.getPageNum(), query.getPageSize());
// 步骤3:将日期范围转换为字符串格式,便于XML中直接拼接SQL
String cjsjStartStr = query.getCjsjStart() != null ? query.getCjsjStart().format(DATE_TIME_FORMATTER) : null;
String cjsjEndStr = query.getCjsjEnd() != null ? query.getCjsjEnd().format(DATE_TIME_FORMATTER) : null;
// 步骤4:调用Mapper执行分页查询,关联课程表和教员表
Page<ScheduleAdjustApplyVO> resultPage = ssdksqMapper.selectPageList(
page,
query.getSskcbbh(),
query.getXydrwbh(),
query.getKbh(),
query.getSqjybh(),
query.getJysdh(),
query.getNd(),
query.getJyspzzt(),
query.getJyscszt(),
query.getJgspzt(),
query.getSczt(),
cjsjStartStr,
cjsjEndStr
);
// 步骤5:封装分页结果并返回
return new PageResult<>(
resultPage.getRecords(),
resultPage.getTotal(),
@@ -900,6 +908,73 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic
);
}
@Override
public List<AdjustableLessonVO> listAdjustableLessons(Integer nd) {
if (jwglRoleHelper.isDepartmentPersonnel()) {
throw new BusinessException(BasicManagementConstants.FORBIDDEN, "机关人员仅可查看和审批调课申请");
}
if (jwglRoleHelper.isResearchOffice()) {
throw new BusinessException(BasicManagementConstants.FORBIDDEN, "教研室仅可查看和审批本室调课申请");
}
String teacherId = jwglRoleHelper.requireTeacherId();
return ssdksqMapper.selectAdjustableLessons(teacherId, nd);
}
private void applyScheduleAdjustDataScope(ScheduleAdjustQueryDTO query) {
if (jwglRoleHelper.isAdmin() || jwglRoleHelper.isDepartmentPersonnel()) {
return;
}
if (jwglRoleHelper.isResearchOffice()) {
query.setJysdh(jwglRoleHelper.requireResearchOfficeId());
query.setSqjybh(null);
return;
}
if (jwglRoleHelper.isTeacher()) {
query.setSqjybh(jwglRoleHelper.requireTeacherId());
query.setJysdh(null);
return;
}
throw new BusinessException(BasicManagementConstants.FORBIDDEN, "无权查看调课申请");
}
private void assertCanViewScheduleAdjust(SSDKSQ apply) {
if (jwglRoleHelper.isAdmin() || jwglRoleHelper.isDepartmentPersonnel()) {
return;
}
if (jwglRoleHelper.isResearchOffice()) {
String officeId = jwglRoleHelper.requireResearchOfficeId();
String applyOffice = apply.getJysdh();
if (StringUtils.isEmpty(applyOffice) && StringUtils.isNotEmpty(apply.getSqjybh())) {
JYB teacher = jybMapper.selectById(apply.getSqjybh());
applyOffice = teacher == null ? null : teacher.getJysdh();
}
if (applyOffice == null || !officeId.equals(applyOffice.trim())) {
throw new BusinessException(BasicManagementConstants.FORBIDDEN, "只能查看本教研室的调课申请");
}
return;
}
if (jwglRoleHelper.isTeacher()) {
String teacherId = jwglRoleHelper.requireTeacherId();
if (apply.getSqjybh() == null || !teacherId.equals(apply.getSqjybh().trim())) {
throw new BusinessException(BasicManagementConstants.FORBIDDEN, "只能查看本人提交的调课申请");
}
return;
}
throw new BusinessException(BasicManagementConstants.FORBIDDEN, "无权查看调课申请");
}
private String currentOperatorId() {
try {
String username = SecurityUtils.getUsername();
if (StringUtils.isNotEmpty(username)) {
return username;
}
} catch (Exception ignored) {
// 未登录时由调用方继续校验
}
return null;
}
/**
* 查询单个调课申请的完整详情。
* <p>
@@ -922,6 +997,10 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic
if (detail == null) {
throw new BusinessException(BasicManagementConstants.NOT_FOUND, "未找到该调课申请");
}
SSDKSQ apply = ssdksqMapper.selectById(ssdksqbh);
if (apply != null) {
assertCanViewScheduleAdjust(apply);
}
// 步骤3:查询关联的机关审批记录列表
detail.setAuditList(ssdksqspMapper.selectBySsdksqbh(ssdksqbh));
@@ -957,22 +1036,24 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic
@Override
@Transactional(rollbackFor = Exception.class)
public String submitScheduleAdjust(ScheduleAdjustApplyDTO dto) {
// 步骤1:校验调课申请参数
if (dto == null) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "调课申请参数不能为空");
}
if (dto.getSskcbbh() == null || dto.getSskcbbh().trim().isEmpty()) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "课次编号不能为空");
if (jwglRoleHelper.isDepartmentPersonnel()) {
throw new BusinessException(BasicManagementConstants.FORBIDDEN, "机关人员仅可查看和审批,不能提交调课申请");
}
if (dto.getSqjybh() == null || dto.getSqjybh().trim().isEmpty()) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "申请教员编号不能为空");
if (jwglRoleHelper.isResearchOffice()) {
throw new BusinessException(BasicManagementConstants.FORBIDDEN, "教研室仅可查看和审批本室申请,不能提交调课申请");
}
if (StringUtils.isEmpty(dto.getSskcbbh())) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "课次编号不能为空");
}
if (dto.getSy() == null || dto.getSy().trim().isEmpty()) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "调课事由不能为空");
}
// 步骤2:查询课次实体,校验课次是否存在
SSKCB lesson = sskcbMapper.selectById(dto.getSskcbbh());
String teacherId = jwglRoleHelper.requireTeacherId();
SSKCB lesson = sskcbMapper.selectById(dto.getSskcbbh().trim());
if (lesson == null) {
throw new BusinessException(BasicManagementConstants.NOT_FOUND, "课次不存在");
}
@@ -980,58 +1061,63 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "该课次已删除,不允许申请调课");
}
// 调课时间段策略校验
validateScheduleAdjustTimeLimitStrategy(dto.getRq());
AdjustableLessonVO picked = null;
List<AdjustableLessonVO> mine = ssdksqMapper.selectAdjustableLessons(teacherId, lesson.getNd());
if (mine != null) {
for (AdjustableLessonVO item : mine) {
if (lesson.getBh().equals(item.getSskcbbh())) {
picked = item;
break;
}
}
}
if (picked == null) {
throw new BusinessException(BasicManagementConstants.FORBIDDEN, "只能对自己任课的课次申请调课");
}
// 判断是否命中免审批策略
validateScheduleAdjustTimeLimitStrategy(dto.getRq());
boolean autoApproved = matchAutoApproveStrategy();
// 步骤3:构建并保存调课申请主表实体
SSDKSQ apply = new SSDKSQ();
String ssdksqbh = UuidUtil.getOriginalUUID(); // 生成唯一调课申请编号
String ssdksqbh = UuidUtil.getOriginalUUID();
apply.setBh(ssdksqbh);
apply.setSskcbbh(dto.getSskcbbh()); // 课次编号
apply.setSqjybh(dto.getSqjybh()); // 申请教员编号
apply.setSy(dto.getSy()); // 调课事由
apply.setRq(dto.getRq()); // 新日期
apply.setJc(dto.getJc()); // 新节次
apply.setNd(dto.getNd()); // 年度
apply.setJxnr(dto.getJxnr()); // 教学内容
apply.setJxyd(dto.getJxyd()); // 教学要点
apply.setJxff(dto.getJxff()); // 教学方法
apply.setJxbzbz(dto.getJxbzbz()); // 教学保障备注
apply.setYcxx(dto.getYcxx()); // 用车信息
apply.setBz(dto.getBz()); // 备注
// 将原日期节次描述追加到备注中(ydsjap字段类型为Integer,用于存储其他业务数据)
apply.setSskcbbh(lesson.getBh());
apply.setXydrwbh(StringUtils.isNotEmpty(dto.getXydrwbh()) ? dto.getXydrwbh() : picked.getXydrwbh());
apply.setKbh(StringUtils.isNotEmpty(dto.getKbh()) ? dto.getKbh() : picked.getKbh());
apply.setKcbbh(StringUtils.isNotEmpty(dto.getKcbbh()) ? dto.getKcbbh() : picked.getKcbbh());
apply.setJysdh(picked.getJysdh());
apply.setYjsbh(picked.getJsbh());
apply.setXjsbh(resolveNewClassroom(dto));
apply.setSqjybh(teacherId);
apply.setSy(dto.getSy());
apply.setRq(dto.getRq());
apply.setJc(dto.getJc());
apply.setNd(dto.getNd() != null ? dto.getNd() : lesson.getNd());
apply.setJxnr(dto.getJxnr());
apply.setJxyd(dto.getJxyd());
apply.setJxff(dto.getJxff());
apply.setJxbzbz(dto.getJxbzbz());
apply.setYcxx(dto.getYcxx());
String originalDesc = buildOriginalScheduleDesc(lesson);
if (dto.getBz() != null && !dto.getBz().trim().isEmpty()) {
apply.setBz(dto.getBz() + " | " + originalDesc);
} else {
apply.setBz(originalDesc);
}
// 命中免审批策略时在备注追加标记,便于教务人员识别
if (autoApproved) {
apply.setBz((apply.getBz() == null ? "" : apply.getBz() + " | ") + "[免审批策略自动通过]");
}
// 审批状态初始化:未审批、未查收、未删除
if (autoApproved) {
// 命中免审批策略:教研室和教学系审批状态直接设为已通过,跳过三级审批
apply.setJyspzzt(1);
apply.setJxxspzzt(1);
} else {
apply.setJyspzzt(0);
}
apply.setJyscszt(0);
apply.setSczt(0);
// 时间字段
apply.setFhzt(0);
apply.setDk(1);
LocalDateTime now = LocalDateTime.now();
apply.setCreateTime(now);
apply.setUpdateTime(now);
// 步骤4:插入调课申请主表数据
apply.setCjsj(now);
ssdksqMapper.insert(apply);
// 步骤5:保存新教室关联数据
if (dto.getRoomList() != null && !dto.getRoomList().isEmpty()) {
for (AdjustRoomDTO roomDto : dto.getRoomList()) {
DKSQJS roomEntity = new DKSQJS();
@@ -1041,9 +1127,13 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic
roomEntity.setBz(roomDto.getBz());
dksqjsMapper.insert(roomEntity);
}
} else if (StringUtils.isNotEmpty(apply.getXjsbh())) {
DKSQJS roomEntity = new DKSQJS();
roomEntity.setBh(UuidUtil.getOriginalUUID());
roomEntity.setDksqbh(ssdksqbh);
roomEntity.setJsbh(apply.getXjsbh());
dksqjsMapper.insert(roomEntity);
}
// 步骤6:保存新辅助教员关联数据
if (dto.getTeacherList() != null && !dto.getTeacherList().isEmpty()) {
for (AdjustTeacherDTO teacherDto : dto.getTeacherList()) {
DKSQFZJY teacherEntity = new DKSQFZJY();
@@ -1055,8 +1145,6 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic
dksqfzjyMapper.insert(teacherEntity);
}
}
// 步骤7:保存新学员队关联数据
if (dto.getTeamList() != null && !dto.getTeamList().isEmpty()) {
for (AdjustTeamDTO teamDto : dto.getTeamList()) {
DKSQXYD teamEntity = new DKSQXYD();
@@ -1066,8 +1154,6 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic
dksqxydMapper.insert(teamEntity);
}
}
// 步骤8:保存新保障明细关联数据
if (dto.getSupportList() != null && !dto.getSupportList().isEmpty()) {
for (AdjustSupportDTO supportDto : dto.getSupportList()) {
DKSQBZMX supportEntity = new DKSQBZMX();
@@ -1080,19 +1166,54 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic
}
}
// 步骤9:更新课次数据,将日期节次替换为调整后的新值
lesson.setRq(dto.getRq());
lesson.setJc(dto.getJc());
lesson.setBdsj(now);
sskcbMapper.updateById(lesson);
if (autoApproved) {
applyApprovedSchedule(apply);
}
// 步骤10:记录调课申请操作日志
recordOperationLog(ssdksqbh, "申请", "提交调课申请:" + ssdksqbh);
// 步骤11:返回调课申请编号
return ssdksqbh;
}
private XYDRWB findRelatedTeamTask(SSKC course, SSKCB lesson) {
if (course == null || StringUtils.isEmpty(course.getKmbh()) || lesson == null) {
return null;
}
List<SSKCBXYD> teams = sskcbxyMapper.selectBySskcbh(lesson.getBh());
String teamId = (teams == null || teams.isEmpty()) ? null : teams.get(0).getXydbh();
LambdaQueryWrapper<XYDRWB> wrapper = new LambdaQueryWrapper<XYDRWB>()
.eq(XYDRWB::getKbh, course.getKmbh())
.eq(XYDRWB::getJybh, course.getJybh())
.eq(lesson.getNd() != null, XYDRWB::getNd, lesson.getNd());
if (StringUtils.isNotEmpty(teamId)) {
wrapper.eq(XYDRWB::getXydbh, teamId);
}
wrapper.last("LIMIT 1");
return studentTeamTaskMapper.selectOne(wrapper);
}
private KCB findRelatedScheduleSlot(SSKC course) {
if (course == null || StringUtils.isEmpty(course.getJybh()) || course.getJykcxh() == null) {
return null;
}
LambdaQueryWrapper<KCB> wrapper = new LambdaQueryWrapper<KCB>()
.eq(KCB::getJybh, course.getJybh())
.eq(KCB::getKcxh, course.getJykcxh())
.eq(course.getNd() != null, KCB::getNd, course.getNd())
.last("LIMIT 1");
return kcbMapper.selectOne(wrapper);
}
private String resolveNewClassroom(ScheduleAdjustApplyDTO dto) {
if (StringUtils.isNotEmpty(dto.getXjsbh())) {
return dto.getXjsbh().trim();
}
if (dto.getRoomList() != null && !dto.getRoomList().isEmpty()
&& StringUtils.isNotEmpty(dto.getRoomList().get(0).getJsbh())) {
return dto.getRoomList().get(0).getJsbh().trim();
}
return null;
}
/**
* 教研室审批调课申请。
* <p>
@@ -1105,10 +1226,18 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic
@Override
@Transactional(rollbackFor = Exception.class)
public void auditByTeachingOffice(ScheduleAdjustAuditDTO dto) {
// 步骤1:校验审批参数
if (dto == null) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "审批参数不能为空");
}
if (jwglRoleHelper.isTeacher()) {
throw new BusinessException(BasicManagementConstants.FORBIDDEN, "教员无权审批调课申请");
}
if (jwglRoleHelper.isDepartmentPersonnel() && !jwglRoleHelper.isAdmin()) {
throw new BusinessException(BasicManagementConstants.FORBIDDEN, "机关人员请使用机关审批,不办理教研室审批");
}
if (!jwglRoleHelper.isAdmin() && !jwglRoleHelper.isResearchOffice()) {
throw new BusinessException(BasicManagementConstants.FORBIDDEN, "仅教研室可进行一审");
}
if (!"jys".equals(dto.getAuditRole())) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "审批角色不正确,应为jys");
}
@@ -1117,7 +1246,6 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "审批结果不正确,应为1(同意)、2(发回)或3(拒绝)");
}
// 步骤2:查询调课申请实体,校验申请是否存在
SSDKSQ apply = ssdksqMapper.selectById(dto.getSsdksqbh());
if (apply == null) {
throw new BusinessException(BasicManagementConstants.NOT_FOUND, "调课申请不存在");
@@ -1125,33 +1253,33 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic
if (apply.getSczt() != null && apply.getSczt() == 1) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "该调课申请已撤销,不允许审批");
}
// 步骤3:校验申请是否已审批,避免重复审批
if (jwglRoleHelper.isResearchOffice()) {
String officeId = jwglRoleHelper.requireResearchOfficeId();
String applyOffice = apply.getJysdh();
if (StringUtils.isEmpty(applyOffice) && StringUtils.isNotEmpty(apply.getSqjybh())) {
JYB teacher = jybMapper.selectById(apply.getSqjybh());
applyOffice = teacher == null ? null : teacher.getJysdh();
}
if (applyOffice == null || !officeId.equals(applyOffice.trim())) {
throw new BusinessException(BasicManagementConstants.FORBIDDEN, "只能审批本教研室的调课申请");
}
}
if (apply.getJyspzzt() != null && apply.getJyspzzt() != 0) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "该调课申请已审批,无需重复审批");
}
// 步骤4:更新调课申请主表的教研室审批状态
LocalDateTime now = LocalDateTime.now();
apply.setJyspzzt(dto.getSpzt()); // 教研室审批状态
apply.setJyspzrbh(dto.getSprbh()); // 教研室审批人编号
apply.setJyspzsj(now); // 教研室审批时间
apply.setFhyj(dto.getFhyj()); // 发回意见
apply.setUpdateTime(now); // 更新时间
String auditor = StringUtils.isNotEmpty(dto.getSprbh()) ? dto.getSprbh() : currentOperatorId();
apply.setJyspzzt(dto.getSpzt());
apply.setJyspzrbh(auditor);
apply.setJyspzsj(now);
apply.setFhyj(dto.getFhyj());
if (dto.getSpzt() != null && dto.getSpzt() == 2) {
apply.setFhzt(1);
apply.setFhsj(now);
}
ssdksqMapper.updateById(apply);
// 步骤5:根据审批结果执行不同的后续处理
if (dto.getSpzt() == 3) {
// 拒绝:恢复课次原数据,标记申请最终结束
restoreLessonSchedule(apply);
} else if (dto.getSpzt() == 2) {
// 发回:发回教员修改,课次保留调整后数据,由教员重新申请
// 此处无需修改课次数据
} else {
// 同意:课次数据已由申请时更新,无需重复处理
}
// 步骤6:记录审批操作日志
recordOperationLog(apply.getBh(), "教研室审批", "教研室审批调课申请:" + apply.getBh()
+ ",审批结果:" + dto.getSpzt() + ",审批意见:" + dto.getFhyj());
}
@@ -1288,11 +1416,13 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic
|| (dto.getSpzt() != 1 && dto.getSpzt() != 2 && dto.getSpzt() != 3)) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "审批结果不正确");
}
if (dto.getJgbh() == null || dto.getJgbh().trim().isEmpty()) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "机关编号不能为空");
if (jwglRoleHelper.isTeacher() || jwglRoleHelper.isResearchOffice()) {
throw new BusinessException(BasicManagementConstants.FORBIDDEN, "仅机关人员可进行终审");
}
if (!jwglRoleHelper.isAdmin() && !jwglRoleHelper.isDepartmentPersonnel()) {
throw new BusinessException(BasicManagementConstants.FORBIDDEN, "仅机关人员可进行终审");
}
// 步骤2:查询调课申请实体
SSDKSQ apply = ssdksqMapper.selectById(dto.getSsdksqbh());
if (apply == null) {
throw new BusinessException(BasicManagementConstants.NOT_FOUND, "调课申请不存在");
@@ -1300,23 +1430,18 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic
if (apply.getSczt() != null && apply.getSczt() == 1) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "该调课申请已撤销,不允许审批");
}
// 步骤3:校验教研室是否已同意,机关审批应在教研室同意之后进行
if (apply.getJyspzzt() == null || apply.getJyspzzt() != 1) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "教研室未同意或未审批,无法进行机关审批");
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "教研室未同意,无法进行机关审批");
}
// 步骤4:校验教学系是否已同意,机关审批应在教学系同意之后进行(三级审批)
if (apply.getJxxspzzt() == null || apply.getJxxspzzt() != 1) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "教学系未同意或未审批,无法进行机关审批");
}
String jgbh = StringUtils.isNotEmpty(dto.getJgbh()) ? dto.getJgbh() : "JG";
String auditor = StringUtils.isNotEmpty(dto.getSprbh()) ? dto.getSprbh() : currentOperatorId();
// 步骤5:构建并保存机关审批记录
SSDKSQSP audit = new SSDKSQSP();
audit.setBh(UuidUtil.getOriginalUUID());
audit.setSsdksqbh(dto.getSsdksqbh());
audit.setJgbh(dto.getJgbh());
audit.setSprbh(dto.getSprbh());
audit.setJgbh(jgbh);
audit.setSprbh(auditor);
audit.setSpsj(LocalDateTime.now());
audit.setSpzt(dto.getSpzt());
audit.setFhyj(dto.getFhyj());
@@ -1325,19 +1450,12 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic
audit.setUpdateTime(LocalDateTime.now());
ssdksqspMapper.insert(audit);
// 步骤5:根据审批结果执行不同的后续处理
if (dto.getSpzt() == 3) {
// 拒绝:恢复课次原数据,标记申请最终结束
restoreLessonSchedule(apply);
} else if (dto.getSpzt() == 2) {
// 发回:发回教研室或教员,课次保留调整后数据
} else {
// 同意:课次数据已由申请时更新,无需重复处理
if (dto.getSpzt() == 1) {
applyApprovedSchedule(apply);
}
// 步骤6:记录机关审批操作日志
recordOperationLog(apply.getBh(), "机关审批", "机关审批调课申请:" + apply.getBh()
+ ",机关编号:" + dto.getJgbh()
+ ",机关编号:" + jgbh
+ ",审批结果:" + dto.getSpzt() + ",审批意见:" + dto.getFhyj());
}
@@ -1412,44 +1530,34 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic
@Override
@Transactional(rollbackFor = Exception.class)
public void cancelScheduleAdjust(String ssdksqbh, String sqjybh) {
// 步骤1:校验参数
if (jwglRoleHelper.isDepartmentPersonnel() || jwglRoleHelper.isResearchOffice()) {
throw new BusinessException(BasicManagementConstants.FORBIDDEN, "仅申请教员本人可撤销调课申请");
}
if (ssdksqbh == null || ssdksqbh.trim().isEmpty()) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "调课申请编号不能为空");
}
if (sqjybh == null || sqjybh.trim().isEmpty()) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "申请教员编号不能为空");
String teacherId = jwglRoleHelper.requireTeacherId();
if (sqjybh != null && !sqjybh.trim().isEmpty() && !teacherId.equals(sqjybh.trim())) {
throw new BusinessException(BasicManagementConstants.FORBIDDEN, "仅申请教员本人可撤销该调课申请");
}
sqjybh = teacherId;
// 步骤2:查询调课申请实体
SSDKSQ apply = ssdksqMapper.selectById(ssdksqbh);
if (apply == null) {
throw new BusinessException(BasicManagementConstants.NOT_FOUND, "调课申请不存在");
}
// 步骤3:校验是否已删除,避免重复撤销
if (apply.getSczt() != null && apply.getSczt() == 1) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "该调课申请已撤销,无需重复撤销");
}
// 步骤4:校验权限,仅申请教员本人可撤销
if (!sqjybh.equals(apply.getSqjybh())) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "仅申请教员本人可撤销该调课申请");
throw new BusinessException(BasicManagementConstants.FORBIDDEN, "仅申请教员本人可撤销该调课申请");
}
// 步骤5:校验审批状态,已审批通过的申请不允许撤销
if (apply.getJyspzzt() != null && apply.getJyspzzt() == 1) {
throw new BusinessException(BasicManagementConstants.BAD_REQUEST, "该调课申请教研室已同意,不允许撤销");
}
// 步骤6:软删除调课申请主表
apply.setSczt(1); // 删除状态:已删除
apply.setUpdateTime(LocalDateTime.now()); // 更新时间
apply.setSczt(1);
ssdksqMapper.updateById(apply);
// 步骤7:恢复课次原数据
restoreLessonSchedule(apply);
// 步骤8:记录撤销操作日志
recordOperationLog(apply.getBh(), "撤销", "撤销调课申请:" + apply.getBh());
}
@@ -1462,6 +1570,63 @@ public class CourseRunningImportServiceImpl implements CourseRunningImportServic
* @param lesson 课次实体
* @return 原日期节次描述
*/
/**
* 机关终审同意后,将调整结果写回学员队任务表、课程表,以及可选的实施课次。
*/
private void applyApprovedSchedule(SSDKSQ apply) {
if (apply == null || StringUtils.isEmpty(apply.getSskcbbh())) {
return;
}
SSKCB lesson = sskcbMapper.selectById(apply.getSskcbbh());
if (lesson != null) {
lesson.setRq(apply.getRq());
lesson.setJc(apply.getJc());
lesson.setBdsj(LocalDateTime.now());
if (StringUtils.isNotEmpty(apply.getJxnr())) {
lesson.setJxnr(apply.getJxnr());
}
if (StringUtils.isNotEmpty(apply.getJxyd())) {
lesson.setJxyd(apply.getJxyd());
}
if (StringUtils.isNotEmpty(apply.getJxff())) {
lesson.setJxff(apply.getJxff());
}
sskcbMapper.updateById(lesson);
}
String newRoom = apply.getXjsbh();
if (StringUtils.isEmpty(newRoom)) {
List<AdjustRoomVO> applyRooms = dksqjsMapper.selectByDksqbh(apply.getBh());
if (applyRooms != null && !applyRooms.isEmpty()) {
newRoom = applyRooms.get(0).getJsbh();
}
}
XYDRWB task = StringUtils.isNotEmpty(apply.getXydrwbh())
? studentTeamTaskMapper.selectById(apply.getXydrwbh())
: findRelatedTeamTask(null, lesson);
if (task != null && StringUtils.isNotEmpty(newRoom)) {
task.setJsbh(newRoom);
studentTeamTaskMapper.updateById(task);
}
KCB slot = StringUtils.isNotEmpty(apply.getKcbbh())
? kcbMapper.selectById(apply.getKcbbh())
: null;
if (slot != null) {
if (StringUtils.isNotEmpty(newRoom)) {
slot.setJsbh(newRoom);
}
if (apply.getRq() != null) {
int packed = apply.getRq().getYear() * 10000
+ apply.getRq().getMonthValue() * 100
+ apply.getRq().getDayOfMonth();
slot.setApsj(packed);
}
kcbMapper.updateById(slot);
}
}
private String buildOriginalScheduleDesc(SSKCB lesson) {
if (lesson == null) {
return "";
@@ -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);
}
}
}
@@ -1,13 +1,30 @@
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.mapper.KCBMapper;
import com.roomroot.jwgl.mapper.KcbTimetableMapper;
import com.roomroot.jwgl.mapper.SystemInitializationMapper;
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.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 jakarta.annotation.Resource;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
/**
@@ -16,9 +33,20 @@ import java.util.List;
@Service
public class KCBServiceImpl implements KCBService {
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@Resource
private KCBMapper kcbMapper;
@Resource
private KcbTimetableMapper kcbTimetableMapper;
@Resource
private SystemInitializationMapper systemInitializationMapper;
@Resource
private JwglRoleHelper jwglRoleHelper;
@Override
public void add(KCB kcb) {
if (kcb.getCjsj() == null) {
@@ -56,4 +84,108 @@ public class KCBServiceImpl implements KCBService {
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){
LocalDateTime kxrq = xq.getKxrq();
LocalDateTime jsrq = xq.getJsrq();
String startDate = localDateTimeToDateStr(kxrq);
String endDate = localDateTimeToDateStr(jsrq);
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;
}
}
@@ -9,9 +9,11 @@ import com.roomroot.common.exception.ServiceException;
import com.roomroot.common.utils.SecurityUtils;
import com.roomroot.common.utils.StringUtils;
import com.roomroot.jwgl.entity.JYB;
import com.roomroot.jwgl.entity.XYDQB;
import com.roomroot.jwgl.entity.XYXX;
import com.roomroot.jwgl.mapper.JYBMapper;
import com.roomroot.jwgl.mapper.JYSDLBMapper;
import com.roomroot.jwgl.mapper.XYDQBMapper;
import com.roomroot.jwgl.mapper.XYXXMapper;
import org.springframework.stereotype.Component;
@@ -40,6 +42,9 @@ public class JwglRoleHelper {
@Resource
private JYSDLBMapper jysdlbMapper;
@Resource
private XYDQBMapper xydqbMapper;
public boolean isAdmin() {
try {
if (SecurityUtils.isAdmin()) {
@@ -112,6 +117,34 @@ public class JwglRoleHelper {
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() {
try {
String username = SecurityUtils.getUsername();
@@ -24,8 +24,29 @@ public class ScheduleAdjustApplyVO {
*/
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;
@@ -26,6 +26,27 @@ public class ScheduleAdjustDetailVO {
*/
private String sskcbbh;
/** 学员队任务表编号 */
private String xydrwbh;
/** 课编号 */
private String kbh;
/** 课程表编号 */
private String kcbbh;
/** 学员队名称 */
private String xydmc;
/** 教研室代号 */
private String jysdh;
/** 原教室编号 */
private String yjsbh;
/** 拟调整教室编号 */
private String xjsbh;
/**
* 课程名称。
*/
@@ -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;
}