Ver Fonte

课程数据同步

dzx há 4 meses atrás
pai
commit
d1d6983787

+ 1 - 1
src/main/java/com/xjrsoft/module/hikvision/util/OutInRecordUtil.java

@@ -218,7 +218,7 @@ public class OutInRecordUtil {
                 LocalDateTime recordTimeDate = LocalDateTime.parse(recordTimeStr, formatter);
                 String attendanceStatus = discernStudentStatus(recordTimeDate, status, personId);
                 studentInsertRecord(personId, faceImportMapper.GetTeacherIdByPersonId(personId),
-                        faceImportMapper.GetClassIdByPersonId(personId), ApiUtil.GetRedirectURL(uri), recordTimeStr, status, eventId, attendanceStatus);
+                    faceImportMapper.GetClassIdByPersonId(personId), ApiUtil.GetRedirectURL(uri), recordTimeStr, status, eventId, attendanceStatus);
             }
         }
     }

+ 519 - 49
src/main/java/com/xjrsoft/module/job/JianyuekbScheduleTask.java

@@ -1,26 +1,69 @@
 package com.xjrsoft.module.job;
 
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.hutool.extra.spring.SpringUtil;
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.fasterxml.jackson.core.type.TypeReference;
+import com.github.yulichang.wrapper.MPJLambdaWrapper;
 import com.google.gson.JsonArray;
+import com.xjrsoft.common.enums.CourseAdjustTypeEnum;
+import com.xjrsoft.common.enums.EnabledMark;
+import com.xjrsoft.common.enums.WorkflowApproveType;
+import com.xjrsoft.common.enums.WorkflowMultiInstanceType;
 import com.xjrsoft.common.mybatis.SqlRunnerAdapter;
 import com.xjrsoft.common.utils.RedisUtil;
+import com.xjrsoft.common.utils.VoToColumnUtil;
+import com.xjrsoft.config.CommonPropertiesConfig;
 import com.xjrsoft.module.base.entity.BaseClass;
 import com.xjrsoft.module.base.service.IBaseClassService;
+import com.xjrsoft.module.courseTable.entity.CourseTable;
+import com.xjrsoft.module.courseTable.service.ICourseTableService;
+import com.xjrsoft.module.organization.dto.WeChatSendMessageDto;
+import com.xjrsoft.module.organization.entity.User;
+import com.xjrsoft.module.organization.service.IUserService;
+import com.xjrsoft.module.organization.service.IWeChatService;
+import com.xjrsoft.module.schedule.entity.CourseTableBak;
+import com.xjrsoft.module.schedule.entity.WfCourseAdjust;
+import com.xjrsoft.module.schedule.service.ICourseTableBakService;
+import com.xjrsoft.module.schedule.service.IWfCourseAdjustService;
 import com.xjrsoft.module.schedule.util.DataUtil;
+import com.xjrsoft.module.teacher.entity.BaseTeacher;
+import com.xjrsoft.module.workflow.constant.WorkflowConstant;
+import com.xjrsoft.module.workflow.entity.WorkflowExtra;
+import com.xjrsoft.module.workflow.entity.WorkflowFormRelation;
+import com.xjrsoft.module.workflow.entity.WorkflowRecord;
+import com.xjrsoft.module.workflow.entity.XjrWorkflowOperateRecord;
+import com.xjrsoft.module.workflow.mapper.XjrWorkflowOperateRecordMapper;
+import com.xjrsoft.module.workflow.service.IWorkflowExecuteService;
+import com.xjrsoft.module.workflow.service.IWorkflowExtraService;
+import com.xjrsoft.module.workflow.service.IWorkflowFormRelationService;
+import com.xjrsoft.module.workflow.service.IWorkflowRecordService;
 import lombok.extern.slf4j.Slf4j;
+import me.zhyd.oauth.log.Log;
+import org.camunda.bpm.engine.RuntimeService;
+import org.camunda.bpm.engine.TaskService;
+import org.camunda.bpm.engine.history.HistoricProcessInstance;
+import org.camunda.bpm.engine.impl.persistence.entity.TaskEntity;
+import org.camunda.bpm.engine.runtime.ActivityInstance;
+import org.camunda.bpm.engine.runtime.ProcessInstance;
+import org.camunda.bpm.engine.task.Task;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
 import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -40,11 +83,52 @@ public class JianyuekbScheduleTask {
     @Autowired
     private RedisUtil redisUtil;
 
+    @Autowired
+    private IWfCourseAdjustService adjustService;
+
+    @Autowired
+    private ICourseTableService courseTableService;
+
+    @Autowired
+    private ICourseTableBakService courseTableBakService;
+
+    @Autowired
+    private RuntimeService runtimeService;
+
+    @Autowired
+    private IWorkflowFormRelationService formRelationService;
+
+    @Autowired
+    private TaskService taskService;
+
+    @Autowired
+    private IWorkflowExecuteService workflowExecuteService;
+
+    @Autowired
+    private IWorkflowRecordService workflowRecordService;
+
+    @Autowired
+    private XjrWorkflowOperateRecordMapper xjrWorkflowOperateRecordMapper;
+
+    private final static String wechatTemplate = "OO5Ryu9_6Hh5LQW0aKG7qu3g5uV8VxvBusq1i5UFesk";
+
+    @Autowired
+    private IWeChatService weChatService;
+
+    @Autowired
+    private CommonPropertiesConfig commonPropertiesConfig;
+
+    @Autowired
+    private IUserService userService;
+
+    @Autowired
+    private IWorkflowExtraService workflowExtraService;
+
     @Scheduled(cron = "0 */10 * * * ?")
-    public void execute() throws Exception {
+    public void execute(){
         doExecute();
     }
-    public void doExecute() throws Exception {
+    public void doExecute() {
         String active = SpringUtil.getActiveProfile();
         if(!"prod".equals(active)){
             log.info("非正式环境,无法执行获取课表数据");
@@ -56,7 +140,6 @@ public class JianyuekbScheduleTask {
             return;
         }
 
-
         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
         //查询出传入排课系统的年级和班级
         List<String> eduYearSerialNo1 = receiveMsgs.stream().map(x -> "'" + x.get("edu_year_serial_no").toString() + "'").collect(Collectors.toList());
@@ -66,7 +149,7 @@ public class JianyuekbScheduleTask {
             " and table_name = 'base_grade'" +
             " and jianyue_id in (" + eduYearSerialNo1.toString().replace("[","").replace("]","") + ")";
         List<Map<String, Object>> jianyueData = SqlRunnerAdapter.db().selectList(sql);
-        Map<String, List<Long>> gradeClassMaps = new HashMap<>();//存入对应年级的所有班级id
+        Map<String, List<BaseClass>> gradeClassMaps = new HashMap<>();//存入对应年级的所有班级id
         for (Map<String, Object> el : jianyueData) {
             String gradeId = el.get("source_id").toString();
             String orgId = null;
@@ -80,9 +163,7 @@ public class JianyuekbScheduleTask {
                             .eq(BaseClass::getGradeId, gradeId)
                             .eq(BaseClass::getOrgId, orgId)
             );
-            List<Long> classIds = classList.stream().map(BaseClass::getId).collect(Collectors.toList());
-
-            gradeClassMaps.put(el.get("jianyue_id").toString(), classIds);
+            gradeClassMaps.put(el.get("jianyue_id").toString(), classList);
         }
         LocalDate today = LocalDate.now();
 
@@ -101,56 +182,67 @@ public class JianyuekbScheduleTask {
             ongoing.add(eduYearSerialNo);
             redisUtil.set(taskKey, ongoing);
 
-            String updSql = "update course_receive_msg set is_callback = 0 where id = " + receiveMsg.get("id").toString();
-            SqlRunnerAdapter.db().update(updSql);
+            try {
+                String updSql = "update course_receive_msg set is_callback = 0 where id = " + receiveMsg.get("id").toString();
+                SqlRunnerAdapter.db().update(updSql);
 
-            JsonArray allScheduleInfo = new JsonArray();
-            String startDateStr = receiveMsg.get("start_date").toString();
-            LocalDate startDateObj = LocalDate.parse(startDateStr);
-            String endDateStr = receiveMsg.get("end_date").toString();
-            LocalDate endDateObj = LocalDate.parse(endDateStr);
-            if(today.isAfter(startDateObj)){
-                startDateStr = today.format(formatter);
-            }
-            //删除课表信息;
-            String classIds = gradeClassMaps.get(eduYearSerialNo).toString().replace("[", "").replace("]", "");
-            String delSql = "delete from course_table where schedule_date between '" + startDateStr + "'" +
-                    " and '" + endDateStr +
-                    "' and class_id in (" + classIds + ")";
-            SqlRunnerAdapter.db().delete(delSql);
-
-            long between = ChronoUnit.DAYS.between(startDateObj, endDateObj);
-            int times = Integer.parseInt(((between / 7) + 1) + "");
-
-            for (int index = 0; index < times; index ++) {
-                LocalDate statrTime = startDateObj.plusDays(index * 7L);
-                String startDate = statrTime.format(formatter);
-                LocalDate endTime = statrTime.plusDays(6L);
-                if(endTime.isAfter(endDateObj)){
-                    endTime = endDateObj;
+                JsonArray allScheduleInfo = new JsonArray();
+                String startDateStr = receiveMsg.get("start_date").toString();
+                LocalDate startDateObj = LocalDate.parse(startDateStr);
+                String endDateStr = receiveMsg.get("end_date").toString();
+                LocalDate endDateObj = LocalDate.parse(endDateStr);
+                if(today.isAfter(startDateObj)){
+                    startDateStr = today.format(formatter);
                 }
 
-                String endDate = endTime.format(formatter);
-                //获取课表并存到数据库
-                JsonArray scheduleInfo = dataUtil.getScheduleInfoByGrade(eduYearSerialNo, startDate, endDate);
-                allScheduleInfo.addAll(scheduleInfo);
-            }
+                //删除课表信息;
+                List<BaseClass> classList = gradeClassMaps.get(eduYearSerialNo);
+                List<Long> classIdList = classList.stream().map(BaseClass::getId).collect(Collectors.toList());
+                String classIds = classIdList.toString().replace("[", "").replace("]", "");
+                String delSql = "delete from course_table where schedule_date between '" + startDateStr + "'" +
+                        " and '" + endDateStr +
+                        "' and class_id in (" + classIds + ")";
+                SqlRunnerAdapter.db().delete(delSql);
 
-            updSql = "update course_receive_msg set is_callback = 1 where id = " + receiveMsg.get("id").toString();
-            SqlRunnerAdapter.db().update(updSql);
-            //作废调课和顶课
-            updSql = "UPDATE wf_course_adjust SET enabled_mark = 0 WHERE adjust_type BETWEEN '" + startDateStr + "'" +
-                    " and '" + endDateStr + "' and class_id in (" + classIds + ")";
-            SqlRunnerAdapter.db().update(updSql);
+                startDateStr = receiveMsg.get("start_date").toString();
+                List<String> processIds = suspendedCourseAdjust(classList, startDateStr, endDateStr);
 
-            ongoing.remove(eduYearSerialNo);
-            redisUtil.set(taskKey, ongoing);
-            doExecute(allScheduleInfo, dataMap, dataUtil);
-        }
+                long between = ChronoUnit.DAYS.between(startDateObj, endDateObj);
+                int times = Integer.parseInt(((between / 7) + 1) + "");
+
+                for (int index = 0; index < times; index ++) {
+                    LocalDate statrTime = startDateObj.plusDays(index * 7L);
+                    String startDate = statrTime.format(formatter);
+                    LocalDate endTime = statrTime.plusDays(6L);
+                    if(endTime.isAfter(endDateObj)){
+                        endTime = endDateObj;
+                    }
+
+                    String endDate = endTime.format(formatter);
+                    //获取课表并存到数据库
+                    JsonArray scheduleInfo = dataUtil.getScheduleInfoByGrade(eduYearSerialNo, startDate, endDate);
+                    allScheduleInfo.addAll(scheduleInfo);
+                }
+
+                updSql = "update course_receive_msg set is_callback = 1 where id = " + receiveMsg.get("id").toString();
+                SqlRunnerAdapter.db().update(updSql);
 
+                ongoing.remove(eduYearSerialNo);
+                redisUtil.set(taskKey, ongoing);
+                insertCourse(allScheduleInfo, dataMap, dataUtil);
+                //恢复挂起的流程
+                restoreCourseAdjust(processIds);
+                //处理该日期内已经审批通过的调课和顶课申请
+                handleCourseAdjust(classList, startDateStr, endDateStr);
+            }catch (Exception e){
+                Log.error(e.getMessage(), e);
+                ongoing.remove(eduYearSerialNo);
+                redisUtil.set(taskKey, ongoing);
+            }
+        }
     }
 
-    void doExecute(JsonArray scheduleInfo, Map<String, Map<String, String>> dataMap, DataUtil dataUtil){
+    void insertCourse(JsonArray scheduleInfo, Map<String, Map<String, String>> dataMap, DataUtil dataUtil){
         //获取年级
         String tableName = "base_grade";
 //            Map<String, Long> gradeMap = dataMap.get(tableName);
@@ -198,4 +290,382 @@ public class JianyuekbScheduleTask {
         return dataMap;
     }
 
+
+    /**
+     * 课程数据同步完之后,处理调课和顶课申请
+     * 1、课程数据入库后,将新课表日期之后的的调课顶课数据再次更新到课表中
+     * 2、筛选出课表变化了的调课和顶课申请,通知发起人
+     */
+    public void handleCourseAdjust(List<BaseClass> classList, String startDate, String endDate){
+        List<Long> classIds = classList.stream().map(BaseClass::getId).collect(Collectors.toList());
+        Map<Long, String> classMap = classList.stream().collect(Collectors.toMap(BaseClass::getId, BaseClass::getName));
+        List<CourseTableBak> bakList = courseTableBakService.list(
+                new QueryWrapper<CourseTableBak>().lambda()
+                        .in(CourseTableBak::getClassId, classIds)
+                        .between(CourseTableBak::getScheduleDate, startDate, endDate)
+        );
+        Set<Long> courseAdjustIdSet = bakList.stream().map(CourseTableBak::getWfCourseAdjustId).collect(Collectors.toSet());
+        List<Long> courseAdjustIds = new ArrayList<>(courseAdjustIdSet);
+        List<WfCourseAdjust> list = adjustService.list(
+                new MPJLambdaWrapper<WfCourseAdjust>()
+                        .select(WfCourseAdjust::getId)
+                        .select(WfCourseAdjust.class, x -> VoToColumnUtil.fieldsToColumns(WfCourseAdjust.class).contains(x.getProperty()))
+                        .innerJoin(WorkflowFormRelation.class, WorkflowFormRelation::getFormKeyValue, WfCourseAdjust::getId)
+                        .in(!courseAdjustIds.isEmpty(), WfCourseAdjust::getId, courseAdjustIds)
+                        .between(WfCourseAdjust::getAdjustDate, startDate, endDate)
+                        .eq(WorkflowFormRelation::getCurrentState, HistoricProcessInstance.STATE_COMPLETED)
+        );
+        List<WfCourseAdjust> cancelList = new ArrayList<>();//需要作废的申请
+
+        List<Long> userIds = list.stream().map(WfCourseAdjust::getUserId).collect(Collectors.toList());
+        List<User> userList = userService.list(
+                new MPJLambdaWrapper<User>()
+                        .select(User::getId)
+                        .select(User.class, x -> VoToColumnUtil.fieldsToColumns(User.class).contains(x.getProperty()))
+                        .innerJoin(BaseTeacher.class, BaseTeacher::getUserId, User::getId)
+                        .in(!userIds.isEmpty(), User::getId, userIds)
+        );
+        Map<Long, String> userOpenIdMap = new HashMap<>();
+        for (User user : userList) {
+            userOpenIdMap.put(user.getId(), user.getOpenId());
+        }
+
+        for (WfCourseAdjust courseAdjust : list) {
+            List<CourseTable> courseList = courseTableService.list(
+                    new MPJLambdaWrapper<CourseTable>()
+                            .select(CourseTable::getId)
+                            .select(CourseTable.class, x -> VoToColumnUtil.fieldsToColumns(CourseTable.class).contains(x.getProperty()))
+                            .innerJoin(CourseTableBak.class, CourseTableBak::getKeyInfo, CourseTable::getKeyInfo)
+                            .eq(CourseTableBak::getWfCourseAdjustId, courseAdjust.getId())
+            );
+
+            String[] courseIds = courseAdjust.getCourseId().split(",");
+            String[] exchangeCourseIds = null;
+            int courseCount = courseIds.length;
+            if (CourseAdjustTypeEnum.courseExchange.getCode().equals(courseAdjust.getAdjustType())) {
+                exchangeCourseIds = courseAdjust.getExchangeCourseId().split(",");
+                courseCount += exchangeCourseIds.length;
+            }
+            if(courseList.size() != courseCount){
+                //表明课程变化了,需要重新申请,需要将原来的申请作废并进行微信消息通知
+                courseAdjust.setEnabledMark(EnabledMark.DISABLED.getCode());
+                courseAdjust.setCancelReason("由于课表更新,当前调/顶课课程发生变化,该调顶课已失效,请重新发起调顶课");
+                cancelList.add(courseAdjust);
+                List<String> thing16Str = new ArrayList<>();
+                List<String> thing2Str = new ArrayList<>();
+                for (CourseTableBak tableBak : bakList) {
+                    if(!tableBak.getWfCourseAdjustId().equals(courseAdjust.getId())){
+                        continue;
+                    }
+
+                    thing16Str.add(classMap.get(tableBak.getId()));
+                    thing2Str.add(tableBak.getClassName());
+                }
+
+                WeChatSendMessageDto weChatSendMessageDto = new WeChatSendMessageDto();
+                weChatSendMessageDto.setTemplateId(wechatTemplate);
+                JSONObject paramJson = new JSONObject();
+
+                JSONObject thing16 = new JSONObject();
+                thing16.put("value", thing16Str.toString().replace(" ", "").replace("[", "").replace("]", ""));
+                paramJson.put("thing16", thing16);
+
+                JSONObject thing2 = new JSONObject();
+                thing2.put("value", thing2Str.toString().replace(" ", "").replace("[", "").replace("]", ""));
+                paramJson.put("thing2", thing2);
+
+                String const12Str = "";
+                if (CourseAdjustTypeEnum.courseExchange.getCode().equals(courseAdjust.getAdjustType())) {
+                    const12Str = "调课失败";
+                } else if (CourseAdjustTypeEnum.courseSubstitute.getCode().equals(courseAdjust.getAdjustType())) {
+                    const12Str = "顶课失败";
+                }
+
+                JSONObject const12 = new JSONObject();
+                const12.put("value", const12Str);
+                paramJson.put("const23", const12);
+
+                //迟到人数或者缺勤人数
+                JSONObject thing5 = new JSONObject();
+                thing5.put("value", "由于课表更新,该调顶课已失效");
+                paramJson.put("thing5", thing5);
+
+                String url = StrUtil.format(
+                        "{}/xjrsoft/pages/workflow/look?processId={}&type=my",
+                        commonPropertiesConfig.getDomainApp(),
+                        "relation.getProcessId()"
+                );
+                weChatSendMessageDto.setContent(paramJson);
+                weChatSendMessageDto.setUrl(url);
+                weChatSendMessageDto.setMsgId(IdUtil.getSnowflakeNextId() + "");
+                String openId = userOpenIdMap.get(courseAdjust.getUserId());
+                if(openId != null && !"".equals(openId)){
+                    weChatSendMessageDto.setUserId(openId);
+                    weChatService.sendTemplateMessage(weChatSendMessageDto);
+                }
+                continue;
+            }
+            courseTableService.adjustCourse(courseAdjust);
+        }
+        if(!cancelList.isEmpty()){
+            adjustService.updateBatchById(cancelList);
+        }
+
+        List<WorkflowFormRelation> activeList = formRelationService.list(
+                new MPJLambdaWrapper<WorkflowFormRelation>()
+                        .select(WorkflowFormRelation::getId)
+                        .select(WorkflowFormRelation.class, x -> VoToColumnUtil.fieldsToColumns(WorkflowFormRelation.class).contains(x.getProperty()))
+                        .innerJoin(WfCourseAdjust.class, WfCourseAdjust::getId, WorkflowFormRelation::getFormKeyValue)
+                        .in(!courseAdjustIds.isEmpty(), WfCourseAdjust::getId, courseAdjustIds)
+                        .between(WfCourseAdjust::getAdjustDate, startDate, endDate)
+                        .eq(WorkflowFormRelation::getCurrentState, HistoricProcessInstance.STATE_ACTIVE)
+        );
+        for (WorkflowFormRelation relation : activeList) {
+            List<CourseTable> courseList = courseTableService.list(
+                    new MPJLambdaWrapper<CourseTable>()
+                            .select(CourseTable::getId)
+                            .select(CourseTable.class, x -> VoToColumnUtil.fieldsToColumns(CourseTable.class).contains(x.getProperty()))
+                            .innerJoin(CourseTableBak.class, CourseTableBak::getKeyInfo, CourseTable::getKeyInfo)
+                            .eq(CourseTableBak::getWfCourseAdjustId, relation.getFormKeyValue())
+            );
+            List<CourseTableBak> thisbakList = courseTableBakService.list(
+                    new QueryWrapper<CourseTableBak>().lambda()
+                            .eq(CourseTableBak::getWfCourseAdjustId, relation.getFormKeyValue())
+            );
+            if(courseList.size() == thisbakList.size()){
+                continue;
+            }
+            WfCourseAdjust courseAdjust = adjustService.getById(relation.getFormKeyValue());
+            //表明课程变化了,进行内部终止
+            List<String> thing16Str = new ArrayList<>();
+            List<String> thing2Str = new ArrayList<>();
+            for (CourseTableBak tableBak : bakList) {
+                if(!tableBak.getWfCourseAdjustId().equals(courseAdjust.getId())){
+                    continue;
+                }
+
+                thing16Str.add(classMap.get(tableBak.getId()));
+                thing2Str.add(tableBak.getClassName());
+            }
+
+            WeChatSendMessageDto weChatSendMessageDto = new WeChatSendMessageDto();
+            weChatSendMessageDto.setTemplateId(wechatTemplate);
+            JSONObject paramJson = new JSONObject();
+
+            JSONObject thing16 = new JSONObject();
+            thing16.put("value", thing16Str.toString().replace(" ", "").replace("[", "").replace("]", ""));
+            paramJson.put("thing16", classMap);
+
+            JSONObject thing2 = new JSONObject();
+            thing2.put("value", thing2Str.toString().replace(" ", "").replace("[", "").replace("]", ""));
+            paramJson.put("thing2", thing2);
+
+            String const12Str = "";
+            if (CourseAdjustTypeEnum.courseExchange.getCode().equals(courseAdjust.getAdjustType())) {
+                const12Str = "调课失败";
+            } else if (CourseAdjustTypeEnum.courseSubstitute.getCode().equals(courseAdjust.getAdjustType())) {
+                const12Str = "顶课失败";
+            }
+
+            JSONObject const12 = new JSONObject();
+            const12.put("value", const12Str);
+            paramJson.put("const23", const12);
+
+            //迟到人数或者缺勤人数
+            JSONObject thing5 = new JSONObject();
+            thing5.put("value", "由于课表更新,该调顶课已失效");
+            paramJson.put("thing5", thing5);
+
+            weChatSendMessageDto.setContent(paramJson);
+            List<WorkflowExtra> extraList = workflowExtraService.list(
+                    new QueryWrapper<WorkflowExtra>().lambda()
+                            .eq(WorkflowExtra::getProcessId, relation.getProcessId())
+                            .orderByDesc(WorkflowExtra::getStartTime)
+            );
+
+            if(!extraList.isEmpty()){
+                String url = StrUtil.format(
+                        "{}/xjrsoft/pages/workflow/approval?taskId={}&processId={}&type=todo",
+                        commonPropertiesConfig.getDomainApp(),
+                        extraList.get(0).getTaskId(),
+                        relation.getProcessId()
+                );
+                weChatSendMessageDto.setUrl(url);
+
+                weChatSendMessageDto.setMsgId(IdUtil.getSnowflakeNextId() + "");
+                String openId = userOpenIdMap.get(courseAdjust.getExchangeTeacherId());
+                if(openId != null && !"".equals(openId)){
+                    weChatSendMessageDto.setUserId(openId);
+                    weChatService.sendTemplateMessage(weChatSendMessageDto);
+                }
+            }
+            String url = StrUtil.format(
+                    "{}/xjrsoft/pages/workflow/look?processId={}&type=my",
+                    commonPropertiesConfig.getDomainApp(),
+                    relation.getProcessId()
+            );
+            weChatSendMessageDto.setUrl(url);
+            weChatSendMessageDto.setMsgId(IdUtil.getSnowflakeNextId() + "");
+            String openId = userOpenIdMap.get(courseAdjust.getUserId());
+            if(openId != null && !"".equals(openId)){
+                weChatSendMessageDto.setUserId(openId);
+                weChatService.sendTemplateMessage(weChatSendMessageDto);
+            }
+            String processId = relation.getProcessId();
+
+            List<Task> taskList = taskService.createTaskQuery().processInstanceId(processId).list();
+            Task task = taskList.stream().filter(x -> x.getProcessInstanceId().equals(processId)).findFirst().orElse(new TaskEntity());
+            Long schemaId = Convert.toLong(taskService.getVariable(task.getId(), WorkflowConstant.PROCESS_SCHEMA_ID_KEY));
+            //获取到当前活动的实例
+            ActivityInstance activityInstance = runtimeService.getActivityInstance(task.getProcessInstanceId());
+
+            String message = "    因课表更新之后当前课程已发生变更,该流程已内部终止,如还需调/顶课,请重新发起调/顶课流程";
+            //先停止当前活动示例  然后  关闭流程
+            runtimeService.createProcessInstanceModification(task.getProcessInstanceId())
+                    .cancelActivityInstance(activityInstance.getId())
+                    .cancelAllForActivity(activityInstance.getId())
+                    .setAnnotation("因课表更新之后当前课程已发生变更,该流程已内部终止")
+                    .execute();
+
+            //新增流程发起流程记录
+            WorkflowRecord record = new WorkflowRecord();
+            record.setNodeId(task.getId());
+            record.setNodeName(task.getName());
+            record.setNodeType(WorkflowConstant.USER_TASK_TYPE_NAME);
+            record.setProcessId(task.getProcessInstanceId());
+            record.setSchemaId(schemaId);
+            record.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
+            record.setRecordTime(LocalDateTime.now());
+            record.setWorkflowApproveType(WorkflowApproveType.FINISH.getCode());
+
+            record.setMessage(message);
+
+            workflowRecordService.save(record);
+
+            //新增流程发起流程记录
+            XjrWorkflowOperateRecord xjrWorkflowOperateRecord = new XjrWorkflowOperateRecord();
+            xjrWorkflowOperateRecord.setNodeId(task.getId());
+            xjrWorkflowOperateRecord.setNodeName(task.getName());
+            xjrWorkflowOperateRecord.setNodeType(WorkflowConstant.USER_TASK_TYPE_NAME);
+            xjrWorkflowOperateRecord.setProcessId(task.getProcessInstanceId());
+            xjrWorkflowOperateRecord.setSchemaId(schemaId);
+            xjrWorkflowOperateRecord.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
+            xjrWorkflowOperateRecord.setRecordTime(LocalDateTime.now());
+            xjrWorkflowOperateRecord.setUsageScenario(1);
+
+
+            xjrWorkflowOperateRecord.setOperateInfo(message);
+            xjrWorkflowOperateRecordMapper.insert(xjrWorkflowOperateRecord);
+
+
+            Optional<HistoricProcessInstance> historicProcessInstance = workflowExecuteService.getHistoricProcessInstance(processId);
+            historicProcessInstance.ifPresent(item -> {
+                formRelationService.updateCurrentState(new WorkflowFormRelation() {{
+                    setProcessId(processId);
+                    setCurrentState(item.getState());
+                    setStartTime(item.getStartTime());
+                    setEndTime(item.getEndTime());
+                }});
+            });
+        }
+    }
+
+
+    /**
+     * 挂起未通过的流程
+     */
+    List<String> suspendedCourseAdjust(List<BaseClass> classList, String startDate, String endDate) {
+        List<Long> classIds = classList.stream().map(BaseClass::getId).collect(Collectors.toList());
+        List<CourseTableBak> bakList = courseTableBakService.list(
+                new QueryWrapper<CourseTableBak>().lambda()
+                        .in(CourseTableBak::getClassId, classIds)
+                        .between(CourseTableBak::getScheduleDate, startDate, endDate)
+        );
+        Set<Long> courseAdjustIdSet = bakList.stream().map(CourseTableBak::getWfCourseAdjustId).collect(Collectors.toSet());
+
+        List<Long> courseAdjustIds = new ArrayList<>(courseAdjustIdSet);
+        List<WorkflowFormRelation> list = formRelationService.list(
+                new MPJLambdaWrapper<WorkflowFormRelation>()
+                        .select(WorkflowFormRelation::getId)
+                        .select(WorkflowFormRelation.class, x -> VoToColumnUtil.fieldsToColumns(WorkflowFormRelation.class).contains(x.getProperty()))
+                        .innerJoin(WfCourseAdjust.class, WfCourseAdjust::getId, WorkflowFormRelation::getFormKeyValue)
+                        .between(WfCourseAdjust::getAdjustDate, startDate, endDate)
+                        .in(!courseAdjustIds.isEmpty(), WfCourseAdjust::getId, courseAdjustIds)
+                        .eq(WorkflowFormRelation::getCurrentState, HistoricProcessInstance.STATE_ACTIVE)
+        );
+        List<String> processIds = new ArrayList<>();
+        for (WorkflowFormRelation relation : list) {
+            String processId = relation.getProcessId();
+            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
+            List<Task> taskList = taskService.createTaskQuery().processInstanceId(processId).list();
+            Task task = taskList.stream().filter(x -> x.getProcessInstanceId().equals(processId)).findFirst().orElse(new TaskEntity());
+            Long schemaId = Convert.toLong(taskService.getVariable(task.getId(), WorkflowConstant.PROCESS_SCHEMA_ID_KEY));
+            //新增流程发起流程记录
+            WorkflowRecord record = new WorkflowRecord();
+            record.setNodeId(task.getId());
+            record.setNodeName(task.getName());
+            record.setNodeType(WorkflowConstant.USER_TASK_TYPE_NAME);
+            record.setProcessId(task.getProcessInstanceId());
+            record.setSchemaId(schemaId);
+            record.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
+            record.setRecordTime(LocalDateTime.now());
+
+            if (processInstance.isSuspended()) {
+                runtimeService.activateProcessInstanceById(relation.getProcessId());
+                //[操作人名称] 将流程恢复
+                record.setMessage("课表同步成功,流程恢复");
+            } else {
+                runtimeService.suspendProcessInstanceById(relation.getProcessId());
+                //[操作人名称] 将流程挂起
+                record.setMessage("课表更新中,暂停流程审核,请等待课表更新完成之后进行审核");
+            }
+            workflowRecordService.save(record);
+            Optional<HistoricProcessInstance> historicProcessInstance = workflowExecuteService.getHistoricProcessInstance(processId);
+            historicProcessInstance.ifPresent(item -> {
+                formRelationService.updateCurrentState(new WorkflowFormRelation() {{
+                    setProcessId(processId);
+                    setCurrentState(item.getState());
+                    setStartTime(item.getStartTime());
+                    setEndTime(item.getEndTime());
+                }});
+            });
+
+            processIds.add(relation.getProcessId());
+        }
+        return processIds;
+    }
+
+    /**
+     * 将挂起的流程恢复
+     */
+    void restoreCourseAdjust(List<String> processIds){
+        for (String processId : processIds) {
+            List<Task> taskList = taskService.createTaskQuery().processInstanceId(processId).list();
+            Task task = taskList.stream().filter(x -> x.getProcessInstanceId().equals(processId)).findFirst().orElse(new TaskEntity());
+            Long schemaId = Convert.toLong(taskService.getVariable(task.getId(), WorkflowConstant.PROCESS_SCHEMA_ID_KEY));
+            //新增流程发起流程记录
+            WorkflowRecord record = new WorkflowRecord();
+            record.setNodeId(task.getId());
+            record.setNodeName(task.getName());
+            record.setNodeType(WorkflowConstant.USER_TASK_TYPE_NAME);
+            record.setProcessId(task.getProcessInstanceId());
+            record.setSchemaId(schemaId);
+            record.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
+            record.setRecordTime(LocalDateTime.now());
+            record.setMessage("课表同步成功,流程恢复");
+            workflowRecordService.save(record);
+            runtimeService.activateProcessInstanceById(processId);
+
+            Optional<HistoricProcessInstance> historicProcessInstance = workflowExecuteService.getHistoricProcessInstance(processId);
+            historicProcessInstance.ifPresent(item -> {
+                formRelationService.updateCurrentState(new WorkflowFormRelation() {{
+                    setProcessId(processId);
+                    setCurrentState(item.getState());
+                    setStartTime(item.getStartTime());
+                    setEndTime(item.getEndTime());
+                }});
+            });
+        }
+    }
+
 }

+ 0 - 2
src/main/resources/mapper/personnel/BasePersonnelLabourCapitalMapper.xml

@@ -26,7 +26,6 @@
     </select>
 
     <select id="getYearPage" parameterType="com.xjrsoft.module.personnel.dto.BasePersonnelLabourCapitalYearPageDto" resultType="com.xjrsoft.module.personnel.vo.BasePersonnelLabourCapitalYearPageVo">
-        <if test="dto.year != null and dto.project != null">
         SELECT
             t.id,
             t.name,
@@ -46,7 +45,6 @@
             t.delete_mark = 0
         and YEAR(t1.date_of_issue) = #{dto.year}
         and t1.name = #{dto.project}
-        </if>
     </select>
 </mapper>
         <!--    SELECT-->

+ 520 - 52
src/test/java/com/xjrsoft/module/job/JianyuekbScheduleTaskTest2.java

@@ -1,15 +1,54 @@
 package com.xjrsoft.module.job;
 
-import cn.hutool.extra.spring.SpringUtil;
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.fasterxml.jackson.core.type.TypeReference;
+import com.github.yulichang.wrapper.MPJLambdaWrapper;
 import com.google.gson.JsonArray;
 import com.xjrsoft.XjrSoftApplication;
+import com.xjrsoft.common.enums.CourseAdjustTypeEnum;
+import com.xjrsoft.common.enums.EnabledMark;
+import com.xjrsoft.common.enums.WorkflowApproveType;
+import com.xjrsoft.common.enums.WorkflowMultiInstanceType;
 import com.xjrsoft.common.mybatis.SqlRunnerAdapter;
 import com.xjrsoft.common.utils.RedisUtil;
+import com.xjrsoft.common.utils.VoToColumnUtil;
+import com.xjrsoft.config.CommonPropertiesConfig;
 import com.xjrsoft.module.base.entity.BaseClass;
 import com.xjrsoft.module.base.service.IBaseClassService;
+import com.xjrsoft.module.courseTable.entity.CourseTable;
+import com.xjrsoft.module.courseTable.service.ICourseTableService;
+import com.xjrsoft.module.organization.dto.WeChatSendMessageDto;
+import com.xjrsoft.module.organization.entity.User;
+import com.xjrsoft.module.organization.service.IUserService;
+import com.xjrsoft.module.organization.service.IWeChatService;
+import com.xjrsoft.module.schedule.entity.CourseTableBak;
+import com.xjrsoft.module.schedule.entity.WfCourseAdjust;
+import com.xjrsoft.module.schedule.service.ICourseTableBakService;
+import com.xjrsoft.module.schedule.service.IWfCourseAdjustService;
 import com.xjrsoft.module.schedule.util.DataUtil;
+import com.xjrsoft.module.teacher.entity.BaseTeacher;
+import com.xjrsoft.module.workflow.constant.WorkflowConstant;
+import com.xjrsoft.module.workflow.entity.WorkflowExtra;
+import com.xjrsoft.module.workflow.entity.WorkflowFormRelation;
+import com.xjrsoft.module.workflow.entity.WorkflowRecord;
+import com.xjrsoft.module.workflow.entity.XjrWorkflowOperateRecord;
+import com.xjrsoft.module.workflow.mapper.XjrWorkflowOperateRecordMapper;
+import com.xjrsoft.module.workflow.service.IWorkflowExecuteService;
+import com.xjrsoft.module.workflow.service.IWorkflowExtraService;
+import com.xjrsoft.module.workflow.service.IWorkflowFormRelationService;
+import com.xjrsoft.module.workflow.service.IWorkflowRecordService;
+import me.zhyd.oauth.log.Log;
+import org.camunda.bpm.engine.RuntimeService;
+import org.camunda.bpm.engine.TaskService;
+import org.camunda.bpm.engine.history.HistoricProcessInstance;
+import org.camunda.bpm.engine.impl.persistence.entity.TaskEntity;
+import org.camunda.bpm.engine.runtime.ActivityInstance;
+import org.camunda.bpm.engine.runtime.ProcessInstance;
+import org.camunda.bpm.engine.task.Task;
 import org.junit.jupiter.api.Test;
 import org.junit.runner.RunWith;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -17,17 +56,18 @@ import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.junit4.SpringRunner;
 
 import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 
-import static org.junit.jupiter.api.Assertions.*;
-
 /**
  * @author dzx
  * @date 2024/10/15
@@ -43,12 +83,52 @@ class JianyuekbScheduleTaskTest2 {
     @Autowired
     private RedisUtil redisUtil;
 
+    @Autowired
+    private IWfCourseAdjustService adjustService;
+
+    @Autowired
+    private ICourseTableService courseTableService;
+
+    @Autowired
+    private ICourseTableBakService courseTableBakService;
+
+    @Autowired
+    private RuntimeService runtimeService;
+
+    @Autowired
+    private IWorkflowFormRelationService formRelationService;
+
+    @Autowired
+    private TaskService taskService;
+
+    @Autowired
+    private IWorkflowExecuteService workflowExecuteService;
+
+    @Autowired
+    private IWorkflowRecordService workflowRecordService;
+
+    @Autowired
+    private XjrWorkflowOperateRecordMapper xjrWorkflowOperateRecordMapper;
+
+    private final static String wechatTemplate = "OO5Ryu9_6Hh5LQW0aKG7qu3g5uV8VxvBusq1i5UFesk";
+
+    @Autowired
+    private IWeChatService weChatService;
+
+    @Autowired
+    private CommonPropertiesConfig commonPropertiesConfig;
+
+    @Autowired
+    private IUserService userService;
+
+    @Autowired
+    private IWorkflowExtraService workflowExtraService;
+
     @Test
-    public void execute() throws Exception {
+    public void execute(){
         doExecute();
     }
-    public void doExecute() throws Exception {
-        String active = SpringUtil.getActiveProfile();
+    public void doExecute() {
         String sql = "SELECT * FROM course_receive_msg WHERE delete_mark = 0 AND is_callback IS NULL";
         List<Map<String, Object>> receiveMsgs = SqlRunnerAdapter.db().selectList(sql);
         if(receiveMsgs.isEmpty()){
@@ -64,7 +144,7 @@ class JianyuekbScheduleTaskTest2 {
                 " and table_name = 'base_grade'" +
                 " and jianyue_id in (" + eduYearSerialNo1.toString().replace("[","").replace("]","") + ")";
         List<Map<String, Object>> jianyueData = SqlRunnerAdapter.db().selectList(sql);
-        Map<String, List<Long>> gradeClassMaps = new HashMap<>();//存入对应年级的所有班级id
+        Map<String, List<BaseClass>> gradeClassMaps = new HashMap<>();//存入对应年级的所有班级id
         for (Map<String, Object> el : jianyueData) {
             String gradeId = el.get("source_id").toString();
             String orgId = null;
@@ -78,9 +158,7 @@ class JianyuekbScheduleTaskTest2 {
                             .eq(BaseClass::getGradeId, gradeId)
                             .eq(BaseClass::getOrgId, orgId)
             );
-            List<Long> classIds = classList.stream().map(BaseClass::getId).collect(Collectors.toList());
-
-            gradeClassMaps.put(el.get("jianyue_id").toString(), classIds);
+            gradeClassMaps.put(el.get("jianyue_id").toString(), classList);
         }
         LocalDate today = LocalDate.now();
 
@@ -99,56 +177,68 @@ class JianyuekbScheduleTaskTest2 {
             ongoing.add(eduYearSerialNo);
             redisUtil.set(taskKey, ongoing);
 
-            String updSql = "update course_receive_msg set is_callback = 0 where id = " + receiveMsg.get("id").toString();
-            SqlRunnerAdapter.db().update(updSql);
+            try {
+                String updSql = "update course_receive_msg set is_callback = 0 where id = " + receiveMsg.get("id").toString();
+                SqlRunnerAdapter.db().update(updSql);
 
-            JsonArray allScheduleInfo = new JsonArray();
-            String startDateStr = receiveMsg.get("start_date").toString();
-            LocalDate startDateObj = LocalDate.parse(startDateStr);
-            String endDateStr = receiveMsg.get("end_date").toString();
-            LocalDate endDateObj = LocalDate.parse(endDateStr);
-            if(today.isAfter(startDateObj)){
-                startDateStr = today.format(formatter);
-            }
-            //删除课表信息;
-            String classIds = gradeClassMaps.get(eduYearSerialNo).toString().replace("[", "").replace("]", "");
-            String delSql = "delete from course_table where schedule_date between '" + startDateStr + "'" +
-                    " and '" + endDateStr +
-                    "' and class_id in (" + classIds + ")";
-            SqlRunnerAdapter.db().delete(delSql);
-
-            long between = ChronoUnit.DAYS.between(startDateObj, endDateObj);
-            int times = Integer.parseInt(((between / 7) + 1) + "");
-
-            for (int index = 0; index < times; index ++) {
-                LocalDate statrTime = startDateObj.plusDays(index * 7L);
-                String startDate = statrTime.format(formatter);
-                LocalDate endTime = statrTime.plusDays(6L);
-                if(endTime.isAfter(endDateObj)){
-                    endTime = endDateObj;
+                JsonArray allScheduleInfo = new JsonArray();
+                String startDateStr = receiveMsg.get("start_date").toString();
+                LocalDate startDateObj = LocalDate.parse(startDateStr);
+                String endDateStr = receiveMsg.get("end_date").toString();
+                LocalDate endDateObj = LocalDate.parse(endDateStr);
+                if(today.isAfter(startDateObj)){
+                    startDateStr = today.format(formatter);
                 }
 
-                String endDate = endTime.format(formatter);
-                //获取课表并存到数据库
-                JsonArray scheduleInfo = dataUtil.getScheduleInfoByGrade(eduYearSerialNo, startDate, endDate);
-                allScheduleInfo.addAll(scheduleInfo);
-            }
+                //删除课表信息;
+                List<BaseClass> classList = gradeClassMaps.get(eduYearSerialNo);
+                List<Long> classIdList = classList.stream().map(BaseClass::getId).collect(Collectors.toList());
+                String classIds = classIdList.toString().replace("[", "").replace("]", "");
+                String delSql = "delete from course_table where schedule_date between '" + startDateStr + "'" +
+                        " and '" + endDateStr +
+                        "' and class_id in (" + classIds + ")";
+                SqlRunnerAdapter.db().delete(delSql);
 
-            updSql = "update course_receive_msg set is_callback = 1 where id = " + receiveMsg.get("id").toString();
-            SqlRunnerAdapter.db().update(updSql);
-            //作废调课和顶课
-            updSql = "UPDATE wf_course_adjust SET enabled_mark = 0 WHERE adjust_type BETWEEN '" + startDateStr + "'" +
-                    " and '" + endDateStr + "' and class_id in (" + classIds + ")";
-            SqlRunnerAdapter.db().update(updSql);
 
-            ongoing.remove(eduYearSerialNo);
-            redisUtil.set(taskKey, ongoing);
-            doExecute(allScheduleInfo, dataMap, dataUtil);
-        }
+                startDateStr = receiveMsg.get("start_date").toString();
+                List<String> processIds = suspendedCourseAdjust(classList, startDateStr, endDateStr);
+
+                long between = ChronoUnit.DAYS.between(startDateObj, endDateObj);
+                int times = Integer.parseInt(((between / 7) + 1) + "");
+
+                for (int index = 0; index < times; index ++) {
+                    LocalDate statrTime = startDateObj.plusDays(index * 7L);
+                    String startDate = statrTime.format(formatter);
+                    LocalDate endTime = statrTime.plusDays(6L);
+                    if(endTime.isAfter(endDateObj)){
+                        endTime = endDateObj;
+                    }
 
+                    String endDate = endTime.format(formatter);
+                    //获取课表并存到数据库
+                    JsonArray scheduleInfo = dataUtil.getScheduleInfoByGrade(eduYearSerialNo, startDate, endDate);
+                    allScheduleInfo.addAll(scheduleInfo);
+                }
+
+                updSql = "update course_receive_msg set is_callback = 1 where id = " + receiveMsg.get("id").toString();
+                SqlRunnerAdapter.db().update(updSql);
+
+                ongoing.remove(eduYearSerialNo);
+                redisUtil.set(taskKey, ongoing);
+                insertCourse(allScheduleInfo, dataMap, dataUtil);
+                //恢复挂起的流程
+                restoreCourseAdjust(processIds);
+                //处理该日期内已经审批通过的调课和顶课申请
+                handleCourseAdjust(classList, startDateStr, endDateStr);
+            }catch (Exception e){
+                Log.error(e.getMessage(), e);
+                ongoing.remove(eduYearSerialNo);
+                redisUtil.set(taskKey, ongoing);
+            }
+        }
     }
 
-    void doExecute(JsonArray scheduleInfo, Map<String, Map<String, String>> dataMap, DataUtil dataUtil){
+    void insertCourse(JsonArray scheduleInfo, Map<String, Map<String, String>> dataMap, DataUtil dataUtil){
         //获取年级
         String tableName = "base_grade";
 //            Map<String, Long> gradeMap = dataMap.get(tableName);
@@ -195,4 +285,382 @@ class JianyuekbScheduleTaskTest2 {
         }
         return dataMap;
     }
+
+
+    /**
+     * 课程数据同步完之后,处理调课和顶课申请
+     * 1、课程数据入库后,将新课表日期之后的的调课顶课数据再次更新到课表中
+     * 2、筛选出课表变化了的调课和顶课申请,通知发起人
+     */
+    public void handleCourseAdjust(List<BaseClass> classList, String startDate, String endDate){
+        List<Long> classIds = classList.stream().map(BaseClass::getId).collect(Collectors.toList());
+        Map<Long, String> classMap = classList.stream().collect(Collectors.toMap(BaseClass::getId, BaseClass::getName));
+        List<CourseTableBak> bakList = courseTableBakService.list(
+                new QueryWrapper<CourseTableBak>().lambda()
+                        .in(CourseTableBak::getClassId, classIds)
+                        .between(CourseTableBak::getScheduleDate, startDate, endDate)
+        );
+        Set<Long> courseAdjustIdSet = bakList.stream().map(CourseTableBak::getWfCourseAdjustId).collect(Collectors.toSet());
+        List<Long> courseAdjustIds = new ArrayList<>(courseAdjustIdSet);
+        List<WfCourseAdjust> list = adjustService.list(
+                new MPJLambdaWrapper<WfCourseAdjust>()
+                        .select(WfCourseAdjust::getId)
+                        .select(WfCourseAdjust.class, x -> VoToColumnUtil.fieldsToColumns(WfCourseAdjust.class).contains(x.getProperty()))
+                        .innerJoin(WorkflowFormRelation.class, WorkflowFormRelation::getFormKeyValue, WfCourseAdjust::getId)
+                        .in(!courseAdjustIds.isEmpty(), WfCourseAdjust::getId, courseAdjustIds)
+                        .between(WfCourseAdjust::getAdjustDate, startDate, endDate)
+                        .eq(WorkflowFormRelation::getCurrentState, HistoricProcessInstance.STATE_COMPLETED)
+        );
+        List<WfCourseAdjust> cancelList = new ArrayList<>();//需要作废的申请
+
+        List<Long> userIds = list.stream().map(WfCourseAdjust::getUserId).collect(Collectors.toList());
+        List<User> userList = userService.list(
+                new MPJLambdaWrapper<User>()
+                        .select(User::getId)
+                        .select(User.class, x -> VoToColumnUtil.fieldsToColumns(User.class).contains(x.getProperty()))
+                        .innerJoin(BaseTeacher.class, BaseTeacher::getUserId, User::getId)
+                        .in(!userIds.isEmpty(), User::getId, userIds)
+        );
+        Map<Long, String> userOpenIdMap = new HashMap<>();
+        for (User user : userList) {
+            userOpenIdMap.put(user.getId(), user.getOpenId());
+        }
+
+        for (WfCourseAdjust courseAdjust : list) {
+            List<CourseTable> courseList = courseTableService.list(
+                    new MPJLambdaWrapper<CourseTable>()
+                            .select(CourseTable::getId)
+                            .select(CourseTable.class, x -> VoToColumnUtil.fieldsToColumns(CourseTable.class).contains(x.getProperty()))
+                            .innerJoin(CourseTableBak.class, CourseTableBak::getKeyInfo, CourseTable::getKeyInfo)
+                            .eq(CourseTableBak::getWfCourseAdjustId, courseAdjust.getId())
+            );
+
+            String[] courseIds = courseAdjust.getCourseId().split(",");
+            String[] exchangeCourseIds = null;
+            int courseCount = courseIds.length;
+            if (CourseAdjustTypeEnum.courseExchange.getCode().equals(courseAdjust.getAdjustType())) {
+                exchangeCourseIds = courseAdjust.getExchangeCourseId().split(",");
+                courseCount += exchangeCourseIds.length;
+            }
+            if(courseList.size() != courseCount){
+                //表明课程变化了,需要重新申请,需要将原来的申请作废并进行微信消息通知
+                courseAdjust.setEnabledMark(EnabledMark.DISABLED.getCode());
+                courseAdjust.setCancelReason("由于课表更新,当前调/顶课课程发生变化,该调顶课已失效,请重新发起调顶课");
+                cancelList.add(courseAdjust);
+                List<String> thing16Str = new ArrayList<>();
+                List<String> thing2Str = new ArrayList<>();
+                for (CourseTableBak tableBak : bakList) {
+                    if(!tableBak.getWfCourseAdjustId().equals(courseAdjust.getId())){
+                        continue;
+                    }
+
+                    thing16Str.add(classMap.get(tableBak.getId()));
+                    thing2Str.add(tableBak.getClassName());
+                }
+
+                WeChatSendMessageDto weChatSendMessageDto = new WeChatSendMessageDto();
+                weChatSendMessageDto.setTemplateId(wechatTemplate);
+                JSONObject paramJson = new JSONObject();
+
+                JSONObject thing16 = new JSONObject();
+                thing16.put("value", thing16Str.toString().replace(" ", "").replace("[", "").replace("]", ""));
+                paramJson.put("thing16", thing16);
+
+                JSONObject thing2 = new JSONObject();
+                thing2.put("value", thing2Str.toString().replace(" ", "").replace("[", "").replace("]", ""));
+                paramJson.put("thing2", thing2);
+
+                String const12Str = "";
+                if (CourseAdjustTypeEnum.courseExchange.getCode().equals(courseAdjust.getAdjustType())) {
+                    const12Str = "调课失败";
+                } else if (CourseAdjustTypeEnum.courseSubstitute.getCode().equals(courseAdjust.getAdjustType())) {
+                    const12Str = "顶课失败";
+                }
+
+                JSONObject const12 = new JSONObject();
+                const12.put("value", const12Str);
+                paramJson.put("const23", const12);
+
+                //迟到人数或者缺勤人数
+                JSONObject thing5 = new JSONObject();
+                thing5.put("value", "由于课表更新,该调顶课已失效");
+                paramJson.put("thing5", thing5);
+
+                String url = StrUtil.format(
+                        "{}/xjrsoft/pages/workflow/look?processId={}&type=my",
+                        commonPropertiesConfig.getDomainApp(),
+                        "relation.getProcessId()"
+                );
+                weChatSendMessageDto.setContent(paramJson);
+                weChatSendMessageDto.setUrl(url);
+                weChatSendMessageDto.setMsgId(IdUtil.getSnowflakeNextId() + "");
+                String openId = userOpenIdMap.get(courseAdjust.getUserId());
+                if(openId != null && !"".equals(openId)){
+                    weChatSendMessageDto.setUserId(openId);
+                    weChatService.sendTemplateMessage(weChatSendMessageDto);
+                }
+                continue;
+            }
+            courseTableService.adjustCourse(courseAdjust);
+        }
+        if(!cancelList.isEmpty()){
+            adjustService.updateBatchById(cancelList);
+        }
+
+        List<WorkflowFormRelation> activeList = formRelationService.list(
+                new MPJLambdaWrapper<WorkflowFormRelation>()
+                        .select(WorkflowFormRelation::getId)
+                        .select(WorkflowFormRelation.class, x -> VoToColumnUtil.fieldsToColumns(WorkflowFormRelation.class).contains(x.getProperty()))
+                        .innerJoin(WfCourseAdjust.class, WfCourseAdjust::getId, WorkflowFormRelation::getFormKeyValue)
+                        .in(!courseAdjustIds.isEmpty(), WfCourseAdjust::getId, courseAdjustIds)
+                        .between(WfCourseAdjust::getAdjustDate, startDate, endDate)
+                        .eq(WorkflowFormRelation::getCurrentState, HistoricProcessInstance.STATE_ACTIVE)
+        );
+        for (WorkflowFormRelation relation : activeList) {
+            List<CourseTable> courseList = courseTableService.list(
+                    new MPJLambdaWrapper<CourseTable>()
+                            .select(CourseTable::getId)
+                            .select(CourseTable.class, x -> VoToColumnUtil.fieldsToColumns(CourseTable.class).contains(x.getProperty()))
+                            .innerJoin(CourseTableBak.class, CourseTableBak::getKeyInfo, CourseTable::getKeyInfo)
+                            .eq(CourseTableBak::getWfCourseAdjustId, relation.getFormKeyValue())
+            );
+            List<CourseTableBak> thisbakList = courseTableBakService.list(
+                    new QueryWrapper<CourseTableBak>().lambda()
+                            .eq(CourseTableBak::getWfCourseAdjustId, relation.getFormKeyValue())
+            );
+            if(courseList.size() == thisbakList.size()){
+                continue;
+            }
+            WfCourseAdjust courseAdjust = adjustService.getById(relation.getFormKeyValue());
+            //表明课程变化了,进行内部终止
+            List<String> thing16Str = new ArrayList<>();
+            List<String> thing2Str = new ArrayList<>();
+            for (CourseTableBak tableBak : bakList) {
+                if(!tableBak.getWfCourseAdjustId().equals(courseAdjust.getId())){
+                    continue;
+                }
+
+                thing16Str.add(classMap.get(tableBak.getId()));
+                thing2Str.add(tableBak.getClassName());
+            }
+
+            WeChatSendMessageDto weChatSendMessageDto = new WeChatSendMessageDto();
+            weChatSendMessageDto.setTemplateId(wechatTemplate);
+            JSONObject paramJson = new JSONObject();
+
+            JSONObject thing16 = new JSONObject();
+            thing16.put("value", thing16Str.toString().replace(" ", "").replace("[", "").replace("]", ""));
+            paramJson.put("thing16", classMap);
+
+            JSONObject thing2 = new JSONObject();
+            thing2.put("value", thing2Str.toString().replace(" ", "").replace("[", "").replace("]", ""));
+            paramJson.put("thing2", thing2);
+
+            String const12Str = "";
+            if (CourseAdjustTypeEnum.courseExchange.getCode().equals(courseAdjust.getAdjustType())) {
+                const12Str = "调课失败";
+            } else if (CourseAdjustTypeEnum.courseSubstitute.getCode().equals(courseAdjust.getAdjustType())) {
+                const12Str = "顶课失败";
+            }
+
+            JSONObject const12 = new JSONObject();
+            const12.put("value", const12Str);
+            paramJson.put("const23", const12);
+
+            //迟到人数或者缺勤人数
+            JSONObject thing5 = new JSONObject();
+            thing5.put("value", "由于课表更新,该调顶课已失效");
+            paramJson.put("thing5", thing5);
+
+            weChatSendMessageDto.setContent(paramJson);
+            List<WorkflowExtra> extraList = workflowExtraService.list(
+                    new QueryWrapper<WorkflowExtra>().lambda()
+                            .eq(WorkflowExtra::getProcessId, relation.getProcessId())
+                            .orderByDesc(WorkflowExtra::getStartTime)
+            );
+
+            if(!extraList.isEmpty()){
+                String url = StrUtil.format(
+                        "{}/xjrsoft/pages/workflow/approval?taskId={}&processId={}&type=todo",
+                        commonPropertiesConfig.getDomainApp(),
+                        extraList.get(0).getTaskId(),
+                        relation.getProcessId()
+                );
+                weChatSendMessageDto.setUrl(url);
+
+                weChatSendMessageDto.setMsgId(IdUtil.getSnowflakeNextId() + "");
+                String openId = userOpenIdMap.get(courseAdjust.getExchangeTeacherId());
+                if(openId != null && !"".equals(openId)){
+                    weChatSendMessageDto.setUserId(openId);
+                    weChatService.sendTemplateMessage(weChatSendMessageDto);
+                }
+            }
+            String url = StrUtil.format(
+                    "{}/xjrsoft/pages/workflow/look?processId={}&type=my",
+                    commonPropertiesConfig.getDomainApp(),
+                    relation.getProcessId()
+            );
+            weChatSendMessageDto.setUrl(url);
+            weChatSendMessageDto.setMsgId(IdUtil.getSnowflakeNextId() + "");
+            String openId = userOpenIdMap.get(courseAdjust.getUserId());
+            if(openId != null && !"".equals(openId)){
+                weChatSendMessageDto.setUserId(openId);
+                weChatService.sendTemplateMessage(weChatSendMessageDto);
+            }
+            String processId = relation.getProcessId();
+
+            List<Task> taskList = taskService.createTaskQuery().processInstanceId(processId).list();
+            Task task = taskList.stream().filter(x -> x.getProcessInstanceId().equals(processId)).findFirst().orElse(new TaskEntity());
+            Long schemaId = Convert.toLong(taskService.getVariable(task.getId(), WorkflowConstant.PROCESS_SCHEMA_ID_KEY));
+            //获取到当前活动的实例
+            ActivityInstance activityInstance = runtimeService.getActivityInstance(task.getProcessInstanceId());
+
+            String message = "    因课表更新之后当前课程已发生变更,该流程已内部终止,如还需调/顶课,请重新发起调/顶课流程";
+            //先停止当前活动示例  然后  关闭流程
+            runtimeService.createProcessInstanceModification(task.getProcessInstanceId())
+                    .cancelActivityInstance(activityInstance.getId())
+                    .cancelAllForActivity(activityInstance.getId())
+                    .setAnnotation("因课表更新之后当前课程已发生变更,该流程已内部终止")
+                    .execute();
+
+            //新增流程发起流程记录
+            WorkflowRecord record = new WorkflowRecord();
+            record.setNodeId(task.getId());
+            record.setNodeName(task.getName());
+            record.setNodeType(WorkflowConstant.USER_TASK_TYPE_NAME);
+            record.setProcessId(task.getProcessInstanceId());
+            record.setSchemaId(schemaId);
+            record.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
+            record.setRecordTime(LocalDateTime.now());
+            record.setWorkflowApproveType(WorkflowApproveType.FINISH.getCode());
+
+            record.setMessage(message);
+
+            workflowRecordService.save(record);
+
+            //新增流程发起流程记录
+            XjrWorkflowOperateRecord xjrWorkflowOperateRecord = new XjrWorkflowOperateRecord();
+            xjrWorkflowOperateRecord.setNodeId(task.getId());
+            xjrWorkflowOperateRecord.setNodeName(task.getName());
+            xjrWorkflowOperateRecord.setNodeType(WorkflowConstant.USER_TASK_TYPE_NAME);
+            xjrWorkflowOperateRecord.setProcessId(task.getProcessInstanceId());
+            xjrWorkflowOperateRecord.setSchemaId(schemaId);
+            xjrWorkflowOperateRecord.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
+            xjrWorkflowOperateRecord.setRecordTime(LocalDateTime.now());
+            xjrWorkflowOperateRecord.setUsageScenario(1);
+
+
+            xjrWorkflowOperateRecord.setOperateInfo(message);
+            xjrWorkflowOperateRecordMapper.insert(xjrWorkflowOperateRecord);
+
+
+            Optional<HistoricProcessInstance> historicProcessInstance = workflowExecuteService.getHistoricProcessInstance(processId);
+            historicProcessInstance.ifPresent(item -> {
+                formRelationService.updateCurrentState(new WorkflowFormRelation() {{
+                    setProcessId(processId);
+                    setCurrentState(item.getState());
+                    setStartTime(item.getStartTime());
+                    setEndTime(item.getEndTime());
+                }});
+            });
+        }
+    }
+
+
+    /**
+     * 挂起未通过的流程
+     */
+    List<String> suspendedCourseAdjust(List<BaseClass> classList, String startDate, String endDate) {
+        List<Long> classIds = classList.stream().map(BaseClass::getId).collect(Collectors.toList());
+        List<CourseTableBak> bakList = courseTableBakService.list(
+                new QueryWrapper<CourseTableBak>().lambda()
+                        .in(CourseTableBak::getClassId, classIds)
+                        .between(CourseTableBak::getScheduleDate, startDate, endDate)
+        );
+        Set<Long> courseAdjustIdSet = bakList.stream().map(CourseTableBak::getWfCourseAdjustId).collect(Collectors.toSet());
+
+        List<Long> courseAdjustIds = new ArrayList<>(courseAdjustIdSet);
+        List<WorkflowFormRelation> list = formRelationService.list(
+                new MPJLambdaWrapper<WorkflowFormRelation>()
+                        .select(WorkflowFormRelation::getId)
+                        .select(WorkflowFormRelation.class, x -> VoToColumnUtil.fieldsToColumns(WorkflowFormRelation.class).contains(x.getProperty()))
+                        .innerJoin(WfCourseAdjust.class, WfCourseAdjust::getId, WorkflowFormRelation::getFormKeyValue)
+                        .between(WfCourseAdjust::getAdjustDate, startDate, endDate)
+                        .in(!courseAdjustIds.isEmpty(), WfCourseAdjust::getId, courseAdjustIds)
+                        .eq(WorkflowFormRelation::getCurrentState, HistoricProcessInstance.STATE_ACTIVE)
+        );
+        List<String> processIds = new ArrayList<>();
+        for (WorkflowFormRelation relation : list) {
+            String processId = relation.getProcessId();
+            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
+            List<Task> taskList = taskService.createTaskQuery().processInstanceId(processId).list();
+            Task task = taskList.stream().filter(x -> x.getProcessInstanceId().equals(processId)).findFirst().orElse(new TaskEntity());
+            Long schemaId = Convert.toLong(taskService.getVariable(task.getId(), WorkflowConstant.PROCESS_SCHEMA_ID_KEY));
+            //新增流程发起流程记录
+            WorkflowRecord record = new WorkflowRecord();
+            record.setNodeId(task.getId());
+            record.setNodeName(task.getName());
+            record.setNodeType(WorkflowConstant.USER_TASK_TYPE_NAME);
+            record.setProcessId(task.getProcessInstanceId());
+            record.setSchemaId(schemaId);
+            record.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
+            record.setRecordTime(LocalDateTime.now());
+
+            if (processInstance.isSuspended()) {
+                runtimeService.activateProcessInstanceById(relation.getProcessId());
+                //[操作人名称] 将流程恢复
+                record.setMessage("课表同步成功,流程恢复");
+            } else {
+                runtimeService.suspendProcessInstanceById(relation.getProcessId());
+                //[操作人名称] 将流程挂起
+                record.setMessage("课表更新中,暂停流程审核,请等待课表更新完成之后进行审核");
+            }
+            workflowRecordService.save(record);
+            Optional<HistoricProcessInstance> historicProcessInstance = workflowExecuteService.getHistoricProcessInstance(processId);
+            historicProcessInstance.ifPresent(item -> {
+                formRelationService.updateCurrentState(new WorkflowFormRelation() {{
+                    setProcessId(processId);
+                    setCurrentState(item.getState());
+                    setStartTime(item.getStartTime());
+                    setEndTime(item.getEndTime());
+                }});
+            });
+
+            processIds.add(relation.getProcessId());
+        }
+        return processIds;
+    }
+
+    /**
+     * 将挂起的流程恢复
+     */
+    void restoreCourseAdjust(List<String> processIds){
+        for (String processId : processIds) {
+            List<Task> taskList = taskService.createTaskQuery().processInstanceId(processId).list();
+            Task task = taskList.stream().filter(x -> x.getProcessInstanceId().equals(processId)).findFirst().orElse(new TaskEntity());
+            Long schemaId = Convert.toLong(taskService.getVariable(task.getId(), WorkflowConstant.PROCESS_SCHEMA_ID_KEY));
+            //新增流程发起流程记录
+            WorkflowRecord record = new WorkflowRecord();
+            record.setNodeId(task.getId());
+            record.setNodeName(task.getName());
+            record.setNodeType(WorkflowConstant.USER_TASK_TYPE_NAME);
+            record.setProcessId(task.getProcessInstanceId());
+            record.setSchemaId(schemaId);
+            record.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
+            record.setRecordTime(LocalDateTime.now());
+            record.setMessage("课表同步成功,流程恢复");
+            workflowRecordService.save(record);
+            runtimeService.activateProcessInstanceById(processId);
+
+            Optional<HistoricProcessInstance> historicProcessInstance = workflowExecuteService.getHistoricProcessInstance(processId);
+            historicProcessInstance.ifPresent(item -> {
+                formRelationService.updateCurrentState(new WorkflowFormRelation() {{
+                    setProcessId(processId);
+                    setCurrentState(item.getState());
+                    setStartTime(item.getStartTime());
+                    setEndTime(item.getEndTime());
+                }});
+            });
+        }
+    }
 }