diff --git a/jeelowcode-admin/src/test/java/com/jeelowcode/test/alibaba/WorkOrderSimpleTest.java b/jeelowcode-admin/src/test/java/com/jeelowcode/test/alibaba/WorkOrderSimpleTest.java
new file mode 100644
index 0000000..332fcd4
--- /dev/null
+++ b/jeelowcode-admin/src/test/java/com/jeelowcode/test/alibaba/WorkOrderSimpleTest.java
@@ -0,0 +1,19 @@
+package com.jeelowcode.test.alibaba;
+
+import com.jeelowcode.module.biz.job.AlibabaWorkOrderJob;
+import com.jeelowcode.tool.framework.test.core.ut.BaseDbAndRedisUnitTest;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+
+public class WorkOrderSimpleTest extends BaseDbAndRedisUnitTest {
+
+ @Resource
+ private AlibabaWorkOrderJob alibabaWorkOrderJob;
+
+ @Test
+ public void testBuildSql() {
+ System.out.println(alibabaWorkOrderJob.buildSql(null, null));
+ }
+
+}
diff --git a/jeelowcode-module/jeelowcode-module-biz/src/main/java/com/jeelowcode/module/biz/entity/AlibabaWorkOrder.java b/jeelowcode-module/jeelowcode-module-biz/src/main/java/com/jeelowcode/module/biz/entity/AlibabaWorkOrder.java
new file mode 100644
index 0000000..c541747
--- /dev/null
+++ b/jeelowcode-module/jeelowcode-module-biz/src/main/java/com/jeelowcode/module/biz/entity/AlibabaWorkOrder.java
@@ -0,0 +1,157 @@
+package com.jeelowcode.module.biz.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.jeelowcode.framework.utils.model.global.BaseTenantEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDateTime;
+
+/**
+ * 阿里工单实体类
+ *
+ * 用于存储和管理阿里平台产生的工单信息,包括检查项、整改要求、责任人等相关信息
+ *
+ * @author yangchenjj
+ */
+@Data
+@EqualsAndHashCode
+@TableName("lc_alibaba_work_order")
+public class AlibabaWorkOrder extends BaseTenantEntity {
+
+ /**
+ * 主键
+ */
+ @TableId(value = "id", type = IdType.ASSIGN_ID)
+ private Long id;
+
+ /**
+ * 阿里工单ID
+ */
+ @TableField("work_order_id")
+ private String workOrderId;
+
+ /**
+ * 园区ID
+ */
+ @TableField("campus_id")
+ private String campusId;
+
+ /**
+ * 园区名称
+ */
+ @TableField("campus_name")
+ private String campusName;
+
+ /**
+ * 工单等级
+ */
+ @TableField("work_order_level")
+ private String workOrderLevel;
+
+ /**
+ * 检查区域
+ */
+ @TableField("check_area")
+ private String checkArea;
+
+ /**
+ * 检查项
+ */
+ @TableField("check_item")
+ private String checkItem;
+
+ /**
+ * 检查内容
+ */
+ @TableField("check_content")
+ private String checkContent;
+
+ /**
+ * 检查人ID
+ */
+ @TableField("check_person_id")
+ private String checkPersonId;
+
+ /**
+ * 检查人姓名
+ */
+ @TableField("check_person_name")
+ private String checkPersonName;
+
+ /**
+ * 检查时间
+ */
+ @TableField("check_time")
+ private LocalDateTime checkTime;
+
+ /**
+ * 检查发现的问题
+ */
+ @TableField("check_question")
+ private String checkQuestion;
+
+ /**
+ * 整改截止时间
+ */
+ @TableField("correct_deadline")
+ private LocalDateTime correctDeadline;
+
+ /**
+ * 整改责任人ID
+ */
+ @TableField("correct_person_id")
+ private String correctPersonId;
+
+ /**
+ * 整改责任人姓名
+ */
+ @TableField("correct_person_name")
+ private String correctPersonName;
+
+ /**
+ * 整改措施
+ */
+ @TableField("correct_action")
+ private String correctAction;
+
+ /**
+ * 确认人ID
+ */
+ @TableField("confirm_person_id")
+ private String confirmPersonId;
+
+ /**
+ * 确认人姓名
+ */
+ @TableField("confirm_person_name")
+ private String confirmPersonName;
+
+ /**
+ * 修改时间
+ */
+ @TableField("gmt_modified")
+ private LocalDateTime gmtModified;
+
+ /**
+ * 子表修改时间
+ */
+ @TableField("gmt_sub_modified")
+ private LocalDateTime gmtSubModified;
+
+ /**
+ * 关联数据修改时间
+ */
+ @TableField("gmt_relate_modified")
+ private LocalDateTime gmtRelateModified;
+
+ /**
+ * 关联子表修改时间
+ */
+ @TableField("gmt_relate_sub_modified")
+ private LocalDateTime gmtRelateSubModified;
+
+}
\ No newline at end of file
diff --git a/jeelowcode-module/jeelowcode-module-biz/src/main/java/com/jeelowcode/module/biz/job/AlibabaWorkOrderJob.java b/jeelowcode-module/jeelowcode-module-biz/src/main/java/com/jeelowcode/module/biz/job/AlibabaWorkOrderJob.java
new file mode 100644
index 0000000..c752ef2
--- /dev/null
+++ b/jeelowcode-module/jeelowcode-module-biz/src/main/java/com/jeelowcode/module/biz/job/AlibabaWorkOrderJob.java
@@ -0,0 +1,514 @@
+package com.jeelowcode.module.biz.job;
+
+import cn.hutool.core.date.LocalDateTimeUtil;
+import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
+import com.jeelowcode.module.biz.entity.AlibabaWorkOrder;
+import com.jeelowcode.module.biz.service.IAlibabaWorkOrderService;
+import com.jeelowcode.tool.framework.quartz.core.handler.JobHandler;
+import com.jeelowcode.tool.framework.tenant.core.job.TenantJob;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import static com.jeelowcode.tool.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+/**
+ * 从阿里巴巴工单同步任务(包含查询、清洗)
+ */
+@Slf4j
+@Component
+public class AlibabaWorkOrderJob implements JobHandler {
+
+ /**
+ * 阿里数据源名称
+ */
+ private static final String ALIBABA_DATASOURCE_NAME = "alidb";
+
+ @Resource
+ private IAlibabaWorkOrderService workOrderService;
+
+ @Resource
+ private DynamicRoutingDataSource dynamicRoutingDataSource;
+
+ @Override
+ @TenantJob
+ public String execute(String param) throws Exception {
+ // 拿到系统中的所有的数据源,检查是否有阿里的数据源
+ Map dataSources = dynamicRoutingDataSource.getDataSources();
+ if (!dataSources.containsKey(ALIBABA_DATASOURCE_NAME)) {
+ log.error("未找到阿里数据源,请检查数据源配置");
+ return "未找到阿里数据源,请检查数据源配置";
+ }
+ // 查询我数据库中最新的时间点
+ LocalDateTime maxWorkOrderTime = workOrderService.getMaxWorkOrderTime();
+ StringBuilder sqlBuilder = new StringBuilder();
+ if (maxWorkOrderTime == null) {
+ sqlBuilder.append(buildSql(null, null));
+ } else {
+ LocalDateTime endTime = LocalDateTimeUtil.endOfDay(LocalDateTime.now());
+ sqlBuilder.append(buildSql(maxWorkOrderTime, endTime));
+ }
+ // 执行SQL查询数据
+ DataSource dataSource = dataSources.get(ALIBABA_DATASOURCE_NAME);
+ try (Connection connection = dataSource.getConnection();
+ ResultSet resultSet = connection.createStatement().executeQuery(sqlBuilder.toString())) {
+ // 使用连接执行SQL并得到结果
+ List workOrderList = new ArrayList<>();
+ // 将resultSet解析成List list
+ while (resultSet.next()) {
+ AlibabaWorkOrder workOrder = new AlibabaWorkOrder();
+ workOrder.setId(resultSet.getLong("id"));
+ workOrder.setWorkOrderId(resultSet.getString("workOrderId"));
+ workOrder.setCampusId(resultSet.getString("campusId"));
+ workOrder.setCampusName(resultSet.getString("campusName"));
+ workOrder.setCheckArea(resultSet.getString("checkArea"));
+ workOrder.setCheckItem(resultSet.getString("checkItem"));
+ workOrder.setCheckContent(resultSet.getString("checkContent"));
+ workOrder.setCheckPersonId(resultSet.getString("checkPersonId"));
+ workOrder.setCheckPersonName(resultSet.getString("checkPersonName"));
+ workOrder.setCheckTime(resultSet.getObject("checkTime", LocalDateTime.class));
+ workOrder.setCheckQuestion(resultSet.getString("checkQuestion"));
+ workOrder.setCorrectAction(resultSet.getString("correctAction"));
+ workOrder.setCorrectPersonId(resultSet.getString("correctPersonId"));
+ workOrder.setCorrectPersonName(resultSet.getString("correctPersonName"));
+ workOrder.setWorkOrderLevel(resultSet.getString("workOrderLevel"));
+ workOrder.setCorrectDeadline(resultSet.getObject("correctDeadline", LocalDateTime.class));
+ workOrder.setConfirmPersonId(resultSet.getString("confirmPersonId"));
+ workOrder.setConfirmPersonName(resultSet.getString("confirmPersonName"));
+ workOrder.setGmtModified(resultSet.getObject("gmtModified", LocalDateTime.class));
+ workOrder.setGmtSubModified(resultSet.getObject("gmtSubModified", LocalDateTime.class));
+ workOrder.setGmtRelateModified(resultSet.getObject("gmtRelateModified", LocalDateTime.class));
+ workOrder.setGmtRelateSubModified(resultSet.getObject("gmtRelateSubModified", LocalDateTime.class));
+ workOrderList.add(workOrder);
+ }
+
+ // 解析得到的数据由服务存储到数据库
+ int num = workOrderService.batchSaveWorkOrder(workOrderList);
+ log.info("同步阿里工单完成,共处理{}条数据", workOrderList.size());
+ log.info("同步阿里工单完成,共成功{}条数据", num);
+ return "同步阿里工单完成,共处理" + num + "条数据";
+ } catch (SQLException e) {
+ log.error("执行SQL查询或解析ResultSet异常", e);
+ throw e;
+ }
+ }
+
+ /**
+ * 构建查询SQL
+ *
+ * @param startTime 开始时间
+ * @param endTime 结束时间
+ * @return SQL
+ */
+ public static String buildSql(LocalDateTime startTime, LocalDateTime endTime) {
+ StringBuilder sqlBuilder = new StringBuilder();
+ sqlBuilder.append("SELECT\n" +
+ " mwoe.ID AS \"id\",\n" +
+ " mwoe.WORK_ORDER_ID AS \"workOrderId\",\n" +
+ " mwoe.CAMPUS_ID AS \"campusId\",\n" +
+ " mwoe.CAMPUS_NAME AS \"campusName\",\n" +
+ " mwoe.REMARK AS \"checkArea\",\n" +
+ " mwoe.OBJ_NAME AS \"checkItem\",\n" +
+ " mwoe.SUB_SOP_SCHEMA AS \"checkContent\",\n" +
+ " mwoe.CREATOR_ID AS \"checkPersonId\",\n" +
+ " mwoe.CREATOR_NAME AS \"checkPersonName\",\n" +
+ " mwoe.GMT_CREATE AS \"checkTime\",\n" +
+ " mwoe.PROCESS_CONTENT AS \"checkQuestion\",\n" +
+ " CONCAT_WS(',', rwoa.PROCESS_CONTENT, rwoa.SUB_SOP_SCHEMA) AS \"correctAction\",\n" +
+ " rwoa.PROCESSOR_ID AS \"correctPersonId\",\n" +
+ " rwoa.PROCESSOR_NAME AS \"correctPersonName\",\n" +
+ " mwoe.\"LEVEL\" AS \"workOrderLevel\",\n" +
+ " rwoa.RESERVED_TIME3 AS \"correctDeadline\",\n" +
+ " rwoa.CONFIRM_PERSON_ID AS \"confirmPersonId\",\n" +
+ " rwoa.CONFIRM_PERSON_NAME AS \"confirmPersonName\",\n" +
+ " mwoe.GMT_MODIFIED AS \"gmtModified\",\n" +
+ " mwoe.SUB_GMT_MODIFIED AS \"gmtSubModified\",\n" +
+ " rwoa.GMT_MODIFIED AS \"gmtRelateModified\",\n" +
+ " rwoa.SUB_GMT_MODIFIED AS \"gmtRelateSubModified\"\n" +
+ "FROM\n" +
+ " (\n" +
+ " SELECT\n" +
+ " mwo.*,\n" +
+ " swo.SOP_SCHEMA AS SUB_SOP_SCHEMA,\n" +
+ " swo.PROCESS_CONTENT AS SUB_PROCESS_CONTENT,\n" +
+ " swo.GMT_MODIFIED AS SUB_GMT_MODIFIED,\n" +
+ " swo.OBJ_NAME AS OBJ_NAME\n" +
+ " FROM\n" +
+ " (\n" +
+ " SELECT\n" +
+ " wo.ID,\n" +
+ " wo.WORK_ORDER_ID,\n" +
+ " wo.CAMPUS_ID,\n" +
+ " c.NAME AS CAMPUS_NAME,\n" +
+ " wo.REMARK,\n" +
+ " wo.CREATOR_ID,\n" +
+ " wo.CREATOR_NAME,\n" +
+ " wo.GMT_CREATE,\n" +
+ " wo.GMT_MODIFIED,\n" +
+ " wo.RESERVED_TIME3,\n" +
+ " CASE\n" +
+ " WHEN wo.\"LEVEL\" = 'P' THEN '重大'\n" +
+ " ELSE '一般'\n" +
+ " END AS \"LEVEL\",\n" +
+ " wo.\"CONFIRM_PERSON_ID\",\n" +
+ " wo.CONFIRM_PERSON_NAME,\n" +
+ " wo.TENANT,\n" +
+ " ml.PROCESSOR_ID,\n" +
+ " ml.PROCESSOR_NAME,\n" +
+ " JSON_VALUE(ml.CONTENT, '$.processedContent') AS PROCESS_CONTENT\n" +
+ " FROM\n" +
+ " \"XCAMPUS_WORKORDER\".\"WORK_ORDER\" wo\n" +
+ " LEFT JOIN (\n" +
+ " SELECT\n" +
+ " *\n" +
+ " FROM\n" +
+ " \"XCAMPUS_WORKORDER\".\"WORK_ORDER_LOG\" wol\n" +
+ " WHERE\n" +
+ " wol.\"OPERATE_NAME\" = '完成'\n" +
+ " ) ml ON wo.\"WORK_ORDER_ID\" = ml.\"WORK_ORDER_ID\"\n" +
+ " LEFT JOIN \"XCAMPUS_SPACECENTER\".\"CAMPUS\" c ON c.UUID = wo.CAMPUS_ID\n" +
+ " WHERE\n" +
+ " wo.PARENT_ID IS NULL\n" +
+ " AND wo.ORDER_TYPE_PATH_NAME = '风险隐患'\n" +
+ " AND wo.TENANT = '1821414781587267584'\n" +
+ " AND EXISTS (\n" +
+ " SELECT\n" +
+ " 1\n" +
+ " FROM\n" +
+ " \"XCAMPUS_WORKORDER\".\"WORK_ORDER\" wo2\n" +
+ " WHERE\n" +
+ " wo.\"WORK_ORDER_ID\" = wo2.\"WORK_ORDER_ID\"\n" +
+ " AND wo2.\"RELATE_WORK_ORDER_ID\" IS NULL\n" +
+ " )\n" +
+ " ) mwo\n" +
+ " LEFT JOIN (\n" +
+ " SELECT\n" +
+ " wo.PARENT_ID,\n" +
+ " LISTAGG(\n" +
+ " REPLACE(\n" +
+ " REPLACE(\n" +
+ " JSON_QUERY(\n" +
+ " wos.SOP_SCHEMA,\n" +
+ " '$[0].sopSchema.componentsTree[0].children[0].children[*].props.label' WITH CONDITIONAL WRAPPER,\n" +
+ " '[',\n" +
+ " ']',\n" +
+ " ','\n" +
+ " ),\n" +
+ " ']',\n" +
+ " ''\n" +
+ " ),\n" +
+ " '[',\n" +
+ " ''\n" +
+ " ),\n" +
+ " ','\n" +
+ " ) WITHIN GROUP (\n" +
+ " ORDER BY\n" +
+ " wo.WORK_ORDER_ID\n" +
+ " ) AS SOP_SCHEMA,\n" +
+ " LISTAGG(\n" +
+ " REPLACE(\n" +
+ " REPLACE(\n" +
+ " JSON_QUERY(\n" +
+ " sl.CONTENT,\n" +
+ " '$.sopInfo.*' WITH CONDITIONAL WRAPPER,\n" +
+ " '[',\n" +
+ " ']',\n" +
+ " ','\n" +
+ " ),\n" +
+ " ']',\n" +
+ " ''\n" +
+ " ),\n" +
+ " '[',\n" +
+ " ''\n" +
+ " ),\n" +
+ " ','\n" +
+ " ) WITHIN GROUP (\n" +
+ " ORDER BY\n" +
+ " wo.WORK_ORDER_ID\n" +
+ " ) AS PROCESS_CONTENT,\n" +
+ " LISTAGG(OBJ_NAME, ',') WITHIN GROUP (\n" +
+ " ORDER BY\n" +
+ " wo.WORK_ORDER_ID\n" +
+ " ) AS OBJ_NAME,\n" +
+ " MAX(wo.GMT_MODIFIED) AS GMT_MODIFIED\n" +
+ " FROM\n" +
+ " \"XCAMPUS_WORKORDER\".\"WORK_ORDER\" wo\n" +
+ " LEFT JOIN (\n" +
+ " SELECT\n" +
+ " *\n" +
+ " FROM\n" +
+ " \"XCAMPUS_WORKORDER\".\"WORK_ORDER_LOG\" wol\n" +
+ " WHERE\n" +
+ " wol.\"OPERATE_NAME\" = '处理'\n" +
+ " ) sl ON wo.\"WORK_ORDER_ID\" = sl.\"WORK_ORDER_ID\"\n" +
+ " LEFT JOIN \"XCAMPUS_WORKORDER\".\"WORK_ORDER_SCHEMA\" wos ON wo.\"WORK_ORDER_ID\" = wos.\"WORK_ORDER_ID\"\n" +
+ " WHERE\n" +
+ " wo.PARENT_ID IN (\n" +
+ " SELECT\n" +
+ " WORK_ORDER_ID\n" +
+ " FROM\n" +
+ " \"XCAMPUS_WORKORDER\".\"WORK_ORDER\"\n" +
+ " WHERE\n" +
+ " ORDER_TYPE_PATH_NAME = '风险隐患'\n" +
+ " )\n" +
+ " AND wo.ORDER_TYPE_PATH_NAME = '系统默认子单类型'\n" +
+ " AND wo.TENANT = '1821414781587267584'\n" +
+ " GROUP BY\n" +
+ " wo.PARENT_ID\n" +
+ " ) swo ON mwo.WORK_ORDER_ID = swo.PARENT_ID\n" +
+ " ) mwoe\n" +
+ " LEFT JOIN (\n" +
+ " SELECT\n" +
+ " rwoe.\"RELATE_WORK_ORDER_ID\",\n" +
+ " LISTAGG(rwoe.\"PROCESS_CONTENT\", ',') WITHIN GROUP (\n" +
+ " ORDER BY\n" +
+ " rwoe.WORK_ORDER_ID\n" +
+ " ) AS PROCESS_CONTENT,\n" +
+ " LISTAGG(rwoe.SUB_PROCESS_CONTENT, ',') WITHIN GROUP (\n" +
+ " ORDER BY\n" +
+ " rwoe.WORK_ORDER_ID\n" +
+ " ) AS SUB_PROCESS_CONTENT,\n" +
+ " LISTAGG(rwoe.SUB_SOP_SCHEMA, ',') WITHIN GROUP (\n" +
+ " ORDER BY\n" +
+ " rwoe.WORK_ORDER_ID\n" +
+ " ) AS SUB_SOP_SCHEMA,\n" +
+ " LISTAGG(rwoe.PROCESSOR_ID, ',') WITHIN GROUP (\n" +
+ " ORDER BY\n" +
+ " rwoe.WORK_ORDER_ID\n" +
+ " ) AS PROCESSOR_ID,\n" +
+ " LISTAGG(rwoe.PROCESSOR_NAME, ',') WITHIN GROUP (\n" +
+ " ORDER BY\n" +
+ " rwoe.WORK_ORDER_ID\n" +
+ " ) AS PROCESSOR_NAME,\n" +
+ " LISTAGG(rwoe.CONFIRM_PERSON_ID, ',') WITHIN GROUP (\n" +
+ " ORDER BY\n" +
+ " rwoe.WORK_ORDER_ID\n" +
+ " ) AS CONFIRM_PERSON_ID,\n" +
+ " LISTAGG(rwoe.CONFIRM_PERSON_NAME, ',') WITHIN GROUP (\n" +
+ " ORDER BY\n" +
+ " rwoe.WORK_ORDER_ID\n" +
+ " ) AS CONFIRM_PERSON_NAME,\n" +
+ " MAX(rwoe.RESERVED_TIME3) AS RESERVED_TIME3,\n" +
+ " MAX(rwoe.GMT_MODIFIED) AS GMT_MODIFIED,\n" +
+ " MAX(rwoe.SUB_GMT_MODIFIED) AS SUB_GMT_MODIFIED\n" +
+ " FROM\n" +
+ " (\n" +
+ " SELECT\n" +
+ " rwo.*,\n" +
+ " swo.SOP_SCHEMA AS SUB_SOP_SCHEMA,\n" +
+ " swo.PROCESS_CONTENT AS SUB_PROCESS_CONTENT,\n" +
+ " swo.GMT_MODIFIED AS SUB_GMT_MODIFIED,\n" +
+ " swo.OBJ_NAME AS OBJ_NAME\n" +
+ " FROM\n" +
+ " (\n" +
+ " SELECT\n" +
+ " wo.ID,\n" +
+ " wo.WORK_ORDER_ID,\n" +
+ " wo.CAMPUS_ID,\n" +
+ " c.NAME AS CAMPUS_NAME,\n" +
+ " wo.REMARK,\n" +
+ " wo.CREATOR_ID,\n" +
+ " wo.CREATOR_NAME,\n" +
+ " wo.GMT_CREATE,\n" +
+ " wo.GMT_MODIFIED,\n" +
+ " wo.RESERVED_TIME3,\n" +
+ " CASE\n" +
+ " WHEN wo.\"LEVEL\" = 'P' THEN '重大'\n" +
+ " ELSE '一般'\n" +
+ " END AS \"LEVEL\",\n" +
+ " wo.\"CONFIRM_PERSON_ID\",\n" +
+ " wo.CONFIRM_PERSON_NAME,\n" +
+ " wo.TENANT,\n" +
+ " wo.RELATE_WORK_ORDER_ID,\n" +
+ " ml.PROCESSOR_ID,\n" +
+ " ml.PROCESSOR_NAME,\n" +
+ " JSON_VALUE(ml.CONTENT, '$.processedContent') AS PROCESS_CONTENT\n" +
+ " FROM\n" +
+ " \"XCAMPUS_WORKORDER\".\"WORK_ORDER\" wo\n" +
+ " LEFT JOIN (\n" +
+ " SELECT\n" +
+ " *\n" +
+ " FROM\n" +
+ " \"XCAMPUS_WORKORDER\".\"WORK_ORDER_LOG\" wol\n" +
+ " WHERE\n" +
+ " wol.\"OPERATE_NAME\" = '完成'\n" +
+ " ) ml ON wo.\"WORK_ORDER_ID\" = ml.\"WORK_ORDER_ID\"\n" +
+ " LEFT JOIN \"XCAMPUS_SPACECENTER\".\"CAMPUS\" c ON c.UUID = wo.CAMPUS_ID\n" +
+ " WHERE\n" +
+ " wo.PARENT_ID IS NULL\n" +
+ " AND wo.ORDER_TYPE_PATH_NAME = '风险隐患'\n" +
+ " AND wo.TENANT = '1821414781587267584'\n" +
+ " AND EXISTS (\n" +
+ " SELECT\n" +
+ " 1\n" +
+ " FROM\n" +
+ " \"XCAMPUS_WORKORDER\".\"WORK_ORDER\" wo2\n" +
+ " WHERE\n" +
+ " wo.\"WORK_ORDER_ID\" = wo2.\"WORK_ORDER_ID\"\n" +
+ " AND wo2.\"RELATE_WORK_ORDER_ID\" IS NOT NULL\n" +
+ " )\n" +
+ " ) rwo\n" +
+ " LEFT JOIN (\n" +
+ " SELECT\n" +
+ " wo.PARENT_ID,\n" +
+ " LISTAGG(\n" +
+ " REPLACE(\n" +
+ " REPLACE(\n" +
+ " JSON_QUERY(\n" +
+ " wos.SOP_SCHEMA,\n" +
+ " '$[0].sopSchema.componentsTree[0].children[0].children[*].props.label' WITH CONDITIONAL WRAPPER,\n" +
+ " '[',\n" +
+ " ']',\n" +
+ " ','\n" +
+ " ),\n" +
+ " ']',\n" +
+ " ''\n" +
+ " ),\n" +
+ " '[',\n" +
+ " ''\n" +
+ " ),\n" +
+ " ','\n" +
+ " ) WITHIN GROUP (\n" +
+ " ORDER BY\n" +
+ " wo.WORK_ORDER_ID\n" +
+ " ) AS SOP_SCHEMA,\n" +
+ " LISTAGG(\n" +
+ " REPLACE(\n" +
+ " REPLACE(\n" +
+ " JSON_QUERY(\n" +
+ " sl.CONTENT,\n" +
+ " '$.sopInfo.*' WITH CONDITIONAL WRAPPER,\n" +
+ " '[',\n" +
+ " ']',\n" +
+ " ','\n" +
+ " ),\n" +
+ " ']',\n" +
+ " ''\n" +
+ " ),\n" +
+ " '[',\n" +
+ " ''\n" +
+ " ),\n" +
+ " ','\n" +
+ " ) WITHIN GROUP (\n" +
+ " ORDER BY\n" +
+ " wo.WORK_ORDER_ID\n" +
+ " ) AS PROCESS_CONTENT,\n" +
+ " LISTAGG(OBJ_NAME, ',') WITHIN GROUP (\n" +
+ " ORDER BY\n" +
+ " wo.WORK_ORDER_ID\n" +
+ " ) AS OBJ_NAME,\n" +
+ " MAX(wo.GMT_MODIFIED) AS GMT_MODIFIED\n" +
+ " FROM\n" +
+ " \"XCAMPUS_WORKORDER\".\"WORK_ORDER\" wo\n" +
+ " LEFT JOIN (\n" +
+ " SELECT\n" +
+ " *\n" +
+ " FROM\n" +
+ " \"XCAMPUS_WORKORDER\".\"WORK_ORDER_LOG\" wol\n" +
+ " WHERE\n" +
+ " wol.\"OPERATE_NAME\" = '处理'\n" +
+ " ) sl ON wo.\"WORK_ORDER_ID\" = sl.\"WORK_ORDER_ID\"\n" +
+ " LEFT JOIN \"XCAMPUS_WORKORDER\".\"WORK_ORDER_SCHEMA\" wos ON wo.\"WORK_ORDER_ID\" = wos.\"WORK_ORDER_ID\"\n" +
+ " WHERE\n" +
+ " wo.PARENT_ID IN (\n" +
+ " SELECT\n" +
+ " WORK_ORDER_ID\n" +
+ " FROM\n" +
+ " \"XCAMPUS_WORKORDER\".\"WORK_ORDER\"\n" +
+ " WHERE\n" +
+ " ORDER_TYPE_PATH_NAME = '风险隐患'\n" +
+ " )\n" +
+ " AND wo.ORDER_TYPE_PATH_NAME = '系统默认子单类型'\n" +
+ " AND wo.TENANT = '1821414781587267584'\n" +
+ " GROUP BY\n" +
+ " wo.PARENT_ID\n" +
+ " ) swo ON rwo.WORK_ORDER_ID = swo.PARENT_ID\n" +
+ " ) rwoe\n" +
+ " GROUP BY\n" +
+ " rwoe.\"RELATE_WORK_ORDER_ID\"\n" +
+ " ) rwoa ON mwoe.WORK_ORDER_ID = rwoa.RELATE_WORK_ORDER_ID\n");
+ // 如果时间参数存在并且合法,则添加时间条件
+ if (Objects.nonNull(startTime) && Objects.nonNull(endTime) && startTime.isBefore(endTime)) {
+ String formattedStart = LocalDateTimeUtil.format(startTime, FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND);
+ String formattedEnd = LocalDateTimeUtil.format(endTime, FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND);
+
+ sqlBuilder.append("WHERE\n" +
+ " (\n" +
+ " mwoe.GMT_MODIFIED BETWEEN TO_DATE(\n" +
+ " NVL(\n" +
+ " '" + formattedStart + "',\n" +
+ " TO_CHAR(SYSDATE, 'YYYY-MM-DD') || ' 00:00:00'\n" +
+ " ),\n" +
+ " 'YYYY-MM-DD HH24:MI:SS'\n" +
+ " )\n" +
+ " AND TO_DATE(\n" +
+ " NVL(\n" +
+ " '" + formattedEnd + "',\n" +
+ " TO_CHAR(SYSDATE, 'YYYY-MM-DD') || ' 23:59:59'\n" +
+ " ),\n" +
+ " 'YYYY-MM-DD HH24:MI:SS'\n" +
+ " )\n" +
+ " )\n" +
+ " OR (\n" +
+ " mwoe.SUB_GMT_MODIFIED BETWEEN TO_DATE(\n" +
+ " NVL(\n" +
+ " '" + formattedStart + "',\n" +
+ " TO_CHAR(SYSDATE, 'YYYY-MM-DD') || ' 00:00:00'\n" +
+ " ),\n" +
+ " 'YYYY-MM-DD HH24:MI:SS'\n" +
+ " )\n" +
+ " AND TO_DATE(\n" +
+ " NVL(\n" +
+ " '" + formattedEnd + "',\n" +
+ " TO_CHAR(SYSDATE, 'YYYY-MM-DD') || ' 23:59:59'\n" +
+ " ),\n" +
+ " 'YYYY-MM-DD HH24:MI:SS'\n" +
+ " )\n" +
+ " )\n" +
+ " OR (\n" +
+ " rwoa.GMT_MODIFIED BETWEEN TO_DATE(\n" +
+ " NVL(\n" +
+ " '" + formattedStart + "',\n" +
+ " TO_CHAR(SYSDATE, 'YYYY-MM-DD') || ' 00:00:00'\n" +
+ " ),\n" +
+ " 'YYYY-MM-DD HH24:MI:SS'\n" +
+ " )\n" +
+ " AND TO_DATE(\n" +
+ " NVL(\n" +
+ " '" + formattedEnd + "',\n" +
+ " TO_CHAR(SYSDATE, 'YYYY-MM-DD') || ' 23:59:59'\n" +
+ " ),\n" +
+ " 'YYYY-MM-DD HH24:MI:SS'\n" +
+ " )\n" +
+ " )\n" +
+ " OR (\n" +
+ " rwoa.SUB_GMT_MODIFIED BETWEEN TO_DATE(\n" +
+ " NVL(\n" +
+ " '" + formattedStart + "',\n" +
+ " TO_CHAR(SYSDATE, 'YYYY-MM-DD') || ' 00:00:00'\n" +
+ " ),\n" +
+ " 'YYYY-MM-DD HH24:MI:SS'\n" +
+ " )\n" +
+ " AND TO_DATE(\n" +
+ " NVL(\n" +
+ " '" + formattedEnd + "',\n" +
+ " TO_CHAR(SYSDATE, 'YYYY-MM-DD') || ' 23:59:59'\n" +
+ " ),\n" +
+ " 'YYYY-MM-DD HH24:MI:SS'\n" +
+ " )\n" +
+ " )");
+ }
+ return sqlBuilder.toString();
+ }
+
+}
diff --git a/jeelowcode-module/jeelowcode-module-biz/src/main/java/com/jeelowcode/module/biz/mapper/AlibabaWorkOrderMapper.java b/jeelowcode-module/jeelowcode-module-biz/src/main/java/com/jeelowcode/module/biz/mapper/AlibabaWorkOrderMapper.java
new file mode 100644
index 0000000..ee2dcae
--- /dev/null
+++ b/jeelowcode-module/jeelowcode-module-biz/src/main/java/com/jeelowcode/module/biz/mapper/AlibabaWorkOrderMapper.java
@@ -0,0 +1,32 @@
+package com.jeelowcode.module.biz.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.jeelowcode.module.biz.entity.AlibabaWorkOrder;
+import com.jeelowcode.tool.framework.mybatis.core.query.LambdaQueryWrapperX;
+
+import java.time.LocalDateTime;
+
+/**
+ * 阿里工单Mapper
+ */
+public interface AlibabaWorkOrderMapper extends BaseMapper {
+
+ /**
+ * 根据工单ID查询工单信息
+ *
+ * @param workOrderId 工单ID
+ * @return 工单信息
+ */
+ default AlibabaWorkOrder selectByWorkOrderId(String workOrderId) {
+ return selectOne(new LambdaQueryWrapperX()
+ .eq(AlibabaWorkOrder::getWorkOrderId, workOrderId));
+ }
+
+ /**
+ * 查询最大更新时间
+ *
+ * @return 最大更新时间
+ */
+ LocalDateTime selectMaxUpdateTime();
+
+}
diff --git a/jeelowcode-module/jeelowcode-module-biz/src/main/java/com/jeelowcode/module/biz/service/IAlibabaWorkOrderService.java b/jeelowcode-module/jeelowcode-module-biz/src/main/java/com/jeelowcode/module/biz/service/IAlibabaWorkOrderService.java
new file mode 100644
index 0000000..6e675ae
--- /dev/null
+++ b/jeelowcode-module/jeelowcode-module-biz/src/main/java/com/jeelowcode/module/biz/service/IAlibabaWorkOrderService.java
@@ -0,0 +1,36 @@
+package com.jeelowcode.module.biz.service;
+
+import com.jeelowcode.module.biz.entity.AlibabaWorkOrder;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 阿里工单服务
+ */
+public interface IAlibabaWorkOrderService {
+
+ /**
+ * 保存阿里工单
+ *
+ * @param workOrder 阿里工单
+ * @return 影响行数
+ */
+ int saveWorkOrder(AlibabaWorkOrder workOrder);
+
+ /**
+ * 批量保存阿里工单
+ *
+ * @param workOrders 阿里工单
+ * @return 影响行数
+ */
+ int batchSaveWorkOrder(List workOrders);
+
+ /**
+ * 获取最大工单时间
+ *
+ * @return 最大工单时间
+ */
+ LocalDateTime getMaxWorkOrderTime();
+
+}
diff --git a/jeelowcode-module/jeelowcode-module-biz/src/main/java/com/jeelowcode/module/biz/service/impl/AlibabaWorkOrderServiceImpl.java b/jeelowcode-module/jeelowcode-module-biz/src/main/java/com/jeelowcode/module/biz/service/impl/AlibabaWorkOrderServiceImpl.java
new file mode 100644
index 0000000..0fc5069
--- /dev/null
+++ b/jeelowcode-module/jeelowcode-module-biz/src/main/java/com/jeelowcode/module/biz/service/impl/AlibabaWorkOrderServiceImpl.java
@@ -0,0 +1,97 @@
+package com.jeelowcode.module.biz.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.IdUtil;
+import com.jeelowcode.module.biz.entity.AlibabaWorkOrder;
+import com.jeelowcode.module.biz.mapper.AlibabaWorkOrderMapper;
+import com.jeelowcode.module.biz.service.IAlibabaWorkOrderService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 阿里工单服务实现类
+ *
+ * @author yangchenjj
+ */
+@Slf4j
+@Service
+public class AlibabaWorkOrderServiceImpl implements IAlibabaWorkOrderService {
+
+ @Resource
+ private AlibabaWorkOrderMapper baseMapper;
+
+ /**
+ * 保存工单信息
+ * 根据工单对象的状态和数据库中的现有记录,决定执行插入或更新操作
+ *
+ * @param workOrder 工单对象,包含要保存的工单信息
+ * @return 返回操作影响的行数,0表示保存失败
+ */
+ @Override
+ public int saveWorkOrder(AlibabaWorkOrder workOrder) {
+ // 检查工单对象及工单ID是否为空
+ if (Objects.isNull(workOrder)) return 0;
+ if (Objects.isNull(workOrder.getWorkOrderId())) return 0;
+
+ // 判断工单ID是否为空,为空表示新增工单
+ if (Objects.isNull(workOrder.getId())) {
+ // 先检查工单id是否有重复
+ AlibabaWorkOrder dbWorkOrder = baseMapper.selectByWorkOrderId(workOrder.getWorkOrderId());
+ if (Objects.nonNull(dbWorkOrder)) {
+ // 如果工单已存在,则更新工单信息
+ return baseMapper.updateById(workOrder.setId(dbWorkOrder.getId()));
+ } else {
+ // 如果工单不存在,则插入新的工单信息
+ return baseMapper.insert(workOrder.setId(IdUtil.getSnowflakeNextId()));
+ }
+ } else {
+ // 工单ID不为空,可能是更新操作
+
+ // 先确定数据库中是否有工单信息,如果有则更新,如果没有则插入
+ AlibabaWorkOrder dbWorkOrder1 = baseMapper.selectById(workOrder.getId());
+ AlibabaWorkOrder dbWorkOrder2 = baseMapper.selectByWorkOrderId(workOrder.getWorkOrderId());
+
+ // dbWorkOrder2优先,如果dbWorkOrder2不为空则尽量使用dbWorkOrder2
+ if (Objects.isNull(dbWorkOrder2) && Objects.isNull(dbWorkOrder1)) {
+ // 如果dbWorkOrder1和dbWorkOrder2都为空,则插入
+ return baseMapper.insert(workOrder);
+ } else {
+ // 如果dbWorkOrder1和dbWorkOrder2至少有一个不为空
+ if (Objects.nonNull(dbWorkOrder2)) {
+ workOrder.setId(dbWorkOrder2.getId());
+ } else {
+ workOrder.setId(dbWorkOrder1.getId());
+ }
+ // 更新工单信息
+ return baseMapper.updateById(workOrder);
+ }
+ }
+ }
+
+ /**
+ * 批量保存工单信息
+ * 使用并行流处理多个工单的保存操作,提高处理效率
+ *
+ * @param workOrders 工单对象列表
+ * @return 返回成功保存的工单数量
+ */
+ @Override
+ public int batchSaveWorkOrder(List workOrders) {
+ // 检查工单列表是否为空
+ if (CollUtil.isEmpty(workOrders)) return 0;
+ // 使用并行流处理每个工单,并统计成功保存的工单数量
+ return workOrders.parallelStream()
+ .mapToInt(this::saveWorkOrder).sum();
+ }
+
+ @Override
+ public LocalDateTime getMaxWorkOrderTime() {
+ return baseMapper.selectMaxUpdateTime();
+ }
+
+}
diff --git a/jeelowcode-module/jeelowcode-module-biz/src/main/resources/mapper/AlibabaWorkOrderMapper.xml b/jeelowcode-module/jeelowcode-module-biz/src/main/resources/mapper/AlibabaWorkOrderMapper.xml
new file mode 100644
index 0000000..9f490fc
--- /dev/null
+++ b/jeelowcode-module/jeelowcode-module-biz/src/main/resources/mapper/AlibabaWorkOrderMapper.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jeelowcode-tool/tool-spring-boot-starter-test/src/main/java/com/jeelowcode/tool/framework/test/core/ut/BaseDbAndRedisUnitTest.java b/jeelowcode-tool/tool-spring-boot-starter-test/src/main/java/com/jeelowcode/tool/framework/test/core/ut/BaseDbAndRedisUnitTest.java
index d25b761..7ccf8f8 100644
--- a/jeelowcode-tool/tool-spring-boot-starter-test/src/main/java/com/jeelowcode/tool/framework/test/core/ut/BaseDbAndRedisUnitTest.java
+++ b/jeelowcode-tool/tool-spring-boot-starter-test/src/main/java/com/jeelowcode/tool/framework/test/core/ut/BaseDbAndRedisUnitTest.java
@@ -16,13 +16,13 @@ import org.springframework.test.context.jdbc.Sql;
/**
* 依赖内存 DB + Redis 的单元测试
- *
+ *
* 相比 {@link BaseDbUnitTest} 来说,额外增加了内存 Redis
*
* @author 芋道源码
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbAndRedisUnitTest.Application.class)
-@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
+@ActiveProfiles("local") // 设置使用 application-unit-test 配置文件
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB
public class BaseDbAndRedisUnitTest {