Quellcode durchsuchen

课时统计导出调整

dzx vor 4 Monaten
Ursprung
Commit
b2f009918e
59 geänderte Dateien mit 6705 neuen und 0 gelöschten Zeilen
  1. 15 0
      src/main/java/com/xjrsoft/common/utils/SortCodeUtil.java
  2. 21 0
      src/main/java/com/xjrsoft/config/JianyuekbConfig.java
  3. 1 0
      src/main/java/com/xjrsoft/module/classtime/controller/ClassTimeStatisticsController.java
  4. 1429 0
      src/main/java/com/xjrsoft/module/workflow/utils/WorkFlowUtil.java.back
  5. 21 0
      src/main/java/com/xjrsoft/module/workflow/vo/TaskCountVo.java
  6. 10 0
      src/main/resources/mapper/personnel/StundentFaceProcessMapper.xml
  7. 59 0
      src/main/resources/sqlScript/20241016_sql.sql
  8. 60 0
      src/main/resources/sqlScript/20241019_sql.sql
  9. 13 0
      src/main/resources/sqlScript/20241029_sql.sql
  10. 8 0
      src/main/resources/sqlScript/20241031_sql.sql
  11. 2 0
      src/main/resources/sqlScript/20241107_sql.sql
  12. 89 0
      src/main/resources/sqlScript/question[2024年4月28日].sql
  13. 51 0
      src/main/resources/sqlScript/班级视图.sql
  14. 217 0
      src/main/resources/sqlScript/视图信息.sql
  15. 215 0
      src/main/resources/sqlScript/视图信息.sql.bak
  16. 10 0
      src/test/QRCode.html
  17. 27 0
      src/test/java/com/xjrsoft/common/utils/QrCodeTest.java
  18. 85 0
      src/test/java/com/xjrsoft/module/classtime/service/impl/ClassTimeStatisticsServiceImplTest.java
  19. 743 0
      src/test/java/com/xjrsoft/module/excel/CellStyleModel.java
  20. 29 0
      src/test/java/com/xjrsoft/module/excel/CustomCellRangeAddress.java
  21. 368 0
      src/test/java/com/xjrsoft/module/excel/CustomCellStyleHandler.java
  22. 71 0
      src/test/java/com/xjrsoft/module/excel/CustomMergeStrategy.java
  23. 90 0
      src/test/java/com/xjrsoft/module/excel/CustomMergeStrategy2.java
  24. 40 0
      src/test/java/com/xjrsoft/module/excel/DemoData.java
  25. 52 0
      src/test/java/com/xjrsoft/module/excel/ExcelData.java
  26. 102 0
      src/test/java/com/xjrsoft/module/excel/ExcelMergeUtil.java
  27. 109 0
      src/test/java/com/xjrsoft/module/excel/ExcelTest.java
  28. 73 0
      src/test/java/com/xjrsoft/module/excel/ExcelUtil.java
  29. 34 0
      src/test/java/com/xjrsoft/module/excel/ReportUserInfoBO.java
  30. 467 0
      src/test/java/com/xjrsoft/module/excel/Test2.java
  31. 233 0
      src/test/java/com/xjrsoft/module/excel/Test3.java
  32. 33 0
      src/test/java/com/xjrsoft/module/hikvision/util/OutInRecordUtilTest.java
  33. 44 0
      src/test/java/com/xjrsoft/module/job/AttendanceMessageTaskTest2.java
  34. 169 0
      src/test/java/com/xjrsoft/module/job/HikvisionLeaveTaskTest.java
  35. 64 0
      src/test/java/com/xjrsoft/module/job/InsertOutInRecordTaskTest.java
  36. 91 0
      src/test/java/com/xjrsoft/module/job/ProcessTimeoutAlertTaskTest.java
  37. 38 0
      src/test/java/com/xjrsoft/module/liteflow/node/StudentChangeClassNodeTest.java
  38. 41 0
      src/test/java/com/xjrsoft/module/liteflow/node/StudentDropOutNodeTest.java
  39. 34 0
      src/test/java/com/xjrsoft/module/liteflow/node/WfCourseAdjustNodeTest.java
  40. 43 0
      src/test/java/com/xjrsoft/module/liteflow/node/WfCourseBackupsNodeTest.java
  41. 47 0
      src/test/java/com/xjrsoft/module/liteflow/node/WfRoomApplicantNodeTest.java
  42. 56 0
      src/test/java/com/xjrsoft/module/liteflow/node/WfSubscriptionNodeTest.java
  43. 23 0
      src/test/java/com/xjrsoft/module/schedule/util/DataUtilTest.java
  44. 130 0
      src/test/java/com/xjrsoft/module/student/controller/StudentManagerControllerTest.java
  45. 38 0
      src/test/java/com/xjrsoft/module/system/controller/LoginControllerTest.java
  46. 43 0
      src/test/java/com/xjrsoft/xjrsoftboot/ExcelDownloadTest.java
  47. 326 0
      src/test/java/com/xjrsoft/xjrsoftboot/ExcelExample.java
  48. 64 0
      src/test/java/com/xjrsoft/xjrsoftboot/FileTest.java
  49. 78 0
      src/test/java/com/xjrsoft/xjrsoftboot/HIkvisionTest.java
  50. 28 0
      src/test/java/com/xjrsoft/xjrsoftboot/HttpUtil.java
  51. 15 0
      src/test/java/com/xjrsoft/xjrsoftboot/IdCreateTest.java
  52. 23 0
      src/test/java/com/xjrsoft/xjrsoftboot/OutInTest.java
  53. 74 0
      src/test/java/com/xjrsoft/xjrsoftboot/ScheduleDataTest.java
  54. 45 0
      src/test/java/com/xjrsoft/xjrsoftboot/ScheduleUtil.java
  55. 17 0
      src/test/java/com/xjrsoft/xjrsoftboot/SubStringtest.java
  56. 79 0
      src/test/java/com/xjrsoft/xjrsoftboot/TeacherTest.java
  57. 23 0
      src/test/java/com/xjrsoft/xjrsoftboot/Test2.java
  58. 23 0
      src/test/java/com/xjrsoft/xjrsoftboot/UrlTest.java
  59. 272 0
      ter

+ 15 - 0
src/main/java/com/xjrsoft/common/utils/SortCodeUtil.java

@@ -0,0 +1,15 @@
+package com.xjrsoft.common.utils;
+
+import com.baomidou.mybatisplus.extension.toolkit.SimpleQuery;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.poi.ss.formula.functions.T;
+
+/**
+ * @author dzx
+ * @date 2024/1/8
+ */
+public class SortCodeUtil {
+    public Integer getMaxSortCode(T mapper, T Entity){
+        return null;
+    }
+}

+ 21 - 0
src/main/java/com/xjrsoft/config/JianyuekbConfig.java

@@ -0,0 +1,21 @@
+package com.xjrsoft.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author dzx
+ * @date 2024/1/10
+ */
+@Data
+@Component
+@ConfigurationProperties("xjrsoft.jianyuekb")
+public class JianyuekbConfig {
+    private String schoolId;
+    private String secert;
+    private String password;
+    private String url;
+    private String clientIp;
+
+}

+ 1 - 0
src/main/java/com/xjrsoft/module/classtime/controller/ClassTimeStatisticsController.java

@@ -105,6 +105,7 @@ public class ClassTimeStatisticsController {
                 new QueryWrapper<DictionaryDetail>().lambda()
                         .eq(DictionaryDetail::getItemId, 1833772737004875778L)
                         .eq(DictionaryDetail::getDeleteMark, DeleteMark.NODELETE.getCode())
+                        .orderByAsc(DictionaryDetail::getCode)
         );
         return RT.ok(CourseTimeTypeList);
     }

+ 1429 - 0
src/main/java/com/xjrsoft/module/workflow/utils/WorkFlowUtil.java.back

@@ -0,0 +1,1429 @@
+package com.xjrsoft.module.workflow.utils;
+
+import camundafeel.de.odysseus.el.ExpressionFactoryImpl;
+import camundafeel.de.odysseus.el.util.SimpleContext;
+import camundafeel.javax.el.ExpressionFactory;
+import camundafeel.javax.el.ValueExpression;
+import cn.dev33.satoken.session.SaSession;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.db.Db;
+import cn.hutool.extra.spring.SpringUtil;
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.core.toolkit.StringPool;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.xjrsoft.common.constant.GlobalConstant;
+import com.xjrsoft.common.enums.ParamAssignmentType;
+import com.xjrsoft.common.enums.WorkflowAuth;
+import com.xjrsoft.common.enums.WorkflowEventType;
+import com.xjrsoft.common.enums.WorkflowMemberType;
+import com.xjrsoft.common.enums.WorkflowNoticePolicyType;
+import com.xjrsoft.common.enums.WorkflowParamType;
+import com.xjrsoft.common.utils.DatasourceUtil;
+import com.xjrsoft.common.utils.RedisUtil;
+import com.xjrsoft.module.form.entity.FormTemplate;
+import com.xjrsoft.module.magicapi.service.IMagicApiService;
+import com.xjrsoft.module.magicapi.vo.MagicApiInfoVo;
+import com.xjrsoft.module.oa.utils.SendMessageUtil;
+import com.xjrsoft.module.organization.entity.Department;
+import com.xjrsoft.module.organization.entity.Post;
+import com.xjrsoft.module.organization.entity.User;
+import com.xjrsoft.module.organization.entity.UserDeptRelation;
+import com.xjrsoft.module.organization.entity.UserPostRelation;
+import com.xjrsoft.module.organization.entity.UserRoleRelation;
+import com.xjrsoft.module.organization.service.IUserService;
+import com.xjrsoft.module.system.entity.DictionaryDetail;
+import com.xjrsoft.module.workflow.constant.WorkflowConstant;
+import com.xjrsoft.module.workflow.dto.LaunchDto;
+import com.xjrsoft.module.workflow.entity.WorkflowApproveRecord;
+import com.xjrsoft.module.workflow.entity.WorkflowDelegate;
+import com.xjrsoft.module.workflow.entity.WorkflowSchema;
+import com.xjrsoft.module.workflow.model.ApiConfig;
+import com.xjrsoft.module.workflow.model.ApiRequestParamsConfig;
+import com.xjrsoft.module.workflow.model.AssignmentConfig;
+import com.xjrsoft.module.workflow.model.AuthConfig;
+import com.xjrsoft.module.workflow.model.ConditionConfig;
+import com.xjrsoft.module.workflow.model.FormAssignmentConfig;
+import com.xjrsoft.module.workflow.model.FormAssignmentSourceConfig;
+import com.xjrsoft.module.workflow.model.FormConfig;
+import com.xjrsoft.module.workflow.model.FormFieldConfig;
+import com.xjrsoft.module.workflow.model.LeaderConfig;
+import com.xjrsoft.module.workflow.model.MemberConfig;
+import com.xjrsoft.module.workflow.model.NameRuleConfig;
+import com.xjrsoft.module.workflow.model.NodeBasicConfig;
+import com.xjrsoft.module.workflow.model.NoticePolicyParam;
+import com.xjrsoft.module.workflow.model.ParamAssignmentConfig;
+import com.xjrsoft.module.workflow.model.ProcessParamConfig;
+import com.xjrsoft.module.workflow.model.SequenceFlowConfig;
+import com.xjrsoft.module.workflow.model.UserTaskConfig;
+import com.xjrsoft.module.workflow.model.WorkflowSchemaConfig;
+import com.xjrsoft.module.workflow.service.IWorkflowApproveRecordService;
+import com.yomahub.liteflow.util.JsonUtil;
+import lombok.SneakyThrows;
+import org.camunda.bpm.engine.HistoryService;
+import org.camunda.bpm.engine.ProcessEngines;
+import org.camunda.bpm.engine.RepositoryService;
+import org.camunda.bpm.engine.RuntimeService;
+import org.camunda.bpm.engine.history.HistoricVariableInstance;
+import org.camunda.bpm.engine.variable.VariableMap;
+import org.camunda.bpm.model.bpmn.instance.BaseElement;
+import org.camunda.bpm.model.bpmn.instance.ConditionExpression;
+import org.camunda.bpm.model.bpmn.instance.FlowNode;
+import org.camunda.bpm.model.bpmn.instance.Gateway;
+import org.camunda.bpm.model.bpmn.instance.SequenceFlow;
+import org.camunda.bpm.model.xml.instance.ModelElementInstance;
+import org.ssssssss.magicapi.core.service.MagicAPIService;
+
+import javax.sql.DataSource;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+/**
+ * @Author: tzx
+ * @Date: 2022/9/26 15:54
+ */
+public class WorkFlowUtil {
+
+    /**
+     * 替换占位符
+     *
+     * @param str            源字符串
+     * @param workflowSchema 工作流模板信息
+     * @param serialNumber   流水号  可不填
+     * @return 替换后的字符串
+     */
+    public static String replacePlaceHolder(String str, WorkflowSchema workflowSchema, Long serialNumber, Long workflowSerialNumber) {
+        SaSession tokenSession = StpUtil.getTokenSession();
+        User user = tokenSession.get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
+        Department department = tokenSession.get(GlobalConstant.LOGIN_USER_DEPT_INFO_KEY, new Department());
+        Post post = tokenSession.get(GlobalConstant.LOGIN_USER_POST_INFO_KEY, new Post());
+
+        //流程流水号
+        if (workflowSerialNumber != null && workflowSerialNumber != 0) {
+            char zeroChar = '0';
+            String serialNumberStr = StrUtil.toString(workflowSerialNumber);
+            String fillStr = StringPool.EMPTY;
+
+            if (str.contains(WorkflowConstant.SERIAL_NUBMER_FOUR_PLACEHOLDER)) {
+                //4位数 前面补0
+                fillStr = StrUtil.fillBefore(serialNumberStr, zeroChar, 4);
+            } else if (str.contains(WorkflowConstant.SERIAL_NUBMER_FIVE_PLACEHOLDER)) {
+                //4位数 前面补0
+                fillStr = StrUtil.fillAfter(serialNumberStr, zeroChar, 5);
+            } else if (str.contains(WorkflowConstant.SERIAL_NUBMER_SIX_PLACEHOLDER)) {
+                //4位数 前面补0
+                fillStr = StrUtil.fillAfter(serialNumberStr, zeroChar, 6);
+            } else if (str.contains(WorkflowConstant.SERIAL_NUBMER_SEVEN_PLACEHOLDER)) {
+                //4位数 前面补0
+                fillStr = StrUtil.fillAfter(serialNumberStr, zeroChar, 7);
+            } else if (str.contains(WorkflowConstant.SERIAL_NUBMER_EIGHT_PLACEHOLDER)) {
+                //4位数 前面补0
+                fillStr = StrUtil.fillAfter(serialNumberStr, zeroChar, 8);
+            }
+
+            if (StrUtil.isNotBlank(fillStr)) {
+                str = str.replace(WorkflowConstant.SERIAL_NUBMER_FOUR_PLACEHOLDER, fillStr);
+                str = str.replace(WorkflowConstant.SERIAL_NUBMER_FIVE_PLACEHOLDER, fillStr);
+                str = str.replace(WorkflowConstant.SERIAL_NUBMER_SIX_PLACEHOLDER, fillStr);
+                str = str.replace(WorkflowConstant.SERIAL_NUBMER_SEVEN_PLACEHOLDER, fillStr);
+                str = str.replace(WorkflowConstant.SERIAL_NUBMER_EIGHT_PLACEHOLDER, fillStr);
+            }
+
+        }
+        if (str.contains(WorkflowConstant.TEMPLATE_CATEGORY_PLACEHOLDER) && ObjectUtil.isNotNull(workflowSchema.getCategory())) {
+            RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
+            List<DictionaryDetail> detailList = redisUtil.get(GlobalConstant.DIC_DETAIL_CACHE_KEY, new TypeReference<List<DictionaryDetail>>() {
+            });
+
+            Optional<DictionaryDetail> dictionaryDetail = detailList.stream().filter(x -> x.getId().equals(workflowSchema.getCategory())).findFirst();
+
+            if (dictionaryDetail.isPresent()) {
+                str = str.replace(WorkflowConstant.TEMPLATE_CATEGORY_PLACEHOLDER, StrUtil.toString(dictionaryDetail.get().getName()));
+            } else {
+                str = str.replace(WorkflowConstant.TEMPLATE_CATEGORY_PLACEHOLDER, StrUtil.toString(workflowSchema.getCategory()));
+            }
+
+        }
+        if(str.contains(WorkflowConstant.TEMPLATE_REMARK_PLACEHOLDER) && StrUtil.isNotBlank(workflowSchema.getRemark())){ //workflowSchema.getRemark()可能为空
+            str = str.replace(WorkflowConstant.TEMPLATE_REMARK_PLACEHOLDER, workflowSchema.getRemark());
+        }
+        if(StrUtil.isNotBlank(post.getName())){
+            str = str.replace(WorkflowConstant.INITIATOR_POST_NAME_PLACEHOLDER, post.getName());
+        }else {
+            str = str.replace(WorkflowConstant.INITIATOR_POST_NAME_PLACEHOLDER, StringPool.EMPTY);
+        }
+
+        if (str.contains(WorkflowConstant.RANDOM_DASH)){
+            str = str.replace(WorkflowConstant.RANDOM_DASH,StringPool.DASH);
+        }
+
+        return str.replace(WorkflowConstant.TEMPLATE_CODE_PLACEHOLDER, workflowSchema.getCode())
+                .replace(WorkflowConstant.TEMPLATE_NAME_PLACEHOLDER, workflowSchema.getName())
+                .replace(WorkflowConstant.TEMPLATE_REMARK_PLACEHOLDER, workflowSchema.getRemark())
+                .replace(WorkflowConstant.TASK_ID_PLACEHOLDER, workflowSchema.getDefinitionId())
+                .replace(WorkflowConstant.INITIATOR_ID_PLACEHOLDER, StrUtil.toString(user.getId()))
+                .replace(WorkflowConstant.INITIATOR_USER_NAME_PLACEHOLDER, user.getName())
+                .replace(WorkflowConstant.INITIATOR_CODE_PLACEHOLDER, user.getCode())
+                .replace(WorkflowConstant.INITIATOR_MOBILE_PLACEHOLDER, user.getMobile())
+                .replace(WorkflowConstant.INITIATOR_DEPT_NAME_PLACEHOLDER, department.getName())
+//                .replace(WorkflowConstant.INITIATOR_POST_NAME_PLACEHOLDER, post.getName())
+                .replace(WorkflowConstant.YYYYMMDDHHMMSS_24_PLACEHOLDER, DateUtil.format(LocalDateTime.now(), GlobalConstant.YYYY_MM_DD_HH_MM_SS_24))
+                .replace(WorkflowConstant.YYYYMMDDHHMMSS_12_PLACEHOLDER, DateUtil.format(LocalDateTime.now(), GlobalConstant.YYYY_MM_DD_HH_MM_SS_12))
+                .replace(WorkflowConstant.YYYYMMDD_PLACEHOLDER, DateUtil.format(LocalDateTime.now(), GlobalConstant.YYYY_MM_DD))
+                .replace(WorkflowConstant.HHMMSS_24_PLACEHOLDER, DateUtil.format(LocalDateTime.now(), GlobalConstant.HH_MM_SS_24))
+                .replace(WorkflowConstant.HHMMSS_12_PLACEHOLDER, DateUtil.format(LocalDateTime.now(), GlobalConstant.HH_MM_SS_12))
+                .replace(WorkflowConstant.RANDOM_2_PLACEHOLDER, RandomUtil.randomNumbers(2))
+                .replace(WorkflowConstant.RANDOM_2_MIX_PLACEHOLDER, RandomUtil.randomString(2))
+                .replace(WorkflowConstant.RANDOM_4_PLACEHOLDER, RandomUtil.randomNumbers(4))
+                .replace(WorkflowConstant.RANDOM_4_MIX_PLACEHOLDER, RandomUtil.randomString(4))
+                .replace(WorkflowConstant.RANDOM_6_PLACEHOLDER, RandomUtil.randomNumbers(6))
+                .replace(WorkflowConstant.RANDOM_6_MIX_PLACEHOLDER, RandomUtil.randomString(6))
+                .replace(WorkflowConstant.RANDOM_8_PLACEHOLDER, RandomUtil.randomNumbers(8))
+                .replace(WorkflowConstant.RANDOM_8_MIX_PLACEHOLDER, RandomUtil.randomString(8))
+                .replace(WorkflowConstant.SERIAL_NUBMER_PLACEHOLDER, StrUtil.toString(serialNumber))
+                .replace(StringPool.HASH, StringPool.EMPTY)
+                .replace(StringPool.LEFT_BRACE, StringPool.EMPTY)
+                .replace(StringPool.RIGHT_BRACE, StringPool.EMPTY);
+
+    }
+
+    /**
+     * 生成流程名称
+     * @param workflowSchema
+     * @param workflowSchemaConfig
+     * @param workflowSerialNumber
+     * @return
+     */
+    public static String generatorProcessName(WorkflowSchema workflowSchema, WorkflowSchemaConfig workflowSchemaConfig, Long workflowSerialNumber, LaunchDto dto) {
+
+        if (workflowSchemaConfig.getProcessConfig().getNameRuleConfigs().size() == 0) {
+            if(dto.getFormData() != null){
+                Map<String, Map<String, Object>> formData = dto.getFormData();
+                String title_last = null;
+                for (Map<String, Object> value : formData.values()) {
+                    if(title_last != null){
+                        continue;
+                    }
+                    //物品申购特殊处理
+                    if(value.containsKey("wf_subscription_listList")){
+                        ArrayList<Map<String, Object>> list = (ArrayList<Map<String, Object>>) value.get("wf_subscription_listList");
+                        if(list.size() > 0){
+                            Map<String, Object> objectMap = list.get(0);
+//                        Set<String> keys = objectMap.keySet();
+                            String firstKey = "name_of_the_purchased_item";
+//                        for (String key : keys) {
+//                            if(firstKey != null){
+//                                continue;
+//                            }
+//                            firstKey = key;
+//                        }
+                            title_last = objectMap.get(firstKey).toString();
+                        }
+                    }
+                    for (Object o : value.values()) {
+                        if(title_last != null){
+                            continue;
+                        }
+                        title_last = o.toString();
+                    }
+                }
+                return "【" + workflowSchema.getName() + "】" + title_last;
+            }
+            return workflowSchema.getName();
+        }
+
+        String nameTemplate = workflowSchemaConfig.getProcessConfig().getNameRuleConfigs().stream().map(NameRuleConfig::getKey).map(String::valueOf).collect(Collectors.joining(StringPool.SPACE));
+        String replacePlaceHolder = WorkFlowUtil.replacePlaceHolder(nameTemplate, workflowSchema, workflowSerialNumber, workflowSerialNumber);
+        if(dto.getFormData() != null){
+            String nameStr = "";
+            Map<String, Map<String, Object>> formData = dto.getFormData();
+            String title_last = null;
+            if(dto.getFormData().toString().contains("wf_subscription_listList")){
+                for (Map<String, Object> value : formData.values()) {
+                    if(title_last != null){
+                        continue;
+                    }
+                    //物品申购特殊处理
+                    if(value.containsKey("wf_subscription_listList")){
+                        ArrayList<Map<String, Object>> list = (ArrayList<Map<String, Object>>) value.get("wf_subscription_listList");
+                        if(list.size() > 0){
+                            Map<String, Object> objectMap = list.get(0);
+                            String firstKey = "name_of_the_purchased_item";
+                            title_last = objectMap.get(firstKey).toString();
+                        }
+                    }
+                }
+            }
+
+            if(title_last != null){
+                String[] names = replacePlaceHolder.split(" ");
+                for(int i = 0; i < names.length; i ++){
+                    String name = names[i];
+                    if(i == 0){
+                        nameStr += "【" + name + "】";
+                    }else{
+                        nameStr += name;
+                    }
+
+                    for (Map<String, Object> value : formData.values()) {
+                        if(value.containsKey(name)){
+                            nameStr += value.get(name).toString();
+                        }
+                    }
+                }
+            }
+
+            return nameStr;
+        }
+        return replacePlaceHolder;
+    }
+
+    /**
+     * 根据memberConfig  获取所有userIds
+     * 例如查审批人  推送人   权限 等
+     *
+     * @return
+     */
+    public static List<Long> getUserIdsByMemberConfig(List<MemberConfig> memberConfigs, List<Map<String, Object>> childNodeConfig, String processId) {
+        List<Long> result = new ArrayList<>();
+
+        if (memberConfigs.size() == 0) {
+            return result;
+        }
+
+        RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
+
+        //如果有选择角色
+        List<UserRoleRelation> userRoleRelations = null;
+        //如果有选择用户
+        List<User> users = null;
+        //如果选择岗位
+        List<Post> posts = null;
+        List<UserPostRelation> userPostRelations = null;
+
+//        Optional<LeaderConfig> maxLevel = memberConfigs.parallelStream().filter(m -> m.getMemberType() == WorkflowMemberType.LEADER.getCode()).map(MemberConfig::getLeaderConfig).max(Comparator.comparing(LeaderConfig::getLevel));
+
+
+        for (MemberConfig memberConfig : memberConfigs) {
+            //如果是用户  memberlistconfig  存储的就是 用户id   直接  添加到返回值中
+            if (memberConfig.getMemberType() == WorkflowMemberType.USER.getCode()) {
+                result.add(Convert.toLong(memberConfig.getId()));
+            }
+            //如果是角色
+            // 从 缓存中获取到所有角色数据
+            // 根据所有角色id   从 缓存中获取 所有 用户角色关联信息
+            // 获取到所有用户id  添加到返回值中
+            if (memberConfig.getMemberType() == WorkflowMemberType.ROLE.getCode()) {
+
+                if (ObjectUtil.isNull(userRoleRelations)) {
+                    userRoleRelations = redisUtil.get(GlobalConstant.USER_ROLE_RELATION_CACHE_KEY, new TypeReference<List<UserRoleRelation>>() {
+                    });
+                }
+
+                //获取到所有角色的关联用户
+                List<Long> userIds = userRoleRelations.stream().filter(x -> x.getRoleId().equals(Convert.toLong(memberConfig.getId()))).map(UserRoleRelation::getUserId).collect(Collectors.toList());
+                result.addAll(userIds);
+            }
+            //根据岗位获取用户
+            if (memberConfig.getMemberType() == WorkflowMemberType.POST.getCode()) {
+
+                if (ObjectUtil.isNull(userPostRelations)) {
+                    userPostRelations = redisUtil.get(GlobalConstant.USER_POST_RELATION_CACHE_KEY, new TypeReference<List<UserPostRelation>>() {
+                    });
+                }
+                List<Long> userIds = userPostRelations.stream().filter(p -> ObjectUtil.equals(Convert.toLong(memberConfig.getId()), p.getPostId())).map(UserPostRelation::getUserId).collect(Collectors.toList());
+                result.addAll(userIds);
+            }
+            //指定节点审批人
+            if (memberConfig.getMemberType() == WorkflowMemberType.APPROVE.getCode()) {
+
+                //如果是选择的开始节点
+                if (memberConfig.getId().equals(WorkflowConstant.START_NODE_DEFAULT_ID)) {
+                    //将map 转为 java类  默认只有用户任务节点才有审批人
+                    RuntimeService runtimeService = ProcessEngines.getDefaultProcessEngine().getRuntimeService();
+
+                    Object startUserIdObj = runtimeService.getVariable(processId, WorkflowConstant.PROCESS_START_USER_ID_KEY);
+                    Long startUserId = Convert.toLong(startUserIdObj);
+                    result.add(startUserId);
+                    break;
+                }
+
+                //必须是用户任务节点
+                Optional<Map<String, Object>> userTaskConfigMapOp = childNodeConfig.stream().filter(x -> x.containsValue(memberConfig.getId())).findFirst();
+                if (!userTaskConfigMapOp.isPresent()) {
+                    break;
+                }
+                //    将map 转为 java类
+                UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, userTaskConfigMapOp.get());
+                List<Long> approverUserIds = getUserIdsByMemberConfig(userTaskConfig.getApproverConfigs(), childNodeConfig, processId);
+                result.addAll(approverUserIds);
+
+            }
+            //上级领导
+            if (memberConfig.getMemberType() == WorkflowMemberType.LEADER.getCode()) {
+
+                if (ObjectUtil.isNull(posts)) {
+                    posts = redisUtil.get(GlobalConstant.POST_CACHE_KEY, new TypeReference<List<Post>>() {
+                    });
+                }
+                if (ObjectUtil.isNull(users)) {
+                    users = redisUtil.get(GlobalConstant.USER_CACHE_KEY, new TypeReference<List<User>>() {
+                    });
+                }
+
+                LeaderConfig leaderConfig = memberConfig.getLeaderConfig();
+                //获取到节点配置
+                Optional<Map<String, Object>> nodeMap = childNodeConfig.stream().filter(x -> x.get(GlobalConstant.DEFAULT_PK).equals(leaderConfig.getNodeId())).findFirst();
+
+                if (!nodeMap.isPresent()) {
+                    return result;
+                }
+
+                //如果是开始节点
+                if (leaderConfig.getNodeId().equals(WorkflowConstant.START_NODE_DEFAULT_ID)) {
+                    //将map 转为 java类  默认只有用户任务节点才有审批人
+                    RuntimeService runtimeService = ProcessEngines.getDefaultProcessEngine().getRuntimeService();
+
+                    Object startUserIdObj = runtimeService.getVariable(processId, WorkflowConstant.PROCESS_START_USER_ID_KEY);
+                    Long startUserId = Convert.toLong(startUserIdObj);
+
+                    //获取到发起人用户信息
+                    Optional<User> startUserOp = users.stream().filter(x -> x.getId().equals(startUserId)).findFirst();
+                    if (!startUserOp.isPresent()) {
+                        return result;
+                    }
+
+                    Long postId;
+                    //如果发起人id等于当前登陆人id
+                    if (startUserId == StpUtil.getLoginIdAsLong()) {
+                        Object postIdObj = runtimeService.getVariable(processId, WorkflowConstant.PROCESS_START_USER_POST_ID_KEY);
+                        postId = Convert.toLong(postIdObj);
+                    } else {
+                        //获取当前发起的身份
+                        HistoryService historyService = ProcessEngines.getDefaultProcessEngine().getHistoryService();
+                        HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery().processInstanceId(processId).variableName(WorkflowConstant.PROCESS_START_USER_POST_ID_KEY).singleResult();
+
+                        postId = Convert.toLong(historicVariableInstance.getValue());
+                    }
+
+                    Optional<Post> thisApproveOp = posts.stream().filter(x -> x.getId().equals(postId)).findFirst();
+                    if (!thisApproveOp.isPresent()) {
+                        return result;
+                    }
+
+                    Post thisUserPost = thisApproveOp.get();
+
+                    for (int i = 1; i <= leaderConfig.getLevel(); i++) {
+
+                        Post finalThisUserPost = thisUserPost;
+                        Optional<Post> parentPostOp = posts.stream().filter(x -> finalThisUserPost.getParentId().equals(x.getId())).findFirst();
+
+                        //如果当前循环次数 找不到上级
+                        if (!parentPostOp.isPresent()) {
+                            return result;
+                        } else {
+                            thisUserPost = parentPostOp.get();
+                        }
+
+
+                        //当前级别 跟 循环次数一致  就把当前所查出来的岗位 所属人员 加入到 leaderUserIds
+                        if (i == leaderConfig.getLevel()) {
+                            //找到当前角色下的所有人员 id 存入 result
+                            Post post = parentPostOp.get();
+                            userPostRelations = redisUtil.get(GlobalConstant.USER_POST_RELATION_CACHE_KEY, new TypeReference<List<UserPostRelation>>() {
+                            });
+                            List<Long> leaderUserIds = userPostRelations.stream().filter(x -> ObjectUtil.equals(post.getId(), x.getPostId())).map(UserPostRelation::getUserId).collect(Collectors.toList());
+//                            List<Long> leaderUserIds = users.stream().filter(x -> ObjectUtil.equals(post.getId(),x.getPostId())).map(User::getId).collect(Collectors.toList());
+                            result.addAll(leaderUserIds);
+                        }
+
+
+                    }
+
+//                    Long finalCurrentPostParentId = thisApproveOp.get().getParentId();
+//                    for (int i = 0; i < leaderConfig.getLevel(); i++) {
+//                        for (Post post : posts) {
+//                            if (post.getId().equals(finalCurrentPostParentId) && leaderConfig.getLevel() == i) {
+//                                final long parentId = finalCurrentPostParentId;
+//                                List<Long> leaderUserIds = users.stream().filter(u -> u.getPostId().equals(parentId)).map(User::getId).collect(Collectors.toList());
+//                                result.addAll(leaderUserIds);
+//                                finalCurrentPostParentId = post.getParentId();
+//                            }
+//                        }
+//                    }
+                    return result;
+
+                } else {
+                    //将map 转为 java类  默认只有用户任务节点才有审批人
+                    UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, nodeMap.get());
+//                    List<MemberConfig> approverConfigs = userTaskConfig.getApproverConfigs();
+
+                    IWorkflowApproveRecordService workflowApproveRecordService = SpringUtil.getBean(IWorkflowApproveRecordService.class);
+
+                    //获取到当前流程的当前task 的审批记录  可能涉及到 驳回、流程画图流转回来、多实例 等清空 可能会有多条
+                    //大部分是一条 默认根据id 倒叙排序 取最后一条数据  代表最新的审批人
+                    List<WorkflowApproveRecord> list = workflowApproveRecordService.list(Wrappers.lambdaQuery(WorkflowApproveRecord.class)
+                            .eq(WorkflowApproveRecord::getProcessId, processId)
+                            .eq(WorkflowApproveRecord::getTaskDefinitionKey, userTaskConfig.getId())
+                            .orderByDesc(WorkflowApproveRecord::getId)
+                    );
+
+                    //如果查不到审批记录  默认不走下面逻辑
+                    if (ObjectUtil.isNull(list) || list.size() == 0) {
+                        break;
+                    }
+                    //获取到最新的一条审批记录中 所存储的 岗位
+                    Long approveUserPostId = list.get(0).getApproveUserPostId();
+
+                    Optional<Post> thisApproveOp = posts.stream().filter(x -> x.getId().equals(approveUserPostId)).findFirst();
+                    // 如果postId 找不到信息  停止循环
+                    if (!thisApproveOp.isPresent()) {
+                        break;
+                    }
+                    Long finalCurrentPostParentId = thisApproveOp.get().getParentId();
+
+                    if (ObjectUtil.isNull(userPostRelations)) {
+                        userPostRelations = redisUtil.get(GlobalConstant.USER_POST_RELATION_CACHE_KEY, new TypeReference<List<UserPostRelation>>() {
+                        });
+                    }
+
+                    for (int i = 1; i <= leaderConfig.getLevel(); i++) {
+                        for (Post post : posts) {
+                            if (post.getId().equals(finalCurrentPostParentId) && leaderConfig.getLevel() == i) {
+                                final long parentId = finalCurrentPostParentId;
+
+                                List<Long> leaderUserIds = userPostRelations.stream().filter(x -> x.getPostId().equals(parentId)).map(UserPostRelation::getUserId).collect(Collectors.toList());
+//                                List<Long> leaderUserIds = users.stream().filter(u -> u.getPostId().equals(parentId)).map(User::getId).collect(Collectors.toList());
+                                result.addAll(leaderUserIds);
+                                finalCurrentPostParentId = post.getParentId();
+                            }
+                        }
+                    }
+                }
+
+
+            }
+            //表单字段
+            if (memberConfig.getMemberType() == WorkflowMemberType.FORM_FIELD.getCode()) {
+                //将map 转为 java类  默认只有用户任务节点才有审批人
+                RuntimeService runtimeService = ProcessEngines.getDefaultProcessEngine().getRuntimeService();
+
+                FormFieldConfig formFieldConfigs = memberConfig.getFormFieldConfig();
+
+                Object formDataObj = runtimeService.getVariable(processId, formFieldConfigs.getFormKey());
+                Map<String, Object> formData = Convert.toMap(String.class, Object.class, formDataObj);
+
+                Object fieldValue = formData.get(formFieldConfigs.getFormField());
+
+                //可能转换不了 捕获错误。
+                try {
+                    if (fieldValue instanceof String) {
+                        //如果包含逗号 表示 可能多个
+                        String approveStr = StrUtil.toString(fieldValue);
+                        if (approveStr.contains(StringPool.COMMA)) {
+                            String[] approveSplit = approveStr.split(StringPool.COMMA);
+
+                            for (String approve : approveSplit) {
+                                Long approveId = Convert.toLong(approve);
+                                result.add(approveId);
+                            }
+                        } else {
+                            Long approveId = Convert.toLong(fieldValue);
+                            result.add(approveId);
+                        }
+                    } else {
+                        Long approveId = Convert.toLong(fieldValue);
+                        result.add(approveId);
+                    }
+
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+
+            }
+        }
+        return result;
+    }
+
+
+    /**
+     * approveUserIds  获取所有所有委托userids
+     * 只用于查询审批人 包括委托人
+     *
+     * @return
+     */
+    public static List<Long> getDelegateUserIdsByApproveUserId(List<Long> approveUserIds, List<WorkflowDelegate> delegateList, Long schemaId) {
+
+        List<Long> result = new ArrayList<>(approveUserIds);
+        for (Long approveUserId : approveUserIds) {
+            List<WorkflowDelegate> thisUserDelegate = delegateList.stream().filter(d -> d.getUserId().equals(approveUserId) && d.getSchemaIds().contains(schemaId.toString())).collect(Collectors.toList());
+
+            for (WorkflowDelegate workflowDelegate : thisUserDelegate) {
+                String[] split = workflowDelegate.getDelegateUserIds().split(StringPool.COMMA);
+                List<Long> delegateUserIds = Arrays.stream(split).map(Convert::toLong).collect(Collectors.toList());
+
+                result.addAll(delegateUserIds);
+            }
+
+        }
+
+
+        return result;
+    }
+
+
+    /**
+     * 获取流程线的所有值  存入变量
+     *
+     * @param processDefinitionId  流程定义id
+     * @param workflowSchemaConfig schema
+     * @param nodeId               nodeId当前节点id(key)
+     * @param variableMap          变量Map
+     */
+    @Deprecated
+    public static void getFlowConditionVar(String processDefinitionId, WorkflowSchemaConfig workflowSchemaConfig, String nodeId, VariableMap variableMap, Map<String, Map<String, Object>> formData) {
+
+        RepositoryService repositoryService = ProcessEngines.getDefaultProcessEngine().getRepositoryService();
+
+        //找到当前节点 element 对象
+        ModelElementInstance currentElement = repositoryService.getBpmnModelInstance(processDefinitionId).getModelElementById(nodeId);
+        //找到当前用户节点的子节点 是网关的
+        Collection<Gateway> gateways = currentElement.getChildElementsByType(Gateway.class);
+
+        //如果下级节点有网关
+        if (gateways.size() > 0) {
+            //存储有哪些变量要 存入到工作流数据表中
+            List<String> allFlowVarKeys = new ArrayList<>();
+            //存储有哪些变量需要从表单的某个字段获取值
+            List<String> allFlowVarBindFormField = new ArrayList<>();
+            //所有流程线配置
+            List<ConditionConfig> conditionConfigs = new ArrayList<>();
+            //流程线的key
+            List<String> allFlowKey = new ArrayList<>();
+
+            //遍历所有网关
+            for (Gateway gateway : gateways) {
+                //找到网关所有的下级连接线 的id
+                List<String> gatewayFlowIds = gateway.getOutgoing().stream().map(BaseElement::getId).collect(Collectors.toList());
+
+                //遍历所有连接线的配置
+                for (String gatewayFlowId : gatewayFlowIds) {
+                    Optional<Map<String, Object>> sequenceflowConfigMapOp = workflowSchemaConfig.getChildNodeConfig().stream().filter(x -> x.containsValue(gatewayFlowId)).findFirst();
+
+                    //流程线 流转条件 变量名字 取名规则 form:{nodeId}:{formId}
+                    sequenceflowConfigMapOp.ifPresent(flow -> {
+                        SequenceFlowConfig sequenceFlowConfig = Convert.convert(SequenceFlowConfig.class, flow);
+
+                        //获取到所有需要的  变量key  和绑定表单字段
+                        for (ConditionConfig config : sequenceFlowConfig.getConditionConfigs()) {
+                            conditionConfigs.add(config);
+                            allFlowKey.add(gatewayFlowId);
+                            allFlowVarKeys.add(WorkflowConstant.PROCESS_FORMDATA_PREFIX_KEY + config.getFormId());
+                            allFlowVarBindFormField.add(config.getFormField());
+                        }
+
+                    });
+                }
+            }
+
+
+            //key === flow:{flowId}:{nodeId}:{formId}:{formField}   value == 需要存入的值
+            for (int i = 0; i < allFlowKey.size(); i++) {
+                Object formDataObject = formData.get(allFlowVarKeys.get(i));
+                Map<String, Object> formDataMap = Convert.toMap(String.class, Object.class, formDataObject);
+                if (formDataMap != null) {
+                    Object fieldValue = MapUtil.get(formDataMap, allFlowVarBindFormField.get(i), Object.class);
+                    ConditionConfig config = conditionConfigs.get(i);
+
+                    String key = WorkflowConstant.FLOW_PARAM_PREFIX_KEY +
+                            allFlowKey.get(i) + StringPool.COLON +
+                            config.getNodeId() + StringPool.COLON + config.getFormId() + StringPool.COLON +
+                            config.getFormField();
+
+
+                    variableMap.putValue(key, fieldValue);
+
+                }
+
+            }
+
+
+        }
+    }
+
+
+    /**
+     * 根据子集查询下一个执行节点id
+     *
+     * @param variableMap 变量map
+     * @param list        当前节点的子节点
+     * @return
+     */
+    @Deprecated
+    public static String getNextTaskNode(VariableMap variableMap, List<FlowNode> list) {
+        String result = null;
+        for (FlowNode flowNode : list) {
+            //如果是用户任务
+            if (WorkflowConstant.BPMN_USER_TASK_TYPE_NAME.equals(flowNode.getElementType().getTypeName())) {
+                result = flowNode.getId();
+            }
+            //如果是排他网关
+            else if (WorkflowConstant.BPMN_EXCLUSIVE_GATEWAY_TYPE_NAME.equals(flowNode.getElementType().getTypeName())) {
+                Collection<SequenceFlow> outgoing = flowNode.getOutgoing();
+
+
+                for (SequenceFlow sequenceFlow : outgoing) {
+                    ConditionExpression conditionExpression = sequenceFlow.getConditionExpression();
+                    ExpressionFactory expressionFactory = new ExpressionFactoryImpl();
+                    SimpleContext simpleContext = new SimpleContext();
+
+                    for (String key : variableMap.keySet()) {
+                        ValueExpression valueExpression = expressionFactory.createValueExpression(variableMap.get(key), String.class);
+                        simpleContext.setVariable(key, valueExpression);
+                    }
+                    ValueExpression valueExpression = expressionFactory.createValueExpression(simpleContext, conditionExpression.getTextContent(), boolean.class);
+                    //判断是否满足条件
+                    if ((Boolean) valueExpression.getValue(simpleContext)) {
+                        return sequenceFlow.getTarget().getId();
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+
+    @Deprecated
+    public static boolean judgmentCondition(SequenceFlow sequenceFlow, Map<String, Object> variableMap, List<Map<String, Object>> childNodeConfig) {
+
+        Optional<Map<String, Object>> flowConfigOp = childNodeConfig.stream().filter(x -> x.containsValue(sequenceFlow.getId())).findFirst();
+        if (!flowConfigOp.isPresent()) {
+            return false;
+        }
+        //将map 转为 java类
+        SequenceFlowConfig sequenceFlowConfig = Convert.convert(SequenceFlowConfig.class, flowConfigOp.get());
+
+
+        ConditionExpression conditionExpression = sequenceFlow.getConditionExpression();
+        ExpressionFactory expressionFactory = new ExpressionFactoryImpl();
+        SimpleContext simpleContext = new SimpleContext();
+
+        for (String key : variableMap.keySet()) {
+            ValueExpression valueExpression = expressionFactory.createValueExpression(variableMap.get(key), String.class);
+            simpleContext.setVariable(key, valueExpression);
+        }
+        ValueExpression valueExpression = expressionFactory.createValueExpression(simpleContext, conditionExpression.getTextContent(), boolean.class);
+        //判断是否满足条件
+        return (Boolean) valueExpression.getValue(simpleContext);
+
+    }
+
+    /**
+     * 根据id 从 工作流模板配置 获取 节点的配置信息
+     *
+     * @param id              节点id
+     * @param childNodeConfig 所有节点配置
+     * @param clazz           类型
+     * @param <T>             配置类型
+     * @return
+     */
+    public static <T> T getNodeConfig(String id, List<Map<String, Object>> childNodeConfig, Class<T> clazz) {
+        Optional<Map<String, Object>> configOp = childNodeConfig.stream().filter(c -> c.containsValue(id)).findFirst();
+        return configOp.map(stringObjectMap -> Convert.convert(clazz, stringObjectMap)).orElse(null);
+    }
+
+    /**
+     * 执行参数赋值
+     *
+     * @param assignmentConfig 赋值配置
+     * @param processParam     流程参数
+     * @param varMap           流程变量
+     */
+    public static void invokeParamAssignment(AssignmentConfig assignmentConfig, Map<String, Object> processParam, Map<String, Object> varMap, WorkflowSchema workflowSchema) {
+        List<ParamAssignmentConfig> paramAssignmentConfigs = assignmentConfig.getParamAssignmentConfigs();
+
+        for (ParamAssignmentConfig paramAssignmentConfig : paramAssignmentConfigs) {
+            //如果是值
+            if (paramAssignmentConfig.getType() == ParamAssignmentType.VALUE.getCode()) {
+                if (processParam.containsKey(paramAssignmentConfig.getTarget())) {
+                    processParam.put(paramAssignmentConfig.getTarget(), paramAssignmentConfig.getValue());
+                }
+            }
+            //如果是变量
+            if (paramAssignmentConfig.getType() == ParamAssignmentType.VAR.getCode()) {
+
+                Long serialNumber = MapUtil.get(varMap, WorkflowConstant.PROCESS_SERIAL_NUMBER_KEY, Long.class);
+
+                String newStr = WorkFlowUtil.replacePlaceHolder(paramAssignmentConfig.getVarValue(), workflowSchema, serialNumber, 0L);
+
+                processParam.put(paramAssignmentConfig.getTarget(), newStr);
+            }
+            //如果是表单
+            if (paramAssignmentConfig.getType() == ParamAssignmentType.FORM.getCode()) {
+
+                //使用3下划线切割
+                String[] split = paramAssignmentConfig.getFormConfig().getKey().split(StringPool.UNDERSCORE + StringPool.UNDERSCORE + StringPool.UNDERSCORE);
+                String formKey = split[1];
+                Map<String, Object> formData = MapUtil.get(varMap, formKey, new cn.hutool.core.lang.TypeReference<Map<String, Object>>() {});
+                Object fieldValue = MapUtil.get(formData, paramAssignmentConfig.getFormConfig().getFormField(), Object.class);
+
+                processParam.put(paramAssignmentConfig.getTarget(), fieldValue);
+            }
+
+            //如果是api
+            if (paramAssignmentConfig.getType() == ParamAssignmentType.API.getCode()) {
+
+                IMagicApiService magicApiService = SpringUtil.getBean(IMagicApiService.class);
+                MagicAPIService magicAPIService = SpringUtil.getBean(MagicAPIService.class);
+
+                ApiConfig apiConfig = paramAssignmentConfig.getApiConfig();
+
+                MagicApiInfoVo info = magicApiService.info(apiConfig.getId());
+//                Long serialNumber = MapUtil.get(varMap, WorkflowConstant.PROCESS_SERIAL_NUMBER_KEY, Long.class);
+
+
+                Map<String, Object> params = new HashMap<>();
+                for (ApiRequestParamsConfig requestParamsConfig : apiConfig.getRequestParamsConfigs()) {
+                    initApiParams(processParam, varMap, params, requestParamsConfig);
+                }
+                for (ApiRequestParamsConfig requestHeaderConfig : apiConfig.getRequestHeaderConfigs()) {
+                    initApiParams(processParam, varMap, params, requestHeaderConfig);
+                }
+                for (ApiRequestParamsConfig requestBodyConfig : apiConfig.getRequestBodyConfigs()) {
+                    initApiParams(processParam, varMap, params, requestBodyConfig);
+                }
+
+
+                Object result = magicAPIService.execute(info.getMethod(), info.getPath(), params);
+
+                processParam.put(paramAssignmentConfig.getTarget(), result);
+
+            }
+
+        }
+
+    }
+
+    public static void initApiParams(Map<String, Object> processParam, Map<String, Object> varMap, Map<String, Object> params, ApiRequestParamsConfig requestParamsConfig) {
+        if (Objects.equals(requestParamsConfig.getAssignmentType(), "value")) { //值
+            params.put(requestParamsConfig.getName(), requestParamsConfig.getValue());
+        } else if (Objects.equals(requestParamsConfig.getAssignmentType(), "processParameter")) { //流程参数
+            params.put(requestParamsConfig.getName(), processParam.get(requestParamsConfig.getConfig()));
+        } else if (Objects.equals(requestParamsConfig.getAssignmentType(), "originator")) {//发起人信息
+
+            //如果是设置的发起人
+            if (requestParamsConfig.getConfig().contains("initiator_id")) {
+                Long startUserId = MapUtil.get(varMap, WorkflowConstant.PROCESS_START_USER_ID_KEY, Long.class);
+
+                params.put(requestParamsConfig.getName(), startUserId);
+            }
+            //组织架构名称
+            if (requestParamsConfig.getConfig().contains("initiator_dept_name")) {
+                Long startUserId = MapUtil.get(varMap, WorkflowConstant.PROCESS_START_USER_ID_KEY, Long.class);
+                RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
+                List<UserDeptRelation> userDeptRelations = redisUtil.get(GlobalConstant.USER_DEPT_RELATION_CACHE_KEY, new TypeReference<List<UserDeptRelation>>() {
+                });
+
+                List<Department> departmentList = redisUtil.get(GlobalConstant.DEP_CACHE_KEY, new TypeReference<List<Department>>() {
+                });
+                List<UserDeptRelation> startUserDeptRelation = userDeptRelations.stream().filter(x -> x.getUserId().equals(startUserId)).collect(Collectors.toList());
+
+                List<Long> allDeptIds = startUserDeptRelation.stream().map(UserDeptRelation::getDeptId).collect(Collectors.toList());
+
+                List<Department> allDept = departmentList.stream().filter(x -> allDeptIds.contains(x.getId())).collect(Collectors.toList());
+
+                params.put(requestParamsConfig.getName(), allDept.stream().map(Department::getName).collect(Collectors.joining(",")));
+            }
+        } else {//表单数据
+            //使用3下划线切割
+            String[] split = requestParamsConfig.getConfig().split(StringPool.UNDERSCORE + StringPool.UNDERSCORE + StringPool.UNDERSCORE);
+            String formKey = split[1];
+            Map<String, Object> formData = MapUtil.get(varMap, formKey, new cn.hutool.core.lang.TypeReference<Map<String, Object>>() {
+            });
+            Object fieldValue = MapUtil.get(formData, split[2], Object.class);
+            params.put(requestParamsConfig.getName(), fieldValue);
+        }
+    }
+
+
+    /**
+     * 根据配置信息 执行表单赋值
+     *
+     * @param formData         表单数据
+     * @param assignmentConfig 赋值配置
+     * @param formConfigs      表单配置
+     * @param varMap           变量的Map
+     */
+    public static void invokeFormAssignment(Map<String, Map<String, Object>> formData, AssignmentConfig assignmentConfig, List<FormConfig> formConfigs, Map<String, Object> varMap) {
+        //获取 所有赋值操作数据
+
+        List<FormAssignmentConfig> formAssignmentConfigs = assignmentConfig.getFormAssignmentConfigs();
+
+        for (FormAssignmentConfig formAssignmentConfig : formAssignmentConfigs) {
+            Object currentVariable = varMap.get(formAssignmentConfig.getSource());
+
+            FormAssignmentSourceConfig targetSource = formAssignmentConfig.getTarget();
+
+            Optional<FormConfig> formConfig = formConfigs.stream().filter(form -> form.getFormId().equals(targetSource.getFormId())).findFirst();
+            formConfig.ifPresent(form -> {
+                Map<String, Object> currentFormData = formData.get(form.getKey());
+
+                if (currentFormData.containsKey(targetSource.getFormField())) {
+                    currentFormData.put(targetSource.getFormField(), currentVariable);
+                }
+
+            });
+        }
+    }
+
+
+    /**
+     * 判断是否有权限
+     *
+     * @param authConfig 权限配置
+     * @return 是否有权限
+     */
+    public static Boolean hasPermissions(AuthConfig authConfig) {
+        //如果是所有人 默认返回true
+        if (authConfig.getAuthType() == WorkflowAuth.ALL.getCode()) {
+            return true;
+        }
+        List<MemberConfig> authMemberConfigs = authConfig.getAuthMemberConfigs();
+        SaSession tokenSession = StpUtil.getTokenSession();
+        User user = tokenSession.get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
+
+        //这里可以不传 第二个参数 因为 只有角色岗位人员  用不着childNodeConfig
+        List<Long> userIdsByMemberConfig = getUserIdsByMemberConfig(authMemberConfigs, null, null);
+
+        return userIdsByMemberConfig.contains(user.getId());
+    }
+
+
+    /**
+     * 获取流程参数
+     *
+     * @param processParamConfigs
+     * @param workflowSchema
+     * @return
+     */
+    @SneakyThrows
+    public static Map<String, Object> getProcessParam(List<ProcessParamConfig> processParamConfigs, WorkflowSchema workflowSchema, Long serialNumber,Map<String, Object> varMap) {
+        Map<String, Object> result = new HashMap<>(processParamConfigs.size());
+
+        for (ProcessParamConfig processParamConfig : processParamConfigs) {
+
+            //如果是值  直接前端配置  直接传入就行
+            if (processParamConfig.getType() == WorkflowParamType.VALUE.getCode()) {
+                result.put(processParamConfig.getId(), processParamConfig.getValue());
+            }
+            //如果是变量
+            if (processParamConfig.getType() == WorkflowParamType.VAR.getCode()) {
+
+                String newStr = WorkFlowUtil.replacePlaceHolder(processParamConfig.getValue(), workflowSchema, serialNumber, 0L);
+
+                result.put(processParamConfig.getId(), newStr);
+            }
+
+
+            //如果是api
+            if (processParamConfig.getType() == WorkflowParamType.API.getCode()) {
+
+                IMagicApiService magicApiService = SpringUtil.getBean(IMagicApiService.class);
+                MagicAPIService magicAPIService = SpringUtil.getBean(MagicAPIService.class);
+
+                ApiConfig apiConfig = processParamConfig.getApiConfig();
+
+                MagicApiInfoVo info = magicApiService.info(processParamConfig.getApiConfig().getId());
+
+                Map<String, Object> params = new HashMap<>();
+                for (ApiRequestParamsConfig requestParamsConfig : apiConfig.getRequestParamsConfigs()) {
+                    //值
+                    if (Objects.equals(requestParamsConfig.getAssignmentType(), "value")) {
+                        params.put(requestParamsConfig.getName(), requestParamsConfig.getValue());
+                    } else if (Objects.equals(requestParamsConfig.getAssignmentType(),"originator")) {
+                        dealApiParam(requestParamsConfig,params,varMap);
+                    }
+                    //流程数据
+                    else {
+                        String newStr = WorkFlowUtil.replacePlaceHolder(requestParamsConfig.getConfig(), workflowSchema, serialNumber, 0L);
+                        params.put(requestParamsConfig.getName(), newStr);
+                    }
+
+                }
+                for (ApiRequestParamsConfig requestParamsConfig : apiConfig.getRequestHeaderConfigs()) {
+                    //值
+                    if (Objects.equals(requestParamsConfig.getDataType(), "value")) {
+                        params.put(requestParamsConfig.getName(), requestParamsConfig.getValue());
+                    }else if (Objects.equals(requestParamsConfig.getAssignmentType(),"originator")) {
+                        dealApiParam(requestParamsConfig,params,varMap);
+                    }
+                    //流程参数
+                    else {
+                        String newStr = WorkFlowUtil.replacePlaceHolder(processParamConfig.getValue(), workflowSchema, serialNumber, 0L);
+                        params.put(requestParamsConfig.getName(), newStr);
+                    }
+
+                }
+                for (ApiRequestParamsConfig requestParamsConfig : apiConfig.getRequestBodyConfigs()) {
+                    //值
+                    if (Objects.equals(requestParamsConfig.getDataType(), "value")) {
+                        params.put(requestParamsConfig.getName(), requestParamsConfig.getValue());
+                    }else if (Objects.equals(requestParamsConfig.getAssignmentType(),"originator")) {
+                        dealApiParam(requestParamsConfig,params,varMap);
+                    }
+                    //流程参数
+                    else {
+                        String newStr = WorkFlowUtil.replacePlaceHolder(processParamConfig.getValue(), workflowSchema, serialNumber, 0L);
+                        params.put(requestParamsConfig.getName(), newStr);
+                    }
+
+                }
+                Object execute = magicAPIService.execute(info.getMethod(), info.getPath(), params);
+                result.put(processParamConfig.getId(), execute);
+            }
+        }
+
+        return result;
+    }
+
+    private static void dealApiParam(ApiRequestParamsConfig requestParamsConfig, Map<String, Object> params, Map<String, Object> varMap){
+        if (CollectionUtils.isEmpty(varMap)){//为空就是发起时的数据
+            //如果是拿发起人的数据,作为流程参数,发起和重新发起赋值,使用当前登录人的信息,如果是其它节点,使用流程的发起人数据信息
+            SaSession tokenSession = StpUtil.getTokenSession();
+            User user = tokenSession.get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
+            //如果是设置的发起人
+            if (requestParamsConfig.getConfig().contains("initiator_id")) {
+                params.put(requestParamsConfig.getName(), user.getId().toString());
+            }
+            //组织架构名称
+            if (requestParamsConfig.getConfig().contains("initiator_dept_name")) {
+                RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
+                List<UserDeptRelation> userDeptRelations = redisUtil.get(GlobalConstant.USER_DEPT_RELATION_CACHE_KEY, new TypeReference<List<UserDeptRelation>>() {
+                });
+
+                List<Department> departmentList = redisUtil.get(GlobalConstant.DEP_CACHE_KEY, new TypeReference<List<Department>>() {
+                });
+                List<UserDeptRelation> startUserDeptRelation = userDeptRelations.stream().filter(x -> x.getUserId().equals(user.getId())).collect(Collectors.toList());
+
+                List<Long> allDeptIds = startUserDeptRelation.stream().map(UserDeptRelation::getDeptId).collect(Collectors.toList());
+
+                List<Department> allDept = departmentList.stream().filter(x -> allDeptIds.contains(x.getId())).collect(Collectors.toList());
+
+                params.put(requestParamsConfig.getName(), allDept.stream().map(Department::getName).collect(Collectors.joining(",")));
+            }
+        }else {
+            //如果是设置的发起人
+            if (requestParamsConfig.getConfig().contains("initiator_id")) {
+                Long startUserId = MapUtil.get(varMap, WorkflowConstant.PROCESS_START_USER_ID_KEY, Long.class);
+                params.put(requestParamsConfig.getName(), startUserId);
+            }
+            //组织架构名称
+            if (requestParamsConfig.getConfig().contains("initiator_dept_name")) {
+                Long startUserId = MapUtil.get(varMap, WorkflowConstant.PROCESS_START_USER_ID_KEY, Long.class);
+                RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
+                List<UserDeptRelation> userDeptRelations = redisUtil.get(GlobalConstant.USER_DEPT_RELATION_CACHE_KEY, new TypeReference<List<UserDeptRelation>>() {
+                });
+                List<Department> departmentList = redisUtil.get(GlobalConstant.DEP_CACHE_KEY, new TypeReference<List<Department>>() {
+                });
+                List<UserDeptRelation> startUserDeptRelation = userDeptRelations.stream().filter(x -> x.getUserId().equals(startUserId)).collect(Collectors.toList());
+
+                List<Long> allDeptIds = startUserDeptRelation.stream().map(UserDeptRelation::getDeptId).collect(Collectors.toList());
+
+                List<Department> allDept = departmentList.stream().filter(x -> allDeptIds.contains(x.getId())).collect(Collectors.toList());
+
+                params.put(requestParamsConfig.getName(), allDept.stream().map(Department::getName).collect(Collectors.joining(",")));
+            }
+        }
+    }
+
+    /**
+     * 自定义表单默认使用的就是数据库字段名
+     * 而系统表单的表单数据 默认会将 数据库字段转为驼峰命名
+     * 所以将formData 的所有key 转为数据库字段名
+     * 为了系统表单 所填写 表单数据 能够使用自定义表单接口执行
+     *
+     * @param forData
+     * @return
+     */
+    public Map<String, Object> conventFormDataKeyToDbField(Map<String, Object> forData) {
+        return null;
+    }
+
+
+    /**
+     * 清理掉此任务得所有超时提醒
+     *
+     * @param taskId
+     */
+    public static void removeTaskTimeoutRemid(String taskId) {
+        RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
+        redisUtil.deleteBatch(WorkflowConstant.TIMEOUT_REMID_KEY + taskId + StringPool.COLON + StringPool.STAR);
+    }
+
+    /**
+     * 清理掉此流程所有的缓存
+     *
+     * @param processInstanceId 流程实例id
+     */
+    public static void removeProcessCache(String processInstanceId) {
+        RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
+        redisUtil.deleteBatch(processInstanceId + StringPool.COLON + StringPool.STAR);
+    }
+
+    /**
+     * 缓存所有 任务审批人的 记录
+     * 格式:
+     * {
+     * "taskid1" : [userid]                 //普通用户任务节点
+     * "taskid2" : [userid,userid,userid]   //会签任务节点(并行)
+     * }
+     *
+     * @param taskId
+     */
+    public static void cacheTaskAssigneeRecord(String processInstanceId, String taskId, Long userId) {
+        RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
+        if (redisUtil.containsKey(processInstanceId + WorkflowConstant.TASK_ASSIGNEE_RECORD_VAR_KEY)) {
+            Map<String, List<Long>> taskAssigneeRecordMap = redisUtil.get(processInstanceId + WorkflowConstant.TASK_ASSIGNEE_RECORD_VAR_KEY, new TypeReference<Map<String, List<Long>>>() {
+            });
+            List<Long> assigneeUserIds = new ArrayList<>();
+            if (taskAssigneeRecordMap.containsKey(taskId)) {
+                assigneeUserIds = taskAssigneeRecordMap.get(taskId);
+                assigneeUserIds.add(userId);
+            } else {
+                assigneeUserIds.add(userId);
+            }
+            taskAssigneeRecordMap.put(taskId, assigneeUserIds);
+            redisUtil.set(processInstanceId + WorkflowConstant.TASK_ASSIGNEE_RECORD_VAR_KEY, taskAssigneeRecordMap);
+        } else {
+            //必须要有序Map
+            Map<String, List<Long>> taskAssigneeRecordMap = new LinkedHashMap<>(1);
+            List<Long> assigneeUserIds = new ArrayList<>();
+            assigneeUserIds.add(StpUtil.getLoginIdAsLong());
+            taskAssigneeRecordMap.put(taskId, assigneeUserIds);
+            redisUtil.set(processInstanceId + WorkflowConstant.TASK_ASSIGNEE_RECORD_VAR_KEY, taskAssigneeRecordMap);
+        }
+    }
+
+
+    /**
+     * 根据流程id  和 任务id  获取审批人
+     *
+     * @param processInstanceId
+     * @param taskId
+     * @return
+     */
+    public static List<Long> getTaskAssigneeRecordCache(String processInstanceId, String taskId) {
+        RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
+
+        Map<String, List<Long>> cache = redisUtil.get(processInstanceId + WorkflowConstant.TASK_ASSIGNEE_RECORD_VAR_KEY, new TypeReference<Map<String, List<Long>>>() {
+        });
+
+        return cache.get(taskId);
+
+    }
+
+
+    /**
+     * 缓存任务审批类型 记录
+     *
+     * @param processInstanceId   流程实例id
+     * @param taskId              任务id
+     * @param workflowApproveType 审批类型枚举
+     */
+    public static void cacheTaskApproveType(String processInstanceId, String taskId, Integer workflowApproveType) {
+        RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
+        Map<String, Integer> approveTypeMap;
+        if (redisUtil.containsKey(processInstanceId + WorkflowConstant.TASK_APPROVE_TYPE_RECORD_VAR_KEY)) {
+            approveTypeMap = redisUtil.get(processInstanceId + WorkflowConstant.TASK_APPROVE_TYPE_RECORD_VAR_KEY, new TypeReference<Map<String, Integer>>() {
+            });
+
+
+        } else {
+            approveTypeMap = new HashMap<>(1);
+        }
+        approveTypeMap.put(taskId, workflowApproveType);
+        redisUtil.set(processInstanceId + WorkflowConstant.TASK_APPROVE_TYPE_RECORD_VAR_KEY, approveTypeMap);
+    }
+
+    /**
+     * 根据参数获取审批类型
+     *
+     * @param processInstanceId
+     * @param taskId
+     * @return
+     */
+    public static Integer getTaskApproveTypeCache(String processInstanceId, String taskId) {
+        RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
+        Map<String, Integer> approveTypeMap = redisUtil.get(processInstanceId + WorkflowConstant.TASK_APPROVE_TYPE_RECORD_VAR_KEY, new TypeReference<Map<String, Integer>>() {
+        });
+
+        return approveTypeMap.get(taskId);
+    }
+
+    /**
+     * 发送传阅人消息提醒(多线程 异步)
+     *
+     * @param param
+     */
+    public static void sendCirculatedNoticePolicy(NoticePolicyParam param, String name) {
+        //如果有设置通知策略
+        if (param.getNoticePolicyConfigs().size() > 0) {
+
+            //如果包含系统消息
+            if (param.getNoticePolicyConfigs().contains(WorkflowNoticePolicyType.SYSTEM.getCode())) {
+                CompletableFuture.runAsync(() -> {
+                    SendMessageUtil.sendWorkflowCirculatedMessage(param);
+                });
+            }
+
+            //如果包含短信
+            if (param.getNoticePolicyConfigs().contains(WorkflowNoticePolicyType.SMS.getCode())) {
+                //TODO 发送短信
+                CompletableFuture.runAsync(() -> {
+                    SendMessageUtil.sendWorkflowCirculatedSms(param, name);
+                });
+            }
+            //如果包含email
+            if (param.getNoticePolicyConfigs().contains(WorkflowNoticePolicyType.EMAIL.getCode())) {
+                //TODO 发邮件
+                CompletableFuture.runAsync(() -> {
+                    SendMessageUtil.sendWorkflowCirculatedEmail(param);
+                });
+            }
+            //如果包含企业微信
+            if (param.getNoticePolicyConfigs().contains(WorkflowNoticePolicyType.WECHAT.getCode())) {
+                //TODO 发送企业微信
+            }
+            //如果包含钉钉
+            if (param.getNoticePolicyConfigs().contains(WorkflowNoticePolicyType.DING.getCode())) {
+                //TODO 发送钉钉
+            }
+
+        }
+    }
+
+    /**
+     * 发送审批人消息提醒(多线程 异步)
+     *
+     * @param param
+     */
+    public static void sendApproveNoticePolicy(NoticePolicyParam param, String name) {
+        //如果有设置通知策略
+        if (param.getNoticePolicyConfigs().size() > 0) {
+
+            //如果包含系统消息
+            if (param.getNoticePolicyConfigs().contains(WorkflowNoticePolicyType.SYSTEM.getCode())) {
+                CompletableFuture.runAsync(() -> {
+                    SendMessageUtil.sendWorkflowApproveMessage(param);
+                });
+            }
+
+            //如果包含短信
+            if (param.getNoticePolicyConfigs().contains(WorkflowNoticePolicyType.SMS.getCode())) {
+                //TODO 发送短信
+                CompletableFuture.runAsync(() -> {
+                    SendMessageUtil.sendWorkflowApproveSms(param, name);
+                });
+            }
+            //如果包含email
+            if (param.getNoticePolicyConfigs().contains(WorkflowNoticePolicyType.EMAIL.getCode())) {
+                CompletableFuture.runAsync(() -> {
+                    SendMessageUtil.sendWorkflowApproveEmail(param);
+                });
+            }
+            //如果包含企业微信
+            if (param.getNoticePolicyConfigs().contains(WorkflowNoticePolicyType.WECHAT.getCode())) {
+                CompletableFuture.runAsync(() -> {
+                    SendMessageUtil.sendWorkflowApproveWx(param, name);
+                });
+            }
+            //如果包含钉钉
+            if (param.getNoticePolicyConfigs().contains(WorkflowNoticePolicyType.DING.getCode())) {
+                //TODO 发送钉钉
+            }
+
+        }
+    }
+
+    /**
+     * 发送超时消息提醒(多线程 异步)
+     *
+     * @param param
+     */
+    public static void sendTimeOutNoticePolicy(NoticePolicyParam param, String name) {
+        //如果有设置通知策略
+        if (param.getNoticePolicyConfigs().size() > 0) {
+
+            //如果包含系统消息
+            if (param.getNoticePolicyConfigs().contains(WorkflowNoticePolicyType.SYSTEM.getCode())) {
+                CompletableFuture.runAsync(() -> {
+                    SendMessageUtil.sendWorkflowTimeoutMessage(param);
+                });
+            }
+
+            //如果包含短信
+            if (param.getNoticePolicyConfigs().contains(WorkflowNoticePolicyType.SMS.getCode())) {
+                //TODO 发送短信
+                CompletableFuture.runAsync(() -> {
+                    SendMessageUtil.sendWorkflowTimeoutSms(param, name);
+                });
+            }
+            //如果包含email
+            if (param.getNoticePolicyConfigs().contains(WorkflowNoticePolicyType.EMAIL.getCode())) {
+                CompletableFuture.runAsync(() -> {
+                    SendMessageUtil.sendWorkflowTimeoutEmail(param);
+                });
+            }
+            //如果包含企业微信
+            if (param.getNoticePolicyConfigs().contains(WorkflowNoticePolicyType.WECHAT.getCode())) {
+                //TODO 发送企业微信
+            }
+            //如果包含钉钉
+            if (param.getNoticePolicyConfigs().contains(WorkflowNoticePolicyType.DING.getCode())) {
+                //TODO 发送钉钉
+            }
+
+        }
+    }
+
+    /**
+     * 根据用户id获取对应的用户名,并进行拼接输出
+     *
+     * @param allUserIds 用户ids
+     * @return 用户名(张三、李四)
+     */
+    public static String getAllUserNamesByIds(List<Long> allUserIds) {
+        String allDelegateUserNames = StringPool.EMPTY;
+
+        //TODO 第一种:redis中获取用户 ->用户数量太多时json反序列化耗时比较长
+//        RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
+//        List<User> userList = redisUtil.get(GlobalConstant.USER_CACHE_KEY, new TypeReference<List<User>>() {});
+//        //如果缓存中不存在用户信息,就直接去数据库查询,并保存到缓存中去
+//        if (userList.size() == 0) {
+//            IUserService userService = SpringUtil.getBean(IUserService.class);
+//            userList = userService.list();
+//            redisUtil.set(GlobalConstant.USER_CACHE_KEY, userList);
+//        }
+//        if (allUserIds.size() > 0) {
+//            //获取用户id对应的用户名
+//            List<String> allUserName = userList.stream().filter(u -> allUserIds.contains(u.getId())).map(User::getName).collect(Collectors.toList());
+//            String chineseDot = "、";
+//
+//            if (allUserName.size() > 0) {
+//                allDelegateUserNames = StrUtil.join(chineseDot, allUserName);
+//            }
+//        }
+//        return allDelegateUserNames;
+
+        //TODO 第二种:mysql中直接获取用户信息,数据量大时建议采用第二种
+        if (allUserIds != null && allUserIds.size()>0){
+            IUserService userService = SpringUtil.getBean(IUserService.class);
+            List<User> userList = userService.listByIds(allUserIds);
+            if (allUserIds.size() > 0) {
+                //获取用户id对应的用户名
+                List<String> allUserName = userList.stream().filter(u -> allUserIds.contains(u.getId())).map(User::getName).collect(Collectors.toList());
+                String chineseDot = "、";
+
+                if (allUserName.size() > 0) {
+                    allDelegateUserNames = StrUtil.join(chineseDot, allUserName);
+                }
+            }
+        }
+        return allDelegateUserNames;
+
+    }
+
+    /**
+     * 发起流程时, 遍历所有节点 如果有需要监听器的  默认缓存 key -> deployId:ActivityId
+     *
+     * @param deployId        部署id
+     * @param childNodeConfig 所有节点配置
+     */
+    public static void cacheNodeListener(String deployId, List<Map<String, Object>> childNodeConfig) {
+        RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
+        for (Map<String, Object> map : childNodeConfig) {
+            //转换为node 配置类型
+            NodeBasicConfig nodeBasicConfig = BeanUtil.toBean(map, NodeBasicConfig.class);
+
+            if (nodeBasicConfig.getStartEventConfigs().size() > 0) {
+                String startKey = WorkflowConstant.START_EVENT_CACHE_PRE + deployId + StringPool.COLON + nodeBasicConfig.getId() + StringPool.COLON + WorkflowEventType.START.getValue();
+                redisUtil.set(startKey, nodeBasicConfig.getStartEventConfigs());
+            }
+            if (nodeBasicConfig.getEndEventConfigs().size() > 0) {
+                String endKey = WorkflowConstant.END_EVENT_CACHE_PRE + deployId + StringPool.COLON + nodeBasicConfig.getId() + StringPool.COLON + WorkflowEventType.END.getValue();
+                redisUtil.set(endKey, nodeBasicConfig.getEndEventConfigs());
+            }
+
+
+        }
+    }
+
+    /**
+     * 删除监听器
+     *
+     * @param deployId 部署id
+     */
+    public static void removeNodeListener(String deployId) {
+        RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
+
+        redisUtil.deleteBatch(deployId + StringPool.COLON + "*");
+    }
+
+    /**
+     * 获取一天的开始时间 00:00:00
+     *
+     * @param date 某一天时间
+     * @return 某一天的开始时间, 比如2023-01-01 00:00:00
+     */
+    public static Date getStartOfDay(Date date) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.MINUTE, 0);
+        calendar.set(Calendar.SECOND, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+        return calendar.getTime();
+    }
+
+    /**
+     * 获取一天的结束时间 23:59:59
+     *
+     * @param date 某一天时间
+     * @return 某一天的结束时间, 比如2023-01-01 23:59:59
+     */
+    public static Date getEndOfDay(Date date) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        calendar.set(Calendar.HOUR_OF_DAY, 23);
+        calendar.set(Calendar.MINUTE, 59);
+        calendar.set(Calendar.SECOND, 59);
+        calendar.set(Calendar.MILLISECOND, 999);
+        return calendar.getTime();
+    }
+}
+

+ 21 - 0
src/main/java/com/xjrsoft/module/workflow/vo/TaskCountVo.java

@@ -0,0 +1,21 @@
+package com.xjrsoft.module.workflow.vo;
+
+import lombok.Data;
+
+/**
+ * @Author: tzx
+ * @Date: 2022/10/27 18:28
+ */
+@Data
+public class TaskCountVo {
+    /**
+     * 当前任务的表单信息
+     */
+    private Long nodeId;
+
+    /**
+     * 模板信息
+     */
+    private Integer recordCount;
+
+}

+ 10 - 0
src/main/resources/mapper/personnel/StundentFaceProcessMapper.xml

@@ -0,0 +1,10 @@
+<?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.xjrsoft.module.personnel.mapper.StundentFaceProcessMapper">
+    <select id="getOneByQuery" parameterType="com.xjrsoft.module.personnel.entity.FaceManagement" resultType="com.xjrsoft.module.personnel.entity.StundentFaceProcess">
+        SELECT * FROM stundent_face_process WHERE delete_mark = 0 AND user_id = #{dto.userId} and face_photo = #{dto.fileId}
+    </select>
+
+</mapper>

+ 59 - 0
src/main/resources/sqlScript/20241016_sql.sql

@@ -0,0 +1,59 @@
+-- 课表增加关键信息键字段
+-- 存课程的教师id、班级id、日期、节次、课程id、场地
+DROP PROCEDURE IF EXISTS createCom;
+DELIMITER $$
+CREATE
+    PROCEDURE createCom()
+BEGIN
+    IF NOT EXISTS(SELECT 1 FROM information_schema.columns  WHERE table_name='course_table' AND COLUMN_NAME = 'key_info') THEN
+ALTER TABLE `course_table`
+    ADD COLUMN `key_info` VARCHAR(1000) NULL COMMENT '关键信息(存课程的教师id、班级id、日期、课程id、场地、节次以“_”拼接)';
+END IF;
+END$$
+DELIMITER ;
+CALL createCom;
+DROP PROCEDURE createCom;
+
+UPDATE course_table SET key_info = CONCAT(teacher_id, '_', class_id, '_', schedule_date,'_', course_id, '_', site_id, '_', time_number);
+
+-- 课表增加关键信息键字段
+-- 存课程的教师id、班级id、日期、节次、课程id、场地
+DROP PROCEDURE IF EXISTS createCom;
+DELIMITER $$
+CREATE
+    PROCEDURE createCom()
+BEGIN
+    IF NOT EXISTS(SELECT 1 FROM information_schema.columns  WHERE table_name='course_table_bak' AND COLUMN_NAME = 'key_info') THEN
+ALTER TABLE `course_table_bak`
+    ADD COLUMN `key_info` VARCHAR(1000) NULL COMMENT '关键信息(存课程的教师id、班级id、日期、课程id、场地、节次以“_”拼接)';
+END IF;
+END$$
+DELIMITER ;
+CALL createCom;
+DROP PROCEDURE createCom;
+
+UPDATE course_table_bak SET key_info = CONCAT(teacher_id, '_', class_id, '_', schedule_date,'_', course_id, '_', site_id, '_', time_number);
+
+ALTER TABLE class_time_delete 
+ADD COLUMN `time_period` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '删除课程 多个用逗号隔开' AFTER enabled_mark;
+
+ALTER TABLE class_time_calendar 
+ADD COLUMN `time_period` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '补课课程 多个用逗号隔开' AFTER enabled_mark;
+
+ALTER TABLE class_time_calendar 
+ADD COLUMN `cover_type` INT NULL DEFAULT 0 COMMENT '覆盖方式 1:选中覆盖 0:全天覆盖' AFTER enabled_mark;
+
+-- 调课顶课流程数据表增加作废原因
+DROP PROCEDURE IF EXISTS createCom;
+DELIMITER $$
+CREATE
+    PROCEDURE createCom()
+BEGIN
+    IF NOT EXISTS(SELECT 1 FROM information_schema.columns  WHERE table_name='wf_course_adjust' AND COLUMN_NAME = 'cancel_reason') THEN
+ALTER TABLE `wf_course_adjust`
+    ADD COLUMN `cancel_reason` VARCHAR(1000) NULL COMMENT '关键信息(存课程的教师id、班级id、日期、节次、课程id、场地以“_”拼接)';
+END IF;
+END$$
+DELIMITER ;
+CALL createCom;
+DROP PROCEDURE createCom;

+ 60 - 0
src/main/resources/sqlScript/20241019_sql.sql

@@ -0,0 +1,60 @@
+-- ---------------------------------------------------------------------------------
+-- 考勤统计
+-- ---------------------------------------------------------------------------------
+DROP TABLE IF EXISTS attendance_statistics;
+CREATE TABLE `attendance_statistics` (
+  `id` BIGINT NOT NULL COMMENT '主键编号',
+  `create_user_id` BIGINT DEFAULT NULL COMMENT '创建人',
+  `create_date` DATETIME DEFAULT NULL COMMENT '创建时间',
+  `modify_user_id` BIGINT DEFAULT NULL COMMENT '修改人',
+  `modify_date` DATETIME DEFAULT NULL COMMENT '修改时间',
+  `delete_mark` INT NOT NULL COMMENT '删除标记',
+  `enabled_mark` INT NOT NULL COMMENT '有效标志',
+  `year` INT DEFAULT NULL COMMENT '统计年份',
+  `month` INT DEFAULT NULL COMMENT '统计月份',
+  `name` VARCHAR(100) DEFAULT NULL COMMENT '统计名称',
+  `start_date` DATE DEFAULT NULL COMMENT '开始日期',
+  `end_date` DATE DEFAULT NULL COMMENT '结束日期',
+  `attendance_rule_category_id` BIGINT DEFAULT NULL COMMENT '关联考勤规则(attendance_rule_category)',
+  `person_count` INT DEFAULT NULL COMMENT '统计人数',
+  `status` INT DEFAULT 0 COMMENT '状态(0:数据统计中 1:统计完成 2:已解锁)',
+  `is_need_refresh` INT DEFAULT 0 COMMENT '是否需要刷新(0:否 1:是)',
+  `time_period` INT DEFAULT NULL COMMENT '时间段(1:上午 2:下午)',
+  `attendance_days` INT DEFAULT NULL COMMENT '应出勤天数',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='考勤统计';
+
+-- ---------------------------------------------------------------------------------
+-- 考勤统计-教师详情
+-- ---------------------------------------------------------------------------------
+DROP TABLE IF EXISTS attendance_statistics_record;
+CREATE TABLE `attendance_statistics_record` (
+  `id` BIGINT NOT NULL COMMENT '主键编号',
+  `create_user_id` BIGINT DEFAULT NULL COMMENT '创建人',
+  `create_date` DATETIME DEFAULT NULL COMMENT '创建时间',
+  `modify_user_id` BIGINT DEFAULT NULL COMMENT '修改人',
+  `modify_date` DATETIME DEFAULT NULL COMMENT '修改时间',
+  `delete_mark` INT NOT NULL COMMENT '删除标记',
+  `enabled_mark` INT NOT NULL COMMENT '有效标志',
+  `attendance_statistics_id` BIGINT DEFAULT NULL COMMENT '课时统计(attendance_statistics)',
+  `user_id` BIGINT DEFAULT NULL COMMENT '教师id(xjr_user)',
+  `normal_count` INT DEFAULT NULL COMMENT '正常考勤次数',
+  `leave_count` INT DEFAULT NULL COMMENT '请假次数',
+  `absentee_count` INT DEFAULT NULL COMMENT '旷工次数',
+  `dept_ids` VARCHAR(1000) DEFAULT NULL COMMENT '所在部门(存在多个,用“,”隔开)',
+  `dept_name` VARCHAR(1000) DEFAULT NULL COMMENT '所在部门(存在多个,用“,”隔开)',
+  `days_data` LONGTEXT DEFAULT NULL COMMENT '每天数据',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='考勤统计-教师详情';
+
+
+ALTER TABLE `teacher_attendance_record`   
+  ADD COLUMN `record_time` DATETIME NULL   COMMENT '考勤时间' AFTER `car_number`;
+
+ALTER TABLE `teacher_attendance_record`   
+  ADD COLUMN `attendance_date` DATE NULL   COMMENT '考勤日期' AFTER `record_time`;
+
+ALTER TABLE `teacher_attendance_record`   
+  ADD COLUMN `start_time` DATETIME NULL   COMMENT '开始时间',
+  ADD COLUMN `end_time` DATETIME NULL   COMMENT '结束时间';
+

+ 13 - 0
src/main/resources/sqlScript/20241029_sql.sql

@@ -0,0 +1,13 @@
+-- ---------------------------------------------------------------------
+-- 评价管理增加年份和月份字段
+-- ---------------------------------------------------------------------
+ALTER TABLE `evaluate_manage`   
+  ADD COLUMN `year` INT NULL   COMMENT '指定年份' AFTER `source_id`,
+  ADD COLUMN `month` INT NULL   COMMENT '指定月份' AFTER `year`;
+
+
+-- ---------------------------------------------------------------------
+-- 菜单分组表增加字段
+-- ---------------------------------------------------------------------
+ALTER TABLE `xjr_menu_group_set`   
+  ADD COLUMN `role_id` BIGINT NULL   COMMENT '角色' AFTER `use_range`;

+ 8 - 0
src/main/resources/sqlScript/20241031_sql.sql

@@ -0,0 +1,8 @@
+ALTER TABLE `wf_teacher_course_time_detail`   
+  CHANGE `teacher_id` `teacher_id` LONGTEXT NULL   COMMENT '补课人员';
+
+ALTER TABLE `teacher_award`   
+  ADD COLUMN `award_date` DATE NULL   COMMENT '获奖日期' AFTER `whole_competition_name`;
+
+ALTER TABLE  `student_leave`   
+  ADD COLUMN `is_notice` INT DEFAULT 0  NULL   COMMENT '是否已通知班主任和家长(0:否 1:是)' AFTER `hikvision_result`;

+ 2 - 0
src/main/resources/sqlScript/20241107_sql.sql

@@ -0,0 +1,2 @@
+ALTER TABLE `class_time_statistics_record`   
+  ADD COLUMN `course_time_type_data` LONGTEXT NULL   COMMENT '课时补充数据,表头动态' AFTER `all_class_time_data`;

Datei-Diff unterdrückt, da er zu groß ist
+ 89 - 0
src/main/resources/sqlScript/question[2024年4月28日].sql


+ 51 - 0
src/main/resources/sqlScript/班级视图.sql

@@ -0,0 +1,51 @@
+DROP VIEW IF EXISTS class_data;
+CREATE VIEW class_data AS 
+SELECT t1.id, t1.name, t1.code,t2.name AS gradeName,t1.grade_id AS gradeId,t3.name AS teacherName,t3.mobile AS teacherMobile,t1.teacher_id AS teacherId,
+t4.name AS enrollTypeCn,t1.enroll_type AS enrollType,t5.name AS classTypeCn,t1.class_type AS classType,
+t6.name AS classroomName,t1.classroom_id AS classroomId,t7.name AS orgName,t1.org_id AS orgId,t8.name AS majorSetName,t1.major_set_id AS majorSetId,
+t1.is_order_class AS isOrderClass,t1.is_graduate AS isGraduate,
+(SELECT COUNT(DISTINCT(a1.id)) FROM xjr_user a1
+LEFT JOIN base_student_school_roll a2 ON a1.id = a2.user_id
+WHERE a1.delete_mark = 0 AND a2.delete_mark = 0
+AND a1.gender = 'SB10001' AND a2.class_id = t1.id
+AND a2.archives_status = 'FB2901') AS maleCount,
+(SELECT COUNT(DISTINCT(a1.id)) FROM xjr_user a1
+LEFT JOIN base_student_school_roll a2 ON a1.id = a2.user_id
+WHERE a1.delete_mark = 0 AND a2.delete_mark = 0
+AND a2.class_id = t1.id AND a2.stduy_status = 'FB3002'
+AND a2.archives_status = 'FB2901' AND a1.gender = 'SB10001') AS maleStayCount,
+(SELECT COUNT(DISTINCT(a1.id)) FROM xjr_user a1
+LEFT JOIN base_student_school_roll a2 ON a1.id = a2.user_id
+WHERE a1.delete_mark = 0 AND a2.delete_mark = 0
+AND a2.class_id = t1.id AND a2.stduy_status = 'FB3001'
+AND a2.archives_status = 'FB2901' AND a1.gender = 'SB10001') AS maleNotStayCount,
+(SELECT COUNT(DISTINCT(a1.id)) FROM xjr_user a1
+LEFT JOIN base_student_school_roll a2 ON a1.id = a2.user_id
+WHERE a1.delete_mark = 0 AND a2.delete_mark = 0
+AND a1.gender = 'SB10002' AND a2.class_id = t1.id
+AND a2.archives_status = 'FB2901') AS femaleCount,
+(SELECT COUNT(DISTINCT(a1.id)) FROM xjr_user a1
+LEFT JOIN base_student_school_roll a2 ON a1.id = a2.user_id
+WHERE a1.delete_mark = 0 AND a2.delete_mark = 0
+AND a2.class_id = t1.id AND a2.stduy_status = 'FB3002'
+AND a2.archives_status = 'FB2901' AND a1.gender = 'SB10002') AS femaleStayCount,
+(SELECT COUNT(DISTINCT(a1.id)) FROM xjr_user a1
+LEFT JOIN base_student_school_roll a2 ON a1.id = a2.user_id
+WHERE a1.delete_mark = 0 AND a2.delete_mark = 0
+AND a2.class_id = t1.id AND a2.stduy_status = 'FB3001'
+AND a2.archives_status = 'FB2901' AND a1.gender = 'SB10002') AS femaleNotStayCount,
+(SELECT COUNT(DISTINCT(a1.id)) FROM xjr_user a1
+LEFT JOIN base_student_school_roll a2 ON a1.id = a2.user_id
+WHERE a1.delete_mark = 0 AND a2.delete_mark = 0
+AND a2.class_id = t1.id
+AND a2.archives_status = 'FB2901') AS allCount
+ FROM base_class t1
+LEFT JOIN base_grade t2 ON t1.grade_id = t2.id
+LEFT JOIN xjr_user t3 ON t1.teacher_id = t3.id
+LEFT JOIN xjr_dictionary_detail t4 ON t4.code = t1.enroll_type
+LEFT JOIN xjr_dictionary_detail t5 ON t5.code = t1.class_type
+LEFT JOIN base_classroom t6 ON t6.id = t1.classroom_id
+LEFT JOIN xjr_department t7 ON t7.id = t1.org_id
+LEFT JOIN base_major_set t8 ON t8.id = t1.major_set_id
+WHERE t1.delete_mark = 0;
+

+ 217 - 0
src/main/resources/sqlScript/视图信息.sql

@@ -0,0 +1,217 @@
+DROP VIEW IF EXISTS student_data;
+CREATE VIEW student_data AS
+SELECT t1.credential_number AS credentialNumber,t2.student_id AS studentId,t3.candidate_number AS candidateNumber,t1.name,t15.name AS gender,
+t1.mobile,t1.email,t2.as_name AS asName,t2.en_name AS enName,t2.py_name AS pyName,
+t2.former_name AS formerName,t9.name AS credentialType,t1.birth_date AS birthDate,t10.name AS birthType,t11.name AS nation,
+t12.name AS bloodType,t13.name AS health,t14.name AS maritalState,t16.name AS politicalState,
+t17.name AS nationality,t18.name AS chineseType,t19.name AS houseHoldType,t20.name AS provinces,
+t21.name AS city,t22.name AS district,t23.name AS houseProvinces, t24.name AS houseCity,t25.name AS houseDistrict,
+t2.belongs_police AS belongsPolice,t26.name AS birthProvinces, t27.name AS birthCity,t28.name AS birthDistrict,t2.house_hold_address AS houseHoldAddress,
+t29.name AS residenceType,t2.train_interval AS trainInterval,REPLACE(REPLACE(t2.is_migrate_children,0,'否'),1,'是') AS isMigrateChildren,
+REPLACE(REPLACE(t2.is_floating_population,0,'否'),1,'是') AS isFloatingPopulation,t2.height ,t2.weight ,t2.vision ,t2.specialty ,
+t6.qq_msn AS qqMsn,t6.wechat,t6.user_page AS userPage,t3.roll_number AS rollNumber,t3.archives_number AS archivesNumber,
+t3.enrollment_date AS enrollmentDate,t31.name AS enrollmentType,t8.name AS gradeName,t32.name AS enrollType,t30.name AS majorSetName,
+t3.study_year AS studyYear,t7.name AS className,t33.name AS studentSource,t34.name AS studentType,t35.name AS archivesStatus,
+t36.name AS learnStatus,t37.name AS stduyStatus,t38.name AS chooseStatus,t39.name AS fosterType,t40.name AS recruitType,
+t41.name AS recruitTarget,t42.name AS highestEducation,t3.graduated_university AS graduatedUniversity,t3.graduated_score AS graduatedScore,
+t3.examinee_number AS examineeNumber,t3.ticket_number AS ticketNumber,t3.admission_scores AS admissionScores,t3.remark,
+REPLACE(REPLACE(t5.is_indemnify,0,'否'),1,'是') AS isIndemnify,REPLACE(REPLACE(t5.is_stipend,0,'否'),1,'是') AS isStipend,t5.stipend_stand AS stipendStand,
+t5.stipend_number AS stipendNumber,REPLACE(REPLACE(t5.is_free,0,'否'),1,'是') AS isFree,REPLACE(REPLACE(t5.is_filing_card,0,'否'),1,'是') AS isFilingCard,
+t4.zip_code AS zipCode,t4.telephone,t4.contact,t4.population,
+t4.address, t4.income_source AS incomeSource,t4.income_number AS incomeNumber, t4.railway_station AS railwayStation,t1.id,t3.grade_id AS gradeId,t3.class_id AS classId,
+t2.belongs_police AS belongsPolices,t43.name AS teacherName,t45.room_name AS roomName,t44.bed_number AS bedNumber FROM xjr_user t1
+INNER JOIN base_student t2 ON t1.id = t2.user_id
+LEFT JOIN base_student_school_roll t3 ON t3.user_id = t1.id
+LEFT JOIN base_student_family t4 ON t4.user_id = t1.id AND t4.delete_mark = 0
+LEFT JOIN base_student_subsidize t5 ON t5.user_id = t1.id
+LEFT JOIN base_student_contact t6 ON t6.id = t1.id
+LEFT JOIN base_class t7 ON t7.id = t3.class_id
+LEFT JOIN base_grade t8 ON t8.id = t3.grade_id
+LEFT JOIN xjr_dictionary_detail t9 ON t1.credential_type = t9.code
+LEFT JOIN xjr_dictionary_detail t10 ON t2.birth_type = t10.code
+LEFT JOIN xjr_dictionary_detail t11 ON t2.nation = t11.code
+LEFT JOIN xjr_dictionary_detail t12 ON t2.blood_type = t12.code
+LEFT JOIN xjr_dictionary_detail t13 ON t2.health = t13.code
+LEFT JOIN xjr_dictionary_detail t14 ON t2.marital_state = t14.code
+LEFT JOIN xjr_dictionary_detail t15 ON t1.gender = t15.code
+LEFT JOIN xjr_dictionary_detail t16 ON t2.political_state = t16.code
+LEFT JOIN xjr_dictionary_detail t17 ON t2.nationality = t17.code
+LEFT JOIN xjr_dictionary_detail t18 ON t2.chinese_type = t18.code
+LEFT JOIN xjr_dictionary_detail t19 ON t2.house_hold_type = t19.code
+LEFT JOIN xjr_area t20 ON t2.provinces = t20.code
+LEFT JOIN xjr_area t21 ON t2.city = t21.code
+LEFT JOIN xjr_area t22 ON t2.district = t22.code
+LEFT JOIN xjr_area t23 ON t2.house_provinces = t23.code
+LEFT JOIN xjr_area t24 ON t2.house_city = t24.code
+LEFT JOIN xjr_area t25 ON t2.house_district = t25.code
+LEFT JOIN xjr_area t26 ON t2.birth_provinces = t26.code
+LEFT JOIN xjr_area t27 ON t2.birth_city = t27.code
+LEFT JOIN xjr_area t28 ON t2.birth_district = t28.code
+LEFT JOIN xjr_dictionary_detail t29 ON t2.residence_type = t29.code
+LEFT JOIN xjr_dictionary_detail t31 ON t3.enrollment_type = t31.code
+LEFT JOIN xjr_dictionary_detail t32 ON t3.enroll_type = t32.code
+LEFT JOIN base_major_set t30 ON t3.major_set_id = t30.id
+LEFT JOIN xjr_dictionary_detail t33 ON t3.student_source = t33.code
+LEFT JOIN xjr_dictionary_detail t34 ON t3.student_type = t34.code
+LEFT JOIN xjr_dictionary_detail t35 ON t3.archives_status = t35.code
+LEFT JOIN xjr_dictionary_detail t36 ON t3.learn_status = t36.code
+LEFT JOIN xjr_dictionary_detail t37 ON t3.stduy_status = t37.code
+LEFT JOIN xjr_dictionary_detail t38 ON t3.choose_status = t38.code
+LEFT JOIN xjr_dictionary_detail t39 ON t3.foster_type = t39.code
+LEFT JOIN xjr_dictionary_detail t40 ON t3.recruit_type = t40.code
+LEFT JOIN xjr_dictionary_detail t41 ON t3.recruit_target = t41.code
+LEFT JOIN xjr_dictionary_detail t42 ON t3.highest_education = t42.code
+LEFT JOIN xjr_user t43 ON t43.id = t7.teacher_id
+LEFT JOIN room_bed t44 ON t44.student_user_id = t1.id
+LEFT JOIN room t45 ON t45.id = t44.room_id
+WHERE t1.delete_mark = 0 
+AND t1.delete_mark = 0 
+AND t2.delete_mark = 0 
+AND t3.delete_mark = 0 
+AND t3.archives_status = 'FB2901';
+
+
+-- 添加学生数据的数据源
+INSERT INTO data_expert_source(id,create_user_id,create_date,delete_mark,enabled_mark,sort_code,NAME,view_name,source_type)
+VALUE(1713871531, 1000000000000000000, NOW(),0,1,1, '学生数据','student_data','student');
+
+
+-- 学生数据 -> 基础信息
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966400, 1000000000000000000, NOW(),0,1, 1713871531,'credentialNumber','证件号码','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966401, 1000000000000000000, NOW(),0,1, 1713871531,'studentId','学号','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966402, 1000000000000000000, NOW(),0,1, 1713871531,'candidateNumber','考号','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966403, 1000000000000000000, NOW(),0,1, 1713871531,'name','姓名','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966404, 1000000000000000000, NOW(),0,1, 1713871531,'gender','性别','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966405, 1000000000000000000, NOW(),0,1, 1713871531,'mobile','手机号','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966406, 1000000000000000000, NOW(),0,1, 1713871531,'email','邮箱','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966407, 1000000000000000000, NOW(),0,1, 1713871531,'asName','别名','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966408, 1000000000000000000, NOW(),0,1, 1713871531,'enName','英文名','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966409, 1000000000000000000, NOW(),0,1, 1713871531,'pyName','姓名拼音','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966410, 1000000000000000000, NOW(),0,1, 1713871531,'formerName','曾用名','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966411, 1000000000000000000, NOW(),0,1, 1713871531,'credentialType','证件类型','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966412, 1000000000000000000, NOW(),0,1, 1713871531,'birthDate','出生日期','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966413, 1000000000000000000, NOW(),0,1, 1713871531,'birthType','生日类型','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966414, 1000000000000000000, NOW(),0,1, 1713871531,'nation','民族','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966415, 1000000000000000000, NOW(),0,1, 1713871531,'bloodType','血型','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966416, 1000000000000000000, NOW(),0,1, 1713871531,'health','健康状况','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966417, 1000000000000000000, NOW(),0,1, 1713871531,'maritalState','婚姻状况','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966418, 1000000000000000000, NOW(),0,1, 1713871531,'politicalState','政治面貌','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966419, 1000000000000000000, NOW(),0,1, 1713871531,'nationality','国籍地区','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966420, 1000000000000000000, NOW(),0,1, 1713871531,'chineseType','港澳台侨外','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966421, 1000000000000000000, NOW(),0,1, 1713871531,'houseHoldType','户口类别','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966422, 1000000000000000000, NOW(),0,1, 1713871531,'provinces','省','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966423, 1000000000000000000, NOW(),0,1, 1713871531,'city','市','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966424, 1000000000000000000, NOW(),0,1, 1713871531,'district','区/县','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966425, 1000000000000000000, NOW(),0,1, 1713871531,'houseProvinces','户口所在地省','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966426, 1000000000000000000, NOW(),0,1, 1713871531,'houseCity','户口所在地市','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966427, 1000000000000000000, NOW(),0,1, 1713871531,'houseDistrict','户口所在地区/县','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966428, 1000000000000000000, NOW(),0,1, 1713871531,'belongsPolices','户口所属派出所','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966429, 1000000000000000000, NOW(),0,1, 1713871531,'birthProvinces','出生所在地省','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966430, 1000000000000000000, NOW(),0,1, 1713871531,'birthCity','出生所在地市','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966431, 1000000000000000000, NOW(),0,1, 1713871531,'birthDistrict','出生所在地/区县','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966432, 1000000000000000000, NOW(),0,1, 1713871531,'houseHoldAddress','户籍地址','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966433, 1000000000000000000, NOW(),0,1, 1713871531,'residenceType','学生居住地类型','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966434, 1000000000000000000, NOW(),0,1, 1713871531,'trainInterval','乘火车区间','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966435, 1000000000000000000, NOW(),0,1, 1713871531,'isMigrateChildren','是否随迁子女','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966436, 1000000000000000000, NOW(),0,1, 1713871531,'isFloatingPopulation','是否流动人口','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966437, 1000000000000000000, NOW(),0,1, 1713871531,'height','身高','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966438, 1000000000000000000, NOW(),0,1, 1713871531,'weight','体重','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966439, 1000000000000000000, NOW(),0,1, 1713871531,'vision','视力','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966440, 1000000000000000000, NOW(),0,1, 1713871531,'specialty','特长','基础信息');
+
+-- 学生数据 -> 联系信息
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872048, 1000000000000000000, NOW(),0,1, 1713871531,'qqMsn','QQ&MSN','联系信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872049, 1000000000000000000, NOW(),0,1, 1713871531,'wechat','微信号','联系信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872050, 1000000000000000000, NOW(),0,1, 1713871531,'userPage','个人主页','联系信息');
+
+-- 学生数据 -> 学籍信息
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872051, 1000000000000000000, NOW(),0,1, 1713871531,'rollNumber','学籍号','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872163, 1000000000000000000, NOW(),0,1, 1713871531,'archivesNumber','学生档案编号','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872164, 1000000000000000000, NOW(),0,1, 1713871531,'enrollmentDate','入学年月','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872165, 1000000000000000000, NOW(),0,1, 1713871531,'enrollmentType','入学方式','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872166, 1000000000000000000, NOW(),0,1, 1713871531,'gradeName','入学年级','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872167, 1000000000000000000, NOW(),0,1, 1713871531,'enrollType','入学招生类型','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872168, 1000000000000000000, NOW(),0,1, 1713871531,'majorSetName','在读专业方向','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872169, 1000000000000000000, NOW(),0,1, 1713871531,'studyYear','学制','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872170, 1000000000000000000, NOW(),0,1, 1713871531,'className','班级','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872171, 1000000000000000000, NOW(),0,1, 1713871531,'studentSource','学生来源','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872172, 1000000000000000000, NOW(),0,1, 1713871531,'studentType','学生类别','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872173, 1000000000000000000, NOW(),0,1, 1713871531,'archivesStatus','学籍状态','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872174, 1000000000000000000, NOW(),0,1, 1713871531,'learnStatus','学习形式','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872175, 1000000000000000000, NOW(),0,1, 1713871531,'stduyStatus','就读方式','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872176, 1000000000000000000, NOW(),0,1, 1713871531,'chooseStatus','分流状态','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872177, 1000000000000000000, NOW(),0,1, 1713871531,'fosterType','分段培养方式','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872178, 1000000000000000000, NOW(),0,1, 1713871531,'recruitType','招生类型','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872179, 1000000000000000000, NOW(),0,1, 1713871531,'recruitTarget','招生对象','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872180, 1000000000000000000, NOW(),0,1, 1713871531,'highestEducation','入学前最高学历','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872181, 1000000000000000000, NOW(),0,1, 1713871531,'graduatedUniversity','入学前毕业院校','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872182, 1000000000000000000, NOW(),0,1, 1713871531,'graduatedScore','入学前毕业成绩','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872183, 1000000000000000000, NOW(),0,1, 1713871531,'examineeNumber','入学考试考生号','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872184, 1000000000000000000, NOW(),0,1, 1713871531,'ticketNumber','入学考试准考证号','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872185, 1000000000000000000, NOW(),0,1, 1713871531,'admissionScores','入学考试成绩','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872186, 1000000000000000000, NOW(),0,1, 1713871531,'remark','备注','学籍信息');
+
+-- 学生数据 -> 资助信息
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872254, 1000000000000000000, NOW(),0,1, 1713871531,'isIndemnify','是否低保户','资助信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872255, 1000000000000000000, NOW(),0,1, 1713871531,'isStipend','是否享受国家助学金','资助信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872256, 1000000000000000000, NOW(),0,1, 1713871531,'stipendStand','助学金发放标准(元)','资助信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872257, 1000000000000000000, NOW(),0,1, 1713871531,'stipendNumber','学生资助卡号','资助信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872258, 1000000000000000000, NOW(),0,1, 1713871531,'isFree','是否免学费','资助信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872259, 1000000000000000000, NOW(),0,1, 1713871531,'isFilingCard','是否建档立卡','资助信息');
+
+-- 学生数据 -> 家庭信息
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872317, 1000000000000000000, NOW(),0,1, 1713871531,'zipCode','家庭邮编','家庭信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872318, 1000000000000000000, NOW(),0,1, 1713871531,'telephone','家庭电话','家庭信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872319, 1000000000000000000, NOW(),0,1, 1713871531,'contact','联系人姓名','家庭信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872320, 1000000000000000000, NOW(),0,1, 1713871531,'population','家庭人口','家庭信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872321, 1000000000000000000, NOW(),0,1, 1713871531,'address','家庭地址','家庭信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872322, 1000000000000000000, NOW(),0,1, 1713871531,'incomeSource','家庭主要收入来源','家庭信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872323, 1000000000000000000, NOW(),0,1, 1713871531,'incomeNumber','家庭月收入(元)','家庭信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872324, 1000000000000000000, NOW(),0,1, 1713871531,'railwayStation','离家最近火车站','家庭信息');
+
+
+
+-- 班级总人数、男生人数、女生人数、住读人数、走读人数
+
+-- 收费视图
+DROP VIEW IF EXISTS fee_data;
+CREATE VIEW fee_data AS 
+SELECT    
+x1.name AS teacherName,t3.name AS classname,t6.name AS baseSemesterCn,
+t.studentcode AS studentId,t.feeobjname AS NAME,t.specname,t.enteryear,
+t.leaveyear,t.personalid,t.address,t.telephone,t.state,t.sex,t.factar,t.factrecarmny,t.refundmny,
+(CASE WHEN t.qfje > 0 THEN t.qfje ELSE 0 END) AS qfje,
+(CASE WHEN t.qfje < 0 THEN ABS(t.qfje) ELSE 0 END) AS ktje,
+t.jfzt,t.feeitemname,t.ratetypename,t.resourcename,t.quartername,t.pksfxxytb AS id,
+t6.id AS semesterId,t3.id AS classIds
+  FROM pb_v_xsxxsfytb  t    
+LEFT JOIN xjr_user t1 ON (t1.credential_number = t.personalid) 
+LEFT JOIN base_student_school_roll t2 ON (t2.user_id = t1.id) 
+LEFT JOIN base_class t3 ON (t3.id = t2.class_id) 
+LEFT JOIN base_student t4 ON (t4.user_id = t1.id) 
+LEFT JOIN pb_semester_config t5 ON (t5.beltcode = t.beltcode) 
+LEFT JOIN base_semester t6 ON (t6.id = t5.base_semester_id) 
+LEFT JOIN xjr_user x1 ON x1.id = t3.teacher_id;
+
+INSERT INTO data_expert_source(id,create_user_id,create_date,delete_mark,enabled_mark,sort_code,NAME,view_name,source_type)
+VALUE(1713871532, 1000000000000000000, NOW(),0,1,1, '财务数据','fee_data','fee');
+
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872325, 1000000000000000000, NOW(),0,1, 1713871532,'teacherName','班主任名称','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872326, 1000000000000000000, NOW(),0,1, 1713871532,'classname','班级名称','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872327, 1000000000000000000, NOW(),0,1, 1713871532,'baseSemesterCn','学期','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872328, 1000000000000000000, NOW(),0,1, 1713871532,'studentId','学号','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872329, 1000000000000000000, NOW(),0,1, 1713871532,'name','姓名','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872330, 1000000000000000000, NOW(),0,1, 1713871532,'specname','所属专业名称','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872331, 1000000000000000000, NOW(),0,1, 1713871532,'enteryear','入学年份','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872332, 1000000000000000000, NOW(),0,1, 1713871532,'state','学籍状态','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872333, 1000000000000000000, NOW(),0,1, 1713871532,'sex','性别','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872334, 1000000000000000000, NOW(),0,1, 1713871532,'factar','应收','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872335, 1000000000000000000, NOW(),0,1, 1713871532,'factrecarmny','实收','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872336, 1000000000000000000, NOW(),0,1, 1713871532,'refundmny','缴费状态','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872337, 1000000000000000000, NOW(),0,1, 1713871532,'qfje','欠费金额','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872338, 1000000000000000000, NOW(),0,1, 1713871532,'ktje','可退金额','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872339, 1000000000000000000, NOW(),0,1, 1713871532,'jfzt','缴费状态','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872340, 1000000000000000000, NOW(),0,1, 1713871532,'feeitemname','项目','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872341, 1000000000000000000, NOW(),0,1, 1713871532,'ratetypename','学制','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872342, 1000000000000000000, NOW(),0,1, 1713871532,'resourcename','贫困类型','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872343, 1000000000000000000, NOW(),0,1, 1713871532,'quartername','就读方式','所有信息');

+ 215 - 0
src/main/resources/sqlScript/视图信息.sql.bak

@@ -0,0 +1,215 @@
+DROP VIEW IF EXISTS student_data;
+CREATE VIEW student_data AS
+SELECT t1.credential_number AS credentialNumber,t2.student_id AS studentId,t3.candidate_number AS candidateNumber,t1.name,t15.name AS gender,
+t1.mobile,t1.email,t2.as_name AS asName,t2.en_name AS enName,t2.py_name AS pyName,
+t2.former_name AS formerName,t9.name AS credentialType,t1.birth_date AS birthDate,t10.name AS birthType,t11.name AS nation,
+t12.name AS bloodType,t13.name AS health,t14.name AS maritalState,t16.name AS politicalState,
+t17.name AS nationality,t18.name AS chineseType,t19.name AS houseHoldType,t20.name AS provinces,
+t21.name AS city,t22.name AS district,t23.name AS houseProvinces, t24.name AS houseCity,t25.name AS houseDistrict,
+t2.belongs_police AS belongsPolice,t26.name AS birthProvinces, t27.name AS birthCity,t28.name AS birthDistrict,t2.house_hold_address AS houseHoldAddress,
+t29.name AS residenceType,t2.train_interval AS trainInterval,REPLACE(REPLACE(t2.is_migrate_children,0,'否'),1,'是') AS isMigrateChildren,
+REPLACE(REPLACE(t2.is_floating_population,0,'否'),1,'是') AS isFloatingPopulation,t2.height ,t2.weight ,t2.vision ,t2.specialty ,
+t6.qq_msn AS qqMsn,t6.wechat,t6.user_page AS userPage,t3.roll_number AS rollNumber,t3.archives_number AS archivesNumber,
+t3.enrollment_date AS enrollmentDate,t31.name AS enrollmentType,t8.name AS gradeName,t32.name AS enrollType,t30.name AS majorSetName,
+t3.study_year AS studyYear,t7.name AS className,t33.name AS studentSource,t34.name AS studentType,t35.name AS archivesStatus,
+t36.name AS learnStatus,t37.name AS stduyStatus,t38.name AS chooseStatus,t39.name AS fosterType,t40.name AS recruitType,
+t41.name AS recruitTarget,t42.name AS highestEducation,t3.graduated_university AS graduatedUniversity,t3.graduated_score AS graduatedScore,
+t3.examinee_number AS examineeNumber,t3.ticket_number AS ticketNumber,t3.admission_scores AS admissionScores,t3.remark,
+REPLACE(REPLACE(t5.is_indemnify,0,'否'),1,'是') AS isIndemnify,REPLACE(REPLACE(t5.is_stipend,0,'否'),1,'是') AS isStipend,t5.stipend_stand AS stipendStand,
+t5.stipend_number AS stipendNumber,REPLACE(REPLACE(t5.is_free,0,'否'),1,'是') AS isFree,REPLACE(REPLACE(t5.is_filing_card,0,'否'),1,'是') AS isFilingCard,
+t4.zip_code AS zipCode,t4.telephone,t4.contact,t4.population,
+t4.address, t4.income_source AS incomeSource,t4.income_number AS incomeNumber, t4.railway_station AS railwayStation,t1.id,t3.grade_id AS gradeId,t3.class_id AS classId,
+t2.belongs_police AS belongsPolices,t43.name AS teacherName,t45.room_name AS roomName,t44.bed_number AS bedNumber FROM xjr_user t1
+INNER JOIN base_student t2 ON t1.id = t2.user_id
+LEFT JOIN base_student_school_roll t3 ON t3.user_id = t1.id
+LEFT JOIN base_student_family t4 ON t4.user_id = t1.id
+LEFT JOIN base_student_subsidize t5 ON t5.user_id = t1.id
+LEFT JOIN base_student_contact t6 ON t6.id = t1.id
+LEFT JOIN base_class t7 ON t7.id = t3.class_id
+LEFT JOIN base_grade t8 ON t8.id = t3.grade_id
+LEFT JOIN xjr_dictionary_detail t9 ON t1.credential_type = t9.code
+LEFT JOIN xjr_dictionary_detail t10 ON t2.birth_type = t10.code
+LEFT JOIN xjr_dictionary_detail t11 ON t2.nation = t11.code
+LEFT JOIN xjr_dictionary_detail t12 ON t2.blood_type = t12.code
+LEFT JOIN xjr_dictionary_detail t13 ON t2.health = t13.code
+LEFT JOIN xjr_dictionary_detail t14 ON t2.marital_state = t14.code
+LEFT JOIN xjr_dictionary_detail t15 ON t1.gender = t15.code
+LEFT JOIN xjr_dictionary_detail t16 ON t2.political_state = t16.code
+LEFT JOIN xjr_dictionary_detail t17 ON t2.nationality = t17.code
+LEFT JOIN xjr_dictionary_detail t18 ON t2.chinese_type = t18.code
+LEFT JOIN xjr_dictionary_detail t19 ON t2.house_hold_type = t19.code
+LEFT JOIN xjr_area t20 ON t2.provinces = t20.code
+LEFT JOIN xjr_area t21 ON t2.city = t21.code
+LEFT JOIN xjr_area t22 ON t2.district = t22.code
+LEFT JOIN xjr_area t23 ON t2.house_provinces = t23.code
+LEFT JOIN xjr_area t24 ON t2.house_city = t24.code
+LEFT JOIN xjr_area t25 ON t2.house_district = t25.code
+LEFT JOIN xjr_area t26 ON t2.birth_provinces = t26.code
+LEFT JOIN xjr_area t27 ON t2.birth_city = t27.code
+LEFT JOIN xjr_area t28 ON t2.birth_district = t28.code
+LEFT JOIN xjr_dictionary_detail t29 ON t2.residence_type = t29.code
+LEFT JOIN xjr_dictionary_detail t31 ON t3.enrollment_type = t31.code
+LEFT JOIN xjr_dictionary_detail t32 ON t3.enroll_type = t32.code
+LEFT JOIN base_major_set t30 ON t3.major_set_id = t30.id
+LEFT JOIN xjr_dictionary_detail t33 ON t3.student_source = t33.code
+LEFT JOIN xjr_dictionary_detail t34 ON t3.student_type = t34.code
+LEFT JOIN xjr_dictionary_detail t35 ON t3.archives_status = t35.code
+LEFT JOIN xjr_dictionary_detail t36 ON t3.learn_status = t36.code
+LEFT JOIN xjr_dictionary_detail t37 ON t3.stduy_status = t37.code
+LEFT JOIN xjr_dictionary_detail t38 ON t3.choose_status = t38.code
+LEFT JOIN xjr_dictionary_detail t39 ON t3.foster_type = t39.code
+LEFT JOIN xjr_dictionary_detail t40 ON t3.recruit_type = t40.code
+LEFT JOIN xjr_dictionary_detail t41 ON t3.recruit_target = t41.code
+LEFT JOIN xjr_dictionary_detail t42 ON t3.highest_education = t42.code
+LEFT JOIN xjr_user t43 ON t43.id = t7.teacher_id
+LEFT JOIN room_bed t44 ON t44.student_user_id = t1.id
+LEFT JOIN room t45 ON t45.id = t44.room_id
+WHERE t1.delete_mark = 0 AND t1.delete_mark = 0 AND t2.delete_mark = 0 
+AND t3.delete_mark = 0 AND t4.delete_mark = 0
+AND t3.archives_status = 'FB2901' ;
+
+
+-- 添加学生数据的数据源
+INSERT INTO data_expert_source(id,create_user_id,create_date,delete_mark,enabled_mark,sort_code,NAME,view_name,source_type)
+VALUE(1713871531, 1000000000000000000, NOW(),0,1,1, '学生数据','student_data','student');
+
+
+-- 学生数据 -> 基础信息
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966400, 1000000000000000000, NOW(),0,1, 1713871531,'credentialNumber','证件号码','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966401, 1000000000000000000, NOW(),0,1, 1713871531,'studentId','学号','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966402, 1000000000000000000, NOW(),0,1, 1713871531,'candidateNumber','考号','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966403, 1000000000000000000, NOW(),0,1, 1713871531,'name','姓名','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966404, 1000000000000000000, NOW(),0,1, 1713871531,'gender','性别','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966405, 1000000000000000000, NOW(),0,1, 1713871531,'mobile','手机号','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966406, 1000000000000000000, NOW(),0,1, 1713871531,'email','邮箱','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966407, 1000000000000000000, NOW(),0,1, 1713871531,'asName','别名','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966408, 1000000000000000000, NOW(),0,1, 1713871531,'enName','英文名','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966409, 1000000000000000000, NOW(),0,1, 1713871531,'pyName','姓名拼音','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966410, 1000000000000000000, NOW(),0,1, 1713871531,'formerName','曾用名','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966411, 1000000000000000000, NOW(),0,1, 1713871531,'credentialType','证件类型','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966412, 1000000000000000000, NOW(),0,1, 1713871531,'birthDate','出生日期','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966413, 1000000000000000000, NOW(),0,1, 1713871531,'birthType','生日类型','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966414, 1000000000000000000, NOW(),0,1, 1713871531,'nation','民族','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966415, 1000000000000000000, NOW(),0,1, 1713871531,'bloodType','血型','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966416, 1000000000000000000, NOW(),0,1, 1713871531,'health','健康状况','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966417, 1000000000000000000, NOW(),0,1, 1713871531,'maritalState','婚姻状况','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966418, 1000000000000000000, NOW(),0,1, 1713871531,'politicalState','政治面貌','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966419, 1000000000000000000, NOW(),0,1, 1713871531,'nationality','国籍地区','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966420, 1000000000000000000, NOW(),0,1, 1713871531,'chineseType','港澳台侨外','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966421, 1000000000000000000, NOW(),0,1, 1713871531,'houseHoldType','户口类别','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966422, 1000000000000000000, NOW(),0,1, 1713871531,'provinces','省','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966423, 1000000000000000000, NOW(),0,1, 1713871531,'city','市','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966424, 1000000000000000000, NOW(),0,1, 1713871531,'district','区/县','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966425, 1000000000000000000, NOW(),0,1, 1713871531,'houseProvinces','户口所在地省','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966426, 1000000000000000000, NOW(),0,1, 1713871531,'houseCity','户口所在地市','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966427, 1000000000000000000, NOW(),0,1, 1713871531,'houseDistrict','户口所在地区/县','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966428, 1000000000000000000, NOW(),0,1, 1713871531,'belongsPolices','户口所属派出所','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966429, 1000000000000000000, NOW(),0,1, 1713871531,'birthProvinces','出生所在地省','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966430, 1000000000000000000, NOW(),0,1, 1713871531,'birthCity','出生所在地市','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966431, 1000000000000000000, NOW(),0,1, 1713871531,'birthDistrict','出生所在地/区县','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966432, 1000000000000000000, NOW(),0,1, 1713871531,'houseHoldAddress','户籍地址','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966433, 1000000000000000000, NOW(),0,1, 1713871531,'residenceType','学生居住地类型','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966434, 1000000000000000000, NOW(),0,1, 1713871531,'trainInterval','乘火车区间','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966435, 1000000000000000000, NOW(),0,1, 1713871531,'isMigrateChildren','是否随迁子女','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966436, 1000000000000000000, NOW(),0,1, 1713871531,'isFloatingPopulation','是否流动人口','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966437, 1000000000000000000, NOW(),0,1, 1713871531,'height','身高','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966438, 1000000000000000000, NOW(),0,1, 1713871531,'weight','体重','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966439, 1000000000000000000, NOW(),0,1, 1713871531,'vision','视力','基础信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1640966440, 1000000000000000000, NOW(),0,1, 1713871531,'specialty','特长','基础信息');
+
+-- 学生数据 -> 联系信息
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872048, 1000000000000000000, NOW(),0,1, 1713871531,'qqMsn','QQ&MSN','联系信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872049, 1000000000000000000, NOW(),0,1, 1713871531,'wechat','微信号','联系信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872050, 1000000000000000000, NOW(),0,1, 1713871531,'userPage','个人主页','联系信息');
+
+-- 学生数据 -> 学籍信息
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872051, 1000000000000000000, NOW(),0,1, 1713871531,'rollNumber','学籍号','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872163, 1000000000000000000, NOW(),0,1, 1713871531,'archivesNumber','学生档案编号','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872164, 1000000000000000000, NOW(),0,1, 1713871531,'enrollmentDate','入学年月','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872165, 1000000000000000000, NOW(),0,1, 1713871531,'enrollmentType','入学方式','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872166, 1000000000000000000, NOW(),0,1, 1713871531,'gradeName','入学年级','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872167, 1000000000000000000, NOW(),0,1, 1713871531,'enrollType','入学招生类型','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872168, 1000000000000000000, NOW(),0,1, 1713871531,'majorSetName','在读专业方向','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872169, 1000000000000000000, NOW(),0,1, 1713871531,'studyYear','学制','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872170, 1000000000000000000, NOW(),0,1, 1713871531,'className','班级','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872171, 1000000000000000000, NOW(),0,1, 1713871531,'studentSource','学生来源','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872172, 1000000000000000000, NOW(),0,1, 1713871531,'studentType','学生类别','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872173, 1000000000000000000, NOW(),0,1, 1713871531,'archivesStatus','学籍状态','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872174, 1000000000000000000, NOW(),0,1, 1713871531,'learnStatus','学习形式','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872175, 1000000000000000000, NOW(),0,1, 1713871531,'stduyStatus','就读方式','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872176, 1000000000000000000, NOW(),0,1, 1713871531,'chooseStatus','分流状态','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872177, 1000000000000000000, NOW(),0,1, 1713871531,'fosterType','分段培养方式','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872178, 1000000000000000000, NOW(),0,1, 1713871531,'recruitType','招生类型','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872179, 1000000000000000000, NOW(),0,1, 1713871531,'recruitTarget','招生对象','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872180, 1000000000000000000, NOW(),0,1, 1713871531,'highestEducation','入学前最高学历','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872181, 1000000000000000000, NOW(),0,1, 1713871531,'graduatedUniversity','入学前毕业院校','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872182, 1000000000000000000, NOW(),0,1, 1713871531,'graduatedScore','入学前毕业成绩','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872183, 1000000000000000000, NOW(),0,1, 1713871531,'examineeNumber','入学考试考生号','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872184, 1000000000000000000, NOW(),0,1, 1713871531,'ticketNumber','入学考试准考证号','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872185, 1000000000000000000, NOW(),0,1, 1713871531,'admissionScores','入学考试成绩','学籍信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872186, 1000000000000000000, NOW(),0,1, 1713871531,'remark','备注','学籍信息');
+
+-- 学生数据 -> 资助信息
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872254, 1000000000000000000, NOW(),0,1, 1713871531,'isIndemnify','是否低保户','资助信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872255, 1000000000000000000, NOW(),0,1, 1713871531,'isStipend','是否享受国家助学金','资助信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872256, 1000000000000000000, NOW(),0,1, 1713871531,'stipendStand','助学金发放标准(元)','资助信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872257, 1000000000000000000, NOW(),0,1, 1713871531,'stipendNumber','学生资助卡号','资助信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872258, 1000000000000000000, NOW(),0,1, 1713871531,'isFree','是否免学费','资助信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872259, 1000000000000000000, NOW(),0,1, 1713871531,'isFilingCard','是否建档立卡','资助信息');
+
+-- 学生数据 -> 家庭信息
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872317, 1000000000000000000, NOW(),0,1, 1713871531,'zipCode','家庭邮编','家庭信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872318, 1000000000000000000, NOW(),0,1, 1713871531,'telephone','家庭电话','家庭信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872319, 1000000000000000000, NOW(),0,1, 1713871531,'contact','联系人姓名','家庭信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872320, 1000000000000000000, NOW(),0,1, 1713871531,'population','家庭人口','家庭信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872321, 1000000000000000000, NOW(),0,1, 1713871531,'address','家庭地址','家庭信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872322, 1000000000000000000, NOW(),0,1, 1713871531,'incomeSource','家庭主要收入来源','家庭信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872323, 1000000000000000000, NOW(),0,1, 1713871531,'incomeNumber','家庭月收入(元)','家庭信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872324, 1000000000000000000, NOW(),0,1, 1713871531,'railwayStation','离家最近火车站','家庭信息');
+
+
+
+-- 班级总人数、男生人数、女生人数、住读人数、走读人数
+
+-- 收费视图
+DROP VIEW IF EXISTS fee_data;
+CREATE VIEW fee_data AS 
+SELECT    
+x1.name AS teacherName,t3.name AS classname,t6.name AS baseSemesterCn,
+t.studentcode AS studentId,t.feeobjname AS NAME,t.specname,t.enteryear,
+t.leaveyear,t.personalid,t.address,t.telephone,t.state,t.sex,t.factar,t.factrecarmny,t.refundmny,
+(CASE WHEN t.qfje > 0 THEN t.qfje ELSE 0 END) AS qfje,
+(CASE WHEN t.qfje < 0 THEN ABS(t.qfje) ELSE 0 END) AS ktje,
+t.jfzt,t.feeitemname,t.ratetypename,t.resourcename,t.quartername,t.pksfxxytb AS id,
+t6.id AS semesterId,t3.id AS classIds
+  FROM pb_v_xsxxsfytb  t    
+LEFT JOIN xjr_user t1 ON (t1.credential_number = t.personalid) 
+LEFT JOIN base_student_school_roll t2 ON (t2.user_id = t1.id) 
+LEFT JOIN base_class t3 ON (t3.id = t2.class_id) 
+LEFT JOIN base_student t4 ON (t4.user_id = t1.id) 
+LEFT JOIN pb_semester_config t5 ON (t5.beltcode = t.beltcode) 
+LEFT JOIN base_semester t6 ON (t6.id = t5.base_semester_id) 
+LEFT JOIN xjr_user x1 ON x1.id = t3.teacher_id;
+
+INSERT INTO data_expert_source(id,create_user_id,create_date,delete_mark,enabled_mark,sort_code,NAME,view_name,source_type)
+VALUE(1713871532, 1000000000000000000, NOW(),0,1,1, '财务数据','fee_data','fee');
+
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872325, 1000000000000000000, NOW(),0,1, 1713871532,'teacherName','班主任名称','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872326, 1000000000000000000, NOW(),0,1, 1713871532,'classname','班级名称','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872327, 1000000000000000000, NOW(),0,1, 1713871532,'baseSemesterCn','学期','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872328, 1000000000000000000, NOW(),0,1, 1713871532,'studentId','学号','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872329, 1000000000000000000, NOW(),0,1, 1713871532,'name','姓名','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872330, 1000000000000000000, NOW(),0,1, 1713871532,'specname','所属专业名称','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872331, 1000000000000000000, NOW(),0,1, 1713871532,'enteryear','入学年份','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872332, 1000000000000000000, NOW(),0,1, 1713871532,'state','学籍状态','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872333, 1000000000000000000, NOW(),0,1, 1713871532,'sex','性别','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872334, 1000000000000000000, NOW(),0,1, 1713871532,'factar','应收','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872335, 1000000000000000000, NOW(),0,1, 1713871532,'factrecarmny','实收','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872336, 1000000000000000000, NOW(),0,1, 1713871532,'refundmny','缴费状态','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872337, 1000000000000000000, NOW(),0,1, 1713871532,'qfje','欠费金额','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872338, 1000000000000000000, NOW(),0,1, 1713871532,'ktje','可退金额','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872339, 1000000000000000000, NOW(),0,1, 1713871532,'jfzt','缴费状态','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872340, 1000000000000000000, NOW(),0,1, 1713871532,'feeitemname','项目','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872341, 1000000000000000000, NOW(),0,1, 1713871532,'ratetypename','学制','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872342, 1000000000000000000, NOW(),0,1, 1713871532,'resourcename','贫困类型','所有信息');
+INSERT INTO data_expert_source_field(id,create_user_id,create_date,delete_mark,enabled_mark,data_expert_source_id,field_name,show_name,data_group) VALUE(1713872343, 1000000000000000000, NOW(),0,1, 1713871532,'quartername','就读方式','所有信息');

+ 10 - 0
src/test/QRCode.html

@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Title</title>
+</head>
+<body>
+    <img src="data:image/jpg;base64,VjAwMSsIAOvmRnb8//DF4unkGwsE46Iamg0Nswg7qYloXD0+XD44SalTYrNHrvzxW9EwPjw=">
+</body>
+</html>

+ 27 - 0
src/test/java/com/xjrsoft/common/utils/QrCodeTest.java

@@ -0,0 +1,27 @@
+package com.xjrsoft.common.utils;
+
+import org.junit.jupiter.api.Test;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.File;
+
+/**
+ * @author dzx
+ * @date 2023/12/26
+ */
+public class QrCodeTest {
+
+    @Test
+    void test() throws Exception {
+        String url = "https://www.baidu.com/";
+        int width = 150;
+        int height = 150;
+        int margin = 1;
+        BufferedImage bufferedImage = QrCodeUtil.createBufferedImage(url, width, height, margin);
+
+        String outputpath = "D:\\workspace\\其他\\教材管理\\test2.png";
+        ImageIO.write(bufferedImage, "png", new File(outputpath));
+
+    }
+}

+ 85 - 0
src/test/java/com/xjrsoft/module/classtime/service/impl/ClassTimeStatisticsServiceImplTest.java

@@ -0,0 +1,85 @@
+package com.xjrsoft.module.classtime.service.impl;
+
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author dzx
+ * @date 2024/9/27
+ */
+class ClassTimeStatisticsServiceImplTest {
+
+
+    @Test
+    void test(){
+        LocalDate startDate = LocalDate.of(2023, 9, 1); // 起始日期
+        LocalDate endDate = LocalDate.of(2023, 9, 10); // 结束日期
+        List<LocalDate> dateList = new ArrayList<>();
+        dateList.addAll(getDatesBetween(startDate, endDate));
+
+        startDate = LocalDate.of(2023, 9, 10); // 结束日期
+        endDate = LocalDate.of(2023, 9, 20); // 结束日期
+        dateList.addAll(getDatesBetween(startDate, endDate));
+
+        List<LocalDate> localDates = removeDuplicates(dateList);
+        // 打印结果
+        for (LocalDate date : localDates) {
+            System.out.println(date);
+        }
+    }
+
+    public static List<LocalDate> getDatesBetween(LocalDate start, LocalDate end) {
+        List<LocalDate> dates = new ArrayList<>();
+
+        // 确保 startDate 不晚于 endDate
+        if (start.isAfter(end)) {
+            throw new IllegalArgumentException("Start date must not be after end date");
+        }
+
+        // 将起始日期加入列表
+        LocalDate currentDate = start;
+        while (!currentDate.isAfter(end)) {
+            dates.add(currentDate);
+            currentDate = currentDate.plusDays(1); // 增加一天
+        }
+
+        return dates;
+    }
+
+    public static List<LocalDate> removeDuplicates(List<LocalDate> dates) {
+        Set<LocalDate> dateSet = new HashSet<>(dates); // 使用 HashSet 去重
+        return new ArrayList<>(dateSet); // 转换回 List
+    }
+
+
+    @Test
+    void test2(){
+        List<String> zkList = Arrays.asList("上1","上2","上3","上4","下1","下2","下3","下4");
+        for (String s : zkList) {
+            System.out.println(s);
+        }
+    }
+
+    @Test
+    void test3(){
+        LocalDate monday = LocalDate.of(2024, 9, 23);
+        LocalDate sunday = LocalDate.of(2024, 9, 29);
+        LocalDate scheduleDate = LocalDate.of(2024, 9, 23);
+        if(!( (scheduleDate.equals(monday) || scheduleDate.isAfter(monday)) && (scheduleDate.equals(sunday) || scheduleDate.isBefore(sunday)) )){
+            System.out.println(111);
+
+        }else{
+            System.out.println(222);
+        }
+
+    }
+
+}

+ 743 - 0
src/test/java/com/xjrsoft/module/excel/CellStyleModel.java

@@ -0,0 +1,743 @@
+package com.xjrsoft.module.excel;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.Data;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+import org.apache.poi.xssf.usermodel.DefaultIndexedColorMap;
+import org.apache.poi.xssf.usermodel.XSSFColor;
+
+/**
+ * @author dzx
+ * @date 2024/1/5
+ */
+@Data
+public class CellStyleModel {
+
+    /**
+     * sheet名称
+     */
+    private String sheetName;
+    /**
+     * 列索引
+     */
+    private int colIndex;
+    /**
+     * 行索引
+     */
+    private int rowIndex;
+    /**
+     * 字体名称
+     */
+    private String fontName;
+    /**
+     * 字体大小
+     */
+    private Double fontHeight;
+    /**
+     * 字体颜色
+     */
+    private Object fontColor;
+    /**
+     * 字体加粗
+     */
+    private Boolean fontBold;
+    /**
+     * 字体斜体
+     */
+    private Boolean fontItalic;
+    /**
+     * 字体下划线
+     */
+    private Byte fontUnderLine;
+    /**
+     * 字体上标下标
+     */
+    private Short fontTypeOffset;
+    /**
+     * 字体删除线
+     */
+    private Boolean fontStrikeout;
+    /**
+     * 背景颜色
+     */
+    private Object backgroundColor;
+    /**
+     * 上边框线条类型
+     */
+    private BorderStyle borderTop;
+    /**
+     * 右边框线条类型
+     */
+    private BorderStyle borderRight;
+    /**
+     * 下边框线条类型
+     */
+    private BorderStyle borderBottom;
+    /**
+     * 左边框线条类型
+     */
+    private BorderStyle borderLeft;
+    /**
+     * 上边框线条颜色
+     */
+    private Object topBorderColor;
+    /**
+     * 上边框线条颜色
+     */
+    private Object rightBorderColor;
+    /**
+     * 下边框线条颜色
+     */
+    private Object bottomBorderColor;
+    /**
+     */
+    private Object leftBorderColor;
+    /**
+     * 水平对齐方式
+     */
+    private HorizontalAlignment horizontalAlignment;
+    /**
+     * 垂直对齐方式
+     */
+    private VerticalAlignment verticalAlignment;
+    /**
+     * 自动换行方式
+     */
+    private Boolean wrapText;
+    /**
+     * 生成字体名称样式信息
+     *
+     * @param sheetName   sheet页名称
+     * @param rowIndex    行号
+     * @param columnIndex 列号
+     * @param fontName    字体名称(默认宋体)
+     * @return
+     */
+    public static CellStyleModel createFontNameCellStyleModel(String sheetName, int rowIndex, int columnIndex, String fontName) {
+        return createFontCellStyleModel(sheetName, rowIndex, columnIndex, fontName, null, null, null, null, null, null, null);
+    }
+    /**
+     * 生成字体名称大小信息
+     *
+     * @param sheetName   sheet页名称
+     * @param rowIndex    行号
+     * @param columnIndex 列号
+     * @param fontHeight  字体大小
+     * @return
+     */
+    public static CellStyleModel createFontHeightCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , Double fontHeight) {
+        return createFontCellStyleModel(sheetName, rowIndex, columnIndex, null, fontHeight, null, null, null, null, null, null);
+    }
+    /**
+     * 得到RBG自定义颜色
+     *
+     * @param redNum   红色数值
+     * @param greenNum 绿色数值
+     * @param blueNum  蓝色数值
+     * @return
+     */
+    public static XSSFColor getRGBColor(int redNum, int greenNum, int blueNum) {
+        XSSFColor color = new XSSFColor(new byte[]{(byte) redNum, (byte) greenNum, (byte) blueNum}, new DefaultIndexedColorMap());
+        return color;
+    }
+    /**
+     * 生成字体颜色样式信息(支持自定义RGB颜色)
+     *
+     * @param sheetName   sheet页名称
+     * @param rowIndex    行号
+     * @param columnIndex 列号
+     * @param redNum      红色数值
+     * @param greenNum    绿色数值
+     * @param blueNum     蓝色数值
+     * @return
+     */
+    public static CellStyleModel createFontColorCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , int redNum, int greenNum, int blueNum) {
+        XSSFColor fontColor = getRGBColor(redNum, greenNum, blueNum);
+        return createFontColorCellStyleModel(sheetName, rowIndex, columnIndex, fontColor);
+    }
+    /**
+     * 生成字体颜色样式信息
+     *
+     * @param sheetName   sheet页名称
+     * @param rowIndex    行号
+     * @param columnIndex 列号
+     * @param fontColor   字体颜色
+     * @return
+     */
+    public static CellStyleModel createFontColorCellStyleModel(String sheetName, int rowIndex, int columnIndex, Object fontColor) {
+        return createFontCellStyleModel(sheetName, rowIndex, columnIndex, null, null, fontColor, null, null, null, null, null);
+    }
+    /**
+     * 生成字体加粗样式信息
+     *
+     * @param sheetName   sheet页名称
+     * @param rowIndex    行号
+     * @param columnIndex 列号
+     * @param fontBold    字体加粗
+     * @return
+     */
+    public static CellStyleModel createFontBoldCellStyleModel(String sheetName, int rowIndex, int columnIndex, Boolean fontBold) {
+        return createFontCellStyleModel(sheetName, rowIndex, columnIndex, null, null, null, fontBold, null, null, null, null);
+    }
+    /**
+     * 生成字体斜体样式信息
+     *
+     * @param sheetName   sheet页名称
+     * @param rowIndex    行号
+     * @param columnIndex 列号
+     * @param fontItalic  字体斜体
+     * @return
+     */
+    public static CellStyleModel createFontItalicCellStyleModel(String sheetName, int rowIndex, int columnIndex, Boolean fontItalic) {
+        return createFontCellStyleModel(sheetName, rowIndex, columnIndex, null, null, null, null, fontItalic, null, null, null);
+    }
+    /**
+     * 生成字体下划线样式信息
+     *
+     * @param sheetName     sheet页名称
+     * @param rowIndex      行号
+     * @param columnIndex   列号
+     * @param fontUnderLine 字体下划线
+     * @return
+     */
+    public static CellStyleModel createFontUnderLineCellStyleModel(String sheetName, int rowIndex, int columnIndex, Byte fontUnderLine) {
+        return createFontCellStyleModel(sheetName, rowIndex, columnIndex, null, null, null, null, null, fontUnderLine, null, null);
+    }
+    /**
+     * 生成字体上标下标样式信息
+     *
+     * @param sheetName      sheet页名称
+     * @param rowIndex       行号
+     * @param columnIndex    列号
+     * @param fontTypeOffset 字体上标下标
+     * @return
+     */
+    public static CellStyleModel createFontTypeOffsetCellStyleModel(String sheetName, int rowIndex, int columnIndex, Short fontTypeOffset) {
+        return createFontCellStyleModel(sheetName, rowIndex, columnIndex, null, null, null, null, null, null, fontTypeOffset, null);
+    }
+    /**
+     * 生成字体删除线样式信息
+     *
+     * @param sheetName     sheet页名称
+     * @param rowIndex      行号
+     * @param columnIndex   列号
+     * @param fontStrikeout 字体删除线
+     * @return
+     */
+    public static CellStyleModel createFontStrikeoutCellStyleModel(String sheetName, int rowIndex, int columnIndex, Boolean fontStrikeout) {
+        return createFontCellStyleModel(sheetName, rowIndex, columnIndex, null, null, null, null, null, null, null, fontStrikeout);
+    }
+    /**
+     * 生成字体样式信息
+     *
+     * @param sheetName      sheet页名称
+     * @param rowIndex       行号
+     * @param columnIndex    列号
+     * @param fontName       字体名称(默认宋体)
+     * @param fontHeight     字体大小
+     * @param fontColor      字体颜色
+     * @param fontBold       字体加粗
+     * @param fontItalic     字体斜体
+     * @param fontUnderLine  字体下划线
+     * @param fontTypeOffset 字体上标下标
+     * @param fontStrikeout  字体删除线
+     * @return
+     */
+    public static CellStyleModel createFontCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , String fontName, Double fontHeight, Object fontColor, Boolean fontBold, Boolean fontItalic, Byte fontUnderLine
+            , Short fontTypeOffset, Boolean fontStrikeout) {
+        return createCellStyleModel(sheetName, rowIndex, columnIndex, fontName, fontHeight, fontColor, fontBold, fontItalic
+                , fontUnderLine, fontTypeOffset, fontStrikeout, null);
+    }
+    /**
+     * 生成背景颜色样式信息
+     *
+     * @param sheetName       sheet页名称
+     * @param rowIndex        行号
+     * @param columnIndex     列号
+     * @param backgroundColor 背景颜色
+     * @return
+     */
+    public static CellStyleModel createBackgroundColorCellStyleModel(String sheetName, int rowIndex, int columnIndex, Object backgroundColor) {
+        return createCellStyleModel(sheetName, rowIndex, columnIndex, null, null, null, null, null, null, null, null, backgroundColor);
+    }
+    /**
+     * 生成背景颜色样式信息(支持自定义RGB颜色)
+     *
+     * @param sheetName   sheet页名称
+     * @param rowIndex    行号
+     * @param columnIndex 列号
+     * @param redNum      红色数值
+     * @param greenNum    绿色数值
+     * @param blueNum     蓝色数值
+     * @return
+     */
+    public static CellStyleModel createBackgroundColorCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , int redNum, int greenNum, int blueNum) {
+        XSSFColor backgroundColor = getRGBColor(redNum, greenNum, blueNum);
+        return createBackgroundColorCellStyleModel(sheetName, rowIndex, columnIndex, backgroundColor);
+    }
+    /**
+     * 生成样式信息
+     *
+     * @param sheetName       sheet页名称
+     * @param rowIndex        行号
+     * @param columnIndex     列号
+     * @param fontName        字体名称(宋体)
+     * @param fontHeight      字体大小
+     * @param fontColor       字体颜色
+     * @param fontBold        字体加粗
+     * @param fontItalic      字体斜体
+     * @param fontUnderLine   字体下划线
+     * @param fontTypeOffset  字体上标下标
+     * @param fontStrikeout   字体删除线
+     * @param backgroundColor 背景颜色
+     * @return
+     */
+    public static CellStyleModel createCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , String fontName, Double fontHeight, Object fontColor, Boolean fontBold, Boolean fontItalic, Byte fontUnderLine
+            , Short fontTypeOffset, Boolean fontStrikeout, Object backgroundColor) {
+        return createCellStyleModel(sheetName, rowIndex, columnIndex, fontName, fontHeight, fontColor, fontBold, fontItalic
+                , fontUnderLine, fontTypeOffset, fontStrikeout, backgroundColor, null, null, null, null, null, null, null, null);
+    }
+    /**
+     * 生成上边框线条颜色样式信息
+     *
+     * @param sheetName      sheet页名称
+     * @param rowIndex       行号
+     * @param columnIndex    列号
+     * @param topBorderColor 上边框线条颜色
+     * @return
+     */
+    public static CellStyleModel createTopBorderColorCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , Object topBorderColor) {
+        return createBorderColorCellStyleModel(sheetName, rowIndex, columnIndex, topBorderColor, null, null, null);
+    }
+    /**
+     * 生成右边框线条颜色样式信息
+     *
+     * @param sheetName        sheet页名称
+     * @param rowIndex         行号
+     * @param columnIndex      列号
+     * @param rightBorderColor 右边框线条颜色
+     * @return
+     */
+    public static CellStyleModel createRightBorderColorCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , Object rightBorderColor) {
+        return createBorderColorCellStyleModel(sheetName, rowIndex, columnIndex, null, rightBorderColor, null, null);
+    }
+    /**
+     * 生成下边框线条颜色样式信息
+     *
+     * @param sheetName         sheet页名称
+     * @param rowIndex          行号
+     * @param columnIndex       列号
+     * @param bottomBorderColor 下边框线条颜色
+     * @return
+     */
+    public static CellStyleModel createBottomBorderColorCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , Object bottomBorderColor) {
+        return createBorderColorCellStyleModel(sheetName, rowIndex, columnIndex, null, null, bottomBorderColor, null);
+    }
+    /**
+     * 生成左边框线条颜色样式信息
+     *
+     * @param sheetName       sheet页名称
+     * @param rowIndex        行号
+     * @param columnIndex     列号
+     * @param leftBorderColor 左边框线条颜色
+     * @return
+     */
+    public static CellStyleModel createLeftBorderColorCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , Object leftBorderColor) {
+        return createBorderColorCellStyleModel(sheetName, rowIndex, columnIndex, null, null, null, leftBorderColor);
+    }
+    /**
+     * 生成上边框线条类型样式信息
+     *
+     * @param sheetName   sheet页名称
+     * @param rowIndex    行号
+     * @param columnIndex 列号
+     * @param borderTop   上边框线条类型
+     * @return
+     */
+    public static CellStyleModel createTopBorderLineTypeCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , BorderStyle borderTop) {
+        return createBorderLineTypeCellStyleModel(sheetName, rowIndex, columnIndex, borderTop, null, null, null);
+    }
+    /**
+     * 生成右边框线条类型样式信息
+     *
+     * @param sheetName   sheet页名称
+     * @param rowIndex    行号
+     * @param columnIndex 列号
+     * @param borderRight 右边框线条类型
+     * @return
+     */
+    public static CellStyleModel createRightBorderLineTypeCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , BorderStyle borderRight) {
+        return createBorderLineTypeCellStyleModel(sheetName, rowIndex, columnIndex, null, borderRight, null, null);
+    }
+    /**
+     * 生成下边框线条类型样式信息
+     *
+     * @param sheetName    sheet页名称
+     * @param rowIndex     行号
+     * @param columnIndex  列号
+     * @param borderBottom 下边框线条类型
+     * @return
+     */
+    public static CellStyleModel createBottomBorderLineTypeCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , BorderStyle borderBottom) {
+        return createBorderLineTypeCellStyleModel(sheetName, rowIndex, columnIndex, null, null, borderBottom, null);
+    }
+    /**
+     * 生成左边框线条类型样式信息
+     *
+     * @param sheetName   sheet页名称
+     * @param rowIndex    行号
+     * @param columnIndex 列号
+     * @param borderLeft  左边框线条类型
+     * @return
+     */
+    public static CellStyleModel createLeftBorderLineTypeCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , BorderStyle borderLeft) {
+        return createBorderLineTypeCellStyleModel(sheetName, rowIndex, columnIndex, null, null, null, borderLeft);
+    }
+    /**
+     * 生成边框线条颜色样式信息
+     *
+     * @param sheetName   sheet页名称
+     * @param rowIndex    行号
+     * @param columnIndex 列号
+     * @param borderColor 边框线条颜色
+     * @return
+     */
+    public static CellStyleModel createBorderColorCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , Object borderColor) {
+        return createBorderCellStyleModel(sheetName, rowIndex, columnIndex, null, borderColor);
+    }
+    /**
+     * 生成边框线条颜色样式信息
+     *
+     * @param sheetName         sheet页名称
+     * @param rowIndex          行号
+     * @param columnIndex       列号
+     * @param topBorderColor    上边框线条颜色
+     * @param rightBorderColor  右边框线条颜色
+     * @param bottomBorderColor 下边框线条颜色
+     * @param leftBorderColor   左边框线条颜色
+     * @return
+     */
+    public static CellStyleModel createBorderColorCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , Object topBorderColor, Object rightBorderColor, Object bottomBorderColor, Object leftBorderColor) {
+        return createBorderCellStyleModel(sheetName, rowIndex, columnIndex, null, null, null, null
+                , topBorderColor, rightBorderColor, bottomBorderColor, leftBorderColor);
+    }
+    /**
+     * 生成边框线条类型样式信息
+     *
+     * @param sheetName      sheet页名称
+     * @param rowIndex       行号
+     * @param columnIndex    列号
+     * @param borderLineType 边框线条类型
+     * @return
+     */
+    public static CellStyleModel createBorderLineTypeCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , BorderStyle borderLineType) {
+        return createBorderCellStyleModel(sheetName, rowIndex, columnIndex, borderLineType, null);
+    }
+    /**
+     * 生成边框线条类型样式信息
+     *
+     * @param sheetName    sheet页名称
+     * @param rowIndex     行号
+     * @param columnIndex  列号
+     * @param borderTop    上边框线条类型
+     * @param borderRight  右边框线条类型
+     * @param borderBottom 下边框线条类型
+     * @param borderLeft   左边框线条类型
+     * @return
+     */
+    public static CellStyleModel createBorderLineTypeCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , BorderStyle borderTop, BorderStyle borderRight, BorderStyle borderBottom, BorderStyle borderLeft) {
+        return createBorderCellStyleModel(sheetName, rowIndex, columnIndex, borderTop, borderRight, borderBottom, borderLeft
+                , null, null, null, null);
+    }
+    /**
+     * 生成边框样式信息
+     *
+     * @param sheetName      sheet页名称
+     * @param rowIndex       行号
+     * @param columnIndex    列号
+     * @param borderLineType 边框线条类型
+     * @param borderColor    边框线条颜色
+     * @return
+     */
+    public static CellStyleModel createBorderCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , BorderStyle borderLineType, Object borderColor) {
+        return createBorderCellStyleModel(sheetName, rowIndex, columnIndex, borderLineType, borderLineType, borderLineType, borderLineType
+                , borderColor, borderColor, borderColor, borderColor);
+    }
+    /**
+     * 生成边框样式信息
+     *
+     * @param sheetName         sheet页名称
+     * @param rowIndex          行号
+     * @param columnIndex       列号
+     * @param borderTop         上边框线条类型
+     * @param borderRight       右边框线条类型
+     * @param borderBottom      下边框线条类型
+     * @param borderLeft        左边框线条类型
+     * @param topBorderColor    上边框线条颜色
+     * @param rightBorderColor  右边框线条颜色
+     * @param bottomBorderColor 下边框线条颜色
+     * @param leftBorderColor   左边框线条颜色
+     * @return
+     */
+    public static CellStyleModel createBorderCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , BorderStyle borderTop, BorderStyle borderRight, BorderStyle borderBottom, BorderStyle borderLeft, Object topBorderColor
+            , Object rightBorderColor, Object bottomBorderColor, Object leftBorderColor) {
+        return createCellStyleModel(sheetName, rowIndex, columnIndex, null, null, null, null, null, null, null, null
+                , null, borderTop, borderRight, borderBottom, borderLeft, topBorderColor, rightBorderColor
+                , bottomBorderColor, leftBorderColor);
+    }
+    /**
+     * 生成样式信息
+     *
+     * @param sheetName         sheet页名称
+     * @param rowIndex          行号
+     * @param columnIndex       列号
+     * @param fontName          字体名称(宋体)
+     * @param fontHeight        字体大小
+     * @param fontColor         字体颜色
+     * @param fontBold          字体加粗
+     * @param fontItalic        字体斜体
+     * @param fontUnderLine     字体下划线
+     * @param fontTypeOffset    字体上标下标
+     * @param fontStrikeout     字体删除线
+     * @param backgroundColor   背景颜色
+     * @param borderTop         上边框线条类型
+     * @param borderRight       右边框线条类型
+     * @param borderBottom      下边框线条类型
+     * @param borderLeft        左边框线条类型
+     * @param topBorderColor    上边框线条颜色
+     * @param rightBorderColor  右边框线条颜色
+     * @param bottomBorderColor 下边框线条颜色
+     * @param leftBorderColor   左边框线条颜色
+     * @return
+     */
+    public static CellStyleModel createCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , String fontName, Double fontHeight, Object fontColor, Boolean fontBold, Boolean fontItalic, Byte fontUnderLine
+            , Short fontTypeOffset, Boolean fontStrikeout, Object backgroundColor, BorderStyle borderTop, BorderStyle borderRight
+            , BorderStyle borderBottom, BorderStyle borderLeft, Object topBorderColor, Object rightBorderColor, Object bottomBorderColor
+            , Object leftBorderColor) {
+        return createCellStyleModel(sheetName, rowIndex, columnIndex, fontName, fontHeight, fontColor, fontBold, fontItalic
+                , fontUnderLine, fontTypeOffset, fontStrikeout, backgroundColor, borderTop, borderRight, borderBottom
+                , borderLeft, topBorderColor, rightBorderColor, bottomBorderColor, leftBorderColor, null, null);
+    }
+    /**
+     * 生成水平对齐方式信息
+     *
+     * @param sheetName           sheet页名称
+     * @param rowIndex            行号
+     * @param columnIndex         列号
+     * @param horizontalAlignment 水平对齐方式
+     * @return
+     */
+    public static CellStyleModel createHorizontalAlignmentCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , HorizontalAlignment horizontalAlignment) {
+        return createAlignmentCellStyleModel(sheetName, rowIndex, columnIndex, horizontalAlignment, null);
+    }
+    /**
+     * 生成垂直对齐方式信息
+     *
+     * @param sheetName         sheet页名称
+     * @param rowIndex          行号
+     * @param columnIndex       列号
+     * @param verticalAlignment 垂直对齐方式
+     * @return
+     */
+    public static CellStyleModel createVerticalAlignmentCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , VerticalAlignment verticalAlignment) {
+        return createAlignmentCellStyleModel(sheetName, rowIndex, columnIndex, null, verticalAlignment);
+    }
+    /**
+     * 生成对齐方式信息
+     *
+     * @param sheetName           sheet页名称
+     * @param rowIndex            行号
+     * @param columnIndex         列号
+     * @param horizontalAlignment 水平对齐方式
+     * @param verticalAlignment   垂直对齐方式
+     * @return
+     */
+    public static CellStyleModel createAlignmentCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment) {
+        return createCellStyleModel(sheetName, rowIndex, columnIndex, null, null, null, null
+                , null, null, null, null, null, null, null
+                , null, null, null, null, null, null
+                , horizontalAlignment, verticalAlignment);
+    }
+    /**
+     * 生成样式信息
+     *
+     * @param sheetName           sheet页名称
+     * @param rowIndex            行号
+     * @param columnIndex         列号
+     * @param fontName            字体名称(宋体)
+     * @param fontHeight          字体大小
+     * @param fontColor           字体颜色
+     * @param fontBold            字体加粗
+     * @param fontItalic          字体斜体
+     * @param fontUnderLine       字体下划线
+     * @param fontTypeOffset      字体上标下标
+     * @param fontStrikeout       字体删除线
+     * @param backgroundColor     背景颜色
+     * @param borderTop           上边框线条类型
+     * @param borderRight         右边框线条类型
+     * @param borderBottom        下边框线条类型
+     * @param borderLeft          左边框线条类型
+     * @param topBorderColor      上边框线条颜色
+     * @param rightBorderColor    右边框线条颜色
+     * @param bottomBorderColor   下边框线条颜色
+     * @param leftBorderColor     左边框线条颜色
+     * @param horizontalAlignment 水平对齐方式
+     * @param verticalAlignment   垂直对齐方式
+     * @return
+     */
+    public static CellStyleModel createCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , String fontName, Double fontHeight, Object fontColor, Boolean fontBold, Boolean fontItalic, Byte fontUnderLine
+            , Short fontTypeOffset, Boolean fontStrikeout, Object backgroundColor, BorderStyle borderTop, BorderStyle borderRight
+            , BorderStyle borderBottom, BorderStyle borderLeft, Object topBorderColor, Object rightBorderColor, Object bottomBorderColor
+            , Object leftBorderColor, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment) {
+        return createCellStyleModel(sheetName, rowIndex, columnIndex, fontName, fontHeight, fontColor, fontBold, fontItalic
+                , fontUnderLine, fontTypeOffset, fontStrikeout, backgroundColor, borderTop, borderRight, borderBottom
+                , borderLeft, topBorderColor, rightBorderColor, bottomBorderColor, leftBorderColor, horizontalAlignment, verticalAlignment, null);
+    }
+    /**
+     * 生成自动换行样式信息
+     *
+     * @param sheetName   sheet页名称
+     * @param rowIndex    行号
+     * @param columnIndex 列号
+     * @param wrapText    自动换行
+     * @return
+     */
+    public static CellStyleModel createWrapTextCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , Boolean wrapText) {
+        return createCellStyleModel(sheetName, rowIndex, columnIndex, null, null, null, null, null
+                , null, null, null, null, null, null, null
+                , null, null, null, null, null, null, null
+                , wrapText);
+    }
+    /**
+     * 生成样式信息
+     *
+     * @param sheetName           sheet页名称
+     * @param rowIndex            行号
+     * @param columnIndex         列号
+     * @param fontName            字体名称(宋体)
+     * @param fontHeight          字体大小
+     * @param fontColor           字体颜色
+     * @param fontBold            字体加粗
+     * @param fontItalic          字体斜体
+     * @param fontUnderLine       字体下划线
+     * @param fontTypeOffset      字体上标下标
+     * @param fontStrikeout       字体删除线
+     * @param backgroundColor     背景颜色
+     * @param borderTop           上边框线条类型
+     * @param borderRight         右边框线条类型
+     * @param borderBottom        下边框线条类型
+     * @param borderLeft          左边框线条类型
+     * @param topBorderColor      上边框线条颜色
+     * @param rightBorderColor    右边框线条颜色
+     * @param bottomBorderColor   下边框线条颜色
+     * @param leftBorderColor     左边框线条颜色
+     * @param horizontalAlignment 水平对齐方式
+     * @param verticalAlignment   垂直对齐方式
+     * @param wrapText            自动换行
+     * @return
+     */
+    public static CellStyleModel createCellStyleModel(String sheetName, int rowIndex, int columnIndex
+            , String fontName, Double fontHeight, Object fontColor, Boolean fontBold, Boolean fontItalic, Byte fontUnderLine
+            , Short fontTypeOffset, Boolean fontStrikeout, Object backgroundColor, BorderStyle borderTop, BorderStyle borderRight
+            , BorderStyle borderBottom, BorderStyle borderLeft, Object topBorderColor, Object rightBorderColor, Object bottomBorderColor
+            , Object leftBorderColor, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment, Boolean wrapText) {
+        CellStyleModel cellStyleModel = new CellStyleModel();
+        //sheet页名称
+        cellStyleModel.setSheetName(sheetName);
+        //行号
+        cellStyleModel.setRowIndex(rowIndex);
+        //列号
+        cellStyleModel.setColIndex(columnIndex);
+        //设置字体样式
+        //字体名称(比如宋体)
+        fontName = fontName != null && StrUtil.equals(fontName, "") ? "宋体" : fontName;
+        cellStyleModel.setFontName(fontName);
+        //字体大小
+        fontHeight = fontHeight != null && fontHeight <= 0 ? null : fontHeight;
+        cellStyleModel.setFontHeight(fontHeight);
+        //字体颜色
+        fontColor = fontColor != null && (fontColor instanceof IndexedColors == false && fontColor instanceof XSSFColor == false)
+                ? null : fontColor;
+        cellStyleModel.setFontColor(fontColor);
+        //字体加粗
+        cellStyleModel.setFontBold(fontBold);
+        //字体斜体
+        cellStyleModel.setFontItalic(fontItalic);
+        //字体下划线
+        fontUnderLine = fontUnderLine != null && (fontUnderLine != Font.U_NONE && fontUnderLine != Font.U_SINGLE && fontUnderLine != Font.U_DOUBLE
+                && fontUnderLine != Font.U_DOUBLE_ACCOUNTING && fontUnderLine != Font.U_SINGLE_ACCOUNTING) ? null : fontUnderLine;
+        cellStyleModel.setFontUnderLine(fontUnderLine);
+        //字体上标下标
+        fontTypeOffset = fontTypeOffset != null && (fontTypeOffset != Font.SS_NONE && fontTypeOffset != Font.SS_SUB && fontTypeOffset != Font.SS_SUPER)
+                ? null : fontTypeOffset;
+        cellStyleModel.setFontTypeOffset(fontTypeOffset);
+        //字体删除线
+        cellStyleModel.setFontStrikeout(fontStrikeout);
+        //背景颜色
+        backgroundColor = backgroundColor != null && (backgroundColor instanceof IndexedColors == false && backgroundColor instanceof XSSFColor == false)
+                ? null : backgroundColor;
+        cellStyleModel.setBackgroundColor(backgroundColor);
+        //边框样式
+        //上边框线条类型
+        cellStyleModel.setBorderTop(borderTop);
+        //右边框线条类型
+        cellStyleModel.setBorderRight(borderRight);
+        //下边框线条类型
+        cellStyleModel.setBorderBottom(borderBottom);
+        //左边框线条类型
+        cellStyleModel.setBorderLeft(borderLeft);
+        //上边框颜色类型
+        cellStyleModel.setTopBorderColor(topBorderColor);
+        //右边框颜色类型
+        cellStyleModel.setRightBorderColor(rightBorderColor);
+        //下边框颜色类型
+        cellStyleModel.setBottomBorderColor(bottomBorderColor);
+        //左边框颜色类型
+        cellStyleModel.setLeftBorderColor(leftBorderColor);
+        //对齐方式
+        //水平对齐方式
+        cellStyleModel.setHorizontalAlignment(horizontalAlignment);
+        //垂直对齐方式
+        cellStyleModel.setVerticalAlignment(verticalAlignment);
+        //自动换行
+        cellStyleModel.setWrapText(wrapText);
+        return cellStyleModel;
+    }
+
+    public static CellStyleModel createWrapTextCellStyleModel(String sheetName, int rowIndex, int columnIndex, String fontName,Double fontHeight, Boolean fontBold
+            , BorderStyle borderStyle, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment) {
+        return createCellStyleModel(sheetName, rowIndex, columnIndex, fontName, fontHeight, null, fontBold, null
+                , null, null, null, null, borderStyle, borderStyle, borderStyle
+                , borderStyle, null, null, null, null, horizontalAlignment, verticalAlignment
+                , null);
+    }
+}

+ 29 - 0
src/test/java/com/xjrsoft/module/excel/CustomCellRangeAddress.java

@@ -0,0 +1,29 @@
+package com.xjrsoft.module.excel;
+
+import org.apache.poi.ss.util.CellRangeAddress;
+
+/**
+ * @author dzx
+ * @date 2024/1/8
+ */
+public class CustomCellRangeAddress extends CellRangeAddress {
+    private boolean isBlod;
+
+    public boolean getIsBlod() {
+        return isBlod;
+    }
+
+    public void setIsBlod(boolean blod) {
+        isBlod = blod;
+    }
+
+    public CustomCellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol) {
+        super(firstRow, lastRow, firstCol, lastCol);
+    }
+
+    public CustomCellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol, Boolean isBlod) {
+        super(firstRow, lastRow, firstCol, lastCol);
+        setIsBlod(isBlod);
+    }
+
+}

+ 368 - 0
src/test/java/com/xjrsoft/module/excel/CustomCellStyleHandler.java

@@ -0,0 +1,368 @@
+package com.xjrsoft.module.excel;
+
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.excel.write.handler.AbstractRowWriteHandler;
+import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
+import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.FillPatternType;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+import org.apache.poi.xssf.usermodel.XSSFCellStyle;
+import org.apache.poi.xssf.usermodel.XSSFColor;
+import org.apache.poi.xssf.usermodel.XSSFFont;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author dzx
+ * @date 2024/1/5
+ */
+public class CustomCellStyleHandler extends AbstractRowWriteHandler {
+    /**
+     * sheet页名称列表
+     */
+    private List<String> sheetNameList;
+    /**
+     * 样式信息
+     */
+    private List<CellStyleModel> cellStyleList = new ArrayList<>();
+    /**
+     * 自定义样式适配器构造方法
+     *
+     * @param cellStyleList 样式信息
+     */
+    public CustomCellStyleHandler(List<CellStyleModel> cellStyleList) {
+        if (cellStyleList == null || cellStyleList.isEmpty()) {
+            return;
+        }
+        cellStyleList = cellStyleList.stream().filter(x -> x != null
+                //判断sheet名称KEY是否存在
+                && StrUtil.isNotBlank(x.getSheetName())
+                //字体样式
+                //判断字体颜色KEY是否存在
+                && (x.getFontColor() == null || x.getFontColor() instanceof IndexedColors
+                || x.getFontColor() instanceof XSSFColor)
+                //判断背景颜色KEY是否存在
+                && (x.getBackgroundColor() == null || x.getBackgroundColor() instanceof IndexedColors
+                || x.getBackgroundColor() instanceof XSSFColor)
+                //边框样式
+                // 判断上边框线条颜色KEY是否存在
+                && (x.getTopBorderColor() == null || x.getTopBorderColor() instanceof IndexedColors
+                || x.getTopBorderColor() instanceof XSSFColor)
+                // 判断右边框线条颜色KEY是否存在
+                && (x.getRightBorderColor() == null || x.getRightBorderColor() instanceof IndexedColors
+                || x.getRightBorderColor() instanceof XSSFColor)
+                // 判断下边框线条颜色KEY是否存在
+                && (x.getBottomBorderColor() == null || x.getBottomBorderColor() instanceof IndexedColors
+                || x.getBottomBorderColor() instanceof XSSFColor)
+                // 判断左边框线条颜色KEY是否存在
+                && (x.getLeftBorderColor() == null || x.getLeftBorderColor() instanceof IndexedColors
+                || x.getLeftBorderColor() instanceof XSSFColor)
+        ).collect(Collectors.toList());
+        this.cellStyleList = cellStyleList;
+        sheetNameList = this.cellStyleList.stream().map(x -> x.getSheetName()).distinct().collect(Collectors.toList());
+    }
+
+//    public CustomCellStyleHandler(CellStyleModel cellStyleModel) {
+//        if (cellStyleModel == null) {
+//            return;
+//        }
+//        cellStyleList = cellStyleList.stream().filter(x -> x != null
+//                //判断sheet名称KEY是否存在
+//                && StrUtil.isNotBlank(x.getSheetName())
+//                //字体样式
+//                //判断字体颜色KEY是否存在
+//                && (x.getFontColor() == null || x.getFontColor() instanceof IndexedColors
+//                || x.getFontColor() instanceof XSSFColor)
+//                //判断背景颜色KEY是否存在
+//                && (x.getBackgroundColor() == null || x.getBackgroundColor() instanceof IndexedColors
+//                || x.getBackgroundColor() instanceof XSSFColor)
+//                //边框样式
+//                // 判断上边框线条颜色KEY是否存在
+//                && (x.getTopBorderColor() == null || x.getTopBorderColor() instanceof IndexedColors
+//                || x.getTopBorderColor() instanceof XSSFColor)
+//                // 判断右边框线条颜色KEY是否存在
+//                && (x.getRightBorderColor() == null || x.getRightBorderColor() instanceof IndexedColors
+//                || x.getRightBorderColor() instanceof XSSFColor)
+//                // 判断下边框线条颜色KEY是否存在
+//                && (x.getBottomBorderColor() == null || x.getBottomBorderColor() instanceof IndexedColors
+//                || x.getBottomBorderColor() instanceof XSSFColor)
+//                // 判断左边框线条颜色KEY是否存在
+//                && (x.getLeftBorderColor() == null || x.getLeftBorderColor() instanceof IndexedColors
+//                || x.getLeftBorderColor() instanceof XSSFColor)
+//        ).collect(Collectors.toList());
+//        this.cellStyleList = cellStyleList;
+//        sheetNameList = this.cellStyleList.stream().map(x -> x.getSheetName()).distinct().collect(Collectors.toList());
+//    }
+
+    @Override
+    public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row
+            , Integer relativeRowIndex, Boolean isHead) {
+        Sheet sheet = writeSheetHolder.getSheet();
+        //不需要添加样式,或者当前sheet页不需要添加样式
+        if (cellStyleList == null || cellStyleList.size() <= 0 || sheetNameList.contains(sheet.getSheetName()) == false) {
+            return;
+        }
+        //获取当前行的样式信息
+        List<CellStyleModel> rowCellStyleList = cellStyleList.stream().filter(x ->
+                StrUtil.equals(x.getSheetName(), sheet.getSheetName()) && x.getRowIndex() == relativeRowIndex).collect(Collectors.toList());
+        //该行不需要设置样式
+        if (rowCellStyleList == null || rowCellStyleList.size() <= 0) {
+            return;
+        }
+        for (CellStyleModel cellStyleModel : rowCellStyleList) {
+            //设置单元格样式
+            setCellStyle(cellStyleModel, row);
+        }
+        //删除已添加的样式信息
+        cellStyleList.removeAll(rowCellStyleList);
+        //重新获取要添加的sheet页姓名
+        sheetNameList = cellStyleList.stream().map(x -> x.getSheetName()).distinct().collect(Collectors.toList());
+    }
+    /**
+     * 给单元格设置样式
+     *
+     * @param cellStyleModel 样式信息
+     * @param row            行对象
+     */
+    private void setCellStyle(CellStyleModel cellStyleModel, Row row) {
+        //背景颜色
+        Object backgroundColor = cellStyleModel.getBackgroundColor();
+        //自动换行
+        Boolean wrapText = cellStyleModel.getWrapText();
+        //列索引
+        int colIndex = cellStyleModel.getColIndex();
+        //边框样式
+        Cell cell = row.getCell(colIndex);
+        if (cell == null) {
+            cell = row.createCell(colIndex);
+        }
+        XSSFCellStyle style = (XSSFCellStyle) cell.getRow().getSheet().getWorkbook().createCellStyle();
+        // 克隆出一个 style
+        style.cloneStyleFrom(cell.getCellStyle());
+        //设置背景颜色
+        if (backgroundColor != null) {
+            //使用IndexedColors定义的颜色
+            if (backgroundColor instanceof IndexedColors) {
+                style.setFillForegroundColor(((IndexedColors) backgroundColor).getIndex());
+            }
+            //使用自定义的RGB颜色
+            else if (backgroundColor instanceof XSSFColor) {
+                style.setFillForegroundColor((XSSFColor) backgroundColor);
+            }
+            style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+        }
+        //设置自动换行
+        if (wrapText != null) {
+            style.setWrapText(wrapText);
+        }
+        //设置字体样式
+        setFontStyle(row, style, cellStyleModel);
+        //设置边框样式
+        setBorderStyle(style, cellStyleModel);
+        //设置对齐方式
+        setAlignmentStyle(style, cellStyleModel);
+        cell.setCellStyle(style);
+    }
+    /**
+     * 设置字体样式
+     *
+     * @param row            行对象
+     * @param style          单元格样式
+     * @param cellStyleModel 样式信息
+     */
+    private void setFontStyle(Row row, XSSFCellStyle style, CellStyleModel cellStyleModel) {
+        //字体名称
+        String fontName = cellStyleModel.getFontName();
+        //字体大小
+        Double fontHeight = cellStyleModel.getFontHeight();
+        //字体颜色
+        Object fontColor = cellStyleModel.getFontColor();
+        //字体加粗
+        Boolean fontBold = cellStyleModel.getFontBold();
+        //字体斜体
+        Boolean fontItalic = cellStyleModel.getFontItalic();
+        //字体下划线
+        Byte fontUnderLine = cellStyleModel.getFontUnderLine();
+        //字体上标下标
+        Short fontTypeOffset = cellStyleModel.getFontTypeOffset();
+        //字体删除线
+        Boolean fontStrikeout = cellStyleModel.getFontStrikeout();
+        //不需要设置字体样式
+        if (fontName == null && fontHeight == null && fontColor == null && fontBold == null && fontItalic == null
+                && fontUnderLine == null && fontTypeOffset == null && fontStrikeout == null) {
+            return;
+        }
+        XSSFFont font = null;
+        //样式存在字体对象时,使用原有的字体对象
+        if (style.getFontIndex() != 0) {
+            font = style.getFont();
+        }
+        //样式不存在字体对象时,创建字体对象
+        else {
+            font = (XSSFFont) row.getSheet().getWorkbook().createFont();
+            //默认字体为宋体
+            font.setFontName("宋体");
+        }
+        //设置字体名称
+        if (fontName != null) {
+            font.setFontName(fontName);
+        }
+        //设置字体大小
+        if (fontHeight != null) {
+            font.setFontHeight(fontHeight);
+        }
+        //设置字体颜色
+        if (fontColor != null) {
+            //使用IndexedColors定义的颜色
+            if (fontColor instanceof IndexedColors) {
+                font.setColor(((IndexedColors) fontColor).getIndex());
+            }
+            //使用自定义的RGB颜色
+            else if (fontColor instanceof XSSFColor) {
+                font.setColor((XSSFColor) fontColor);
+            }
+        }
+        //设置字体加粗
+        if (fontBold != null) {
+            font.setBold(fontBold);
+        }
+        //设置字体斜体
+        if (fontItalic != null) {
+            font.setItalic(fontItalic);
+        }
+        //设置字体下划线
+        if (fontUnderLine != null) {
+            font.setUnderline(fontUnderLine);
+        }
+        //设置字体上标下标
+        if (fontTypeOffset != null) {
+            font.setTypeOffset(fontTypeOffset);
+        }
+        //设置字体删除线
+        if (fontStrikeout != null) {
+            font.setStrikeout(fontStrikeout);
+        }
+        style.setFont(font);
+    }
+    /**
+     * 设置边框样式
+     *
+     * @param style          单元格样式
+     * @param cellStyleModel 样式信息
+     */
+    private void setBorderStyle(XSSFCellStyle style, CellStyleModel cellStyleModel) {
+        //上边框线条类型
+        BorderStyle borderTop = cellStyleModel.getBorderTop();
+        //右边框线条类型
+        BorderStyle borderRight = cellStyleModel.getBorderRight();
+        //下边框线条类型
+        BorderStyle borderBottom = cellStyleModel.getBorderBottom();
+        //左边框线条类型
+        BorderStyle borderLeft = cellStyleModel.getBorderLeft();
+        //上边框颜色类型
+        Object topBorderColor = cellStyleModel.getTopBorderColor();
+        //右边框颜色类型
+        Object rightBorderColor = cellStyleModel.getRightBorderColor();
+        //下边框颜色类型
+        Object bottomBorderColor = cellStyleModel.getBottomBorderColor();
+        //左边框颜色类型
+        Object leftBorderColor = cellStyleModel.getLeftBorderColor();
+        //不需要设置边框样式
+        if (borderTop == null && borderRight == null && borderBottom == null && borderLeft == null && topBorderColor == null
+                && rightBorderColor == null && bottomBorderColor == null && leftBorderColor == null) {
+            return;
+        }
+        //设置上边框线条类型
+        if (borderTop != null) {
+            style.setBorderTop(borderTop);
+        }
+        //设置右边框线条类型
+        if (borderRight != null) {
+            style.setBorderRight(borderRight);
+        }
+        //设置下边框线条类型
+        if (borderBottom != null) {
+            style.setBorderBottom(borderBottom);
+        }
+        //设置左边框线条类型
+        if (borderLeft != null) {
+            style.setBorderLeft(borderLeft);
+        }
+        //设置上边框线条颜色
+        if (topBorderColor != null) {
+            //使用IndexedColors定义的颜色
+            if (topBorderColor instanceof IndexedColors) {
+                style.setTopBorderColor(((IndexedColors) topBorderColor).getIndex());
+            }
+            //使用自定义的RGB颜色
+            else if (topBorderColor instanceof XSSFColor) {
+                style.setTopBorderColor((XSSFColor) topBorderColor);
+            }
+        }
+        //设置右边框线条颜色
+        if (rightBorderColor != null) {
+            //使用IndexedColors定义的颜色
+            if (rightBorderColor instanceof IndexedColors) {
+                style.setRightBorderColor(((IndexedColors) rightBorderColor).getIndex());
+            }
+            //使用自定义的RGB颜色
+            else if (rightBorderColor instanceof XSSFColor) {
+                style.setRightBorderColor((XSSFColor) rightBorderColor);
+            }
+        }
+        //设置下边框线条颜色
+        if (bottomBorderColor != null) {
+            //使用IndexedColors定义的颜色
+            if (bottomBorderColor instanceof IndexedColors) {
+                style.setBottomBorderColor(((IndexedColors) bottomBorderColor).getIndex());
+            }
+            //使用自定义的RGB颜色
+            else if (bottomBorderColor instanceof XSSFColor) {
+                style.setBottomBorderColor((XSSFColor) bottomBorderColor);
+            }
+        }
+        //设置左边框线条颜色
+        if (leftBorderColor != null) {
+            //使用IndexedColors定义的颜色
+            if (leftBorderColor instanceof IndexedColors) {
+                style.setLeftBorderColor(((IndexedColors) leftBorderColor).getIndex());
+            }
+            //使用自定义的RGB颜色
+            else if (topBorderColor instanceof XSSFColor) {
+                style.setLeftBorderColor((XSSFColor) leftBorderColor);
+            }
+        }
+    }
+    /**
+     * 设置对齐方式
+     *
+     * @param style          单元格样式
+     * @param cellStyleModel 样式信息
+     */
+    private void setAlignmentStyle(XSSFCellStyle style, CellStyleModel cellStyleModel) {
+        //水平对齐方式
+        HorizontalAlignment horizontalAlignment = cellStyleModel.getHorizontalAlignment();
+        //垂直对齐方式
+        VerticalAlignment verticalAlignment = cellStyleModel.getVerticalAlignment();
+        //不需要设置对齐方式
+        if (horizontalAlignment == null && verticalAlignment == null) {
+            return;
+        }
+        //设置水平对齐方式
+        if (horizontalAlignment != null) {
+            style.setAlignment(horizontalAlignment);
+        }
+        //设置垂直对齐方式
+        if (verticalAlignment != null) {
+            style.setVerticalAlignment(verticalAlignment);
+        }
+    }
+}

+ 71 - 0
src/test/java/com/xjrsoft/module/excel/CustomMergeStrategy.java

@@ -0,0 +1,71 @@
+package com.xjrsoft.module.excel;
+
+import com.alibaba.excel.metadata.Head;
+import com.alibaba.excel.write.merge.AbstractMergeStrategy;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.util.CellRangeAddress;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author dzx
+ * @date 2024/1/4
+ */
+public class CustomMergeStrategy extends AbstractMergeStrategy {
+
+   //合并开始的第一行
+    private Integer firstRow;
+    //合并开始的最后行
+    private Integer lastRow;
+    //合并开始的第一列
+    private Integer firstCol;
+    private Integer lastCol;
+
+    // exportDataList为待合并目标列的值
+    public CustomMergeStrategy(Integer firstRow, Integer lastRow, Integer firstCol, Integer lastCol) {
+        this.firstRow = firstRow;
+        this.lastRow = lastRow;
+        this.firstCol = firstCol;
+        this.lastCol = lastCol;
+    }
+
+
+    @Override
+    protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
+        mergeGroupColumn(sheet);
+    }
+
+    private void mergeGroupColumn(Sheet sheet) {
+        CellRangeAddress cellRangeAddress = new CellRangeAddress(firstRow, lastRow, firstCol, lastCol);
+        sheet.addMergedRegionUnsafe(cellRangeAddress);
+
+    }
+
+    // 该方法将目标列根据值是否相同连续可合并,存储可合并的行数
+    private List<Integer> getGroupCountList(List<String> exportDataList){
+        if (exportDataList == null || exportDataList.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        List<Integer> groupCountList = new ArrayList<>();
+        int count = 1;
+
+        for (int i = 1; i < exportDataList.size(); i++) {
+            if (exportDataList.get(i).equals(exportDataList.get(i - 1))) {
+                count++;
+            } else {
+                groupCountList.add(count);
+                count = 1;
+            }
+        }
+        // 处理完最后一条后
+        groupCountList.add(count);
+        return groupCountList;
+    }
+
+
+
+
+}

+ 90 - 0
src/test/java/com/xjrsoft/module/excel/CustomMergeStrategy2.java

@@ -0,0 +1,90 @@
+package com.xjrsoft.module.excel;
+
+import com.alibaba.excel.metadata.Head;
+import com.alibaba.excel.write.merge.AbstractMergeStrategy;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.util.CellRangeAddress;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author dzx
+ * @date 2024/1/4
+ */
+public class CustomMergeStrategy2 extends AbstractMergeStrategy {
+    /**
+     * 分组,每几行合并一次
+     */
+    private List<Integer> exportFieldGroupCountList;
+
+    /**
+     * 目标合并列index
+     */
+    private Integer targetColumnIndex;
+
+
+
+    // 需要开始合并单元格的首行index
+    private Integer rowIndex;
+
+    // exportDataList为待合并目标列的值
+    public CustomMergeStrategy2(List<String> exportDataList, Integer targetColumnIndex) {
+        this.exportFieldGroupCountList = getGroupCountList(exportDataList);
+        this.targetColumnIndex = targetColumnIndex;
+    }
+
+
+    @Override
+    protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
+
+        if (null == rowIndex) {
+            rowIndex = cell.getRowIndex();
+        }
+        // 仅从首行以及目标列的单元格开始合并,忽略其他
+        if (cell.getRowIndex() == rowIndex && cell.getColumnIndex() == targetColumnIndex) {
+            mergeGroupColumn(sheet);
+        }
+    }
+
+    private void mergeGroupColumn(Sheet sheet) {
+        int rowCount = rowIndex;
+        for (Integer count : exportFieldGroupCountList) {
+            if(count == 1) {
+                rowCount += count;
+                continue ;
+            }
+            // 合并单元格
+            CellRangeAddress cellRangeAddress = new CellRangeAddress(rowCount, rowCount + count - 1, targetColumnIndex, targetColumnIndex);
+            sheet.addMergedRegionUnsafe(cellRangeAddress);
+            rowCount += count;
+        }
+    }
+
+    // 该方法将目标列根据值是否相同连续可合并,存储可合并的行数
+    private List<Integer> getGroupCountList(List<String> exportDataList){
+        if (exportDataList == null || exportDataList.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        List<Integer> groupCountList = new ArrayList<>();
+        int count = 1;
+
+        for (int i = 1; i < exportDataList.size(); i++) {
+            if (exportDataList.get(i).equals(exportDataList.get(i - 1))) {
+                count++;
+            } else {
+                groupCountList.add(count);
+                count = 1;
+            }
+        }
+        // 处理完最后一条后
+        groupCountList.add(count);
+        return groupCountList;
+    }
+
+
+
+
+}

+ 40 - 0
src/test/java/com/xjrsoft/module/excel/DemoData.java

@@ -0,0 +1,40 @@
+package com.xjrsoft.module.excel;
+
+import java.util.Date;
+
+/**
+ * @author dzx
+ * @date 2024/1/4
+ */
+public class DemoData {
+
+    private String content;
+
+    private Date contentDate;
+
+    private Double doubleData;
+
+    public String getContent() {
+        return content;
+    }
+
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    public Date getContentDate() {
+        return contentDate;
+    }
+
+    public void setContentDate(Date contentDate) {
+        this.contentDate = contentDate;
+    }
+
+    public Double getDoubleData() {
+        return doubleData;
+    }
+
+    public void setDoubleData(Double doubleData) {
+        this.doubleData = doubleData;
+    }
+}

+ 52 - 0
src/test/java/com/xjrsoft/module/excel/ExcelData.java

@@ -0,0 +1,52 @@
+package com.xjrsoft.module.excel;
+
+import com.alibaba.excel.annotation.write.style.ColumnWidth;
+import com.alibaba.excel.annotation.write.style.ContentLoopMerge;
+
+/**
+ * @author dzx
+ * @date 2024/1/4
+ */
+public class ExcelData {
+
+    @ColumnWidth(15)
+    private String column1;
+    @ColumnWidth(15)
+    private String column2;
+    @ColumnWidth(15)
+    private String column3;
+    @ColumnWidth(20)
+    private String column4;
+
+    public String getColumn1() {
+        return column1;
+    }
+
+    public void setColumn1(String column1) {
+        this.column1 = column1;
+    }
+
+    public String getColumn2() {
+        return column2;
+    }
+
+    public void setColumn2(String column2) {
+        this.column2 = column2;
+    }
+
+    public String getColumn3() {
+        return column3;
+    }
+
+    public void setColumn3(String column3) {
+        this.column3 = column3;
+    }
+
+    public String getColumn4() {
+        return column4;
+    }
+
+    public void setColumn4(String column4) {
+        this.column4 = column4;
+    }
+}

+ 102 - 0
src/test/java/com/xjrsoft/module/excel/ExcelMergeUtil.java

@@ -0,0 +1,102 @@
+package com.xjrsoft.module.excel;
+
+import com.alibaba.excel.metadata.Head;
+import com.alibaba.excel.metadata.data.WriteCellData;
+import com.alibaba.excel.write.handler.CellWriteHandler;
+import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
+import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.util.CellRangeAddress;
+
+import java.util.List;
+
+/**
+ * @author dzx
+ * @date 2024/1/4
+ */
+public class ExcelMergeUtil implements CellWriteHandler {
+    private int[] mergeColumnIndex;
+    private int mergeRowIndex;
+
+    public ExcelMergeUtil() {
+    }
+
+    public ExcelMergeUtil(int mergeRowIndex, int[] mergeColumnIndex) {
+        this.mergeRowIndex = mergeRowIndex;
+        this.mergeColumnIndex = mergeColumnIndex;
+    }
+
+    @Override
+    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
+
+    }
+
+    @Override
+    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
+
+    }
+
+
+
+
+    @Override
+    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
+
+        //当前行
+        int curRowIndex = cell.getRowIndex();
+        //当前列
+        int curColIndex = cell.getColumnIndex();
+
+        if (curRowIndex > mergeRowIndex) {
+            for (int i = 0; i < mergeColumnIndex.length; i++) {
+                if (curColIndex == mergeColumnIndex[i]) {
+                    mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);
+                    break;
+                }
+            }
+        }
+
+    }
+
+    /**
+     * 当前单元格向上合并
+     *
+     * @param writeSheetHolder
+     * @param cell             当前单元格
+     * @param curRowIndex      当前行
+     * @param curColIndex      当前列
+     */
+    private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
+        Object curData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
+        Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);
+        Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();
+        // 将当前单元格数据与上一个单元格数据比较
+        Boolean dataBool = preData.equals(curData);
+        //此处需要注意,获取每一行第二列数据和上一行第一列数据进行比较,如果相等合并,getCell里面的值,是名称所在列的下标
+        Boolean bool = cell.getRow().getCell(1).getStringCellValue().equals(cell.getSheet().getRow(curRowIndex - 1).getCell(1).getStringCellValue());
+        if (dataBool && bool) {
+            Sheet sheet = writeSheetHolder.getSheet();
+            List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
+            boolean isMerged = false;
+            for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {
+                CellRangeAddress cellRangeAddr = mergeRegions.get(i);
+                // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
+                if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) {
+                    sheet.removeMergedRegion(i);
+                    cellRangeAddr.setLastRow(curRowIndex);
+                    sheet.addMergedRegion(cellRangeAddr);
+                    isMerged = true;
+                }
+            }
+            // 若上一个单元格未被合并,则新增合并单元
+            if (!isMerged) {
+                CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);
+                sheet.addMergedRegion(cellRangeAddress);
+            }
+        }
+    }
+
+}

+ 109 - 0
src/test/java/com/xjrsoft/module/excel/ExcelTest.java

@@ -0,0 +1,109 @@
+package com.xjrsoft.module.excel;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelWriter;
+import com.alibaba.excel.read.builder.ExcelReaderSheetBuilder;
+import com.alibaba.excel.support.ExcelTypeEnum;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import com.alibaba.excel.write.metadata.WriteTable;
+import net.sf.jsqlparser.statement.merge.Merge;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import com.alibaba.excel.EasyExcelFactory;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author dzx
+ * @date 2024/1/4
+ */
+public class ExcelTest {
+
+    @Test
+    void test() throws IOException {
+        //模板文件
+        InputStream templateFile = Thread.currentThread().getContextClassLoader().getResourceAsStream("C:\\Users\\14263\\Desktop\\test\\宿管值班管理.xlsx");
+        String fileName = "C:\\Users\\14263\\Desktop\\test\\导出结果.xlsx";
+        ByteArrayOutputStream bot = new ByteArrayOutputStream();
+        ExcelWriter excelWriter = null;
+        try {
+
+            //本地导出
+            excelWriter = EasyExcel.write(fileName).withTemplate(templateFile).build();
+//            //流输出
+//            excelWriter = EasyExcel.write(bot)
+//                    .withTemplate(templateFile )
+//                    .build();
+
+            WriteSheet writeSheet = EasyExcel.writerSheet(0).build();
+
+//            //配置多组数据填充完后,需要换行,防止覆盖模板中的单组数据模板
+//            FillConfig fillConfig = FillConfig.builder().forceNewRow(true).build();
+//            // 直接写入list数据
+//            excelWriter.fill(exportTestList, fillConfig, writeSheet);
+
+
+            // 写入自定义的表头
+            Map<String, Object> map = new HashMap<>();
+            map.put("{title}", "测试");
+            map.put("maleHousemaster", "张三");
+            map.put("femaleHousemaster", "里斯");
+            map.put("instructor", "赵雅芝");
+            map.put("maleStudents", "20");
+            map.put("femaleStudents", "25");
+            excelWriter.fill(map, writeSheet);
+
+            excelWriter.finish();
+
+        } catch (Exception e) {
+        } finally {
+            // 千万别忘记关闭流
+            if (excelWriter != null) {
+                excelWriter.finish();
+            }
+        }
+    }
+
+    @Test
+    void test2(){
+        writeExcel01();
+    }
+
+    public static void writeExcel01() {
+
+    }
+
+    private static List<DemoData> data1() {
+        List<DemoData> list = new ArrayList();
+        for (int i = 0; i < 3; i++) {
+            DemoData data = new DemoData();
+            data.setContent("字符串" + 1);
+            list.add(data);
+        }
+        for (int i = 0; i < 3; i++) {
+            DemoData data = new DemoData();
+            data.setContent("字符串" + 2);
+            list.add(data);
+        }
+        for (int i = 0; i < 4; i++) {
+            DemoData data = new DemoData();
+            data.setContent("字符串" + 3);
+            list.add(data);
+        }
+        return list;
+    }
+
+}

+ 73 - 0
src/test/java/com/xjrsoft/module/excel/ExcelUtil.java

@@ -0,0 +1,73 @@
+//package com.xjrsoft.module.excel;
+//
+//import com.alibaba.excel.EasyExcel;
+//import com.alibaba.excel.ExcelWriter;
+//import com.alibaba.excel.write.metadata.WriteSheet;
+//import com.alibaba.excel.write.metadata.style.WriteCellStyle;
+//import com.alibaba.excel.write.metadata.style.WriteFont;
+//import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
+//import org.apache.poi.ss.usermodel.BorderStyle;
+//import org.apache.poi.ss.usermodel.HorizontalAlignment;
+//import org.apache.poi.ss.usermodel.IndexedColors;
+//import org.apache.poi.ss.usermodel.VerticalAlignment;
+//
+//import javax.servlet.http.HttpServletResponse;
+//import java.io.IOException;
+//import java.util.ArrayList;
+//import java.util.List;
+//
+///**
+// * @author dzx
+// * @date 2024/1/4
+// */
+//public class ExcelUtil {
+//
+//    public void ReportExcel(List<ReportUserInfoBO> listVo, HttpServletResponse response) throws IOException {
+//        //末尾备注
+//        List<List<String>> beizhu = new ArrayList<>();
+//        List<String> list = new ArrayList<>();
+//        //末尾添加说明 合并最后一行单元格时赋值在 MergeLastWriteHandler
+//        list.add("说明");
+//        beizhu.add(list);
+//        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
+//        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
+//        // 标题字体大小
+//        WriteFont contentWriteFont = new WriteFont();
+//        contentWriteFont.setFontHeightInPoints((short) 12);
+//        headWriteCellStyle.setWriteFont(contentWriteFont);
+//        //标题黄色底纹
+//        headWriteCellStyle.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.getIndex());
+//
+//        //边框
+//        contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
+//        contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
+//        contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
+//        contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
+//        // 水平居中
+//        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
+//        // 垂直居中
+//        contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+//        // 设置自动换行,前提内容中需要加「\n」才有效
+//        contentWriteCellStyle.setWrapped(true);
+//        //初始化样式
+//        HorizontalCellStyleStrategy horizontalCellStyleStrategy =
+//                new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
+//        //合并单元格所需参数
+//        //合并坐标
+//        int[] mergeColumeIndex = {1, 1};
+//        //从第二行后开始合并
+//        int mergeRowIndex = 1;
+//        try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), ReportUserInfoBO.class).autoCloseStream(Boolean.FALSE)
+//                //风格样式
+//                .registerWriteHandler(horizontalCellStyleStrategy)
+//                //合并说明并赋值
+//                .registerWriteHandler(new MergeLastWriteHandler())
+//                //自动合并市级单元格 注意两个入参是出事
+//                .registerWriteHandler(new ExcelMergeUtil(mergeRowIndex, mergeColumeIndex))
+//                .build()) {
+//            WriteSheet sheet = EasyExcel.writerSheet("测试").build();
+//            excelWriter.write(listVo, sheet);
+//            excelWriter.write(beizhu, sheet);
+//        }
+//    }
+//}

+ 34 - 0
src/test/java/com/xjrsoft/module/excel/ReportUserInfoBO.java

@@ -0,0 +1,34 @@
+package com.xjrsoft.module.excel;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.alibaba.excel.annotation.write.style.ColumnWidth;
+import com.alibaba.excel.annotation.write.style.ContentRowHeight;
+import com.alibaba.excel.annotation.write.style.HeadRowHeight;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @author dzx
+ * @date 2024/1/4
+ */
+@Data
+@ContentRowHeight(25) //内容行高
+@HeadRowHeight(20)//表头行高
+public class ReportUserInfoBO implements Serializable {
+    @ExcelProperty(index = 0, value = "序号")
+    private String title;
+
+    @ExcelProperty(index =1, value = "姓名")
+    private String maleHousemaster;
+
+    @ExcelProperty(index = 2, value = "数量")
+    private String femaleHousemaster;
+
+    @ExcelProperty(index = 3, value = "价格")
+    private String instructor;
+
+    @ExcelProperty(index = 3, value = "价格")
+    private String instructo2r;
+
+}

+ 467 - 0
src/test/java/com/xjrsoft/module/excel/Test2.java

@@ -0,0 +1,467 @@
+package com.xjrsoft.module.excel;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelWriter;
+import com.alibaba.excel.support.ExcelTypeEnum;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import com.alibaba.excel.write.metadata.WriteTable;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+import org.junit.jupiter.api.Test;
+
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author dzx
+ * @date 2024/1/5
+ */
+public class Test2 {
+    @Test
+    void test(){
+        String fileName = "C:\\Users\\14263\\Desktop\\test\\宿管值班管理2.xlsx";
+        String sheetName = "结果";
+        int tableCount = 3;
+        ExcelWriter excelWriter = EasyExcel.write(fileName).registerWriteHandler(new CustomCellStyleHandler(initStyle(sheetName, tableCount))).excelType(ExcelTypeEnum.XLSX).build();
+        WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).needHead(Boolean.FALSE).build();
+
+        Map<String, Object> map = new HashMap<>();
+        map.put("title", "测试");
+        map.put("maleHousemaster", "张三");
+        map.put("femaleHousemaster", "里斯");
+        map.put("instructor", "赵雅芝");
+        map.put("maleStudents", "20");
+        map.put("femaleStudents", "25");
+
+        List<ExcelData> demoDataList = createData();
+        List<ExcelData> demoDataList2 = new ArrayList<>();
+        demoDataList2.add(new ExcelData(){{
+            setColumn1("");
+            setColumn2("");
+            setColumn3("");
+            setColumn4("");
+        }});
+        int tableRows = 6;
+        int j = 0;
+        for (int i = 0; i < tableCount * 2; ){
+
+            WriteTable writeTable = EasyExcel.writerTable(i + 1).head(ExcelData.class).needHead(Boolean.FALSE)
+                    .registerWriteHandler(new CustomMergeStrategy((tableRows * j) + 0, (tableRows * j) + 0, 0, 3))
+                    .registerWriteHandler(new CustomMergeStrategy((tableRows * j) + 1, (tableRows * j) + 1, 0, 1))
+                    .registerWriteHandler(new CustomMergeStrategy((tableRows * j) + 1, (tableRows * j) + 1, 2, 3))
+                    .registerWriteHandler(new CustomMergeStrategy((tableRows * j) + 3, (tableRows * j) + 4, 0, 0))
+                    .registerWriteHandler(new CustomMergeStrategy((tableRows * j) + 3, (tableRows * j) + 4, 1, 1))
+                    .build();
+            excelWriter.write(demoDataList, writeSheet, writeTable);
+            System.out.println("tableNo:" + (i + 1));
+
+            WriteTable writeTable3 = EasyExcel.writerTable(i + 2)
+                    .registerWriteHandler(new CustomMergeStrategy((tableRows * j) + 5, (tableRows * j) + 5, 0, 3))
+                    .build();
+            excelWriter.write(demoDataList2, writeSheet, writeTable3);
+            System.out.println("tableNo:" + (i + 2));
+            j ++;
+            i = i + 2;
+        }
+
+        excelWriter.finish();
+    }
+
+    private List<ExcelData> createData(){
+        List<ExcelData> result = new ArrayList<>();
+
+        result.add(new ExcelData(){{
+            setColumn1("测试");
+            setColumn2("测试");
+            setColumn3("测试");
+            setColumn4("测试");
+        }});
+        result.add(new ExcelData(){{
+            setColumn1("留宿情况");
+            setColumn2("留宿情况");
+            setColumn3("宿舍值班人员安排");
+            setColumn4("宿舍值班人员安排");
+        }});
+
+        result.add(new ExcelData(){{
+            setColumn1("男生留宿情况");
+            setColumn2("女生留宿情况");
+            setColumn3("男生宿管");
+            setColumn4("张三");
+        }});
+
+        result.add(new ExcelData(){{
+            setColumn1("10");
+            setColumn2("20");
+            setColumn3("女生宿管");
+            setColumn4("赵雅芝");
+        }});
+        result.add(new ExcelData(){{
+            setColumn1("10");
+            setColumn2("20");
+            setColumn3("教官");
+            setColumn4("张大彪");
+        }});
+        return result;
+    }
+
+    List<CellStyleModel> initStyle(String sheetName, int tableCount){
+        List<CellStyleModel> cellStyleList = new ArrayList<>();
+        int tableRows = 6;
+
+        for (int i = 0; i < tableCount; i ++){
+            int rowNum = tableRows * i ;
+            cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(rowNum + 0);
+                    setColIndex(0);
+//                    setFontHeight(20d);
+//                    setFontBold(Boolean.TRUE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                    setHorizontalAlignment(HorizontalAlignment.CENTER);
+                    setVerticalAlignment(VerticalAlignment.CENTER);
+                }}
+            );
+            cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(rowNum + 0);
+                    setColIndex(1);
+//                    setFontHeight(20d);
+//                    setFontBold(Boolean.TRUE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                    setHorizontalAlignment(HorizontalAlignment.CENTER);
+                    setVerticalAlignment(VerticalAlignment.CENTER);
+                }}
+            );
+            cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(rowNum + 0);
+                    setColIndex(2);
+//                    setFontHeight(20d);
+//                    setFontBold(Boolean.TRUE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                    setHorizontalAlignment(HorizontalAlignment.CENTER);
+                    setVerticalAlignment(VerticalAlignment.CENTER);
+                }}
+            );
+            cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(rowNum + 0);
+                    setColIndex(3);
+//                    setFontHeight(20d);
+//                    setFontBold(Boolean.TRUE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                    setHorizontalAlignment(HorizontalAlignment.CENTER);
+                    setVerticalAlignment(VerticalAlignment.CENTER);
+                }}
+            );
+            cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(rowNum + 1);
+                    setColIndex(0);
+                    setFontHeight(11d);
+                    setFontBold(Boolean.FALSE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                    setHorizontalAlignment(HorizontalAlignment.CENTER);
+                    setVerticalAlignment(VerticalAlignment.CENTER);
+                }}
+            );
+            cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(rowNum + 1);
+                    setColIndex(1);
+                    setFontHeight(11d);
+                    setFontBold(Boolean.FALSE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                    setHorizontalAlignment(HorizontalAlignment.CENTER);
+                    setVerticalAlignment(VerticalAlignment.CENTER);
+                }}
+            );
+            cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(rowNum + 1);
+                    setColIndex(2);
+                    setFontHeight(11d);
+                    setFontBold(Boolean.FALSE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                    setHorizontalAlignment(HorizontalAlignment.CENTER);
+                    setVerticalAlignment(VerticalAlignment.CENTER);
+                }}
+            );
+            cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(rowNum + 1);
+                    setColIndex(3);
+                    setFontHeight(11d);
+                    setFontBold(Boolean.FALSE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                    setHorizontalAlignment(HorizontalAlignment.CENTER);
+                    setVerticalAlignment(VerticalAlignment.CENTER);
+                }}
+            );
+
+            cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(rowNum + 2);
+                    setColIndex(0);
+                    setFontHeight(11d);
+                    setFontBold(Boolean.FALSE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                    setHorizontalAlignment(HorizontalAlignment.CENTER);
+                    setVerticalAlignment(VerticalAlignment.CENTER);
+                }}
+            );
+            cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(rowNum + 2);
+                    setColIndex(1);
+                    setFontHeight(11d);
+                    setFontBold(Boolean.FALSE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                    setHorizontalAlignment(HorizontalAlignment.CENTER);
+                    setVerticalAlignment(VerticalAlignment.CENTER);
+                }}
+            );
+            cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(rowNum + 2);
+                    setColIndex(2);
+                    setFontHeight(11d);
+                    setFontBold(Boolean.FALSE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                }}
+            );
+            cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(rowNum + 2);
+                    setColIndex(3);
+                    setFontHeight(11d);
+                    setFontBold(Boolean.FALSE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                }}
+            );
+
+            cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(rowNum + 3);
+                    setColIndex(0);
+                    setFontHeight(11d);
+                    setFontBold(Boolean.FALSE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                    setHorizontalAlignment(HorizontalAlignment.CENTER);
+                    setVerticalAlignment(VerticalAlignment.CENTER);
+                }}
+            );
+            cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(rowNum + 3);
+                    setColIndex(1);
+                    setFontHeight(11d);
+                    setFontBold(Boolean.FALSE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                    setHorizontalAlignment(HorizontalAlignment.CENTER);
+                    setVerticalAlignment(VerticalAlignment.CENTER);
+                }}
+            );
+            cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(rowNum + 3);
+                    setColIndex(2);
+                    setFontHeight(11d);
+                    setFontBold(Boolean.FALSE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                }}
+            );
+            cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(rowNum + 3);
+                    setColIndex(3);
+                    setFontHeight(11d);
+                    setFontBold(Boolean.FALSE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                }}
+            );
+
+            cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(rowNum + 4);
+                    setColIndex(0);
+                    setFontHeight(11d);
+                    setFontBold(Boolean.FALSE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                    setHorizontalAlignment(HorizontalAlignment.CENTER);
+                    setVerticalAlignment(VerticalAlignment.CENTER);
+                }}
+            );
+            cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(rowNum + 4);
+                    setColIndex(1);
+                    setFontHeight(11d);
+                    setFontBold(Boolean.FALSE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                    setHorizontalAlignment(HorizontalAlignment.CENTER);
+                    setVerticalAlignment(VerticalAlignment.CENTER);
+                }}
+            );
+            cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(rowNum + 4);
+                    setColIndex(2);
+                    setFontHeight(11d);
+                    setFontBold(Boolean.FALSE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                }}
+            );
+            cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(rowNum + 4);
+                    setColIndex(3);
+                    setFontHeight(11d);
+                    setFontBold(Boolean.FALSE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                }}
+            );
+
+            cellStyleList.add(
+                    new CellStyleModel(){{
+                        setSheetName(sheetName);
+                        setRowIndex(rowNum + 5);
+                        setColIndex(0);
+                        setBackgroundColor(Color.yellow);
+                    }}
+            );
+            cellStyleList.add(
+                    new CellStyleModel(){{
+                        setSheetName(sheetName);
+                        setRowIndex(rowNum + 5);
+                        setColIndex(1);
+                        setBackgroundColor(Color.yellow);
+                    }}
+            );
+            cellStyleList.add(
+                    new CellStyleModel(){{
+                        setSheetName(sheetName);
+                        setRowIndex(rowNum + 5);
+                        setColIndex(2);
+                        setBackgroundColor(Color.yellow);
+                    }}
+            );
+            cellStyleList.add(
+                    new CellStyleModel(){{
+                        setSheetName(sheetName);
+                        setRowIndex(rowNum + 5);
+                        setColIndex(3);
+                        setBackgroundColor(Color.yellow);
+                    }}
+            );
+        }
+
+        return cellStyleList;
+    }
+
+    List<CellStyleModel> initStyle2(String sheetName, int tableCount){
+        List<CellStyleModel> cellStyleList = new ArrayList<>();
+        cellStyleList.add(
+                new CellStyleModel(){{
+                    setSheetName(sheetName);
+                    setRowIndex(0);
+                    setColIndex(0);
+                    setFontHeight(20d);
+                    setFontBold(Boolean.TRUE);
+                    setBorderTop(BorderStyle.THIN);
+                    setBorderLeft(BorderStyle.THIN);
+                    setBorderBottom(BorderStyle.THIN);
+                    setBorderRight(BorderStyle.THIN);
+                    setHorizontalAlignment(HorizontalAlignment.CENTER);
+                    setVerticalAlignment(VerticalAlignment.CENTER);
+                }}
+        );
+
+        return cellStyleList;
+    }
+}

+ 233 - 0
src/test/java/com/xjrsoft/module/excel/Test3.java

@@ -0,0 +1,233 @@
+package com.xjrsoft.module.excel;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelWriter;
+import com.alibaba.excel.support.ExcelTypeEnum;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import com.alibaba.excel.write.metadata.WriteTable;
+import com.xjrsoft.module.room.vo.RoomValueWeekExcelVo;
+import com.xjrsoft.module.room.vo.RoomValueWeekItemExcelVo;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.XSSFCell;
+import org.apache.poi.xssf.usermodel.XSSFCellStyle;
+import org.apache.poi.xssf.usermodel.XSSFFont;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author dzx
+ * @date 2024/1/5
+ */
+public class Test3 {
+    @Test
+    void test() throws IOException {
+        File filePath = new File("C:\\Users\\14263\\Desktop\\test\\testMerge.xlsx");
+        OutputStream os= Files.newOutputStream(filePath.toPath());
+
+        String fileName = "C:\\Users\\14263\\Desktop\\test\\RoomValueWeek.xlsx";
+        int tableCount = 3;
+
+        File tmpFile = new File(fileName);
+
+        //单元格合并
+        List<CustomCellRangeAddress> cellRangeAddressList = new ArrayList<>();
+        int tableRows = 6;
+        int j = 0;
+        for (int i = 0; i < tableCount * 2; ){
+            cellRangeAddressList.add(new CustomCellRangeAddress((tableRows * j) + 0, (tableRows * j) + 0, 0, 3, Boolean.TRUE));
+            cellRangeAddressList.add(new CustomCellRangeAddress((tableRows * j) + 1, (tableRows * j) + 1, 0, 1, Boolean.TRUE));
+            cellRangeAddressList.add(new CustomCellRangeAddress((tableRows * j) + 1, (tableRows * j) + 1, 2, 3, Boolean.TRUE));
+            cellRangeAddressList.add(new CustomCellRangeAddress((tableRows * j) + 3, (tableRows * j) + 4, 0, 0));
+            cellRangeAddressList.add(new CustomCellRangeAddress((tableRows * j) + 3, (tableRows * j) + 4, 1, 1));
+
+            j ++;
+            i = i + 2;
+        }
+
+        InputStream in = Files.newInputStream(tmpFile.toPath());
+        XSSFWorkbook workbook = new XSSFWorkbook(in);
+        XSSFSheet sheetAt = workbook.getSheetAt(0);
+
+        for (CustomCellRangeAddress cellRangeAddress : cellRangeAddressList) {
+            sheetAt.addMergedRegion(cellRangeAddress);
+            setRegionStyle(sheetAt, cellRangeAddress, setDefaultStyle(workbook, cellRangeAddress.getIsBlod()));
+        }
+
+        workbook.write(os);
+        os.flush();
+        os.close();
+    }
+
+    @Test
+    void test2() throws IOException {
+        List<RoomValueWeekExcelVo> dataList = initData();
+        String sheetName = "数据";
+        ByteArrayOutputStream bot = new ByteArrayOutputStream();
+
+        ExcelWriter excelWriter = EasyExcel.write(bot).excelType(ExcelTypeEnum.XLSX).build();
+        WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).needHead(Boolean.FALSE).build();
+
+
+        List<RoomValueWeekItemExcelVo> demoDataList2 = new ArrayList<>();
+        demoDataList2.add(new RoomValueWeekItemExcelVo(){{
+            setColumn1("");
+            setColumn2("");
+            setColumn3("");
+            setColumn4("");
+        }});
+        //写入数据,并计算需要合并的单元格
+        int tableRows = 6;
+        int j = 0;
+        List<CellRangeAddress> cellRangeAddressList = new ArrayList<>();
+        for (int i = 0; i < dataList.size() * 2; ){
+
+            WriteTable writeTable = EasyExcel.writerTable(i + 1).head(RoomValueWeekItemExcelVo.class).needHead(Boolean.FALSE).build();
+            excelWriter.write(dataList.get(j).getItemList(), writeSheet, writeTable);
+            cellRangeAddressList.add(new CellRangeAddress((tableRows * j) + 0, (tableRows * j) + 0, 0, 3));
+            cellRangeAddressList.add(new CellRangeAddress((tableRows * j) + 1, (tableRows * j) + 1, 0, 1));
+            cellRangeAddressList.add(new CellRangeAddress((tableRows * j) + 1, (tableRows * j) + 1, 2, 3));
+            cellRangeAddressList.add(new CellRangeAddress((tableRows * j) + 3, (tableRows * j) + 4, 0, 0));
+            cellRangeAddressList.add(new CellRangeAddress((tableRows * j) + 3, (tableRows * j) + 4, 1, 1));
+
+            WriteTable writeTable3 = EasyExcel.writerTable(i + 2).build();
+            excelWriter.write(demoDataList2, writeSheet, writeTable3);
+            cellRangeAddressList.add(new CellRangeAddress((tableRows * j) + 5, (tableRows * j) + 5, 0, 3));
+
+            j ++;
+            i = i + 2;
+        }
+        excelWriter.finish();
+
+        // 将ByteArrayOutputStream转换为InputStream
+        InputStream in = new ByteArrayInputStream(bot.toByteArray());
+
+        //使用poi合并单元格,使用registerWriteHandler合并单元格会与fill方法中创建单元格后校验合并单元格冲突而引发报错
+        XSSFWorkbook workbook = new XSSFWorkbook(in);
+        XSSFSheet sheetAt = workbook.getSheet(sheetName);
+
+        for (CellRangeAddress cellRangeAddress : cellRangeAddressList) {
+            sheetAt.addMergedRegion(cellRangeAddress);
+            setRegionStyle(sheetAt, cellRangeAddress, setDefaultStyle(workbook, false));
+        }
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        workbook.write(os);
+        os.flush();
+        os.close();
+
+        // 获取字节数组
+        byte[] bytes = os.toByteArray();
+
+        // 创建目标文件
+        File file = new File("C:\\Users\\14263\\Desktop\\test\\testMerge2.xlsx");
+
+        try (FileOutputStream fos = new FileOutputStream(file)) {
+            // 将字节数组写入文件
+            fos.write(bytes);
+
+            System.out.println("文件已成功保存!");
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    //使用poi设置合并单元格后的样式
+    public void setRegionStyle(XSSFSheet sheet, CellRangeAddress region, XSSFCellStyle xssfCellStyle) {
+        for (int i = region.getFirstRow(); i <= region.getLastRow(); i++) {
+            XSSFRow row = sheet.getRow(i);
+            if (null == row) row = sheet.createRow(i);
+            for (int j = region.getFirstColumn(); j <= region.getLastColumn(); j++) {
+                XSSFCell cell = row.getCell(j);
+                if (null == cell) cell = row.createCell(j);
+                cell.setCellStyle(xssfCellStyle);
+            }
+        }
+    }public XSSFCellStyle setDefaultStyle(XSSFWorkbook workbook, Boolean isBold) {
+        XSSFCellStyle cellStyle = workbook.createCellStyle();
+        // 边框
+        cellStyle.setBorderBottom(BorderStyle.THIN);
+        cellStyle.setBorderLeft(BorderStyle.THIN);
+        cellStyle.setBorderRight(BorderStyle.THIN);
+        cellStyle.setBorderTop(BorderStyle.THIN);
+        // 居中
+        cellStyle.setAlignment(HorizontalAlignment.CENTER);
+        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+        // 字体
+        XSSFFont font = workbook.createFont();
+        font.setFontName("Calibri");
+        if(isBold){
+            font.setBold(isBold);
+        }
+        font.setFontHeightInPoints((short) 10);
+        cellStyle.setFont(font);
+        return cellStyle;
+    }
+
+    List<RoomValueWeekExcelVo> initData(){
+        List<RoomValueWeekExcelVo> result = new ArrayList<>();
+
+        List<RoomValueWeekItemExcelVo> itemList = new ArrayList<>();
+        itemList.add(new RoomValueWeekItemExcelVo(){{
+            setColumn1("2024年01月03日-2024年01月04日留宿情况及值班安排表");
+            setColumn2("2024年01月03日-2024年01月04日留宿情况及值班安排表");
+            setColumn3("2024年01月03日-2024年01月04日留宿情况及值班安排表");
+            setColumn4("2024年01月03日-2024年01月04日留宿情况及值班安排表");
+        }});
+        itemList.add(new RoomValueWeekItemExcelVo(){{
+            setColumn1("留宿情况");
+            setColumn2("留宿情况");
+            setColumn3("宿舍值班人员安排");
+            setColumn4("宿舍值班人员安排");
+        }});
+        itemList.add(new RoomValueWeekItemExcelVo(){{
+            setColumn1("男生留宿情况");
+            setColumn2("女生留宿情况");
+            setColumn3("男生宿管");
+            setColumn4("代昂辰");
+        }});
+        itemList.add(new RoomValueWeekItemExcelVo(){{
+            setColumn1("15");
+            setColumn2("20");
+            setColumn3("女生宿管");
+            setColumn4("蔡明兰");
+        }});
+
+        itemList.add(new RoomValueWeekItemExcelVo(){{
+            setColumn1("15");
+            setColumn2("20");
+            setColumn3("教官");
+            setColumn4("余明欢");
+        }});
+
+
+        result.add(new RoomValueWeekExcelVo(){{
+            setId(111111111111l);
+            setItemList(itemList);
+        }});
+
+        result.add(new RoomValueWeekExcelVo(){{
+            setId(222222222222l);
+            setItemList(itemList);
+        }});
+
+        return result;
+    }
+
+
+
+}

+ 33 - 0
src/test/java/com/xjrsoft/module/hikvision/util/OutInRecordUtilTest.java

@@ -0,0 +1,33 @@
+package com.xjrsoft.module.hikvision.util;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author dzx
+ * @date 2024/6/7
+ */
+class OutInRecordUtilTest {
+
+
+    @Test
+    void test(){
+        ApiUtil apiUtil = new ApiUtil();
+        String apiPath = "/api/acs/v2/door/events";
+        JsonObject paramJson = new JsonObject();
+        paramJson.addProperty("pageNo", 1);
+        paramJson.addProperty("pageSize", 100);
+
+        JsonArray array = new JsonArray();
+        array.add(654321987654894L);
+
+        paramJson.add("personIds", array);
+
+        String doPost = apiUtil.doPost(apiPath, String.valueOf(paramJson), null);
+        System.out.println("-------------------------------------------------------");
+        System.out.println(doPost);
+    }
+}

+ 44 - 0
src/test/java/com/xjrsoft/module/job/AttendanceMessageTaskTest2.java

@@ -0,0 +1,44 @@
+package com.xjrsoft.module.job;
+
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author dzx
+ * @date 2024/6/27
+ */
+class AttendanceMessageTaskTest2 {
+
+
+    @Test
+    void test(){
+        LocalDate now = LocalDate.now();
+        LocalDate dateTime = now.plusDays(-20);
+
+        LocalDate dateTim2 = now.plusDays(5);
+
+        List<LocalDate> dateList = getDatesBetween(dateTime, dateTim2);
+        for (LocalDate localDate : dateList) {
+            System.out.println(localDate);
+        }
+
+
+    }
+
+    private List<LocalDate> getDatesBetween(LocalDate startDate, LocalDate endDate) {
+        List<LocalDate> dates = new ArrayList<>();
+
+        long numOfDaysBetween = ChronoUnit.DAYS.between(startDate, endDate) + 1; // +1 包含结束日期
+        for (long i = 0; i < numOfDaysBetween; i++) {
+            LocalDate localDate = startDate.plusDays(i);
+            dates.add(localDate);
+        }
+
+        return dates;
+    }
+}

+ 169 - 0
src/test/java/com/xjrsoft/module/job/HikvisionLeaveTaskTest.java

@@ -0,0 +1,169 @@
+package com.xjrsoft.module.job;
+
+import cn.hutool.extra.spring.SpringUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.xjrsoft.XjrSoftApplication;
+import com.xjrsoft.module.hikvision.entity.HikvisionData;
+import com.xjrsoft.module.hikvision.mapper.HikvisionDataMapper;
+import com.xjrsoft.module.hikvision.util.ApiUtil;
+import com.xjrsoft.module.organization.entity.User;
+import com.xjrsoft.module.organization.service.IUserService;
+import com.xjrsoft.module.student.entity.StudentLeave;
+import com.xjrsoft.module.student.service.IStudentLeaveService;
+import com.xjrsoft.module.teacher.entity.WfTeacherleave;
+import com.xjrsoft.module.teacher.service.IWfTeacherleaveService;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author dzx
+ * @date 2024/9/18
+ */
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = XjrSoftApplication.class)
+class HikvisionLeaveTaskTest {
+    @Autowired
+    private IStudentLeaveService studentLeaveService;
+
+    @Autowired
+    private IWfTeacherleaveService teacherleaveService;
+
+    @Autowired
+    private HikvisionDataMapper hikvisionDataMapper;
+
+    @Autowired
+    private IUserService userService;
+
+    @Test
+    void test(){
+        doExecute();
+    }
+
+    public void doExecute() {
+        List<String> sourceIds = new ArrayList<>();
+
+        List<StudentLeave> studentList = studentLeaveService.list(
+                new QueryWrapper<StudentLeave>().lambda()
+                        .isNotNull(StudentLeave::getHikvisionResult)
+                        .lt(StudentLeave::getEndDate, LocalDate.now())
+        );
+        for (StudentLeave studentLeave : studentList) {
+            sourceIds.add(studentLeave.getStudentUserId().toString());
+        }
+
+        List<WfTeacherleave> teacherList = teacherleaveService.list(
+                new QueryWrapper<WfTeacherleave>().lambda()
+                        .isNotNull(WfTeacherleave::getHikvisionResult)
+                        .lt(WfTeacherleave::getLeaveEndTime, new Date())
+        );
+        for (WfTeacherleave teacherleave : teacherList) {
+            String[] split = teacherleave.getUserId().split(",");
+            sourceIds.addAll( Arrays.asList(split));
+        }
+//        List<String> nameList = new ArrayList<>();
+//        nameList.add("王娟");nameList.add("何晓渝");
+//        List<User> list = userService.list(new QueryWrapper<User>().lambda().in(User::getName, nameList));
+//        for (User user : list) {
+//            sourceIds.add(user.getId().toString());
+//        }
+        if(sourceIds.isEmpty()){
+            return;
+        }
+
+        List<HikvisionData> hikvisionData = hikvisionDataMapper.selectList(
+                new QueryWrapper<HikvisionData>().lambda()
+                        .in(HikvisionData::getSourceId, sourceIds)
+        );
+
+        ApiUtil apiUtil = new ApiUtil();
+        String apiPath = "/api/acps/v1/auth_config/delete";
+
+        JsonObject paramJson = new JsonObject();
+
+        JsonArray personDatas = new JsonArray();
+        JsonObject personData = new JsonObject();
+
+        JsonArray indexCodes = new JsonArray();
+        for (HikvisionData el : hikvisionData) {
+            indexCodes.add(el.getHikvisionId());
+        }
+        personData.addProperty("personDataType","person");
+        personData.add("indexCodes", indexCodes);
+        personDatas.add(personData);
+        paramJson.add("personDatas", personDatas);
+
+        JsonArray resourceInfos = selectResource(apiUtil);
+        paramJson.add("resourceInfos", resourceInfos);
+
+        Map<String, String> header = new HashMap<>();
+        header.put("tagId", "studentleave");
+        //调用接口获取到返回内容,并将其存到数据库中
+        String result = apiUtil.doPost(apiPath, paramJson.toString(), null, header);
+        System.out.println(result);
+
+        //删除成功后,重新下载
+        //1、创建任务
+        paramJson = new JsonObject();
+        paramJson.addProperty("taskType", 1);
+        apiPath = "/api/acps/v1/download/configuration/task/add";
+        String doPost = apiUtil.doPost(apiPath, paramJson.toString(), null, null);
+
+        JsonParser jsonParser = new JsonParser();
+        JsonObject resultJson = jsonParser.parse(doPost).getAsJsonObject();
+
+        if("0".equals(resultJson.get("code").getAsString()) && "success".equals(resultJson.get("msg").getAsString())){
+            String taskId = resultJson.get("data").getAsJsonObject().get("taskId").getAsString();
+            //2、下载
+            apiPath = "/api/acps/v1/download/configuration/data/add";
+            paramJson = new JsonObject();
+            paramJson.add("resourceInfos", resourceInfos);
+            paramJson.addProperty("taskId", taskId);
+            apiUtil.doPost(apiPath, paramJson.toString(), null, null);
+        }
+    }
+
+    JsonArray selectResource(ApiUtil apiUtil){
+        String apiPath = "/api/irds/v2/resource/resourcesByParams";
+        JsonObject jsonObject = new JsonObject();
+        jsonObject.addProperty("pageNo", 1);
+        jsonObject.addProperty("pageSize", 500);
+        jsonObject.addProperty("resourceType", "door");
+
+        String result = apiUtil.doPost(apiPath, jsonObject.toString(), null, null);
+        JsonParser parser = new JsonParser();
+        JsonObject resultJson = parser.parse(result).getAsJsonObject();
+        JsonArray resourceInfos = new JsonArray();
+
+        if("0".equals(resultJson.get("code").getAsString()) && "success".equals(resultJson.get("msg").getAsString())){
+            JsonArray list = resultJson.get("data").getAsJsonObject().get("list").getAsJsonArray();
+            for (JsonElement jsonElement : list) {
+                JsonObject listOne = jsonElement.getAsJsonObject();
+                JsonObject resourceInfo = new JsonObject();
+                resourceInfo.add("resourceIndexCode", listOne.get("indexCode"));
+                resourceInfo.add("resourceType", listOne.get("resourceType"));
+                JsonArray channelNos = new JsonArray();
+                channelNos.add(listOne.get("channelNo"));
+                resourceInfo.add("channelNos", channelNos);
+                resourceInfos.add(resourceInfo);
+            }
+        }
+
+        return resourceInfos;
+    }
+}

+ 64 - 0
src/test/java/com/xjrsoft/module/job/InsertOutInRecordTaskTest.java

@@ -0,0 +1,64 @@
+package com.xjrsoft.module.job;
+
+import com.xjrsoft.XjrSoftApplication;
+import com.xjrsoft.module.hikvision.service.IHikvisionDataService;
+import com.xjrsoft.module.hikvision.util.OutInRecordUtil;
+import com.xjrsoft.module.teacher.mapper.FaceImportMapper;
+import jline.internal.Log;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.text.ParseException;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+
+
+/**
+ * @author dzx
+ * @date 2024/10/19
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = XjrSoftApplication.class)
+class InsertOutInRecordTaskTest {
+
+    @Autowired
+    private FaceImportMapper faceImportMapper;
+
+    OutInRecordUtil out_in_recordUtil = new OutInRecordUtil();
+
+    @Autowired
+    private IHikvisionDataService hikvisionDataService;
+
+    @Test
+    void test() throws ParseException {
+        //获取时间,并计算出前一天的开始时间和结束时间
+        LocalDateTime now = LocalDateTime.of(2024, 10, 1, 0, 0, 0);
+        for (int i = 0; i < 1; i ++){
+            LocalDateTime startDateTime = now.plusDays(i).withHour(0).withMinute(0).withSecond(0).withNano(0);
+            LocalDateTime endDateTime = startDateTime.plusDays(i).withHour(23).withMinute(59).withSecond(59);
+            doExecute(startDateTime, endDateTime);
+        }
+
+    }
+
+    public void doExecute(LocalDateTime startDateTime, LocalDateTime endDateTime) throws ParseException {
+            //获取时间,并计算出前一天的开始时间和结束时间
+//            LocalDateTime now = LocalDateTime.now();
+//            LocalDateTime startDateTime = now.plusDays(-1).withHour(0).withMinute(0).withSecond(0).withNano(0);
+//            LocalDateTime endDateTime = startDateTime.plusDays(1).plusSeconds(-1);
+
+            //Map<String, String> userIdMap = hikvisionDataService.getUserIdMap();
+
+            //教师&学生拉取数据
+            out_in_recordUtil.GetTeacherAndStudentRecords(faceImportMapper, startDateTime, endDateTime);
+            //拉取车辆数据
+            out_in_recordUtil.GetVehicleRecord(faceImportMapper, startDateTime, endDateTime);
+            //拉取访客数据
+//            out_in_recordUtil.GetVisitRecord(faceImportMapper, startDateTime, endDateTime);
+
+    }
+}

+ 91 - 0
src/test/java/com/xjrsoft/module/job/ProcessTimeoutAlertTaskTest.java

@@ -0,0 +1,91 @@
+package com.xjrsoft.module.job;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.xjrsoft.common.mybatis.SqlRunnerAdapter;
+import com.xjrsoft.module.oa.utils.SendMessageUtil;
+import com.xjrsoft.module.workflow.entity.WorkflowSchema;
+import com.xjrsoft.module.workflow.service.IWorkflowSchemaService;
+import org.camunda.bpm.engine.HistoryService;
+import org.camunda.bpm.engine.history.HistoricProcessInstance;
+import org.camunda.bpm.engine.history.HistoricProcessInstanceQuery;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author dzx
+ * @date 2024/7/30
+ */
+@SpringBootTest
+class ProcessTimeoutAlertTaskTest {
+    @Autowired
+    private IWorkflowSchemaService schemaService;
+
+    @Autowired
+    private HistoryService historyService;
+
+    @Test
+    public void doExecute(){
+        LocalDateTime now = LocalDateTime.now();
+        try {
+            HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery().unfinished();
+
+            List<HistoricProcessInstance> historicProcessInstances = historicProcessInstanceQuery.list();
+
+            List<String> processIds = historicProcessInstances.stream().map(HistoricProcessInstance::getId).collect(Collectors.toList());
+
+
+            if(!processIds.isEmpty()){
+                //获取所有未完成流程的所有节点
+                String uncloseProcessIdStr = processIds.stream().map(entity -> "'" + entity + "'").collect(Collectors.joining(","));
+                String listWorkflowExtraSql = "select * " +
+                        "from xjr_workflow_extra "
+                        + "where process_id in (" + uncloseProcessIdStr + ") and end_time is null;";
+                List<Map<String, Object>> workflowExtraList = SqlRunnerAdapter.db().selectList(listWorkflowExtraSql);
+
+                JsonParser parser = new JsonParser();
+                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+                Map<String, StringBuilder> message = new HashMap<>();
+                for (Map<String, Object> el : workflowExtraList) {
+                    WorkflowSchema schema = schemaService.getById(el.get("schema_id").toString());
+                    JsonObject jsonContent = parser.parse(schema.getJsonContent()).getAsJsonObject();
+                    JsonObject timeoutRemidConfig = jsonContent.get("processConfig").getAsJsonObject().get("timeoutRemidConfig").getAsJsonObject();
+                    if(!timeoutRemidConfig.get("enabled").getAsBoolean()){
+                        continue;
+                    }
+                    LocalDateTime startTime = LocalDateTime.parse(el.get("start_time").toString(), formatter);
+                    Duration between = Duration.between(startTime, now);
+                    long abs = Math.abs(between.getSeconds());
+                    int hour = timeoutRemidConfig.get("hour").getAsInt() * 60 * 60;
+                    if(abs >= hour){
+                        //对未结束的流程节点进行处理,通知下一个节点需要处理的人
+                        String startUserId = el.get("start_user_id").toString();
+                        if(message.containsKey(startUserId)){
+                            message.get(startUserId).append(",").append(el.get("process_name").toString());
+                        }else {
+                            message.put(startUserId, new StringBuilder(el.get("process_name").toString()));
+                        }
+                    }
+                }
+
+                CompletableFuture.runAsync(() -> {
+                    //SendMessageUtil.sendWorkflowUnapprovedWx(message);
+                });
+            }
+        } catch (Exception e) {
+            System.out.println(e.getMessage());
+        }
+    }
+}

+ 38 - 0
src/test/java/com/xjrsoft/module/liteflow/node/StudentChangeClassNodeTest.java

@@ -0,0 +1,38 @@
+package com.xjrsoft.module.liteflow.node;
+
+import cn.hutool.core.convert.Convert;
+import com.xjrsoft.module.student.entity.StudentChangeClass;
+import com.xjrsoft.module.student.mapper.StudentChangeClassMapper;
+import com.xjrsoft.module.student.service.IBaseStudentSchoolRollService;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author dzx
+ * @date 2024/4/19
+ */
+@SpringBootTest
+class StudentChangeClassNodeTest {
+    @Autowired
+    private StudentChangeClassMapper studentChangeClassMapper;
+    @Autowired
+    private IBaseStudentSchoolRollService studentSchoolRollService;
+    @Test
+    void test(){
+        // 获取表单中数据编号
+        Long formId = Convert.toLong("1781157840130457600");
+        if (formId != null) {
+            //查询出数据
+            StudentChangeClass changeClass = studentChangeClassMapper.selectById(formId);
+
+            //修改学生班级
+            studentSchoolRollService.updateStudentClass(changeClass.getAfterClassId(), changeClass.getStudentUserId());
+        }
+    }
+
+}

+ 41 - 0
src/test/java/com/xjrsoft/module/liteflow/node/StudentDropOutNodeTest.java

@@ -0,0 +1,41 @@
+package com.xjrsoft.module.liteflow.node;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.xjrsoft.common.enums.ArchivesStatusEnum;
+import com.xjrsoft.common.enums.DeleteMark;
+import com.xjrsoft.module.student.entity.BaseStudentSchoolRoll;
+import com.xjrsoft.module.student.entity.StudentDropOut;
+import com.xjrsoft.module.student.mapper.StudentDropOutMapper;
+import com.xjrsoft.module.student.service.IBaseStudentSchoolRollService;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author dzx
+ * @date 2024/4/19
+ */
+@SpringBootTest
+class StudentDropOutNodeTest {
+    @Autowired
+    private StudentDropOutMapper studentDropOutMapper;
+    @Autowired
+    private IBaseStudentSchoolRollService studentSchoolRollService;
+
+    @Test
+    void test(){
+        Long formId = 1780875585629765632L;
+        StudentDropOut studentDropOut = studentDropOutMapper.selectById(formId);
+        //跟新学籍信息
+        BaseStudentSchoolRoll schoolRoll = studentSchoolRollService.getOne(
+            new QueryWrapper<BaseStudentSchoolRoll>().lambda()
+            .eq(BaseStudentSchoolRoll::getClassId, studentDropOut.getClassId())
+            .eq(BaseStudentSchoolRoll::getUserId, studentDropOut.getStudentUserId())
+            .eq(BaseStudentSchoolRoll::getDeleteMark, DeleteMark.NODELETE.getCode())
+        );
+        schoolRoll.setArchivesStatus(ArchivesStatusEnum.FB2904.getCode());
+        studentSchoolRollService.updateById(schoolRoll);
+    }
+}

+ 34 - 0
src/test/java/com/xjrsoft/module/liteflow/node/WfCourseAdjustNodeTest.java

@@ -0,0 +1,34 @@
+package com.xjrsoft.module.liteflow.node;
+
+import com.xjrsoft.module.courseTable.service.ICourseTableService;
+import com.xjrsoft.module.schedule.entity.WfCourseAdjust;
+import com.xjrsoft.module.schedule.service.IWfCourseAdjustService;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author dzx
+ * @date 2024/9/18
+ */
+@SpringBootTest
+class WfCourseAdjustNodeTest {
+
+    @Autowired
+    private ICourseTableService courseTableService;
+
+    @Autowired
+    private IWfCourseAdjustService wfCourseAdjustService;
+
+    @Test
+    void test() throws Exception {
+        Long formId = 1847200925738921984L;
+        if (formId != null) {
+            // 数据处理
+            WfCourseAdjust courseAdjust = wfCourseAdjustService.getById(formId);
+            courseTableService.adjustCourse(courseAdjust);
+        }
+    }
+}

+ 43 - 0
src/test/java/com/xjrsoft/module/liteflow/node/WfCourseBackupsNodeTest.java

@@ -0,0 +1,43 @@
+package com.xjrsoft.module.liteflow.node;
+
+import cn.hutool.core.convert.Convert;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.xjrsoft.XjrSoftApplication;
+import com.xjrsoft.common.utils.RedisUtil;
+import com.xjrsoft.module.schedule.service.IWfCourseAdjustService;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author dzx
+ * @date 2024/10/17
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = XjrSoftApplication.class)
+class WfCourseBackupsNodeTest {
+    @Autowired
+    private RedisUtil redisUtil;
+
+    @Test
+    public void process(){
+        Set<String> ongoing = redisUtil.get("jianyuekbScheduleTask", new TypeReference<Set<String>>() {});//正在进行中的
+        if(ongoing == null){
+            ongoing = new HashSet<>();
+        }
+
+        String eduYearSerialNo ="EY000000118";
+        if(ongoing.contains(eduYearSerialNo)){
+            ongoing.remove(eduYearSerialNo);
+            redisUtil.set("jianyuekbScheduleTask", ongoing);
+        }
+    }
+}

+ 47 - 0
src/test/java/com/xjrsoft/module/liteflow/node/WfRoomApplicantNodeTest.java

@@ -0,0 +1,47 @@
+package com.xjrsoft.module.liteflow.node;
+
+import cn.hutool.core.convert.Convert;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.xjrsoft.common.enums.RoomApplicantTypeEnum;
+import com.xjrsoft.common.enums.StudyStatusEnum;
+import com.xjrsoft.module.room.entity.WfRoomApplicant;
+import com.xjrsoft.module.room.mapper.WfRoomApplicantMapper;
+import com.xjrsoft.module.student.entity.BaseStudentSchoolRoll;
+import com.xjrsoft.module.student.service.IBaseStudentSchoolRollService;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author dzx
+ * @date 2024/4/19
+ */
+@SpringBootTest
+class WfRoomApplicantNodeTest {
+
+    @Autowired
+    private WfRoomApplicantMapper wfRoomApplicantMapper;
+    @Autowired
+    private IBaseStudentSchoolRollService studentSchoolRollService;
+    @Test
+    public void process() throws Exception {
+        // 获取表单中数据编号
+        Long formId = Convert.toLong("1781242183456702464");
+        if (formId != null) {
+            //查询出数据
+            WfRoomApplicant wfRoomApplicant = wfRoomApplicantMapper.selectById(formId);
+            BaseStudentSchoolRoll schoolRoll = studentSchoolRollService.getOne(new QueryWrapper<BaseStudentSchoolRoll>().lambda().eq(BaseStudentSchoolRoll::getUserId, wfRoomApplicant.getApplicantUserId()));
+            if(RoomApplicantTypeEnum.ToBeBoarder.getCode().equals(wfRoomApplicant.getRecedeType())){
+                schoolRoll.setStduyStatus(StudyStatusEnum.InResidence.getCode());
+            }else if(RoomApplicantTypeEnum.ToBeDayPupil.getCode().equals(wfRoomApplicant.getRecedeType())){
+                schoolRoll.setStduyStatus(StudyStatusEnum.AttendDaySchool.getCode());
+            }
+            //修改学生班级
+            studentSchoolRollService.updateById(schoolRoll);
+        }
+    }
+}

+ 56 - 0
src/test/java/com/xjrsoft/module/liteflow/node/WfSubscriptionNodeTest.java

@@ -0,0 +1,56 @@
+package com.xjrsoft.module.liteflow.node;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.xjrsoft.XjrSoftApplication;
+import com.xjrsoft.module.workflow.entity.WorkflowFormRelation;
+import com.xjrsoft.module.workflow.service.IWorkflowExecuteService;
+import com.xjrsoft.module.workflow.service.IWorkflowFormRelationService;
+import org.camunda.bpm.engine.history.HistoricProcessInstance;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.List;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author dzx
+ * @date 2024/9/24
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = XjrSoftApplication.class)
+class WfSubscriptionNodeTest {
+
+    @Autowired
+    private IWorkflowExecuteService workflowExecuteService;
+
+    @Autowired
+    private IWorkflowFormRelationService relationService;
+
+
+    @Test
+    void test(){
+        List<WorkflowFormRelation> list = relationService.list(
+                new QueryWrapper<WorkflowFormRelation>().lambda()
+                        .isNull(WorkflowFormRelation::getCurrentState)
+        );
+
+        for (WorkflowFormRelation workflowFormRelation : list) {
+            Optional<HistoricProcessInstance> historicProcessInstanceOptional = workflowExecuteService.getHistoricProcessInstance(workflowFormRelation.getProcessId());
+            if (historicProcessInstanceOptional.isEmpty()) {
+                continue;
+            }
+            HistoricProcessInstance historicProcessInstance = historicProcessInstanceOptional.get();
+
+            workflowFormRelation.setStartTime(historicProcessInstance.getStartTime());
+            workflowFormRelation.setEndTime(historicProcessInstance.getEndTime());
+            workflowFormRelation.setCurrentState(historicProcessInstance.getState());
+            relationService.updateCurrentState(workflowFormRelation);
+        }
+    }
+
+}

+ 23 - 0
src/test/java/com/xjrsoft/module/schedule/util/DataUtilTest.java

@@ -0,0 +1,23 @@
+package com.xjrsoft.module.schedule.util;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author dzx
+ * @date 2024/9/3
+ */
+class DataUtilTest {
+
+    @Test
+    void test() throws Exception {
+        DataUtil dataUtil = new DataUtil();
+        long timestamp = System.currentTimeMillis();
+        System.out.println("timestamp --> " + timestamp);
+        //生成签名
+        String sign = ScheduleUtil.createSign(timestamp);
+
+        System.out.println("sign --> " + sign);
+    }
+}

+ 130 - 0
src/test/java/com/xjrsoft/module/student/controller/StudentManagerControllerTest.java

@@ -0,0 +1,130 @@
+package com.xjrsoft.module.student.controller;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelWriter;
+import com.alibaba.excel.support.ExcelTypeEnum;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.xjrsoft.XjrSoftApplication;
+import com.xjrsoft.common.model.result.RT;
+import com.xjrsoft.module.dataexpert.dto.DataExpertDto;
+import com.xjrsoft.module.dataexpert.entity.DataExpertSource;
+import com.xjrsoft.module.dataexpert.entity.DataExpertTemplate;
+import com.xjrsoft.module.dataexpert.service.IDataExpertSourceService;
+import com.xjrsoft.module.dataexpert.service.IDataExpertTemplateService;
+import com.xjrsoft.module.dataexpert.vo.DataExpertTemplateFieldVo;
+import com.xjrsoft.module.student.service.IStudentManagerService;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.ResponseEntity;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.validation.Valid;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.sql.SQLException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * @author dzx
+ * @date 2024/4/23
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = XjrSoftApplication.class)
+class StudentManagerControllerTest {
+    @Autowired
+    private IStudentManagerService studentManagerService;
+
+    @Autowired
+    private IDataExpertSourceService dataExpertSourceService;
+    @Autowired
+    private IDataExpertTemplateService dataExpertTemplateService;
+    @Test
+    void test() throws IOException, ParseException {
+        String filePath = "C:\\Users\\14263\\Desktop\\test\\学生模板.xlsx";
+        File file = new File(filePath);
+        FileInputStream input = new FileInputStream(file);
+        MultipartFile multipartFile = new MockMultipartFile("file", file.getName(), "text/plain", input);
+
+        List<Map<Integer, Object>> excelDataList = EasyExcel.read(multipartFile.getInputStream()).sheet().headRowNumber(3).doReadSync();
+
+        studentManagerService.importStudentData(excelDataList);
+    }
+
+    @Test
+    public ResponseEntity<byte[]> exportData() throws SQLException {
+        DataExpertDto dto = new DataExpertDto();
+        //拼接字段
+        String fields = "";
+        Map<Integer, String> showNameMap = new HashMap<>();
+        List<String> titleList = new ArrayList<>();
+        List<DataExpertTemplateFieldVo> fieldList = dto.getFieldList();
+        DataExpertSource expertSource;
+        if(dto.getDataExpertSourceId() != null){
+            expertSource = dataExpertSourceService.getById(dto.getDataExpertSourceId());
+            for (int i = 0; i < fieldList.size(); i ++){
+                DataExpertTemplateFieldVo fieldVo = fieldList.get(i);
+                showNameMap.put(i, fieldVo.getShowName());
+                if(i > 0){
+                    fields += ",";
+                }
+                fields += fieldVo.getFieldName();
+                titleList.add(fieldVo.getShowName());
+            }
+        }else{
+            //查出想要的数据
+            DataExpertTemplate template = dataExpertTemplateService.getById(dto.getDataExpertTemplateId());
+            expertSource = dataExpertSourceService.getById(template.getDataExpertSourceId());
+            String fieldJsonStr = template.getFieldJson();
+            JsonParser parser = new JsonParser();
+            JsonArray fieldJson = parser.parse(fieldJsonStr).getAsJsonArray();
+            int i = 0;
+            for (JsonElement jsonElement : fieldJson) {
+                JsonObject jsonObject = jsonElement.getAsJsonObject();
+                showNameMap.put(i, jsonObject.get("name").getAsString());
+                if(i > 0){
+                    fields += ",";
+                }
+                fields += jsonObject.get("field").getAsString();
+                titleList.add(jsonObject.get("name").getAsString());
+                i ++;
+            }
+        }
+
+        //查出导出的数据并进行组装
+        List<String[]> dataList = dataExpertTemplateService.getDataList(fields, expertSource.getViewName(), null);
+
+        List<String[]> allDataList = new ArrayList<>();
+        allDataList.add(titleList.toArray(new String[titleList.size()]));
+        allDataList.addAll(dataList);
+
+        String sheetName = "数据";
+        String fileName = "导出结果" + ExcelTypeEnum.XLSX.getValue();
+        ByteArrayOutputStream bot = new ByteArrayOutputStream();
+
+        ExcelWriter excelWriter = EasyExcel.write(bot).excelType(ExcelTypeEnum.XLSX).build();
+        WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).needHead(Boolean.FALSE).build();
+
+        excelWriter.write(allDataList, writeSheet);
+
+        excelWriter.finish();
+
+        return RT.fileStream(bot.toByteArray(), fileName);
+    }
+
+}

+ 38 - 0
src/test/java/com/xjrsoft/module/system/controller/LoginControllerTest.java

@@ -0,0 +1,38 @@
+package com.xjrsoft.module.system.controller;
+
+import com.baomidou.mybatisplus.core.toolkit.StringPool;
+import com.xjrsoft.common.sms.SmsCtcc;
+import com.xjrsoft.common.utils.RedisUtil;
+import org.apache.commons.lang3.RandomUtils;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author dzx
+ * @date 2024/6/17
+ */
+@SpringBootTest
+class LoginControllerTest {
+
+    @Autowired
+    private RedisUtil redisUtil;
+    @Autowired
+    private SmsCtcc smsCtcc;
+
+    @Test
+    void test(){
+        String key = "imgCaptcha:15898a7f-984c-402d-806e-b5a0d79845fa";
+        System.out.println(redisUtil.get(key));
+    }
+
+    @Test
+    void test2(){
+        String code = RandomUtils.nextInt(100000, 999999) + StringPool.EMPTY;
+        String mobile = "13389612083";
+        smsCtcc.sendCaptcha(mobile, code, true);
+    }
+
+}

+ 43 - 0
src/test/java/com/xjrsoft/xjrsoftboot/ExcelDownloadTest.java

@@ -0,0 +1,43 @@
+package com.xjrsoft.xjrsoftboot;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.support.ExcelTypeEnum;
+import com.xjrsoft.XjrSoftApplication;
+import com.xjrsoft.module.ledger.dto.WfSubscriptionPageDto;
+import com.xjrsoft.module.oa.service.IWfSubscriptionService;
+import com.xjrsoft.module.ledger.vo.WfSubscriptionExcelVo;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author dzx
+ * @date 2024/2/20
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = XjrSoftApplication.class)
+public class ExcelDownloadTest {
+    @Autowired
+    private IWfSubscriptionService wfSubscriptionService;
+    @Test
+    void downloadWfSubscription() throws IOException {
+        WfSubscriptionPageDto dto = new WfSubscriptionPageDto();
+        List<WfSubscriptionExcelVo> customerList = wfSubscriptionService.getList(dto);
+        ByteArrayOutputStream bot = new ByteArrayOutputStream();
+        EasyExcel.write(bot, WfSubscriptionExcelVo.class).automaticMergeHead(false).excelType(ExcelTypeEnum.XLSX).sheet().doWrite(customerList);
+        byte[] byteArray = bot.toByteArray();
+
+        File file = new File("D:\\workspace\\其他\\测试\\file.xlsx");
+        FileOutputStream fos = new FileOutputStream(file);
+        fos.write(byteArray);
+        fos.close();
+    }
+}

+ 326 - 0
src/test/java/com/xjrsoft/xjrsoftboot/ExcelExample.java

@@ -0,0 +1,326 @@
+package com.xjrsoft.xjrsoftboot;
+
+import com.xjrsoft.module.classtime.entity.ClassTimeStatistics;
+import com.xjrsoft.module.classtime.vo.WeekTimeRangeVo;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.time.DayOfWeek;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author dzx
+ * @date 2024/10/9
+ */
+public class ExcelExample {
+
+    public static void main(String[] args) {
+        ClassTimeStatistics statistics = new ClassTimeStatistics(){{
+            setMonth(9);
+            setStartDate(LocalDate.of(2024, 9, 2));
+            setEndDate(LocalDate.of(2024, 9, 27));
+        }};
+        List<WeekTimeRangeVo> weekTimeRangeVos = calculateNaturalWeeks(statistics.getStartDate(), statistics.getEndDate());
+
+
+        // 创建一个新的工作簿
+        Workbook workbook = new XSSFWorkbook();
+        // 创建一个工作表(sheet)
+        Sheet sheet = workbook.createSheet("ExampleSheet");
+
+        // 创建行(0基索引)
+        Row row = sheet.createRow(0);
+
+        // 创建单元格并设置值
+        Cell cell = row.createCell(0);
+        cell.setCellValue("Hello");
+
+        // 创建一个字体对象
+        Font font = workbook.createFont();
+        font.setBold(true);      // 设置为粗体
+        font.setFontName("宋体");
+        font.setFontHeightInPoints((short)24);
+
+        // 创建一个单元格样式对象
+        CellStyle cellStyle = workbook.createCellStyle();
+        cellStyle.setFont(font); // 将字体应用到样式
+        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+        cellStyle.setAlignment(HorizontalAlignment.CENTER);
+        cell.setCellStyle(cellStyle);
+
+        //合并第一行的列
+        sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 35 - 1));
+
+
+        createSecondTitle(workbook, sheet, statistics, 17);
+
+
+        //第三行表头
+        createThirdTitle(workbook, sheet, statistics.getMonth(), weekTimeRangeVos, 13);
+
+        createFourthTitle(workbook, sheet, weekTimeRangeVos, 13);
+
+        // 写入到文件
+        try (FileOutputStream outputStream = new FileOutputStream("C:\\Users\\14263\\Desktop\\test\\2024年10月9日\\example.xlsx")) {
+            workbook.write(outputStream);
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            // 确保工作簿被关闭,释放资源
+            if (workbook != null) {
+                try {
+                    workbook.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    static void createSecondTitle(Workbook workbook, Sheet sheet, ClassTimeStatistics statistics, int mergeCoulmn){
+        Font font = workbook.createFont();
+        font.setFontName("宋体");
+        font.setFontHeightInPoints((short)12);
+
+        CellStyle cellStyle = workbook.createCellStyle();
+        cellStyle.setFont(font); // 将字体应用到样式
+        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+        cellStyle.setAlignment(HorizontalAlignment.CENTER);
+
+
+        Row row1 = sheet.createRow(1);
+        Cell row1cell1 = row1.createCell(0);
+        row1cell1.setCellValue("序号");
+        row1cell1.setCellStyle(cellStyle);
+
+        Cell row1cell2 = row1.createCell(1);
+        row1cell2.setCellValue("工号");
+        row1cell2.setCellStyle(cellStyle);
+
+        Cell row1cell3 = row1.createCell(2);
+        row1cell3.setCellValue("姓名");
+        row1cell3.setCellStyle(cellStyle);
+
+        Cell row1cell4 = row1.createCell(3);
+        row1cell4.setCellValue("教研会");
+        row1cell4.setCellStyle(cellStyle);
+
+        Cell row1cell5 = row1.createCell(4);
+        row1cell5.setCellValue("督导听课");
+        row1cell5.setCellStyle(cellStyle);
+
+        Cell row1cell6 = row1.createCell(5);
+        row1cell6.setCellValue("临近三年退休政策");
+        row1cell6.setCellStyle(cellStyle);
+
+        Cell row1cell7 = row1.createCell(6);
+        row1cell7.setCellValue("出题");
+        row1cell7.setCellStyle(cellStyle);
+
+        Cell row1cell8 = row1.createCell(7);
+        row1cell8.setCellValue("阅卷");
+        row1cell8.setCellStyle(cellStyle);
+
+        Cell row1cell9 = row1.createCell(8);
+        row1cell9.setCellValue("周末培优");
+        row1cell9.setCellStyle(cellStyle);
+
+        Cell row1cell10 = row1.createCell(9);
+        row1cell10.setCellValue("早自习");
+        row1cell10.setCellStyle(cellStyle);
+
+        Cell row1cell11 = row1.createCell(10);
+        row1cell11.setCellValue("正课(含调顶课时)");
+        row1cell11.setCellStyle(cellStyle);
+
+        Cell row1cell12 = row1.createCell(11);
+        row1cell12.setCellValue("晚辅(含调顶课时)");
+        row1cell12.setCellStyle(cellStyle);
+
+        Cell row1cell13 = row1.createCell(12);
+        row1cell13.setCellValue("顶课");
+        row1cell13.setCellStyle(cellStyle);
+
+        Cell row1cell14 = row1.createCell(13);
+        row1cell14.setCellValue("调课");
+        row1cell14.setCellStyle(cellStyle);
+
+        Cell row1cell15 = row1.createCell(14);
+        row1cell15.setCellValue("正课课时数(平台导出" + statistics.getMonth() + "月)");
+        row1cell15.setCellStyle(cellStyle);
+
+        int index = 14 + mergeCoulmn;
+        Cell row1cell16 = row1.createCell(index);
+        row1cell16.setCellValue("总课时");
+        row1cell16.setCellStyle(cellStyle);
+
+        Cell row1cell17 = row1.createCell(index + 1);
+        row1cell17.setCellValue("课时费(元)");
+        row1cell17.setCellStyle(cellStyle);
+
+        Cell row1cell18 = row1.createCell(index + 2);
+        row1cell18.setCellValue("外聘教师超课时费(元)");
+        row1cell18.setCellStyle(cellStyle);
+
+        Cell row1cell19 = row1.createCell(index + 3);
+        row1cell19.setCellValue("总金额(元)");
+        row1cell19.setCellStyle(cellStyle);
+        //合并表头
+        for(int i = 0; i < 14; i ++){
+            sheet.addMergedRegion(new CellRangeAddress(1, 3, i, i));
+        }
+
+        //合并中间动态表头
+        sheet.addMergedRegion(new CellRangeAddress(1, 1, 14, index - 1));
+        //合并前端表头
+        for(int i = index; i <  index + 4; i ++){
+            sheet.addMergedRegion(new CellRangeAddress(1, 3, i, i));
+        }
+    }
+
+    /**
+     * 生成第三行表头
+     * 关于周的表头,周一日期到周二日期,第几周,最后拼接一个单独的月份
+     *
+     * @param workbook
+     * @param sheet
+     * @param month 月份
+     * @param weekTimeRangeVos 周的数据,包含周一的日期周二的日期
+     * @param lastIndex 前一个表头的单元格
+     */
+    static void createThirdTitle(Workbook workbook, Sheet sheet, int month, List<WeekTimeRangeVo> weekTimeRangeVos, int lastIndex){
+        Font font = workbook.createFont();
+        font.setFontName("宋体");
+        font.setFontHeightInPoints((short)12);
+
+        CellStyle cellStyle = workbook.createCellStyle();
+        cellStyle.setFont(font); // 将字体应用到样式
+        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+        cellStyle.setAlignment(HorizontalAlignment.CENTER);
+
+        //设置内容
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM.dd");
+        int rowNumber = 2;
+        Row row2 = sheet.createRow(rowNumber);
+
+        for(int i = 0; i < weekTimeRangeVos.size(); i ++){
+            WeekTimeRangeVo rangeVo = weekTimeRangeVos.get(i);
+            String cellValue = rangeVo.getMondayDate().format(formatter) + "-"
+                    + rangeVo.getSundayDate().format(formatter) + "(第" + rangeVo.getWeeks() + "周)";
+            Cell row1cell1 = row2.createCell(lastIndex + 1 + (i * 4));
+            row1cell1.setCellValue(cellValue);
+            row1cell1.setCellStyle(cellStyle);
+        }
+
+        //合并单元格
+        int monthIndex = 0;
+        for(int i = 0; i < weekTimeRangeVos.size(); i ++){
+            int startCoulmn = lastIndex + 1 + (i * 4);
+            int endCoulmn = startCoulmn + 3;
+            sheet.addMergedRegion(new CellRangeAddress(rowNumber, rowNumber, startCoulmn, endCoulmn));
+            monthIndex = endCoulmn;
+        }
+
+        Cell row1cell2 = row2.createCell(monthIndex + 1);
+        row1cell2.setCellValue(month + "月");
+        row1cell2.setCellStyle(cellStyle);
+    }
+
+    /**
+     * 生成第四行表头
+     * 表头内容:总课时(含调顶课节)、顶课(节)、顶课(节)、晚辅(节)、课时晚辅合计
+     *
+     * @param workbook
+     * @param sheet
+     * @param weekTimeRangeVos 周的数据,包含周一的日期周二的日期
+     * @param lastIndex 前一个表头的单元格
+     */
+    static void createFourthTitle(Workbook workbook, Sheet sheet, List<WeekTimeRangeVo> weekTimeRangeVos, int lastIndex){
+        Font font = workbook.createFont();
+        font.setFontName("宋体");
+        font.setFontHeightInPoints((short)12);
+
+        CellStyle cellStyle = workbook.createCellStyle();
+        cellStyle.setFont(font); // 将字体应用到样式
+        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+        cellStyle.setAlignment(HorizontalAlignment.CENTER);
+
+        int rowNumber = 3;
+        Row row2 = sheet.createRow(rowNumber);
+        int index = lastIndex + 1;
+        for(int i = 0; i < weekTimeRangeVos.size(); i ++){
+            Cell row1cell1 = row2.createCell(index);
+            row1cell1.setCellValue("总课时(含调顶课节)");
+            row1cell1.setCellStyle(cellStyle);
+
+            index ++;
+            Cell row1cell2 = row2.createCell(index);
+            row1cell2.setCellValue("顶课(节)");
+            row1cell2.setCellStyle(cellStyle);
+
+            index ++;
+            Cell row1cell3 = row2.createCell(index);
+            row1cell3.setCellValue("顶课(节)");
+            row1cell3.setCellStyle(cellStyle);
+            index ++;
+            Cell row1cell4 = row2.createCell(index);
+            row1cell4.setCellValue("晚辅(节)");
+            row1cell4.setCellStyle(cellStyle);
+
+            index ++;
+        }
+        Cell row1cell5 = row2.createCell(index);
+        row1cell5.setCellValue("课时晚辅合计");
+        row1cell5.setCellStyle(cellStyle);
+    }
+
+
+    static List<WeekTimeRangeVo> calculateNaturalWeeks(LocalDate startDate, LocalDate endDate) {
+        List<WeekTimeRangeVo> result = new ArrayList<>();
+        // 获取第一个周一
+        LocalDate currentMonday = startDate.with(DayOfWeek.MONDAY);
+        // 如果 startDate 不是周一,则用下一个周一
+        if (!startDate.isEqual(currentMonday) && startDate.isAfter(currentMonday)) {
+            currentMonday = currentMonday.plusWeeks(1);
+        }
+
+        int weeks = 1;
+        // 遍历每个周
+        while (!currentMonday.isAfter(endDate)) {
+            // 计算当前周的周日
+            LocalDate currentSunday = currentMonday.plusDays(6);
+
+            // 限制周日的范围
+            if (currentSunday.isAfter(endDate)) {
+                currentSunday = endDate;
+            }
+
+            // 添加结果
+            WeekTimeRangeVo timeRangeVo = new WeekTimeRangeVo();
+            timeRangeVo.setMondayDate(currentMonday);
+            timeRangeVo.setSundayDate(currentSunday);
+            timeRangeVo .setWeeks(weeks);
+            result.add(timeRangeVo);
+
+            // 移动到下一个周的周一
+            currentMonday = currentMonday.plusWeeks(1);
+            weeks ++;
+        }
+
+        return result;
+    }
+}

+ 64 - 0
src/test/java/com/xjrsoft/xjrsoftboot/FileTest.java

@@ -0,0 +1,64 @@
+package com.xjrsoft.xjrsoftboot;
+
+import cn.hutool.core.util.IdUtil;
+import com.google.gson.JsonArray;
+import com.xjrsoft.module.schedule.util.ScheduleUtil;
+import org.junit.jupiter.api.Test;
+
+import java.io.DataInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author dzx
+ * @date 2023/12/4
+ */
+public class FileTest {
+
+    @Test
+    void param() throws Exception {
+        long timestamp = System.currentTimeMillis();
+        System.out.println("timestamp:" + timestamp);
+        //生成签名
+        String sign = ScheduleUtil.createSign(timestamp);
+        System.out.println("sign:" + sign);
+        String startDateStr = "2024-07-29", endDateStr = "2024-08-16";
+        LocalDate startDateObj = LocalDate.parse(startDateStr);
+        LocalDate endDateObj = LocalDate.parse(endDateStr);
+        long between = ChronoUnit.DAYS.between(startDateObj, endDateObj);
+        long times = (between / 7) + 1;
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        for (int i = 0; i < times; i ++) {
+            LocalDate statrTime = startDateObj.plusDays(i * 7L);
+            String startDate = statrTime.format(formatter);
+            LocalDate endTime = statrTime.plusDays(6L);
+            if(endTime.isAfter(endDateObj)){
+                endTime = endDateObj;
+            }
+
+            String endDate = endTime.format(formatter);
+            System.out.println("i =====================================-> " + i);
+            System.out.println("startDate -> " + startDate);
+            System.out.println("endDate -> " + endDate);
+        }
+    }
+
+    @Test
+    void idTest() throws Exception {
+        System.out.println(IdUtil.getSnowflakeNextId());
+
+        String str = "[{\"label\":\"早自习课时单价:正式聘用\",\"field\":\"cost1\",\"value\":22},{\"label\":\"非正式聘用\",\"field\":\"cost2\",\"value\":33},{\"label\":\"正课课时单价:正式聘用\",\"field\":\"cost3\",\"value\":44},{\"label\":\"非正式聘用\",\"field\":\"cost4\",\"value\":43},{\"label\":\"晚自习课时单价:正式聘用\",\"field\":\"cost5\",\"value\":55},{\"label\":\"非正式聘用\",\"field\":\"cost6\",\"value\":134},{\"label\":\"超出课时单价:正式聘用\",\"field\":\"cost7\",\"value\":66},{\"label\":\"非正式聘用\",\"field\":\"cost8\",\"value\":13},{\"label\":\"超出课时标准(每周):正式聘用\",\"field\":\"cost9\",\"value\":77},{\"label\":\"非正式聘用\",\"field\":\"cost10\",\"value\":12},{\"label\":\"顶课课时单价:正式聘用\",\"field\":\"cost11\",\"value\":88},{\"label\":\"非正式聘用\",\"field\":\"cost12\",\"value\":99}]";
+    }
+
+    @Test
+    void idTest2() throws Exception {
+        String timeNumbers = "5,6,7";
+        Arrays.asList(timeNumbers.split(","));
+    }
+}

+ 78 - 0
src/test/java/com/xjrsoft/xjrsoftboot/HIkvisionTest.java

@@ -0,0 +1,78 @@
+package com.xjrsoft.xjrsoftboot;
+
+import com.alibaba.fastjson.JSONObject;
+import com.hikvision.artemis.sdk.ArtemisHttpUtil;
+import com.hikvision.artemis.sdk.config.ArtemisConfig;
+import com.xjrsoft.XjrSoftApplication;
+import com.xjrsoft.module.hikvision.util.ApiUtil;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author dzx
+ * @date 2024/4/28
+ */
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = XjrSoftApplication.class)
+public class HIkvisionTest {
+
+    @Test
+    void test(){
+        String result = GetCameraPreviewURL();
+        System.out.println("result结果示例: " + result);
+    }
+
+
+    public static String GetCameraPreviewURL() {
+
+        /**
+         * STEP1:设置平台参数,根据实际情况,设置host appkey appsecret 三个参数.
+         */
+        ArtemisConfig.host = ApiUtil.host; // 平台的ip端口
+        ArtemisConfig.appKey = ApiUtil.appKey;  // 密钥appkey
+        ArtemisConfig.appSecret = ApiUtil.appSecret;// 密钥appSecret
+
+        /**
+         * STEP2:设置OpenAPI接口的上下文
+         */
+        final String ARTEMIS_PATH = "/artemis";
+
+        /**
+         * STEP3:设置接口的URI地址
+         */
+        final String previewURLsApi = ARTEMIS_PATH + "/api/resource/v1/regions/root";
+        Map<String, String> path = new HashMap<String, String>(2) {
+            {
+                put("https://", previewURLsApi);//根据现场环境部署确认是http还是https
+            }
+        };
+
+        /**
+         * STEP4:设置参数提交方式
+         */
+        String contentType = "application/json";
+
+        /**
+         * STEP5:组装请求参数
+         */
+        JSONObject jsonBody = new JSONObject();
+        jsonBody.put("cameraIndexCode", "748d84750e3a4a5bbad3cd4af9ed5101");
+        jsonBody.put("streamType", 0);
+        jsonBody.put("protocol", "rtsp");
+        jsonBody.put("transmode", 1);
+        jsonBody.put("expand", "streamform=ps");
+        String body = jsonBody.toJSONString();
+        /**
+         * STEP6:调用接口
+         */
+        String result = ArtemisHttpUtil.doPostStringArtemis(path, body, null, null, contentType , null);// post请求application/json类型参数
+        return result;
+    }
+
+}

+ 28 - 0
src/test/java/com/xjrsoft/xjrsoftboot/HttpUtil.java

@@ -0,0 +1,28 @@
+package com.xjrsoft.xjrsoftboot;
+
+import org.apache.http.client.utils.URIBuilder;
+
+import java.net.URISyntaxException;
+import java.util.Map;
+
+/**
+ * @author dzx
+ * @date 2024/1/9
+ */
+public class HttpUtil {
+
+    public static String get(String url, Map<String, String> data){
+        URIBuilder uriBuilder = null;
+        try {
+            uriBuilder = new URIBuilder(url);
+            for (String key : data.keySet()) {
+                uriBuilder.addParameter(key, data.get(key));
+            }
+
+        } catch (URISyntaxException e) {
+            throw new RuntimeException(e);
+        }
+
+        return null;
+    }
+}

+ 15 - 0
src/test/java/com/xjrsoft/xjrsoftboot/IdCreateTest.java

@@ -0,0 +1,15 @@
+package com.xjrsoft.xjrsoftboot;
+
+import cn.hutool.core.util.IdUtil;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author dzx
+ * @date 2024/3/12
+ */
+public class IdCreateTest {
+    @Test
+    void test(){
+        System.out.println(IdUtil.getSnowflakeNextId());
+    }
+}

+ 23 - 0
src/test/java/com/xjrsoft/xjrsoftboot/OutInTest.java

@@ -0,0 +1,23 @@
+package com.xjrsoft.xjrsoftboot;
+
+import com.xjrsoft.module.attendance.service.IAttendanceRuleCategoryService;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.io.File;
+
+/**
+ * @author dzx
+ * @date 2024/6/25
+ */
+public class OutInTest {
+
+    @Test
+    void test(){
+        File rootDirectory = new File("/");
+        String rootPath = rootDirectory.getAbsolutePath();
+
+        System.out.println("Root directory path: " + rootPath);
+    }
+}

+ 74 - 0
src/test/java/com/xjrsoft/xjrsoftboot/ScheduleDataTest.java

@@ -0,0 +1,74 @@
+package com.xjrsoft.xjrsoftboot;
+
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.db.Entity;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.xjrsoft.XjrSoftApplication;
+import com.xjrsoft.common.mybatis.SqlRunnerAdapter;
+import com.xjrsoft.module.courseTable.service.ICourseTableService;
+import com.xjrsoft.module.schedule.service.IWfCourseAdjustService;
+import com.xjrsoft.module.schedule.util.DataUtil;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author dzx
+ * @date 2024/2/3
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = XjrSoftApplication.class)
+public class ScheduleDataTest {
+
+    @Autowired
+    private IWfCourseAdjustService wfCourseAdjustService;
+
+    @Autowired
+    private ICourseTableService courseTableService;
+
+    @Test
+    void test() throws Exception {
+        String eduYearSerialNo = "EY000000129";
+        String startDate = "2024-09-02";
+        String endDate = "2024-09-06";
+
+        DataUtil dataUtil = new DataUtil();
+
+        JsonArray scheduleInfo = dataUtil.getScheduleInfoByGrade(eduYearSerialNo, startDate, endDate);
+
+
+        for (JsonElement jsonElement : scheduleInfo) {
+            JsonObject asJsonObject = jsonElement.getAsJsonObject();
+            JsonArray teachers = asJsonObject.get("teachers").getAsJsonArray();
+            int timeNumber = asJsonObject.get("numberOfDay").getAsInt();//节次
+//            String classId = classMap.get(asJsonObject.get("classSerialNo").getAsString());
+            String scheduleDate = asJsonObject.get("scheduleDate").getAsString();
+//            String courseClassId = coureseMap.get(asJsonObject.get("courseClassId").getAsString());
+            String courseClassName = asJsonObject.get("courseClassName").getAsString();
+            String teacherId = "0";
+            String teacherName = "";
+            String teacher_serial_no = "";
+            if(teachers.size() > 0){
+                JsonObject teacherJson = teachers.get(0).getAsJsonObject();
+                teacherName = teacherJson.get("name").getAsString();
+                teacher_serial_no = teacherJson.get("teacherSerialNo").getAsString();
+                if("黎山磊".equals(teacherName)){
+                    System.out.println(jsonElement.toString());
+                }
+            }
+
+        }
+
+    }
+    
+
+
+}

+ 45 - 0
src/test/java/com/xjrsoft/xjrsoftboot/ScheduleUtil.java

@@ -0,0 +1,45 @@
+package com.xjrsoft.xjrsoftboot;
+
+import cn.dev33.satoken.secure.SaSecureUtil;
+import com.xjrsoft.config.JianyuekbConfig;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * @author dzx
+ * @date 2024/1/9
+ */
+public class ScheduleUtil {
+    public static final String ALGORITHM = "HmacSHA256";
+    public static final String apiUrl = "https://live.jianyuekb.com/api/v1/ScheduleFlowV2/OpenApi/";
+    private static JianyuekbConfig jianyuekbConfig;
+
+    public ScheduleUtil(JianyuekbConfig jianyuekbConfig){
+        this.jianyuekbConfig = jianyuekbConfig;
+    }
+    public String calculateHMac(String key, String data) throws Exception {
+        Mac sha256_HMAC = Mac.getInstance(ALGORITHM);
+
+        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), ALGORITHM);
+        sha256_HMAC.init(secret_key);
+
+        return byteArrayToHex(sha256_HMAC.doFinal(data.getBytes("UTF-8")));
+    }
+
+    public String byteArrayToHex(byte[] a) {
+        StringBuilder sb = new StringBuilder(a.length * 2);
+        for (byte b : a)
+            sb.append(String.format("%02x", b));
+        return sb.toString();
+    }
+
+    public String createSign(Long timestamp){
+        return SaSecureUtil.md5(jianyuekbConfig.getPassword() + timestamp);
+    }
+
+    public static String doPost(){
+        return null;
+    }
+
+}

+ 17 - 0
src/test/java/com/xjrsoft/xjrsoftboot/SubStringtest.java

@@ -0,0 +1,17 @@
+package com.xjrsoft.xjrsoftboot;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author dzx
+ * @date 2024/4/13
+ */
+public class SubStringtest {
+
+    @Test
+    void test(){
+        String text = "审批人:罗育明, 审批结果:确认, 审批内容:大师大师大师大萨斯发给电饭锅地方,福斯达哈佛时间的话福克纳,阿山地啊实打实。发斯蒂芬斯蒂芬是的啊;啊实打实大师?";
+        int i = text.indexOf("审批内容:");
+        System.out.println(text.substring(i+5, text.length()));
+    }
+}

+ 79 - 0
src/test/java/com/xjrsoft/xjrsoftboot/TeacherTest.java

@@ -0,0 +1,79 @@
+package com.xjrsoft.xjrsoftboot;
+
+import cn.hutool.db.Db;
+import cn.hutool.db.Entity;
+import com.alibaba.excel.EasyExcel;
+import com.xjrsoft.XjrSoftApplication;
+import com.xjrsoft.common.constant.GlobalConstant;
+import com.xjrsoft.common.utils.DatasourceUtil;
+import com.xjrsoft.module.schedule.util.DataUtil;
+import com.xjrsoft.module.teacher.entity.XjrUser;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import javax.sql.DataSource;
+import java.io.File;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author dzx
+ * @date 2024/2/28
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = XjrSoftApplication.class)
+public class TeacherTest {
+    @Test
+    void test() throws SQLException {
+        DataSource datasource = DatasourceUtil.getDataSource(GlobalConstant.DEFAULT_DATASOURCE_KEY);
+        Db use = Db.use(datasource);
+        String sql = "SELECT t1.* FROM xjr_user t1" +
+                " INNER JOIN base_teacher t2 ON t1.id = t2.user_id" +
+                " WHERE t1.delete_mark = 0 AND t2.delete_mark  = 0";
+        List<XjrUser> list = use.query(sql, XjrUser.class);
+        Set<String> idNumberSet = new HashSet<>();
+        for (XjrUser xjrUser : list) {
+            idNumberSet.add(xjrUser.getUserName().trim());
+        }
+
+        String filePath = "D:\\工作文件\\20240219  全校教职工  .xlsx";
+        File file = new File(filePath);
+        List<Map<Integer, Object>> excelDataList = EasyExcel.read(file).sheet().headRowNumber(1).doReadSync();
+        StringBuffer sqls = new StringBuffer();
+        Long user_id = 15331006043078L;
+        Map<String, Integer> genderMap = new HashMap<>();
+        genderMap.put("男", 1);
+        genderMap.put("女", 2);
+        String password  = "$2a$10$.Gkoj5nZHrbYCtnWaY3FjeZeK.jl3BgoRRz4etcdUxE6P5oL/hfzS";
+        Long rolerelationId = 1762120178972798977L;
+        int i = 1;
+        for (Map<Integer, Object> integerObjectMap : excelDataList) {
+            if(!idNumberSet.contains(integerObjectMap.get(0).toString().trim())){
+                Long userId = user_id + i;
+                sqls.append("INSERT INTO xjr_user(id,delete_mark,enabled_mark,create_user_id,create_date,user_name,NAME,CODE,nick_name,PASSWORD,gender,mobile,credential_type,credential_number)");
+                sqls.append("VALUE("+ userId + ",0,1,1000000000000000000,NOW()");
+                sqls.append(",'" + integerObjectMap.get(0).toString().trim() + "'");//user_name
+                sqls.append(",'" + integerObjectMap.get(1).toString().trim() + "'");//NAME
+                sqls.append(",'" + integerObjectMap.get(0).toString().trim() + "'");//CODE
+                sqls.append(",'" + integerObjectMap.get(1).toString().trim() + "'");//nick_name
+                sqls.append(",'" + password + "'");//PASSWORD
+                sqls.append("," + genderMap.get(integerObjectMap.get(2).toString().trim()) + "");//gender
+                sqls.append(",'" + integerObjectMap.get(3).toString().trim()+ "'");//mobile
+                sqls.append(",'ZZLS10007'");//credential_type
+                sqls.append(",'" + integerObjectMap.get(6).toString().trim()+ "');");//credential_number
+                sqls.append("\r\n");
+                sqls.append("INSERT INTO xjr_user_role_relation(id,user_id,role_id) VALUE(" + (rolerelationId + i) + "," + userId + ",2);");//credential_number
+                sqls.append("\r\n");//credential_number
+
+                i ++;
+            }
+        }
+        System.out.println(sqls.toString());
+    }
+}

Datei-Diff unterdrückt, da er zu groß ist
+ 23 - 0
src/test/java/com/xjrsoft/xjrsoftboot/Test2.java


+ 23 - 0
src/test/java/com/xjrsoft/xjrsoftboot/UrlTest.java

@@ -0,0 +1,23 @@
+package com.xjrsoft.xjrsoftboot;
+
+import cn.hutool.core.util.IdUtil;
+import org.junit.jupiter.api.Test;
+
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * @author dzx
+ * @date 2024/3/26
+ */
+public class UrlTest {
+
+    @Test
+    void test(){
+        long loginCode = IdUtil.getSnowflakeNextId();
+        String redirect_uri = "https://zhxy.cqtlzjzx.com/api/system/QR-code-login?loginCode=1772432965738688512";
+//        String decode = URLEncoder.encode(redirect_uri, StandardCharsets.UTF_8);
+//        System.out.println(decode);
+    }
+}

+ 272 - 0
ter

@@ -0,0 +1,272 @@
+
+                   SSUUMMMMAARRYY OOFF LLEESSSS CCOOMMMMAANNDDSS
+
+      Commands marked with * may be preceded by a number, _N.
+      Notes in parentheses indicate the behavior if _N is given.
+      A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K.
+
+  h  H                 Display this help.
+  q  :q  Q  :Q  ZZ     Exit.
+ ---------------------------------------------------------------------------
+
+                           MMOOVVIINNGG
+
+  e  ^E  j  ^N  CR  *  Forward  one line   (or _N lines).
+
+                   SSUUMMMMAARRYY OOFF LLEESSSS CCOOMMMMAANNDDSS
+
+      Commands marked with * may be preceded by a number, _N.
+      Notes in parentheses indicate the behavior if _N is given.
+      A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K.
+
+  h  H                 Display this help.
+  q  :q  Q  :Q  ZZ     Exit.
+ ---------------------------------------------------------------------------
+
+                           MMOOVVIINNGG
+
+  e  ^E  j  ^N  CR  *  Forward  one line   (or _N lines).
+  y  ^Y  k  ^K  ^P  *  Backward one line   (or _N lines).
+  f  ^F  ^V  SPACE  *  Forward  one window (or _N lines).
+  b  ^B  ESC-v      *  Backward one window (or _N lines).
+  z                 *  Forward  one window (and set window to _N).
+  w                 *  Backward one window (and set window to _N).
+  ESC-SPACE         *  Forward  one window, but don't stop at end-of-file.
+  d  ^D             *  Forward  one half-window (and set half-window to _N).
+  u  ^U             *  Backward one half-window (and set half-window to _N).
+  ESC-)  RightArrow *  Right one half screen width (or _N positions).
+  ESC-(  LeftArrow  *  Left  one half screen width (or _N positions).
+  ESC-}  ^RightArrow   Right to last column displayed.
+  ESC-{  ^LeftArrow    Left  to first column.
+  F                    Forward forever; like "tail -f".
+  ESC-F                Like F but stop when search pattern is found.
+  r  ^R  ^L            Repaint screen.
+  R                    Repaint screen, discarding buffered input.
+        ---------------------------------------------------
+        Default "window" is the screen height.
+        Default "half-window" is half of the screen height.
+ ---------------------------------------------------------------------------
+
+                          SSEEAARRCCHHIINNGG
+
+  /_p_a_t_t_e_r_n          *  Search forward for (_N-th) matching line.
+  ?_p_a_t_t_e_r_n          *  Search backward for (_N-th) matching line.
+  n                 *  Repeat previous search (for _N-th occurrence).
+  N                 *  Repeat previous search in reverse direction.
+  ESC-n             *  Repeat previous search, spanning files.
+  ESC-N             *  Repeat previous search, reverse dir. & spanning files.
+  ESC-u                Undo (toggle) search highlighting.
+  ESC-U                Clear search highlighting.
+  &_p_a_t_t_e_r_n          *  Display only matching lines.
+        ---------------------------------------------------
+        A search pattern may begin with one or more of:
+        ^N or !  Search for NON-matching lines.
+        ^E or *  Search multiple files (pass thru END OF FILE).
+        ^F or @  Start search at FIRST file (for /) or last file (for ?).
+        ^K       Highlight matches, but don't move (KEEP position).
+        ^R       Don't use REGULAR EXPRESSIONS.
+        ^W       WRAP search if no match found.
+ ---------------------------------------------------------------------------
+
+                           JJUUMMPPIINNGG
+
+  g  <  ESC-<       *  Go to first line in file (or line _N).
+  G  >  ESC->       *  Go to last line in file (or line _N).
+  p  %              *  Go to beginning of file (or _N percent into file).
+  t                 *  Go to the (_N-th) next tag.
+  T                 *  Go to the (_N-th) previous tag.
+  {  (  [           *  Find close bracket } ) ].
+  }  )  ]           *  Find open bracket { ( [.
+  ESC-^F _<_c_1_> _<_c_2_>  *  Find close bracket _<_c_2_>.
+  ESC-^B _<_c_1_> _<_c_2_>  *  Find open bracket _<_c_1_>.
+        ---------------------------------------------------
+        Each "find close bracket" command goes forward to the close bracket 
+          matching the (_N-th) open bracket in the top line.
+        Each "find open bracket" command goes backward to the open bracket 
+          matching the (_N-th) close bracket in the bottom line.
+
+  m_<_l_e_t_t_e_r_>            Mark the current top line with <letter>.
+  M_<_l_e_t_t_e_r_>            Mark the current bottom line with <letter>.
+  '_<_l_e_t_t_e_r_>            Go to a previously marked position.
+  ''                   Go to the previous position.
+  ^X^X                 Same as '.
+  ESC-M_<_l_e_t_t_e_r_>        Clear a mark.
+        ---------------------------------------------------
+        A mark is any upper-case or lower-case letter.
+        Certain marks are predefined:
+             ^  means  beginning of the file
+             $  means  end of the file
+ ---------------------------------------------------------------------------
+
+                        CCHHAANNGGIINNGG FFIILLEESS
+
+  :e [_f_i_l_e]            Examine a new file.
+  ^X^V                 Same as :e.
+  :n                *  Examine the (_N-th) next file from the command line.
+  :p                *  Examine the (_N-th) previous file from the command line.
+  :x                *  Examine the first (or _N-th) file from the command line.
+  :d                   Delete the current file from the command line list.
+  =  ^G  :f            Print current file name.
+ ---------------------------------------------------------------------------
+
+                    MMIISSCCEELLLLAANNEEOOUUSS CCOOMMMMAANNDDSS
+
+  -_<_f_l_a_g_>              Toggle a command line option [see OPTIONS below].
+  --_<_n_a_m_e_>             Toggle a command line option, by name.
+  __<_f_l_a_g_>              Display the setting of a command line option.
+  ___<_n_a_m_e_>             Display the setting of an option, by name.
+  +_c_m_d                 Execute the less cmd each time a new file is examined.
+
+  !_c_o_m_m_a_n_d             Execute the shell command with $SHELL.
+  |XX_c_o_m_m_a_n_d            Pipe file between current pos & mark XX to shell command.
+  s _f_i_l_e               Save input to a file.
+  v                    Edit the current file with $VISUAL or $EDITOR.
+  V                    Print version number of "less".
+ ---------------------------------------------------------------------------
+
+                           OOPPTTIIOONNSS
+
+        Most options may be changed either on the command line,
+        or from within less by using the - or -- command.
+        Options may be given in one of two forms: either a single
+        character preceded by a -, or a name preceded by --.
+
+  -?  ........  --help
+                  Display help (from command line).
+  -a  ........  --search-skip-screen
+                  Search skips current screen.
+  -A  ........  --SEARCH-SKIP-SCREEN
+                  Search starts just after target line.
+  -b [_N]  ....  --buffers=[_N]
+                  Number of buffers.
+  -B  ........  --auto-buffers
+                  Don't automatically allocate buffers for pipes.
+  -c  ........  --clear-screen
+                  Repaint by clearing rather than scrolling.
+  -d  ........  --dumb
+                  Dumb terminal.
+  -D xx_c_o_l_o_r  .  --color=xx_c_o_l_o_r
+                  Set screen colors.
+  -e  -E  ....  --quit-at-eof  --QUIT-AT-EOF
+                  Quit at end of file.
+  -f  ........  --force
+                  Force open non-regular files.
+  -F  ........  --quit-if-one-screen
+                  Quit if entire file fits on first screen.
+  -g  ........  --hilite-search
+                  Highlight only last match for searches.
+  -G  ........  --HILITE-SEARCH
+                  Don't highlight any matches for searches.
+  -h [_N]  ....  --max-back-scroll=[_N]
+                  Backward scroll limit.
+  -i  ........  --ignore-case
+                  Ignore case in searches that do not contain uppercase.
+  -I  ........  --IGNORE-CASE
+                  Ignore case in all searches.
+  -j [_N]  ....  --jump-target=[_N]
+                  Screen position of target lines.
+  -J  ........  --status-column
+                  Display a status column at left edge of screen.
+  -k [_f_i_l_e]  .  --lesskey-file=[_f_i_l_e]
+                  Use a lesskey file.
+  -K  ........  --quit-on-intr
+                  Exit less in response to ctrl-C.
+  -L  ........  --no-lessopen
+                  Ignore the LESSOPEN environment variable.
+  -m  -M  ....  --long-prompt  --LONG-PROMPT
+                  Set prompt style.
+  -n  -N  ....  --line-numbers  --LINE-NUMBERS
+                  Don't use line numbers.
+  -o [_f_i_l_e]  .  --log-file=[_f_i_l_e]
+                  Copy to log file (standard input only).
+  -O [_f_i_l_e]  .  --LOG-FILE=[_f_i_l_e]
+                  Copy to log file (unconditionally overwrite).
+  -p [_p_a_t_t_e_r_n]  --pattern=[_p_a_t_t_e_r_n]
+                  Start at pattern (from command line).
+  -P [_p_r_o_m_p_t]   --prompt=[_p_r_o_m_p_t]
+                  Define new prompt.
+  -q  -Q  ....  --quiet  --QUIET  --silent --SILENT
+                  Quiet the terminal bell.
+  -r  -R  ....  --raw-control-chars  --RAW-CONTROL-CHARS
+                  Output "raw" control characters.
+  -s  ........  --squeeze-blank-lines
+                  Squeeze multiple blank lines.
+  -S  ........  --chop-long-lines
+                  Chop (truncate) long lines rather than wrapping.
+  -t [_t_a_g]  ..  --tag=[_t_a_g]
+                  Find a tag.
+  -T [_t_a_g_s_f_i_l_e] --tag-file=[_t_a_g_s_f_i_l_e]
+                  Use an alternate tags file.
+  -u  -U  ....  --underline-special  --UNDERLINE-SPECIAL
+                  Change handling of backspaces.
+  -V  ........  --version
+                  Display the version number of "less".
+  -w  ........  --hilite-unread
+                  Highlight first new line after forward-screen.
+  -W  ........  --HILITE-UNREAD
+                  Highlight first new line after any forward movement.
+  -x [_N[,...]]  --tabs=[_N[,...]]
+                  Set tab stops.
+  -X  ........  --no-init
+                  Don't use termcap init/deinit strings.
+  -y [_N]  ....  --max-forw-scroll=[_N]
+                  Forward scroll limit.
+  -z [_N]  ....  --window=[_N]
+                  Set size of window.
+  -" [_c[_c]]  .  --quotes=[_c[_c]]
+                  Set shell quote characters.
+  -~  ........  --tilde
+                  Don't display tildes after end of file.
+  -# [_N]  ....  --shift=[_N]
+                  Set horizontal scroll amount (0 = one half screen width).
+                --file-size
+                  Automatically determine the size of the input file.
+                --follow-name
+                  The F command changes files if the input file is renamed.
+                --incsearch
+                  Search file as each pattern character is typed in.
+                --line-num-width=N
+                  Set the width of the -N line number field to N characters.
+                --mouse
+                  Enable mouse input.
+                --no-keypad
+                  Don't send termcap keypad init/deinit strings.
+                --no-histdups
+                  Remove duplicates from command history.
+                --rscroll=C
+                  Set the character used to mark truncated lines.
+                --save-marks
+                  Retain marks across invocations of less.
+                --status-col-width=N
+                  Set the width of the -J status column to N characters.
+                --use-backslash
+                  Subsequent options use backslash as escape char.
+                --use-color
+                  Enables colored text.
+                --wheel-lines=N
+                  Each click of the mouse wheel moves N lines.
+
+
+ ---------------------------------------------------------------------------
+
+                          LLIINNEE EEDDIITTIINNGG
+
+        These keys can be used to edit text being entered 
+        on the "command line" at the bottom of the screen.
+
+ RightArrow ..................... ESC-l ... Move cursor right one character.
+ LeftArrow ...................... ESC-h ... Move cursor left one character.
+ ctrl-RightArrow  ESC-RightArrow  ESC-w ... Move cursor right one word.
+ ctrl-LeftArrow   ESC-LeftArrow   ESC-b ... Move cursor left one word.
+ HOME ........................... ESC-0 ... Move cursor to start of line.
+ END ............................ ESC-$ ... Move cursor to end of line.
+ BACKSPACE ................................ Delete char to left of cursor.
+ DELETE ......................... ESC-x ... Delete char under cursor.
+ ctrl-BACKSPACE   ESC-BACKSPACE ........... Delete word to left of cursor.
+ ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor.
+ ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line.
+ UpArrow ........................ ESC-k ... Retrieve previous command line.
+ DownArrow ...................... ESC-j ... Retrieve next command line.
+ TAB ...................................... Complete filename & cycle.
+ SHIFT-TAB ...................... ESC-TAB   Complete filename & reverse cycle.
+ ctrl-L ................................... Complete filename, list all.

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.