From af3ec57db40f80fba5903aaedae5e66bf46a7ed9 Mon Sep 17 00:00:00 2001
From: 陈昶聿 <chychen@nbjetron.com>
Date: 星期三, 01 七月 2026 10:57:18 +0800
Subject: [PATCH] 【景宁】任务组开发

---
 smartor/src/main/java/com/smartor/domain/ServiceSubtask.java                       |   26 
 smartor/src/main/java/com/smartor/domain/ServiceTaskdept.java                      |   12 
 smartor/src/main/java/com/smartor/domain/ServiceTaskVO.java                        |   12 
 smartor/src/main/resources/mapper/smartor/ServiceOutPathMapper.xml                 |    2 
 smartor/src/main/java/com/smartor/mapper/ServiceTaskScheduleMapper.java            |   59 ++
 smartor/src/main/resources/mapper/smartor/ServiceSubtaskMapper.xml                 |   39 +
 smartor/src/main/resources/mapper/smartor/ServiceTaskScheduleDetailMapper.xml      |  186 ++++++
 smartor/src/main/resources/mapper/smartor/ServiceTaskScheduleMapper.xml            |  176 ++++++
 smartor/src/main/java/com/smartor/domain/AppendPatientsReq.java                    |   21 
 smartor/src/main/java/com/smartor/domain/ServiceTaskSchedule.java                  |  118 ++++
 smartor/src/main/java/com/smartor/service/IServiceTaskScheduleService.java         |   60 ++
 smartor/src/main/resources/mapper/smartor/PatMedInhospMapper.xml                   |    7 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java                       |   49 +
 smartor/src/main/java/com/smartor/domain/PatMedInhosp.java                         |    7 
 smartor/src/main/java/com/smartor/domain/ServiceTaskScheduleVO.java                |   28 
 smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskServiceImpl.java      |  148 +++++
 smartor/src/main/java/com/smartor/domain/ServiceTaskScheduleDetail.java            |  102 +++
 smartor/src/main/java/com/smartor/domain/ServiceTaskdiag.java                      |   12 
 smartor/src/main/java/com/smartor/mapper/ServiceSubtaskMapper.java                 |   17 
 smartor/src/main/java/com/smartor/service/impl/ServiceTaskScheduleServiceImpl.java |  356 ++++++++++++
 smartor/src/main/java/com/smartor/mapper/ServiceTaskScheduleDetailMapper.java      |   67 ++
 smartor/src/main/java/com/smartor/common/ScheduleSubtaskBuilder.java               |  193 ++++++
 22 files changed, 1,679 insertions(+), 18 deletions(-)

diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java
index c5e07e7..3621b81 100644
--- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java
@@ -179,6 +179,9 @@
     @Autowired
     private SysConfigMapper sysConfigMapper;
 
+    @Autowired
+    private IServiceTaskScheduleService serviceTaskScheduleService;
+
     public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i) {
         System.out.println(StringUtils.format("鎵ц澶氬弬鏂规硶锛� 瀛楃涓茬被鍨媨}锛屽竷灏旂被鍨媨}锛岄暱鏁村瀷{}锛屾诞鐐瑰瀷{}锛屾暣褰}", s, b, l, d, i));
     }
@@ -559,20 +562,20 @@
         }
     }
 
-    /**
-     * 澶勭悊鎮h�呬俊鎭紝杩涘叆瀛愪换鍔¤〃
-     */
-    public void dealOutHospInfoByDimension() {
-        String config = configService.selectConfigByKey("visit.early.day");
-        if (port.equals("8095")) {
-            //鍑洪櫌琛�
-            iPatMedInhospService.dealOutHospInfoByDimension(config);
-            //闂ㄨ瘖琛�(鏂板崕鐨勬殏鏃朵笉鍋氶棬璇婇殢璁�)
-            if (!active.equals("xh")) {
-                //iPatMedOuthospService.dealOutpatientInfo();
-            }
-        }
-    }
+//    /**
+//     * 澶勭悊鎮h�呬俊鎭紝杩涘叆瀛愪换鍔¤〃
+//     */
+//    public void dealOutHospInfoByDimension() {
+//        String config = configService.selectConfigByKey("visit.early.day");
+//        if (port.equals("8095")) {
+//            //鍑洪櫌琛�
+//            iPatMedInhospService.dealOutHospInfoByDimension(config);
+//            //闂ㄨ瘖琛�(鏂板崕鐨勬殏鏃朵笉鍋氶棬璇婇殢璁�)
+//            if (!active.equals("xh")) {
+//                //iPatMedOuthospService.dealOutpatientInfo();
+//            }
+//        }
+//    }
 
 
     /**
@@ -1497,4 +1500,22 @@
         int result = QwenLLMUtil.matchRegex(questionText, voiceText, value, regexText);
         return result;
     }
+
+    /**
+     * 浠诲姟缁勶紙appltype=5锛夊惊鐜换鍔℃粴鍔ㄨ皟搴︺��
+     * <p>
+     * 鎵弿鎵�鏈� schedule_style=1 鐨勮鍒掞紝鑻ユ湰杞� subtask 鍏ㄩ儴 finalize锛坰endstate 鈭� 1/2/3锛夛紝
+     * 鍒欏厠闅嗘湰杞偅鑰呭埌涓嬩竴杞紝鎺ㄨ繘 current_loop_count 涓� visit_time銆�
+     * <p>
+     * 寤鸿姣忓皬鏃舵垨姣忓崐灏忔椂鎵ц涓�娆★紝閬垮厤杞绉帇銆�
+     */
+    public void rollTaskGroupCycle() {
+        log.info("銆恟ollTaskGroupCycle銆戝紑濮嬫壂鎻忓惊鐜换鍔$粍");
+        try {
+            int rolled = serviceTaskScheduleService.rollCycleTasks();
+            log.info("銆恟ollTaskGroupCycle銆戞湰娆℃帹杩� {} 涓鍒�", rolled);
+        } catch (Exception e) {
+            log.error("銆恟ollTaskGroupCycle銆戞粴鍔ㄨ皟搴﹀紓甯�", e);
+        }
+    }
 }
diff --git a/smartor/src/main/java/com/smartor/common/ScheduleSubtaskBuilder.java b/smartor/src/main/java/com/smartor/common/ScheduleSubtaskBuilder.java
new file mode 100644
index 0000000..9c89fc5
--- /dev/null
+++ b/smartor/src/main/java/com/smartor/common/ScheduleSubtaskBuilder.java
@@ -0,0 +1,193 @@
+package com.smartor.common;
+
+import com.alibaba.fastjson2.JSON;
+import com.ruoyi.common.exception.base.BaseException;
+import com.ruoyi.common.utils.DateUtils;
+import com.smartor.domain.*;
+import com.smartor.mapper.ServiceSubtaskMapper;
+import com.smartor.mapper.ServiceSubtaskPreachformMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 浠诲姟缁勶紙appltype=5锛塻ubtask 鐢熸垚鍣�
+ * <p>
+ * 鍗曚竴鑱岃矗锛氭牴鎹� schedule + detailList 涓烘瘡涓偅鑰呮淳鐢� subtask銆�
+ * 鍚屾椂鏈嶅姟涓ゆ潯璺緞锛�
+ * (a) 鍓嶇鎵嬪姩鍕鹃�夋偅鑰咃紙insertOrUpdateTask锛� 鈥斺�� 鏃� 6 鍙傚叆鍙o紱
+ * (b) HIS 鑷姩绾冲叆鏂板嚭闄㈡偅鑰咃紙PatMedInhospServiceImpl.writeInSubTask锛� 鈥斺�� 鏂� 5 鍙傚叆鍙c��
+ * 涓ゆ潯璺緞缁熶竴浠� baseSubtask 涓婂彇鍩哄噯鏃ュ瓧娈碉紝閬垮厤鐢熸垚鍣ㄤ笌 PatTaskRelevance/ServiceTaskVO 鑰﹀悎銆�
+ *
+ * @author smartor
+ * @date 2026-06-30
+ */
+@Slf4j
+@Component
+public class ScheduleSubtaskBuilder {
+
+    @Autowired
+    private ServiceSubtaskMapper serviceSubtaskMapper;
+
+    @Autowired
+    private ServiceSubtaskPreachformMapper serviceSubtaskPreachformMapper;
+
+    /**
+     * 涓哄崟涓偅鑰呮淳鐢� subtask锛坅ppltype=5锛夆�斺�� 瑙h�﹀叆鍙o紙鑷姩绾冲叆 & 鎵嬪姩鍕鹃�夌粺涓�璧拌繖閲岋級銆�
+     * <p>
+     * 闈炲惊鐜細鎸� detailList 涓�娆℃�ч摵骞� N 鏉★紱
+     * 寰幆  锛氭寜 schedule.currentLoopCount 鐢熸垚褰撳墠杞� N 鏉★紝鍚庣画杞鐢辫皟搴﹀櫒婊氬姩寤恒��
+     *
+     * @param serviceTask     涓讳换鍔★紙宸茶惤搴擄級
+     * @param schedule        宸茶惤搴撶殑涓昏鍒�
+     * @param detailList      鏄庣粏鍒楄〃锛堟寜 seq 鍗囧簭锛�
+     * @param baseSubtask     妯℃澘 subtask锛堝凡濉厖鎮h�呭熀纭�淇℃伅 + 鍩哄噯鏃ュ瓧娈碉級锛屾柟娉曞唴浼氭繁鎷疯礉
+     * @param preachformList  鍙戦�佹柟寮忥紙璋冪敤鏂瑰凡瑙f瀽濂斤紝鍙负绌猴級
+     */
+    public void buildSubtasksForPatient(ServiceTask serviceTask,
+                                        ServiceTaskSchedule schedule,
+                                        List<ServiceTaskScheduleDetail> detailList,
+                                        ServiceSubtask baseSubtask,
+                                        List<Map<String, Object>> preachformList) {
+        if (CollectionUtils.isEmpty(detailList)) {
+            throw new BaseException("浠诲姟缁勬槑缁嗕负绌猴紝鏃犳硶鐢熸垚 subtask, taskid=" + serviceTask.getTaskid());
+        }
+
+        Date benchmark = resolveBenchmark(schedule, baseSubtask);
+        long currentLoop = schedule.getCurrentLoopCount() == null ? 0L : schedule.getCurrentLoopCount();
+
+        for (ServiceTaskScheduleDetail detail : detailList) {
+            ServiceSubtask subtask = copyFromBase(baseSubtask);
+
+            // 鍥炴函瀛楁
+            subtask.setScheduleId(schedule.getId());
+            subtask.setScheduleDetailId(detail.getId());
+            subtask.setScheduleSeq(detail.getSeq());
+            subtask.setLoopCount(currentLoop);
+
+            // 妯℃澘锛歞etail 浼樺厛浜� task
+            if (detail.getTemplateid() != null) {
+                subtask.setTemplateid(detail.getTemplateid());
+                subtask.setTemplatename(detail.getTemplatename());
+            }
+            if (detail.getLibtemplateid() != null) {
+                subtask.setLibtemplateid(detail.getLibtemplateid());
+            }
+
+            // 鏃堕棿锛氬熀鍑嗘棩 + dayOffset
+            Date visitTime = addDays(benchmark, detail.getDayOffset());
+            subtask.setVisitTime(visitTime);
+            subtask.setSenddate(visitTime);
+            subtask.setLongSendTime(visitTime);
+
+            // 鐘舵�侊細涓庡師娴佺▼涓�鑷达紝鏂板缓鍚庣疆涓哄緟鍙戦��
+            subtask.setSendstate(2L);
+            subtask.setCreateTime(DateUtils.getNowDate());
+            subtask.setUpdateTime(DateUtils.getNowDate());
+
+            serviceSubtaskMapper.insertServiceSubtask(subtask);
+
+            insertPreachform(serviceTask, preachformList, subtask);
+
+            log.info("[浠诲姟缁刔 taskid={} patid={} 鐢熸垚 subtask id={} seq={} dayOffset={} visitTime={}",
+                    serviceTask.getTaskid(), baseSubtask.getPatid(),
+                    subtask.getId(), detail.getSeq(), detail.getDayOffset(), visitTime);
+        }
+    }
+
+    /**
+     * 鍏煎鍏ュ彛锛氬墠绔墜鍔ㄥ嬀閫夋偅鑰咃紙insertOrUpdateTask锛夈��
+     * 鎶� patTaskRelevance 鐨勫熀鍑嗘棩瀛楁鍥炲~鍒� baseSubtask 鍚庯紝濮旀墭缁欒В鑰﹀叆鍙c��
+     */
+    public void buildSubtasksForPatient(ServiceTask serviceTask,
+                                        ServiceTaskVO serviceTaskVO,
+                                        ServiceTaskSchedule schedule,
+                                        List<ServiceTaskScheduleDetail> detailList,
+                                        PatTaskRelevance patTaskRelevance,
+                                        ServiceSubtask baseSubtask) {
+        // 鍥炲~鍩哄噯鏃ュ瓧娈碉紙鎵嬪姩璺緞鐨� baseSubtask 鐢� VO 鎷艰锛屽熀鍑嗘棩瀛楁鍙兘鏈~鍏級
+        if (baseSubtask.getStarttime() == null) {
+            baseSubtask.setStarttime(patTaskRelevance.getInhosptime() != null
+                    ? patTaskRelevance.getInhosptime() : patTaskRelevance.getStarttime());
+        }
+        if (baseSubtask.getEndtime() == null) {
+            baseSubtask.setEndtime(patTaskRelevance.getEndtime());
+        }
+        if (baseSubtask.getAdmindate() == null) {
+            baseSubtask.setAdmindate(patTaskRelevance.getVisittime());
+        }
+        buildSubtasksForPatient(serviceTask, schedule, detailList, baseSubtask,
+                serviceTaskVO.getPreachformList());
+    }
+
+    /**
+     * 鏍规嵁 scheduleBenchmark 瑙f瀽鍩哄噯鏃堕棿锛�
+     * 0-鑷畾涔夋椂闂� 1-鍏ラ櫌鏃堕棿 2-鍑洪櫌鏃堕棿 3-灏辫瘖鏃堕棿 4-鍏ョ粍鏃堕棿
+     * 鍏ㄩ儴浠� baseSubtask 鍙栵紝鍙栦笉鍒板厹搴曞綋鍓嶆椂闂淬��
+     */
+    private Date resolveBenchmark(ServiceTaskSchedule schedule, ServiceSubtask base) {
+        Integer benchmark = schedule.getScheduleBenchmark() == null ? 0 : schedule.getScheduleBenchmark();
+        Date now = DateUtils.getNowDate();
+        switch (benchmark) {
+            case 1:
+                return base.getStarttime() != null ? base.getStarttime() : now;
+            case 2:
+                return base.getEndtime() != null ? base.getEndtime() : now;
+            case 3:
+                return base.getAdmindate() != null ? base.getAdmindate() : now;
+            case 4:
+                return base.getCreateTime() != null ? base.getCreateTime() : now;
+            case 0:
+            default:
+                return schedule.getScheduleBenchmarkCustomTime() != null
+                        ? schedule.getScheduleBenchmarkCustomTime() : now;
+        }
+    }
+
+    private Date addDays(Date base, int dayOffset) {
+        Calendar c = Calendar.getInstance();
+        c.setTime(base);
+        c.add(Calendar.DAY_OF_MONTH, dayOffset);
+        return c.getTime();
+    }
+
+    /**
+     * 鍐� service_subtask_preachform锛屽彂閫佹柟寮忕敱璋冪敤鏂瑰濂姐��
+     */
+    private void insertPreachform(ServiceTask serviceTask, List<Map<String, Object>> pfList, ServiceSubtask subtask) {
+        if (CollectionUtils.isEmpty(pfList)) {
+            return;
+        }
+        for (Map<String, Object> map : pfList) {
+            ServiceSubtaskPreachform pf = new ServiceSubtaskPreachform();
+            pf.setSort(ObjectUtils.isEmpty(map.get("sort")) ? 0 : Long.valueOf(map.get("sort").toString()));
+            pf.setPreachform(ObjectUtils.isEmpty(map.get("preachform")) ? "" : map.get("preachform").toString());
+            pf.setCompensateTime(ObjectUtils.isEmpty(map.get("compensateTime")) ? "" : map.get("compensateTime").toString());
+            pf.setTaskid(serviceTask.getTaskid());
+            pf.setSubid(subtask.getId());
+            pf.setSendstate("1");
+            pf.setOrgid(serviceTask.getOrgid());
+            pf.setCreateTime(new Date());
+            serviceSubtaskPreachformMapper.insertServiceSubtaskPreachform(pf);
+        }
+    }
+
+    /**
+     * 娣辨嫹璐� baseSubtask 鍒颁竴涓柊瀵硅薄锛堟瘡娆� insert 鍚� id 浼氬洖鍐欙紝蹇呴』鐙珛瀹炰緥锛夈��
+     * 鐢� fastjson 鍋氭繁鎷疯礉锛岀渷涓�鍫� set銆�
+     */
+    private ServiceSubtask copyFromBase(ServiceSubtask base) {
+        String json = JSON.toJSONString(base);
+        ServiceSubtask copy = JSON.parseObject(json, ServiceSubtask.class);
+        // 涓婚敭銆乮d 蹇呴』娓呮帀锛屽惁鍒� useGeneratedKeys 浠嶄細鐢ㄤ笂杞殑 id 瑙﹀彂涓婚敭鍐茬獊
+        copy.setId(null);
+        return copy;
+    }
+}
diff --git a/smartor/src/main/java/com/smartor/domain/AppendPatientsReq.java b/smartor/src/main/java/com/smartor/domain/AppendPatientsReq.java
new file mode 100644
index 0000000..9205eca
--- /dev/null
+++ b/smartor/src/main/java/com/smartor/domain/AppendPatientsReq.java
@@ -0,0 +1,21 @@
+package com.smartor.domain;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 鍚戝凡鏈変换鍔¤拷鍔犳偅鑰呯殑璇锋眰浣擄紙鐢ㄤ簬鎷嗗寘瑙e喅413闂锛�
+ */
+@Data
+@ApiModel(value = "AppendPatientsReq", description = "鍚戝凡鏈変换鍔¤拷鍔犳偅鑰�")
+public class AppendPatientsReq {
+
+    @ApiModelProperty(value = "浠诲姟ID锛坕nsertOrUpdateTask 绗竴娆¤皟鐢ㄥ悗杩斿洖鐨� taskId锛�", required = true)
+    private Long taskId;
+
+    @ApiModelProperty(value = "鏈壒娆℃偅鑰呭垪琛�")
+    private List<PatTaskRelevance> patTaskRelevances;
+}
diff --git a/smartor/src/main/java/com/smartor/domain/PatMedInhosp.java b/smartor/src/main/java/com/smartor/domain/PatMedInhosp.java
index 14ec743..7957362 100644
--- a/smartor/src/main/java/com/smartor/domain/PatMedInhosp.java
+++ b/smartor/src/main/java/com/smartor/domain/PatMedInhosp.java
@@ -309,6 +309,13 @@
     private String diagcheckFlag;
 
     /**
+     * 浠诲姟缁勶紙appltype=5锛夐杞撼鍏ユ爣璁帮細0鏈鐞�  1宸茬敓鎴愰杞�  2鏃犲尮閰�/澶辫触
+     * 鐙珛浜� dept/ward/diag check_flag锛屼笓渚涗换鍔$粍鐙珛鎵弿浣跨敤锛涘悗缁疆娆$敱 rollTaskGroupCycle 璐熻矗銆�
+     */
+    @ApiModelProperty(value = "浠诲姟缁�(appltype=5)棣栬疆绾冲叆鏍囪锛�0鏈鐞� 1宸茬敓鎴愰杞� 2鏃犲尮閰�/澶辫触")
+    private String scheduleCheckFlag;
+
+    /**
      * 鍏ラ櫌绉戝闀挎湡浠诲姟锛屾槸鍚﹀鐞嗭細0鏈鐞�   1澶勭悊
      */
     @ApiModelProperty(value = "鍏ラ櫌绉戝闀挎湡浠诲姟锛屾槸鍚﹀鐞嗭細0鏈鐞�   1澶勭悊鎴愬姛    2澶勭悊澶辫触 ")
diff --git a/smartor/src/main/java/com/smartor/domain/ServiceSubtask.java b/smartor/src/main/java/com/smartor/domain/ServiceSubtask.java
index e4d37cf..7e494bd 100644
--- a/smartor/src/main/java/com/smartor/domain/ServiceSubtask.java
+++ b/smartor/src/main/java/com/smartor/domain/ServiceSubtask.java
@@ -722,4 +722,30 @@
     @JsonFormat(pattern = "yyyy-MM-dd")
     @ApiModelProperty(value = "缁撴潫鍙戦�佹棩鏈�")
     private Date endSendDateTime;
+
+    // ===================== 浠诲姟缁勶紙appltype=5锛夊洖婧瓧娈� =====================
+
+    /**
+     * 鍏宠仈 service_task_schedule.id锛坅ppltype=5 鏃朵娇鐢級
+     */
+    @ApiModelProperty(value = "鍏宠仈 service_task_schedule.id锛坅ppltype=5锛�")
+    private Long scheduleId;
+
+    /**
+     * 鍏宠仈 service_task_schedule_detail.id
+     */
+    @ApiModelProperty(value = "鍏宠仈 service_task_schedule_detail.id")
+    private Long scheduleDetailId;
+
+    /**
+     * 鏈潯 subtask 鍦ㄨ鍒掍腑鐨勯『搴忓彿
+     */
+    @ApiModelProperty(value = "鏈潯 subtask 鍦ㄨ鍒掍腑鐨勯『搴忓彿")
+    private Integer scheduleSeq;
+
+    /**
+     * 鎵�鍦ㄥ惊鐜疆娆★紙闈炲惊鐜换鍔′负0锛�
+     */
+    @ApiModelProperty(value = "鎵�鍦ㄥ惊鐜疆娆★紙闈炲惊鐜换鍔′负0锛�")
+    private Long loopCount;
 }
diff --git a/smartor/src/main/java/com/smartor/domain/ServiceTaskSchedule.java b/smartor/src/main/java/com/smartor/domain/ServiceTaskSchedule.java
new file mode 100644
index 0000000..76914bc
--- /dev/null
+++ b/smartor/src/main/java/com/smartor/domain/ServiceTaskSchedule.java
@@ -0,0 +1,118 @@
+package com.smartor.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.BaseEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 闅忚浠诲姟璁″垝涓昏〃 service_task_schedule
+ * <p>
+ * 鐢ㄤ簬 appltype=5锛堟寜浠诲姟缁勶級锛氫竴涓� service_task 瀵瑰簲涓�鏉� schedule + 澶氭潯 detail锛�
+ * 鍚屼竴鍑洪櫌璁板綍鎹鍙敓鎴愬涓� service_subtask銆�
+ *
+ * @author smartor
+ * @date 2026-06-30
+ */
+@Data
+@ApiModel(value = "ServiceTaskSchedule", description = "闅忚浠诲姟璁″垝涓昏〃")
+public class ServiceTaskSchedule extends BaseEntity {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭
+     */
+    @ApiModelProperty("涓婚敭id")
+    private Long id;
+
+    /**
+     * 鍏宠仈 service_task.taskid
+     */
+    @ApiModelProperty("鍏宠仈 service_task.taskid")
+    @Excel(name = "浠诲姟id")
+    private Long taskid;
+
+    /**
+     * 鏈�澶у惊鐜鏁帮紙寰幆浠诲姟鐢紝NULL鎴�0琛ㄧず鏃犻檺锛�
+     */
+    @ApiModelProperty("鏈�澶у惊鐜鏁帮紙寰幆浠诲姟鐢紝NULL鎴�0琛ㄧず鏃犻檺锛�")
+    private Long maxLoopCount;
+
+    /**
+     * 褰撳墠宸插畬鎴愬惊鐜鏁�
+     */
+    @ApiModelProperty("褰撳墠宸插畬鎴愬惊鐜鏁�")
+    private Long currentLoopCount;
+
+    /**
+     * 褰撳墠鎵ц椤哄簭鍙�
+     */
+    @ApiModelProperty("褰撳墠鎵ц椤哄簭鍙�")
+    private Integer currentSeq;
+
+    /**
+     * 涓嬩竴鎵ц椤哄簭鍙�
+     */
+    @ApiModelProperty("涓嬩竴鎵ц椤哄簭鍙�")
+    private Integer nextSeq;
+
+    /**
+     * 鎵ц鍛ㄦ湡鏂瑰紡(1-寰幆 0-闈炲惊鐜�)
+     */
+    @ApiModelProperty("鎵ц鍛ㄦ湡鏂瑰紡(1-寰幆 0-闈炲惊鐜�)")
+    private Integer scheduleStyle;
+
+    /**
+     * 寰幆鏃堕棿鏁�
+     */
+    @ApiModelProperty("寰幆鏃堕棿鏁�")
+    private Integer scheduleTimeNum;
+
+    /**
+     * 寰幆鏃堕棿鍗曚綅; 1-澶� 2-鍛� 3-鏈� 4-骞�
+     */
+    @ApiModelProperty("寰幆鏃堕棿鍗曚綅; 1-澶� 2-鍛� 3-鏈� 4-骞�")
+    private Integer scheduleTimeUnit;
+
+    /**
+     * 涓嬫鎵ц鏃堕棿
+     */
+    @ApiModelProperty("涓嬫鎵ц鏃堕棿")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date nextExecuteTime;
+
+    /**
+     * 鍛ㄦ湡鍩哄噯绫诲瀷; 0-鑷畾涔夋椂闂� 1-鍏ラ櫌鏃堕棿 2-鍑洪櫌鏃堕棿 3-灏辫瘖鏃堕棿 4-鍏ョ粍鏃堕棿
+     */
+    @ApiModelProperty("鍛ㄦ湡鍩哄噯绫诲瀷; 0-鑷畾涔夋椂闂� 1-鍏ラ櫌鏃堕棿 2-鍑洪櫌鏃堕棿 3-灏辫瘖鏃堕棿 4-鍏ョ粍鏃堕棿")
+    private Integer scheduleBenchmark;
+
+    /**
+     * 鍛ㄦ湡鍩哄噯鑷畾涔夋椂闂�
+     */
+    @ApiModelProperty("鍛ㄦ湡鍩哄噯鑷畾涔夋椂闂�")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date scheduleBenchmarkCustomTime;
+
+    /**
+     * 鍒犻櫎鏍囪锛�0瀛樺湪 1鍒犻櫎锛�
+     */
+    @ApiModelProperty("鍒犻櫎鏍囪锛�0瀛樺湪 1鍒犻櫎锛�")
+    private String delFlag;
+
+    /**
+     * 鏈烘瀯ID
+     */
+    @ApiModelProperty("鏈烘瀯ID")
+    private String orgid;
+
+    /**
+     * 闄㈠尯ID
+     */
+    @ApiModelProperty("闄㈠尯ID")
+    private String campusid;
+}
diff --git a/smartor/src/main/java/com/smartor/domain/ServiceTaskScheduleDetail.java b/smartor/src/main/java/com/smartor/domain/ServiceTaskScheduleDetail.java
new file mode 100644
index 0000000..9348d58
--- /dev/null
+++ b/smartor/src/main/java/com/smartor/domain/ServiceTaskScheduleDetail.java
@@ -0,0 +1,102 @@
+package com.smartor.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.BaseEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 闅忚浠诲姟璁″垝鏄庣粏琛� service_task_schedule_detail
+ *
+ * @author smartor
+ * @date 2026-06-30
+ */
+@Data
+@ApiModel(value = "ServiceTaskScheduleDetail", description = "闅忚浠诲姟璁″垝鏄庣粏琛�")
+public class ServiceTaskScheduleDetail extends BaseEntity {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭
+     */
+    @ApiModelProperty("涓婚敭")
+    private Long id;
+
+    /**
+     * 鍏宠仈 service_task.taskid
+     */
+    @ApiModelProperty("鍏宠仈 service_task.taskid")
+    @Excel(name = "浠诲姟id")
+    private Long taskid;
+
+    /**
+     * 鍏宠仈 service_task_schedule.id
+     */
+    @ApiModelProperty("鍏宠仈 service_task_schedule.id")
+    private Long scheduleid;
+
+    /**
+     * 椤哄簭鍙凤紝浠�1寮�濮�
+     */
+    @ApiModelProperty("椤哄簭鍙凤紝浠�1寮�濮�")
+    private Integer seq;
+
+    /**
+     * 闂撮殧澶╂暟锛堝浐瀹氫换鍔★細鐩稿鍩哄噯鏃ワ紱寰幆浠诲姟锛氱浉瀵逛笂涓�娆″畬鎴愭棩锛�
+     */
+    @ApiModelProperty("闂撮殧澶╂暟锛堝浐瀹氫换鍔★細鐩稿鍩哄噯鏃ワ紱寰幆浠诲姟锛氱浉瀵逛笂涓�娆″畬鎴愭棩锛�")
+    private Integer dayOffset;
+
+    /**
+     * 璇ユ浣跨敤鐨勬ā鏉縄D
+     */
+    @ApiModelProperty("璇ユ浣跨敤鐨勬ā鏉縄D")
+    private Long templateid;
+
+    /**
+     * 妯℃澘搴揑D
+     */
+    @ApiModelProperty("妯℃澘搴揑D")
+    private Long libtemplateid;
+
+    /**
+     * 妯℃澘鍚嶇О
+     */
+    @ApiModelProperty("妯℃澘鍚嶇О")
+    private String templatename;
+
+    /**
+     * 璇ユ鑷畾涔夊唴瀹癸紙JSON锛歬cb/jsy/text_param绛夛級
+     */
+    @ApiModelProperty("璇ユ鑷畾涔夊唴瀹癸紙JSON锛歬cb/jsy/text_param绛夛級")
+    private String content;
+
+    /**
+     * 涓嬫鎵ц鏃堕棿
+     */
+    @ApiModelProperty("涓嬫鎵ц鏃堕棿")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date nextExecuteTime;
+
+    /**
+     * 鍒犻櫎鏍囪锛�0瀛樺湪 1鍒犻櫎锛�
+     */
+    @ApiModelProperty("鍒犻櫎鏍囪锛�0瀛樺湪 1鍒犻櫎锛�")
+    private String delFlag;
+
+    /**
+     * 鏈烘瀯ID
+     */
+    @ApiModelProperty("鏈烘瀯ID")
+    private String orgid;
+
+    /**
+     * 闄㈠尯ID
+     */
+    @ApiModelProperty("闄㈠尯ID")
+    private String campusid;
+}
diff --git a/smartor/src/main/java/com/smartor/domain/ServiceTaskScheduleVO.java b/smartor/src/main/java/com/smartor/domain/ServiceTaskScheduleVO.java
new file mode 100644
index 0000000..e9b1fc0
--- /dev/null
+++ b/smartor/src/main/java/com/smartor/domain/ServiceTaskScheduleVO.java
@@ -0,0 +1,28 @@
+package com.smartor.domain;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+/**
+ * 闅忚浠诲姟璁″垝 VO锛堜富璁″垝 + 鏄庣粏鍒楄〃锛�
+ * 鐢ㄤ簬 appltype=5 浠诲姟缁勫垱寤�/淇敼鐨勫叆鍙傘��
+ *
+ * @author smartor
+ * @date 2026-06-30
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "ServiceTaskScheduleVO", description = "闅忚浠诲姟璁″垝")
+public class ServiceTaskScheduleVO extends ServiceTaskSchedule {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 璁″垝鏄庣粏鍒楄〃锛堟寜 seq 鎺掑簭锛�
+     */
+    @ApiModelProperty("璁″垝鏄庣粏鍒楄〃锛堟寜 seq 鎺掑簭锛�")
+    private List<ServiceTaskScheduleDetail> detailList;
+}
diff --git a/smartor/src/main/java/com/smartor/domain/ServiceTaskVO.java b/smartor/src/main/java/com/smartor/domain/ServiceTaskVO.java
index 12ca6d4..b4f8fe6 100644
--- a/smartor/src/main/java/com/smartor/domain/ServiceTaskVO.java
+++ b/smartor/src/main/java/com/smartor/domain/ServiceTaskVO.java
@@ -434,10 +434,10 @@
     @Excel(name = "鎵嬫湳绛夌骇缂栫爜")
     private String oplevelcode;
     /**
-     * 閫傜敤鏉′欢绫诲瀷锛�1銆佹寜绉戝銆�2銆佹寜鐥呭尯銆�3銆佹寜鐤剧梾璇婃柇銆�4銆佹寜鎵嬫湳绫诲瀷
+     * 閫傜敤鏉′欢绫诲瀷锛�1銆佹寜绉戝銆�2銆佹寜鐥呭尯銆�3銆佹寜鐤剧梾璇婃柇銆�4銆佹寜鎵嬫湳绫诲瀷銆�5銆佹寜浠诲姟缁�
      */
-    @ApiModelProperty("閫傜敤鏉′欢绫诲瀷锛�1銆佹寜绉戝銆�2銆佹寜鐥呭尯銆�3銆佹寜鐤剧梾璇婃柇銆�4銆佹寜鎵嬫湳绫诲瀷")
-    @Excel(name = "閫傜敤鏉′欢绫诲瀷锛�1銆佹寜绉戝銆�2銆佹寜鐥呭尯銆�3銆佹寜鐤剧梾璇婃柇銆�4銆佹寜鎵嬫湳绫诲瀷")
+    @ApiModelProperty("閫傜敤鏉′欢绫诲瀷锛�1銆佹寜绉戝銆�2銆佹寜鐥呭尯銆�3銆佹寜鐤剧梾璇婃柇銆�4銆佹寜鎵嬫湳绫诲瀷銆�5銆佹寜浠诲姟缁�")
+    @Excel(name = "閫傜敤鏉′欢绫诲瀷锛�1銆佹寜绉戝銆�2銆佹寜鐥呭尯銆�3銆佹寜鐤剧梾璇婃柇銆�4銆佹寜鎵嬫湳绫诲瀷銆�5銆佹寜浠诲姟缁�")
     private String appltype;
 
     @ApiModelProperty(value = "闅忚閮ㄩ棬缂栫爜")
@@ -452,4 +452,10 @@
     @ApiModelProperty(value = "鐤剧梾鐥呯")
     @Excel(name = " 鐤剧梾鐥呯")
     private String diagType;
+
+    /**
+     * 浠诲姟缁勮鍒掞紙appltype=5 蹇呭~锛屼富璁″垝 + 鏄庣粏鍒楄〃锛�
+     */
+    @ApiModelProperty(value = "浠诲姟缁勮鍒掞紙appltype=5 蹇呭~锛屼富璁″垝+鏄庣粏锛�")
+    private ServiceTaskScheduleVO serviceTaskScheduleVO;
 }
diff --git a/smartor/src/main/java/com/smartor/domain/ServiceTaskdept.java b/smartor/src/main/java/com/smartor/domain/ServiceTaskdept.java
index c2cb6e0..8ced674 100644
--- a/smartor/src/main/java/com/smartor/domain/ServiceTaskdept.java
+++ b/smartor/src/main/java/com/smartor/domain/ServiceTaskdept.java
@@ -84,5 +84,17 @@
     @ApiModelProperty("鍒犻櫎鏍囧織锛�0锛氭湭鍒犻櫎 1锛氬凡鍒犻櫎锛�")
     private String delFlag;
 
+    /**
+     * 鏌ヨ鐢紙闈炶〃瀛楁锛夛細鎺掗櫎鐨� appltype銆備富鎵弿浼� "5" 浠ユ帓闄や换鍔$粍銆�
+     */
+    @ApiModelProperty("鏌ヨ鐢細鎺掗櫎鐨刟ppltype锛堝5锛屼富鎵弿鎺掗櫎浠诲姟缁勶級")
+    private String excludeAppltype;
+
+    /**
+     * 鏌ヨ鐢紙闈炶〃瀛楁锛夛細闄愬畾鐨� appltype銆備换鍔$粍鎵弿浼� "5" 鍙煡浠诲姟缁勩��
+     */
+    @ApiModelProperty("鏌ヨ鐢細闄愬畾鐨刟ppltype锛堝5锛屽彧鏌ヤ换鍔$粍锛�")
+    private String queryAppltype;
+
 }
 
diff --git a/smartor/src/main/java/com/smartor/domain/ServiceTaskdiag.java b/smartor/src/main/java/com/smartor/domain/ServiceTaskdiag.java
index 0b8ad0f..8fd212e 100644
--- a/smartor/src/main/java/com/smartor/domain/ServiceTaskdiag.java
+++ b/smartor/src/main/java/com/smartor/domain/ServiceTaskdiag.java
@@ -135,5 +135,17 @@
     @Excel(name = "鐖禝D")
     private Long pid;
 
+    /**
+     * 鏌ヨ鐢紙闈炶〃瀛楁锛夛細鎺掗櫎鐨� appltype銆備富鎵弿浼� "5" 浠ユ帓闄や换鍔$粍銆�
+     */
+    @ApiModelProperty("鏌ヨ鐢細鎺掗櫎鐨刟ppltype锛堝5锛屼富鎵弿鎺掗櫎浠诲姟缁勶級")
+    private String excludeAppltype;
+
+    /**
+     * 鏌ヨ鐢紙闈炶〃瀛楁锛夛細闄愬畾鐨� appltype銆備换鍔$粍鎵弿浼� "5" 鍙煡浠诲姟缁勩��
+     */
+    @ApiModelProperty("鏌ヨ鐢細闄愬畾鐨刟ppltype锛堝5锛屽彧鏌ヤ换鍔$粍锛�")
+    private String queryAppltype;
+
 }
 
diff --git a/smartor/src/main/java/com/smartor/mapper/ServiceSubtaskMapper.java b/smartor/src/main/java/com/smartor/mapper/ServiceSubtaskMapper.java
index fde930e..812c168 100644
--- a/smartor/src/main/java/com/smartor/mapper/ServiceSubtaskMapper.java
+++ b/smartor/src/main/java/com/smartor/mapper/ServiceSubtaskMapper.java
@@ -68,6 +68,23 @@
     public int insertServiceSubtask(ServiceSubtask ServiceSubtask);
 
     /**
+     * 浠诲姟缁勶紙appltype=5锛夛細缁熻鏌愪竴杞鍐呬粛鍦ㄨ繍琛岀殑 subtask 鏁伴噺銆�
+     * 浠嶅湪杩愯 = sendstate in (1,2,3) 涓旀湭鍒犻櫎銆�
+     * 鐢ㄤ簬鍒ゆ柇鏈疆鏄惁宸插叏閮� finalize锛堟垚鍔�/澶辫触/涓嶆墽琛�/瓒呮椂锛夈��
+     *
+     * @param scheduleId 璁″垝涓婚敭
+     * @param loopCount  杞锛�0=棣栬疆锛�
+     */
+    public int countActiveByScheduleIdAndLoop(@Param("scheduleId") Long scheduleId,
+                                              @Param("loopCount") Long loopCount);
+
+    /**
+     * 浠诲姟缁勶紙appltype=5锛夛細鎷夊彇鏌愪竴杞鐨勫叏閮� subtask锛堢敤浜庡厠闅嗗埌涓嬩竴杞級銆�
+     */
+    public List<ServiceSubtask> selectByScheduleIdAndLoop(@Param("scheduleId") Long scheduleId,
+                                                          @Param("loopCount") Long loopCount);
+
+    /**
      * 闅忚鍙婃椂鐜囷細搴旈殢璁挎椂闂�<瀹為檯闅忚鏃堕棿
      *
      * @param ServiceSubtask
diff --git a/smartor/src/main/java/com/smartor/mapper/ServiceTaskScheduleDetailMapper.java b/smartor/src/main/java/com/smartor/mapper/ServiceTaskScheduleDetailMapper.java
new file mode 100644
index 0000000..de7eb4d
--- /dev/null
+++ b/smartor/src/main/java/com/smartor/mapper/ServiceTaskScheduleDetailMapper.java
@@ -0,0 +1,67 @@
+package com.smartor.mapper;
+
+import com.smartor.domain.ServiceTaskScheduleDetail;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 闅忚浠诲姟璁″垝鏄庣粏 Mapper 鎺ュ彛
+ *
+ * @author smartor
+ * @date 2026-06-30
+ */
+@Mapper
+public interface ServiceTaskScheduleDetailMapper {
+
+    /**
+     * 鏌ヨ鏄庣粏
+     */
+    ServiceTaskScheduleDetail selectServiceTaskScheduleDetailById(Long id);
+
+    /**
+     * 鏌ヨ鏄庣粏鍒楄〃
+     */
+    List<ServiceTaskScheduleDetail> selectServiceTaskScheduleDetailList(ServiceTaskScheduleDetail detail);
+
+    /**
+     * 鎸� scheduleid 鏌ヨ鏄庣粏鍒楄〃锛堟寜 seq 鍗囧簭锛�
+     */
+    List<ServiceTaskScheduleDetail> selectByScheduleid(@Param("scheduleid") Long scheduleid);
+
+    /**
+     * 鎸� taskid 鏌ヨ鏄庣粏鍒楄〃锛堟寜 seq 鍗囧簭锛�
+     */
+    List<ServiceTaskScheduleDetail> selectByTaskid(@Param("taskid") Long taskid);
+
+    /**
+     * 鏂板鏄庣粏
+     */
+    int insertServiceTaskScheduleDetail(ServiceTaskScheduleDetail detail);
+
+    /**
+     * 鎵归噺鏂板鏄庣粏
+     */
+    int batchInsertServiceTaskScheduleDetail(@Param("list") List<ServiceTaskScheduleDetail> list);
+
+    /**
+     * 淇敼鏄庣粏
+     */
+    int updateServiceTaskScheduleDetail(ServiceTaskScheduleDetail detail);
+
+    /**
+     * 閫昏緫鍒犻櫎锛堟寜 id锛�
+     */
+    int deleteServiceTaskScheduleDetailById(Long id);
+
+    /**
+     * 閫昏緫鍒犻櫎锛堟寜 scheduleid锛夛細淇敼浠诲姟鏃舵竻绌烘棫鏄庣粏
+     */
+    int deleteByScheduleid(@Param("scheduleid") Long scheduleid);
+
+    /**
+     * 閫昏緫鍒犻櫎锛堟寜 taskid锛�
+     */
+    int deleteByTaskid(@Param("taskid") Long taskid);
+}
diff --git a/smartor/src/main/java/com/smartor/mapper/ServiceTaskScheduleMapper.java b/smartor/src/main/java/com/smartor/mapper/ServiceTaskScheduleMapper.java
new file mode 100644
index 0000000..59a4703
--- /dev/null
+++ b/smartor/src/main/java/com/smartor/mapper/ServiceTaskScheduleMapper.java
@@ -0,0 +1,59 @@
+package com.smartor.mapper;
+
+import com.smartor.domain.ServiceTaskSchedule;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 闅忚浠诲姟璁″垝涓昏〃 Mapper 鎺ュ彛
+ *
+ * @author smartor
+ * @date 2026-06-30
+ */
+@Mapper
+public interface ServiceTaskScheduleMapper {
+
+    /**
+     * 鏌ヨ璁″垝璇︽儏
+     */
+    ServiceTaskSchedule selectServiceTaskScheduleById(Long id);
+
+    /**
+     * 閫氳繃 taskid 鏌ヨ浠诲姟瀵瑰簲鐨勬湁鏁堣鍒掞紙appltype=5 涓�瀵逛竴锛�
+     */
+    ServiceTaskSchedule selectByTaskid(@Param("taskid") Long taskid);
+
+    /**
+     * 鏌ヨ璁″垝鍒楄〃
+     */
+    List<ServiceTaskSchedule> selectServiceTaskScheduleList(ServiceTaskSchedule serviceTaskSchedule);
+
+    /**
+     * 鏂板璁″垝
+     */
+    int insertServiceTaskSchedule(ServiceTaskSchedule serviceTaskSchedule);
+
+    /**
+     * 淇敼璁″垝
+     */
+    int updateServiceTaskSchedule(ServiceTaskSchedule serviceTaskSchedule);
+
+    /**
+     * 閫昏緫鍒犻櫎锛堟寜 id锛�
+     */
+    int deleteServiceTaskScheduleById(Long id);
+
+    /**
+     * 閫昏緫鍒犻櫎锛堟寜 taskid锛夛細淇敼浠诲姟鏃舵竻绌烘棫璁″垝
+     */
+    int deleteByTaskid(@Param("taskid") Long taskid);
+
+    /**
+     * 浠诲姟缁勶紙appltype=5锛夋粴鍔ㄨ皟搴︾敤锛氭煡璇㈡墍鏈夋湭杈句笂闄愮殑寰幆璁″垝銆�
+     * 鏉′欢锛歴chedule_style=1 涓� (max_loop_count is null or max_loop_count=0 or current_loop_count &lt; max_loop_count)
+     * 闄愬埗杩斿洖鏉℃暟閬垮厤闀夸簨鍔°��
+     */
+    List<ServiceTaskSchedule> selectActiveCycleSchedules(@Param("limit") int limit);
+}
diff --git a/smartor/src/main/java/com/smartor/service/IServiceTaskScheduleService.java b/smartor/src/main/java/com/smartor/service/IServiceTaskScheduleService.java
new file mode 100644
index 0000000..638cc09
--- /dev/null
+++ b/smartor/src/main/java/com/smartor/service/IServiceTaskScheduleService.java
@@ -0,0 +1,60 @@
+package com.smartor.service;
+
+import com.smartor.domain.ServiceTaskSchedule;
+import com.smartor.domain.ServiceTaskScheduleDetail;
+import com.smartor.domain.ServiceTaskScheduleVO;
+
+import java.util.List;
+
+/**
+ * 闅忚浠诲姟璁″垝 Service 鎺ュ彛
+ *
+ * @author smartor
+ * @date 2026-06-30
+ */
+public interface IServiceTaskScheduleService {
+
+    /**
+     * 淇濆瓨璁″垝锛堝惈鏄庣粏锛夈��
+     * 鑻� taskid 宸叉湁璁″垝锛氬厛杞垹鏃х殑锛屽啀鏂板缓銆�
+     *
+     * @param vo 涓昏鍒� + 鏄庣粏
+     * @return 淇濆瓨鍚庣殑涓昏鍒掞紙鍚富閿級
+     */
+    ServiceTaskSchedule saveScheduleWithDetail(ServiceTaskScheduleVO vo);
+
+    /**
+     * 鎸� taskid 鏌ヨ璁″垝璇︽儏锛堝惈鏄庣粏锛�
+     */
+    ServiceTaskScheduleVO getScheduleByTaskid(Long taskid);
+
+    /**
+     * 閫氳繃 taskid 鏌ヨ涓昏鍒�
+     */
+    ServiceTaskSchedule selectByTaskid(Long taskid);
+
+    /**
+     * 閫氳繃 scheduleid 鏌ヨ鏄庣粏
+     */
+    List<ServiceTaskScheduleDetail> selectDetailByScheduleid(Long scheduleid);
+
+    /**
+     * 鎸� taskid 杞垹涓昏鍒� + 鏄庣粏
+     */
+    int removeByTaskid(Long taskid);
+
+    /**
+     * 寰幆浠诲姟婊氬姩璋冨害锛�
+     * <p>
+     * 鎵弿鎵�鏈� schedule_style=1 鐨勬湁鏁堣鍒掞紝鑻� current_loop_count 宸茶揪涓婇檺鍒欒烦杩囷紱
+     * 鍚﹀垯鍒ゅ畾鏈疆锛坈urrent_loop_count锛夋槸鍚﹀凡鍏ㄩ儴 finalize锛坰endstate 鈭� 1/2/3锛夈��
+     * 鏄� -> 鐢熸垚涓嬩竴杞� subtask锛堝厠闅嗘湰杞殑鎮h�咃紝visit_time = 鏈疆 visit_time + N澶�/鍛�/鏈�/骞达級锛�
+     *      current_loop_count + 1锛宯ext_execute_time 鎺ㄨ繘銆�
+     * 鍚� -> 璺宠繃绛夊緟涓嬫鎵弿銆�
+     * <p>
+     * 鐢� RyTask 瀹氭椂璋冪敤锛屽崟娆℃壂鎻忛檺鍒� 200 鏉¢伩鍏嶉暱浜嬪姟銆�
+     *
+     * @return 瀹為檯鎺ㄨ繘浜嗗灏戜釜璁″垝
+     */
+    int rollCycleTasks();
+}
diff --git a/smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskServiceImpl.java b/smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskServiceImpl.java
index 9c5449b..14acb1a 100644
--- a/smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskServiceImpl.java
+++ b/smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskServiceImpl.java
@@ -116,6 +116,12 @@
     private IServiceSubtaskDetailTraceService traceService;
 
     @Autowired
+    private IServiceTaskScheduleService serviceTaskScheduleService;
+
+    @Autowired
+    private com.smartor.common.ScheduleSubtaskBuilder scheduleSubtaskBuilder;
+
+    @Autowired
     private FtpService ftpService;
 
     @Autowired
@@ -727,6 +733,24 @@
                         serviceTaskoperService.insertServiceTaskoper(serviceTaskoper);
                     }
 
+                } else if (serviceTaskVO.getAppltype().equals("5")) {
+                    // 浠诲姟缁勶細蹇呴』涓洪暱鏈熶换鍔★紙鍚﹀垯 findMatchingTaskIds 鎵笉鍒帮紝鏃犳硶鑷姩绾冲叆鏂板嚭闄㈡偅鑰咃級
+                    if (serviceTaskVO.getLongTask() == null || serviceTaskVO.getLongTask() != 1) {
+                        throw new BaseException("appltype=5 浠诲姟缁勫繀椤讳负闀挎湡浠诲姟锛坙ongTask=1锛�");
+                    }
+                    // 淇濆瓨璁″垝 + 鏄庣粏
+                    ServiceTaskScheduleVO scheduleVO = serviceTaskVO.getServiceTaskScheduleVO();
+                    if (scheduleVO == null) {
+                        throw new BaseException("appltype=5 浠诲姟缁勶細serviceTaskScheduleVO 涓嶈兘涓虹┖");
+                    }
+                    scheduleVO.setTaskid(serviceTask.getTaskid());
+                    scheduleVO.setOrgid(serviceTask.getOrgid());
+                    scheduleVO.setCampusid(serviceTask.getCampusid());
+                    scheduleVO.setCreateBy(serviceTask.getCreateBy());
+                    log.info("----serviceTaskSchedule鐨勫�间负锛歿}", scheduleVO);
+                    serviceTaskScheduleService.saveScheduleWithDetail(scheduleVO);
+                    // 缁戝畾缁村害鍐欐潯浠惰〃锛堜緵 HIS 鑷姩绾冲叆鍖归厤锛宭ongtask 鎭�=1锛�
+                    bindDimensionCondition(serviceTask, serviceTaskVO);
                 } else {
                     ServiceTaskdept serviceTaskdept = new ServiceTaskdept();
                     serviceTaskdept.setTaskId(serviceTask.getTaskid());
@@ -812,6 +836,20 @@
                         serviceSubtask.setLongSendTime(patTaskRelevance.getLongSendTime());
                         serviceSubtask.setVisitTime(patTaskRelevance.getVisittime());
                     }
+
+                    // ===== appltype=5 浠诲姟缁勶細鎸� schedule 鐨� detailList 娲剧敓澶氭潯 subtask =====
+                    if ("5".equals(serviceTaskVO.getAppltype())) {
+                        ServiceTaskSchedule schedule = serviceTaskScheduleService.selectByTaskid(serviceTask.getTaskid());
+                        if (schedule == null) {
+                            throw new BaseException("appltype=5 浠诲姟缁勶細鏈壘鍒� taskid=" + serviceTask.getTaskid() + " 鐨勮鍒�");
+                        }
+                        List<ServiceTaskScheduleDetail> detailList =
+                                serviceTaskScheduleService.selectDetailByScheduleid(schedule.getId());
+                        scheduleSubtaskBuilder.buildSubtasksForPatient(
+                                serviceTask, serviceTaskVO, schedule, detailList, patTaskRelevance, serviceSubtask);
+                        continue;
+                    }
+
                     serviceSubtaskMapper.insertServiceSubtask(serviceSubtask);
                     serviceSubtask.getId().intValue();
 
@@ -1026,6 +1064,23 @@
                             }
                         }
                     }
+                } else if (serviceTaskVO.getAppltype().equals("5")) {
+                    // 浠诲姟缁勪慨鏀癸細鍒犳棫 schedule + 鏄庣粏锛岄噸寤恒�俿ubtask 涓婇潰宸叉壒閲忔竻鐞嗐��
+                    if (serviceTaskVO.getLongTask() == null || serviceTaskVO.getLongTask() != 1) {
+                        throw new BaseException("appltype=5 浠诲姟缁勫繀椤讳负闀挎湡浠诲姟锛坙ongTask=1锛�");
+                    }
+                    ServiceTaskScheduleVO scheduleVO = serviceTaskVO.getServiceTaskScheduleVO();
+                    if (scheduleVO == null) {
+                        throw new BaseException("appltype=5 浠诲姟缁勶細serviceTaskScheduleVO 涓嶈兘涓虹┖");
+                    }
+                    scheduleVO.setTaskid(serviceTask.getTaskid());
+                    scheduleVO.setOrgid(serviceTask.getOrgid());
+                    scheduleVO.setCampusid(serviceTask.getCampusid());
+                    scheduleVO.setCreateBy(serviceTask.getUpdateBy());
+                    serviceTaskScheduleService.saveScheduleWithDetail(scheduleVO);
+                    // 鍏堟竻鏃х淮搴︽潯浠惰〃锛屽啀鎸夋渶鏂板叆鍙傞噸鍐欙紙闃茶繃鏈熺淮搴︽畫鐣欏鑷磋绾冲叆锛�
+                    clearDimensionCondition(serviceTask.getTaskid());
+                    bindDimensionCondition(serviceTask, serviceTaskVO);
                 }
             }
             if (ObjectUtils.isNotEmpty(serviceTaskVO.getIvrTaskTemplateVO())) {
@@ -1079,6 +1134,20 @@
                     serviceSubtask.setVisitTime(patTaskRelevance.getVisittime());
                     serviceSubtask.setTextParam(new Gson().toJson(serviceTaskVO.getTextParam()));
                     if (patTaskRelevance.getIsoperation() != null) {
+                        // appltype=5 浠诲姟缁勶細杩藉姞鎮h�咃紙isoperation=1锛夎蛋璁″垝娲剧敓锛涗慨鏀癸紙2锛�/鍒犻櫎锛�3锛変粛璧板師閫昏緫
+                        if ("5".equals(serviceTaskVO.getAppltype()) && patTaskRelevance.getIsoperation() == 1) {
+                            ServiceTaskSchedule schedule = serviceTaskScheduleService.selectByTaskid(serviceTask.getTaskid());
+                            if (schedule == null) {
+                                throw new BaseException("appltype=5 浠诲姟缁勶細鏈壘鍒� taskid=" + serviceTask.getTaskid() + " 鐨勮鍒�");
+                            }
+                            List<ServiceTaskScheduleDetail> detailList =
+                                    serviceTaskScheduleService.selectDetailByScheduleid(schedule.getId());
+                            serviceSubtask.setUpdateBy(serviceTask.getUpdateBy());
+                            serviceSubtask.setCreateBy(serviceTask.getUpdateBy());
+                            scheduleSubtaskBuilder.buildSubtasksForPatient(
+                                    serviceTask, serviceTaskVO, schedule, detailList, patTaskRelevance, serviceSubtask);
+                            continue;
+                        }
                         if (patTaskRelevance.getIsoperation() == 2) {
                             serviceSubtask.setUpdateBy(serviceTask.getUpdateBy());
                             serviceSubtaskMapper.updateServiceSubtaskByCondition(serviceSubtask);
@@ -1132,6 +1201,85 @@
         return map;
     }
 
+    /**
+     * 浠诲姟缁勶紙appltype=5锛夌粦瀹氱淮搴﹀啓鏉′欢琛紝渚� HIS 鑷姩绾冲叆鍖归厤銆�
+     * longtask 鎭�=1锛堝惁鍒欑嫭绔嬫壂鎻�/涓绘壂鎻忔寜 longtask=1 鏌ヤ笉鍒帮級銆�
+     * 澶嶇敤鐜版湁 service_taskdept锛堢瀹eptType=1/鐥呭尯deptType=2锛夈�乻ervice_taskdiag锛堢柧鐥咃級銆�
+     * 鎵嬫湳缁村害鏈湡涓嶅啓锛堟殏涓嶆敮鎸佽嚜鍔ㄧ撼鍏ワ級銆�
+     */
+    private void bindDimensionCondition(ServiceTask serviceTask, ServiceTaskVO vo) {
+        // 绉戝
+        if (StringUtils.isNotEmpty(vo.getDeptcode())) {
+            for (String deptCode : vo.getDeptcode().split(",")) {
+                if (StringUtils.isEmpty(deptCode)) continue;
+                ServiceTaskdept d = new ServiceTaskdept();
+                d.setTaskId(serviceTask.getTaskid());
+                d.setTaskName(serviceTask.getTaskName());
+                d.setOrgid(serviceTask.getOrgid());
+                d.setLongtask(1L);
+                d.setDeptCode(deptCode);
+                d.setDeptType("1");
+                d.setServiceType(vo.getServiceType());
+                serviceTaskdeptService.insertServiceTaskdept(d);
+            }
+        }
+        // 鐥呭尯
+        if (StringUtils.isNotEmpty(vo.getLeavehospitaldistrictcode())) {
+            for (String wardCode : vo.getLeavehospitaldistrictcode().split(",")) {
+                if (StringUtils.isEmpty(wardCode)) continue;
+                ServiceTaskdept d = new ServiceTaskdept();
+                d.setTaskId(serviceTask.getTaskid());
+                d.setTaskName(serviceTask.getTaskName());
+                d.setOrgid(serviceTask.getOrgid());
+                d.setLongtask(1L);
+                d.setDeptCode(wardCode);
+                d.setDeptType("2");
+                d.setServiceType(vo.getServiceType());
+                serviceTaskdeptService.insertServiceTaskdept(d);
+            }
+        }
+        // 鐤剧梾锛堢敤 split(",")锛屽嬁鐓ф妱 appltype=3 鐜扮綉鐨� split("$") bug锛�
+        if (StringUtils.isNotEmpty(vo.getIcd10code())) {
+            String[] codes = vo.getIcd10code().split(",");
+            String[] names = StringUtils.isNotEmpty(vo.getIcd10name()) ? vo.getIcd10name().split(",") : new String[0];
+            for (int i = 0; i < codes.length; i++) {
+                if (StringUtils.isEmpty(codes[i])) continue;
+                ServiceTaskdiag dg = new ServiceTaskdiag();
+                dg.setTaskId(serviceTask.getTaskid());
+                dg.setTaskName(serviceTask.getTaskName());
+                dg.setOrgid(serviceTask.getOrgid());
+                dg.setLongtask(1L);
+                dg.setServiceType(vo.getServiceType());
+                dg.setIcd10code(codes[i]);
+                dg.setIcd10name(i < names.length ? names[i] : null);
+                dg.setGuid(serviceTask.getGuid());
+                dg.setCreateTime(new Date());
+                serviceTaskdiagService.insertServiceTaskdiag(dg);
+            }
+        }
+    }
+
+    /**
+     * 浠诲姟缁勪慨鏀规椂锛屽厛杞垹璇� task 鏃х殑缁村害鏉′欢琛ㄨ褰曪紙闃茶繃鏈熺淮搴︽畫鐣欏鑷磋绾冲叆锛夈��
+     */
+    private void clearDimensionCondition(Long taskId) {
+        ServiceTaskdept dq = new ServiceTaskdept();
+        dq.setTaskId(taskId);
+        List<ServiceTaskdept> depts = serviceTaskdeptService.selectServiceTaskdeptList(dq);
+        if (CollectionUtils.isNotEmpty(depts)) {
+            for (ServiceTaskdept d : depts) {
+                serviceTaskdeptService.deleteServiceTaskdeptById(d.getId());
+            }
+        }
+        ServiceTaskdiag gq = new ServiceTaskdiag();
+        gq.setTaskId(taskId);
+        List<ServiceTaskdiag> diags = serviceTaskdiagService.selectServiceTaskdiagList(gq);
+        if (CollectionUtils.isNotEmpty(diags)) {
+            for (ServiceTaskdiag g : diags) {
+                serviceTaskdiagService.deleteServiceTaskdiagById(g.getId());
+            }
+        }
+    }
 
     @Override
     public void phoneCallBack(PhoneCallBackVO phoneCallBackVO) {
diff --git a/smartor/src/main/java/com/smartor/service/impl/ServiceTaskScheduleServiceImpl.java b/smartor/src/main/java/com/smartor/service/impl/ServiceTaskScheduleServiceImpl.java
new file mode 100644
index 0000000..4f0a035
--- /dev/null
+++ b/smartor/src/main/java/com/smartor/service/impl/ServiceTaskScheduleServiceImpl.java
@@ -0,0 +1,356 @@
+package com.smartor.service.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.ruoyi.common.exception.base.BaseException;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import com.smartor.domain.ServiceSubtask;
+import com.smartor.domain.ServiceSubtaskPreachform;
+import com.smartor.domain.ServiceTaskSchedule;
+import com.smartor.domain.ServiceTaskScheduleDetail;
+import com.smartor.domain.ServiceTaskScheduleVO;
+import com.smartor.mapper.ServiceSubtaskMapper;
+import com.smartor.mapper.ServiceSubtaskPreachformMapper;
+import com.smartor.mapper.ServiceTaskScheduleDetailMapper;
+import com.smartor.mapper.ServiceTaskScheduleMapper;
+import com.smartor.service.IServiceTaskScheduleService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 闅忚浠诲姟璁″垝 Service 瀹炵幇
+ *
+ * @author smartor
+ * @date 2026-06-30
+ */
+@Slf4j
+@Service
+public class ServiceTaskScheduleServiceImpl implements IServiceTaskScheduleService {
+
+    @Autowired
+    private ServiceTaskScheduleMapper serviceTaskScheduleMapper;
+
+    @Autowired
+    private ServiceTaskScheduleDetailMapper serviceTaskScheduleDetailMapper;
+
+    @Autowired
+    private ServiceSubtaskMapper serviceSubtaskMapper;
+
+    @Autowired
+    private ServiceSubtaskPreachformMapper serviceSubtaskPreachformMapper;
+
+    /** 鍗曟鎵弿鐨勬渶澶ц鍒掓暟锛岄伩鍏嶉暱浜嬪姟銆� */
+    private static final int ROLL_BATCH_LIMIT = 200;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public ServiceTaskSchedule saveScheduleWithDetail(ServiceTaskScheduleVO vo) {
+        if (vo == null || vo.getTaskid() == null) {
+            throw new BaseException("浠诲姟璁″垝鍏ュ弬闈炴硶锛歵askid 涓嶈兘涓虹┖");
+        }
+        if (CollectionUtils.isEmpty(vo.getDetailList())) {
+            throw new BaseException("浠诲姟璁″垝鏄庣粏涓嶈兘涓虹┖锛堣嚦灏戜竴鏉� detail锛�");
+        }
+
+        // 1. 鏃ц鍒掕蒋鍒�
+        serviceTaskScheduleMapper.deleteByTaskid(vo.getTaskid());
+        serviceTaskScheduleDetailMapper.deleteByTaskid(vo.getTaskid());
+
+        // 2. 涓昏鍒掕惤搴�
+        Date now = DateUtils.getNowDate();
+        ServiceTaskSchedule schedule = new ServiceTaskSchedule();
+        // 鎷疯礉鐖剁被锛圫erviceTaskSchedule锛夊瓧娈�
+        schedule.setTaskid(vo.getTaskid());
+        schedule.setMaxLoopCount(vo.getMaxLoopCount());
+        schedule.setCurrentLoopCount(vo.getCurrentLoopCount() == null ? 0L : vo.getCurrentLoopCount());
+        schedule.setCurrentSeq(vo.getCurrentSeq() == null ? 0 : vo.getCurrentSeq());
+        schedule.setNextSeq(vo.getNextSeq() == null ? 1 : vo.getNextSeq());
+        schedule.setScheduleStyle(vo.getScheduleStyle() == null ? 0 : vo.getScheduleStyle());
+        schedule.setScheduleTimeNum(vo.getScheduleTimeNum() == null ? 0 : vo.getScheduleTimeNum());
+        schedule.setScheduleTimeUnit(vo.getScheduleTimeUnit() == null ? 0 : vo.getScheduleTimeUnit());
+        schedule.setNextExecuteTime(vo.getNextExecuteTime());
+        schedule.setScheduleBenchmark(vo.getScheduleBenchmark() == null ? 0 : vo.getScheduleBenchmark());
+        schedule.setScheduleBenchmarkCustomTime(vo.getScheduleBenchmarkCustomTime());
+        schedule.setRemark(vo.getRemark());
+        schedule.setDelFlag("0");
+        schedule.setCreateBy(vo.getCreateBy());
+        schedule.setUpdateBy(vo.getCreateBy());
+        schedule.setCreateTime(now);
+        schedule.setUpdateTime(now);
+        schedule.setOrgid(vo.getOrgid());
+        schedule.setCampusid(vo.getCampusid());
+        serviceTaskScheduleMapper.insertServiceTaskSchedule(schedule);
+
+        // 3. 鏄庣粏钀藉簱
+        List<ServiceTaskScheduleDetail> detailList = vo.getDetailList();
+        int seq = 1;
+        for (ServiceTaskScheduleDetail detail : detailList) {
+            detail.setTaskid(vo.getTaskid());
+            detail.setScheduleid(schedule.getId());
+            if (detail.getSeq() == null) {
+                detail.setSeq(seq);
+            }
+            if (detail.getDayOffset() == null) {
+                throw new BaseException("璁″垝鏄庣粏 seq=" + detail.getSeq() + " 鐨� dayOffset 涓嶈兘涓虹┖");
+            }
+            detail.setDelFlag("0");
+            detail.setCreateBy(vo.getCreateBy());
+            detail.setUpdateBy(vo.getCreateBy());
+            detail.setCreateTime(now);
+            detail.setUpdateTime(now);
+            detail.setOrgid(vo.getOrgid());
+            detail.setCampusid(vo.getCampusid());
+            seq++;
+        }
+        serviceTaskScheduleDetailMapper.batchInsertServiceTaskScheduleDetail(detailList);
+
+        log.info("[浠诲姟缁刔 taskid={} 璁″垝淇濆瓨瀹屾垚, scheduleId={}, detail 鏉℃暟={}",
+                vo.getTaskid(), schedule.getId(), detailList.size());
+        return schedule;
+    }
+
+    @Override
+    public ServiceTaskScheduleVO getScheduleByTaskid(Long taskid) {
+        ServiceTaskSchedule schedule = serviceTaskScheduleMapper.selectByTaskid(taskid);
+        if (schedule == null) {
+            return null;
+        }
+        ServiceTaskScheduleVO vo = new ServiceTaskScheduleVO();
+        vo.setId(schedule.getId());
+        vo.setTaskid(schedule.getTaskid());
+        vo.setMaxLoopCount(schedule.getMaxLoopCount());
+        vo.setCurrentLoopCount(schedule.getCurrentLoopCount());
+        vo.setCurrentSeq(schedule.getCurrentSeq());
+        vo.setNextSeq(schedule.getNextSeq());
+        vo.setScheduleStyle(schedule.getScheduleStyle());
+        vo.setScheduleTimeNum(schedule.getScheduleTimeNum());
+        vo.setScheduleTimeUnit(schedule.getScheduleTimeUnit());
+        vo.setNextExecuteTime(schedule.getNextExecuteTime());
+        vo.setScheduleBenchmark(schedule.getScheduleBenchmark());
+        vo.setScheduleBenchmarkCustomTime(schedule.getScheduleBenchmarkCustomTime());
+        vo.setRemark(schedule.getRemark());
+        vo.setOrgid(schedule.getOrgid());
+        vo.setCampusid(schedule.getCampusid());
+        vo.setDetailList(serviceTaskScheduleDetailMapper.selectByScheduleid(schedule.getId()));
+        return vo;
+    }
+
+    @Override
+    public ServiceTaskSchedule selectByTaskid(Long taskid) {
+        return serviceTaskScheduleMapper.selectByTaskid(taskid);
+    }
+
+    @Override
+    public List<ServiceTaskScheduleDetail> selectDetailByScheduleid(Long scheduleid) {
+        return serviceTaskScheduleDetailMapper.selectByScheduleid(scheduleid);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int removeByTaskid(Long taskid) {
+        int n = serviceTaskScheduleMapper.deleteByTaskid(taskid);
+        serviceTaskScheduleDetailMapper.deleteByTaskid(taskid);
+        return n;
+    }
+
+    @Override
+    public int rollCycleTasks() {
+        List<ServiceTaskSchedule> schedules = serviceTaskScheduleMapper.selectActiveCycleSchedules(ROLL_BATCH_LIMIT);
+        if (CollectionUtils.isEmpty(schedules)) {
+            return 0;
+        }
+        log.info("[浠诲姟缁�-婊氬姩] 鎵弿鍒� {} 涓緟璇勪及鐨勫惊鐜鍒�", schedules.size());
+        int rolled = 0;
+        for (ServiceTaskSchedule schedule : schedules) {
+            try {
+                // 閫氳繃 AOP 浠g悊璋冪敤锛岀‘淇� @Transactional 鐢熸晥
+                if (SpringUtils.getAopProxy(this).rollOneSchedule(schedule)) {
+                    rolled++;
+                }
+            } catch (Exception e) {
+                // 鍗曟潯澶辫触涓嶅奖鍝嶅叾瀹冭鍒掞紝璁版棩蹇楃户缁�
+                log.error("[浠诲姟缁�-婊氬姩] scheduleId={} 婊氬姩澶辫触锛歿}", schedule.getId(), e.getMessage(), e);
+            }
+        }
+        log.info("[浠诲姟缁�-婊氬姩] 鏈鎺ㄨ繘 {} 涓鍒�", rolled);
+        return rolled;
+    }
+
+    /**
+     * 鍗曚釜璁″垝鐨勬粴鍔細鏈疆鍏ㄩ儴 finalize 鎵嶆帹杩涳紝姣忔浠呮帹杩� 1 杞��
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public boolean rollOneSchedule(ServiceTaskSchedule schedule) {
+        Long scheduleId = schedule.getId();
+        Long currentLoop = schedule.getCurrentLoopCount() == null ? 0L : schedule.getCurrentLoopCount();
+
+        // 涓婇檺鏍¢獙
+        Long max = schedule.getMaxLoopCount();
+        if (max != null && max > 0 && currentLoop + 1 >= max) {
+            // 娉ㄦ剰锛氬厛鍒ゆ柇"鍐嶅紑涓�杞槸鍚︿細瓒呴檺"鈥斺�攃urrentLoop 鏄凡瀹屾垚杞锛屼笅涓�杞彿 = currentLoop+1
+            // 褰� currentLoop+1 == max 鏃舵濂藉浜庢渶鍚庝竴杞紝涓嶅啀浜х敓鏂颁竴杞�
+            if (currentLoop >= max) {
+                log.info("[浠诲姟缁�-婊氬姩] scheduleId={} 宸茶揪 maxLoopCount={}, 璺宠繃", scheduleId, max);
+                return false;
+            }
+        }
+
+        // 鏈疆鏄惁瀹屾垚
+        int active = serviceSubtaskMapper.countActiveByScheduleIdAndLoop(scheduleId, currentLoop);
+        if (active > 0) {
+            log.debug("[浠诲姟缁�-婊氬姩] scheduleId={} loop={} 浠嶆湁 {} 鏉� subtask 鏈粨鏉�, 璺宠繃", scheduleId, currentLoop, active);
+            return false;
+        }
+
+        // 鎷夋湰杞� subtask 鍋氬厠闅嗘ā鏉匡紙鍙栧幓閲嶅悗鐨勬偅鑰呴泦鍚堬級
+        List<ServiceSubtask> currentLoopSubtasks =
+                serviceSubtaskMapper.selectByScheduleIdAndLoop(scheduleId, currentLoop);
+        if (CollectionUtils.isEmpty(currentLoopSubtasks)) {
+            log.warn("[浠诲姟缁�-婊氬姩] scheduleId={} loop={} 鎵句笉鍒版湰杞� subtask, 璺宠繃", scheduleId, currentLoop);
+            return false;
+        }
+
+        // 鎷夋湰璁″垝鏄庣粏锛歴eq -> detail锛岀敤鏉ュ畾浣嶄笅涓�杞瘡鏉� subtask 鐢ㄥ摢涓ā鏉�
+        List<ServiceTaskScheduleDetail> detailList =
+                serviceTaskScheduleDetailMapper.selectByScheduleid(scheduleId);
+        if (CollectionUtils.isEmpty(detailList)) {
+            log.warn("[浠诲姟缁�-婊氬姩] scheduleId={} 鏄庣粏涓虹┖, 璺宠繃", scheduleId);
+            return false;
+        }
+
+        // 鎺ㄨ繘姝ラ暱锛堣疆闂撮殧锛�
+        int stepNum = schedule.getScheduleTimeNum() == null ? 0 : schedule.getScheduleTimeNum();
+        int stepUnit = schedule.getScheduleTimeUnit() == null ? 1 : schedule.getScheduleTimeUnit();
+        if (stepNum <= 0) {
+            log.warn("[浠诲姟缁�-婊氬姩] scheduleId={} scheduleTimeNum={} 闈炴硶, 璺宠繃", scheduleId, stepNum);
+            return false;
+        }
+
+        long nextLoop = currentLoop + 1;
+
+        // 鎸� patid+inhospid 鍘婚噸锛堝悓鎮h�呮湰杞彲鑳芥湁 N 鏉� detail锛屾ā鏉夸换閫夊叾涓�鍗冲彲鈥斺�斿厠闅嗘椂鎸� seq 澶嶇敤鏄庣粏锛�
+        // 涓虹畝鍖栵細鐩存帴鎸� schedule_seq 澶嶅埗 N 鏉★紝姣忔潯鐢ㄥ搴� detail銆�
+        Date now = DateUtils.getNowDate();
+        // 鎶� detailList 鎸� seq 绱㈠紩
+        java.util.Map<Integer, ServiceTaskScheduleDetail> detailBySeq = new java.util.HashMap<>(detailList.size());
+        for (ServiceTaskScheduleDetail d : detailList) {
+            detailBySeq.put(d.getSeq(), d);
+        }
+
+        int produced = 0;
+        for (ServiceSubtask src : currentLoopSubtasks) {
+            ServiceTaskScheduleDetail detail = detailBySeq.get(src.getScheduleSeq());
+            if (detail == null) {
+                log.warn("[浠诲姟缁�-婊氬姩] scheduleId={} 鎵句笉鍒� seq={} 瀵瑰簲鐨勬槑缁�, 璺宠繃 subtask id={}",
+                        scheduleId, src.getScheduleSeq(), src.getId());
+                continue;
+            }
+            ServiceSubtask copy = cloneForNextLoop(src, detail, nextLoop, stepNum, stepUnit, now);
+            serviceSubtaskMapper.insertServiceSubtask(copy);
+            // 澶嶅埗 preachform
+            copyPreachform(src.getId(), copy);
+            produced++;
+        }
+
+        // 鎺ㄨ繘 schedule 鐨� current_loop_count / next_seq / next_execute_time
+        ServiceTaskSchedule upd = new ServiceTaskSchedule();
+        upd.setId(scheduleId);
+        upd.setCurrentLoopCount(nextLoop);
+        upd.setCurrentSeq(1);
+        upd.setNextSeq(detailList.size() > 1 ? 2 : 1);
+        upd.setNextExecuteTime(advance(now, stepNum, stepUnit));
+        upd.setUpdateTime(now);
+        serviceTaskScheduleMapper.updateServiceTaskSchedule(upd);
+
+        log.info("[浠诲姟缁�-婊氬姩] scheduleId={} 鎺ㄨ繘鍒扮 {} 杞�, 鏂板缓 {} 鏉� subtask", scheduleId, nextLoop, produced);
+        return true;
+    }
+
+    /**
+     * 鍏嬮殕 subtask 鍒颁笅涓�杞細娓呬富閿�佹竻鍥炵瀛楁銆佹帹杩� visit_time銆佽缃� loop_count銆�
+     */
+    private ServiceSubtask cloneForNextLoop(ServiceSubtask src, ServiceTaskScheduleDetail detail,
+                                            long nextLoop, int stepNum, int stepUnit, Date now) {
+        ServiceSubtask copy = JSON.parseObject(JSON.toJSONString(src), ServiceSubtask.class);
+        copy.setId(null);
+        copy.setSenduuid(null);
+        copy.setResult(null);
+        copy.setFinishtime(null);
+        copy.setExrecallcount(null);
+        copy.setCurrentPreachform(null);
+        copy.setSendstate(2L);
+        copy.setLoopCount(nextLoop);
+        copy.setScheduleDetailId(detail.getId());
+        // 妯℃澘锛歞etail 浼樺厛
+        if (detail.getTemplateid() != null) {
+            copy.setTemplateid(detail.getTemplateid());
+            copy.setTemplatename(detail.getTemplatename());
+        }
+        if (detail.getLibtemplateid() != null) {
+            copy.setLibtemplateid(detail.getLibtemplateid());
+        }
+        Date base = src.getVisitTime() != null ? src.getVisitTime() : now;
+        Date nextVisit = advance(base, stepNum, stepUnit);
+        copy.setVisitTime(nextVisit);
+        copy.setSenddate(nextVisit);
+        copy.setLongSendTime(nextVisit);
+        copy.setCreateTime(now);
+        copy.setUpdateTime(now);
+        return copy;
+    }
+
+    /**
+     * 澶嶅埗婧� subtask 鐨勫彂閫佹柟寮忚鍒版柊 subtask銆�
+     */
+    private void copyPreachform(Long srcSubId, ServiceSubtask newSubtask) {
+        ServiceSubtaskPreachform q = new ServiceSubtaskPreachform();
+        q.setSubid(srcSubId);
+        q.setTaskid(newSubtask.getTaskid());
+        List<ServiceSubtaskPreachform> list = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(q);
+        if (CollectionUtils.isEmpty(list)) {
+            return;
+        }
+        for (ServiceSubtaskPreachform src : list) {
+            ServiceSubtaskPreachform pf = new ServiceSubtaskPreachform();
+            pf.setSort(src.getSort());
+            pf.setPreachform(src.getPreachform());
+            pf.setCompensateTime(src.getCompensateTime());
+            pf.setTaskid(newSubtask.getTaskid());
+            pf.setSubid(newSubtask.getId());
+            pf.setSendstate("1");
+            pf.setOrgid(newSubtask.getOrgid());
+            pf.setCreateTime(new Date());
+            serviceSubtaskPreachformMapper.insertServiceSubtaskPreachform(pf);
+        }
+    }
+
+    /**
+     * 鎸� schedule_time_unit 鎺ㄨ繘鏃堕棿锛�1-澶� 2-鍛� 3-鏈� 4-骞�
+     */
+    private Date advance(Date base, int num, int unit) {
+        Calendar c = Calendar.getInstance();
+        c.setTime(base);
+        switch (unit) {
+            case 2:
+                c.add(Calendar.WEEK_OF_YEAR, num);
+                break;
+            case 3:
+                c.add(Calendar.MONTH, num);
+                break;
+            case 4:
+                c.add(Calendar.YEAR, num);
+                break;
+            case 1:
+            default:
+                c.add(Calendar.DAY_OF_MONTH, num);
+        }
+        return c.getTime();
+    }
+}
diff --git a/smartor/src/main/resources/mapper/smartor/PatMedInhospMapper.xml b/smartor/src/main/resources/mapper/smartor/PatMedInhospMapper.xml
index 24c2efc..ccb334c 100644
--- a/smartor/src/main/resources/mapper/smartor/PatMedInhospMapper.xml
+++ b/smartor/src/main/resources/mapper/smartor/PatMedInhospMapper.xml
@@ -52,6 +52,7 @@
         <result property="inDeptcheckFlag" column="in_deptcheck_flag"/>
         <result property="wardcheckFlag" column="wardcheck_flag"/>
         <result property="diagcheckFlag" column="diagcheck_flag"/>
+        <result property="scheduleCheckFlag" column="schedulecheck_flag"/>
         <result property="age" column="age"/>
         <result property="sex" column="sex"/>
         <result property="telcode" column="telcode"/>
@@ -103,6 +104,7 @@
                in_deptcheck_flag,
                in_wardcheck_flag,
                diagcheck_flag,
+               schedulecheck_flag,
                inhospstate,
                patno,
                long_task_reason,
@@ -164,6 +166,7 @@
         b.in_deptcheck_flag,
         b.in_wardcheck_flag,
         b.diagcheck_flag,
+        b.schedulecheck_flag,
         b.inhospstate,
         b.patno,
         b.inhospno,
@@ -253,6 +256,7 @@
         <if test="nurseId != null ">and b.nurse_id = #{nurseId}</if>
         <if test="nurseName != null and nurseName != ''">and b.nurse_name = #{nurseName}</if>
         <if test="deptcheckFlag != null ">and b.deptcheck_flag = #{deptcheckFlag}</if>
+        <if test="scheduleCheckFlag != null ">and b.schedulecheck_flag = #{scheduleCheckFlag}</if>
         <if test="wardcheckFlag != null ">and b.wardcheck_flag = #{wardcheckFlag}</if>
         <if test="inDeptcheckFlag != null ">and b.in_deptcheck_flag = #{inDeptcheckFlag}</if>
         <if test="inWardcheckFlag != null ">and b.in_wardcheck_flag = #{inWardcheckFlag}</if>
@@ -342,6 +346,7 @@
         b.in_deptcheck_flag,
         b.in_wardcheck_flag,
         b.diagcheck_flag,
+        b.schedulecheck_flag,
         b.inhospstate,
         b.patno,
         b.inhospno,
@@ -499,6 +504,7 @@
         <if test="campusid != null ">and b.campusid = #{campusid}</if>
         <if test="diagcheckFlag != null ">and b.diagcheck_flag = #{diagcheckFlag}</if>
         <if test="deptcheckFlag != null ">and b.deptcheck_flag = #{deptcheckFlag}</if>
+        <if test="scheduleCheckFlag != null ">and b.schedulecheck_flag = #{scheduleCheckFlag}</if>
         <if test="wardcheckFlag != null ">and b.wardcheck_flag = #{wardcheckFlag}</if>
         <if test="inDeptcheckFlag != null ">and b.in_deptcheck_flag = #{inDeptcheckFlag}</if>
         <if test="inWardcheckFlag != null ">and b.in_wardcheck_flag = #{inWardcheckFlag}</if>
@@ -838,6 +844,7 @@
             <if test="patno != null">patno = #{patno},</if>
             <if test="inhospstate != null">inhospstate = #{inhospstate},</if>
             <if test="deptcheckFlag != null">deptcheck_flag = #{deptcheckFlag},</if>
+            <if test="scheduleCheckFlag != null">schedulecheck_flag = #{scheduleCheckFlag},</if>
             <if test="wardcheckFlag != null">wardcheck_flag = #{wardcheckFlag},</if>
             <if test="inDeptcheckFlag != null">in_deptcheck_flag = #{inDeptcheckFlag},</if>
             <if test="inWardcheckFlag != null">in_wardcheck_flag = #{inWardcheckFlag},</if>
diff --git a/smartor/src/main/resources/mapper/smartor/ServiceOutPathMapper.xml b/smartor/src/main/resources/mapper/smartor/ServiceOutPathMapper.xml
index 19c62c2..e1ec99e 100644
--- a/smartor/src/main/resources/mapper/smartor/ServiceOutPathMapper.xml
+++ b/smartor/src/main/resources/mapper/smartor/ServiceOutPathMapper.xml
@@ -166,7 +166,7 @@
         FROM INFORMATION_SCHEMA.TABLES
         WHERE TABLE_NAME = 'service_out_path'
           AND table_schema = DATABASE()
-          and onorgid
+--           and onorgid
     </select>
 
 </mapper>
diff --git a/smartor/src/main/resources/mapper/smartor/ServiceSubtaskMapper.xml b/smartor/src/main/resources/mapper/smartor/ServiceSubtaskMapper.xml
index 1c2ca15..4899c45 100644
--- a/smartor/src/main/resources/mapper/smartor/ServiceSubtaskMapper.xml
+++ b/smartor/src/main/resources/mapper/smartor/ServiceSubtaskMapper.xml
@@ -94,6 +94,10 @@
         <result property="continueCount" column="continue_count"/>
         <result property="continueTimeNext" column="continue_time_next"/>
         <result property="operationItemId" column="operation_item_id"/>
+        <result property="scheduleId" column="schedule_id"/>
+        <result property="scheduleDetailId" column="schedule_detail_id"/>
+        <result property="scheduleSeq" column="schedule_seq"/>
+        <result property="loopCount" column="loop_count"/>
     </resultMap>
 
     <resultMap type="com.smartor.domain.ServiceSubtaskCount" id="ServiceSubtaskResult2">
@@ -238,6 +242,10 @@
                continue_count,
                continue_time_next,
                operation_item_id,
+               schedule_id,
+               schedule_detail_id,
+               schedule_seq,
+               loop_count,
                task_situation
         from service_subtask
     </sql>
@@ -963,6 +971,10 @@
             <if test="continueTimeNext != null ">continue_time_next,</if>
             <if test="continueContent != null ">continue_content,</if>
             <if test="operationItemId != null ">operation_item_id,</if>
+            <if test="scheduleId != null ">schedule_id,</if>
+            <if test="scheduleDetailId != null ">schedule_detail_id,</if>
+            <if test="scheduleSeq != null ">schedule_seq,</if>
+            <if test="loopCount != null ">loop_count,</if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="sendname != null">#{sendname},</if>
@@ -1055,6 +1067,10 @@
             <if test="continueTimeNext != null ">#{continueTimeNext,jdbcType=TIMESTAMP},</if>
             <if test="continueContent != null ">#{continueContent},</if>
             <if test="operationItemId != null ">#{operationItemId},</if>
+            <if test="scheduleId != null ">#{scheduleId},</if>
+            <if test="scheduleDetailId != null ">#{scheduleDetailId},</if>
+            <if test="scheduleSeq != null ">#{scheduleSeq},</if>
+            <if test="loopCount != null ">#{loopCount},</if>
         </trim>
     </insert>
 
@@ -1152,6 +1168,10 @@
             <if test="continueTimeNext != null ">continue_time_next = #{continueTimeNext,jdbcType=TIMESTAMP},</if>
             <if test="continueContent != null ">continue_content = #{continueContent},</if>
             <if test="operationItemId != null ">operation_item_id = #{operationItemId},</if>
+            <if test="scheduleId != null ">schedule_id = #{scheduleId},</if>
+            <if test="scheduleDetailId != null ">schedule_detail_id = #{scheduleDetailId},</if>
+            <if test="scheduleSeq != null ">schedule_seq = #{scheduleSeq},</if>
+            <if test="loopCount != null ">loop_count = #{loopCount},</if>
         </trim>
         where id = #{id}
     </update>
@@ -2896,5 +2916,24 @@
         </choose>
     </select>
 
+    <!-- 浠诲姟缁勶紙appltype=5锛夋粴鍔ㄨ皟搴︾敤锛氭湰杞粛鍦ㄨ窇鐨勬暟閲� -->
+    <select id="countActiveByScheduleIdAndLoop" resultType="int">
+        select count(1)
+        from service_subtask
+        where schedule_id = #{scheduleId}
+          and loop_count = #{loopCount}
+          and (del_flag is null or del_flag = '0')
+          and sendstate in (1, 2, 3)
+    </select>
+
+    <!-- 浠诲姟缁勶紙appltype=5锛夋粴鍔ㄨ皟搴︾敤锛氭媺鏈疆鍏ㄩ噺 subtask锛屼綔涓轰笅涓�杞殑鍏嬮殕妯℃澘 -->
+    <select id="selectByScheduleIdAndLoop" resultMap="ServiceSubtaskResult">
+        <include refid="selectServiceSubtaskVo"/>
+        where schedule_id = #{scheduleId}
+          and loop_count = #{loopCount}
+          and (del_flag is null or del_flag = '0')
+        order by schedule_seq asc, id asc
+    </select>
+
 </mapper>
 
diff --git a/smartor/src/main/resources/mapper/smartor/ServiceTaskScheduleDetailMapper.xml b/smartor/src/main/resources/mapper/smartor/ServiceTaskScheduleDetailMapper.xml
new file mode 100644
index 0000000..d75ee96
--- /dev/null
+++ b/smartor/src/main/resources/mapper/smartor/ServiceTaskScheduleDetailMapper.xml
@@ -0,0 +1,186 @@
+<?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.smartor.mapper.ServiceTaskScheduleDetailMapper">
+
+    <resultMap type="com.smartor.domain.ServiceTaskScheduleDetail" id="ServiceTaskScheduleDetailResult">
+        <id property="id" column="id"/>
+        <result property="taskid" column="taskid"/>
+        <result property="scheduleid" column="scheduleid"/>
+        <result property="seq" column="seq"/>
+        <result property="dayOffset" column="day_offset"/>
+        <result property="templateid" column="templateid"/>
+        <result property="libtemplateid" column="libtemplateid"/>
+        <result property="templatename" column="templatename"/>
+        <result property="content" column="content"/>
+        <result property="nextExecuteTime" column="next_execute_time"/>
+        <result property="remark" column="remark"/>
+        <result property="delFlag" column="del_flag"/>
+        <result property="createBy" column="create_by"/>
+        <result property="createTime" column="create_time"/>
+        <result property="updateBy" column="update_by"/>
+        <result property="updateTime" column="update_time"/>
+        <result property="orgid" column="orgid"/>
+        <result property="campusid" column="campusid"/>
+    </resultMap>
+
+    <sql id="selectServiceTaskScheduleDetailVo">
+        select id,
+               taskid,
+               scheduleid,
+               seq,
+               day_offset,
+               templateid,
+               libtemplateid,
+               templatename,
+               content,
+               next_execute_time,
+               remark,
+               del_flag,
+               create_by,
+               create_time,
+               update_by,
+               update_time,
+               orgid,
+               campusid
+        from service_task_schedule_detail
+    </sql>
+
+    <select id="selectServiceTaskScheduleDetailById" parameterType="Long"
+            resultMap="ServiceTaskScheduleDetailResult">
+        <include refid="selectServiceTaskScheduleDetailVo"/>
+        where id = #{id} and del_flag = '0'
+    </select>
+
+    <select id="selectServiceTaskScheduleDetailList"
+            parameterType="com.smartor.domain.ServiceTaskScheduleDetail"
+            resultMap="ServiceTaskScheduleDetailResult">
+        <include refid="selectServiceTaskScheduleDetailVo"/>
+        where del_flag = '0'
+        <if test="taskid != null">and taskid = #{taskid}</if>
+        <if test="scheduleid != null">and scheduleid = #{scheduleid}</if>
+        <if test="seq != null">and seq = #{seq}</if>
+        <if test="templateid != null">and templateid = #{templateid}</if>
+        order by seq asc
+    </select>
+
+    <select id="selectByScheduleid" resultMap="ServiceTaskScheduleDetailResult">
+        <include refid="selectServiceTaskScheduleDetailVo"/>
+        where scheduleid = #{scheduleid} and del_flag = '0'
+        order by seq asc
+    </select>
+
+    <select id="selectByTaskid" resultMap="ServiceTaskScheduleDetailResult">
+        <include refid="selectServiceTaskScheduleDetailVo"/>
+        where taskid = #{taskid} and del_flag = '0'
+        order by seq asc
+    </select>
+
+    <insert id="insertServiceTaskScheduleDetail" parameterType="com.smartor.domain.ServiceTaskScheduleDetail"
+            useGeneratedKeys="true" keyProperty="id">
+        insert into service_task_schedule_detail
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="taskid != null">taskid,</if>
+            <if test="scheduleid != null">scheduleid,</if>
+            <if test="seq != null">seq,</if>
+            <if test="dayOffset != null">day_offset,</if>
+            <if test="templateid != null">templateid,</if>
+            <if test="libtemplateid != null">libtemplateid,</if>
+            <if test="templatename != null">templatename,</if>
+            <if test="content != null">content,</if>
+            <if test="nextExecuteTime != null">next_execute_time,</if>
+            <if test="remark != null">remark,</if>
+            <if test="delFlag != null">del_flag,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateBy != null">update_by,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="orgid != null">orgid,</if>
+            <if test="campusid != null">campusid,</if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="taskid != null">#{taskid},</if>
+            <if test="scheduleid != null">#{scheduleid},</if>
+            <if test="seq != null">#{seq},</if>
+            <if test="dayOffset != null">#{dayOffset},</if>
+            <if test="templateid != null">#{templateid},</if>
+            <if test="libtemplateid != null">#{libtemplateid},</if>
+            <if test="templatename != null">#{templatename},</if>
+            <if test="content != null">#{content},</if>
+            <if test="nextExecuteTime != null">#{nextExecuteTime},</if>
+            <if test="remark != null">#{remark},</if>
+            <if test="delFlag != null">#{delFlag},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateBy != null">#{updateBy},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="orgid != null">#{orgid},</if>
+            <if test="campusid != null">#{campusid},</if>
+        </trim>
+    </insert>
+
+    <insert id="batchInsertServiceTaskScheduleDetail" useGeneratedKeys="true" keyProperty="id">
+        insert into service_task_schedule_detail
+        (taskid, scheduleid, seq, day_offset, templateid, libtemplateid, templatename, content,
+         next_execute_time, remark, del_flag, create_by, create_time, update_by, update_time, orgid, campusid)
+        values
+        <foreach collection="list" item="item" separator=",">
+            (#{item.taskid}, #{item.scheduleid}, #{item.seq}, #{item.dayOffset},
+             #{item.templateid}, #{item.libtemplateid}, #{item.templatename}, #{item.content},
+             #{item.nextExecuteTime}, #{item.remark},
+             <choose>
+                 <when test="item.delFlag != null">#{item.delFlag}</when>
+                 <otherwise>'0'</otherwise>
+             </choose>,
+             #{item.createBy}, #{item.createTime}, #{item.updateBy}, #{item.updateTime},
+             #{item.orgid}, #{item.campusid})
+        </foreach>
+    </insert>
+
+    <update id="updateServiceTaskScheduleDetail" parameterType="com.smartor.domain.ServiceTaskScheduleDetail">
+        update service_task_schedule_detail
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="taskid != null">taskid = #{taskid},</if>
+            <if test="scheduleid != null">scheduleid = #{scheduleid},</if>
+            <if test="seq != null">seq = #{seq},</if>
+            <if test="dayOffset != null">day_offset = #{dayOffset},</if>
+            <if test="templateid != null">templateid = #{templateid},</if>
+            <if test="libtemplateid != null">libtemplateid = #{libtemplateid},</if>
+            <if test="templatename != null">templatename = #{templatename},</if>
+            <if test="content != null">content = #{content},</if>
+            <if test="nextExecuteTime != null">next_execute_time = #{nextExecuteTime},</if>
+            <if test="remark != null">remark = #{remark},</if>
+            <if test="delFlag != null">del_flag = #{delFlag},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="orgid != null">orgid = #{orgid},</if>
+            <if test="campusid != null">campusid = #{campusid},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <update id="deleteServiceTaskScheduleDetailById" parameterType="Long">
+        update service_task_schedule_detail
+        set del_flag = '1',
+            update_time = now()
+        where id = #{id}
+    </update>
+
+    <update id="deleteByScheduleid">
+        update service_task_schedule_detail
+        set del_flag = '1',
+            update_time = now()
+        where scheduleid = #{scheduleid}
+          and del_flag = '0'
+    </update>
+
+    <update id="deleteByTaskid">
+        update service_task_schedule_detail
+        set del_flag = '1',
+            update_time = now()
+        where taskid = #{taskid}
+          and del_flag = '0'
+    </update>
+
+</mapper>
diff --git a/smartor/src/main/resources/mapper/smartor/ServiceTaskScheduleMapper.xml b/smartor/src/main/resources/mapper/smartor/ServiceTaskScheduleMapper.xml
new file mode 100644
index 0000000..878fb33
--- /dev/null
+++ b/smartor/src/main/resources/mapper/smartor/ServiceTaskScheduleMapper.xml
@@ -0,0 +1,176 @@
+<?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.smartor.mapper.ServiceTaskScheduleMapper">
+
+    <resultMap type="com.smartor.domain.ServiceTaskSchedule" id="ServiceTaskScheduleResult">
+        <id property="id" column="id"/>
+        <result property="taskid" column="taskid"/>
+        <result property="maxLoopCount" column="max_loop_count"/>
+        <result property="currentLoopCount" column="current_loop_count"/>
+        <result property="currentSeq" column="current_seq"/>
+        <result property="nextSeq" column="next_seq"/>
+        <result property="scheduleStyle" column="schedule_style"/>
+        <result property="scheduleTimeNum" column="schedule_time_num"/>
+        <result property="scheduleTimeUnit" column="schedule_time_unit"/>
+        <result property="nextExecuteTime" column="next_execute_time"/>
+        <result property="scheduleBenchmark" column="schedule_benchmark"/>
+        <result property="scheduleBenchmarkCustomTime" column="schedule_benchmark_custom_time"/>
+        <result property="remark" column="remark"/>
+        <result property="delFlag" column="del_flag"/>
+        <result property="createBy" column="create_by"/>
+        <result property="createTime" column="create_time"/>
+        <result property="updateBy" column="update_by"/>
+        <result property="updateTime" column="update_time"/>
+        <result property="orgid" column="orgid"/>
+        <result property="campusid" column="campusid"/>
+    </resultMap>
+
+    <sql id="selectServiceTaskScheduleVo">
+        select id,
+               taskid,
+               max_loop_count,
+               current_loop_count,
+               current_seq,
+               next_seq,
+               schedule_style,
+               schedule_time_num,
+               schedule_time_unit,
+               next_execute_time,
+               schedule_benchmark,
+               schedule_benchmark_custom_time,
+               remark,
+               del_flag,
+               create_by,
+               create_time,
+               update_by,
+               update_time,
+               orgid,
+               campusid
+        from service_task_schedule
+    </sql>
+
+    <select id="selectServiceTaskScheduleById" parameterType="Long" resultMap="ServiceTaskScheduleResult">
+        <include refid="selectServiceTaskScheduleVo"/>
+        where id = #{id} and del_flag = '0'
+    </select>
+
+    <select id="selectByTaskid" resultMap="ServiceTaskScheduleResult">
+        <include refid="selectServiceTaskScheduleVo"/>
+        where taskid = #{taskid} and del_flag = '0'
+        order by id desc
+        limit 1
+    </select>
+
+    <select id="selectServiceTaskScheduleList" parameterType="com.smartor.domain.ServiceTaskSchedule"
+            resultMap="ServiceTaskScheduleResult">
+        <include refid="selectServiceTaskScheduleVo"/>
+        where del_flag = '0'
+        <if test="taskid != null">and taskid = #{taskid}</if>
+        <if test="scheduleStyle != null">and schedule_style = #{scheduleStyle}</if>
+        <if test="orgid != null and orgid != ''">and orgid = #{orgid}</if>
+        <if test="campusid != null and campusid != ''">and campusid = #{campusid}</if>
+        order by id desc
+    </select>
+
+    <insert id="insertServiceTaskSchedule" parameterType="com.smartor.domain.ServiceTaskSchedule"
+            useGeneratedKeys="true" keyProperty="id">
+        insert into service_task_schedule
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="taskid != null">taskid,</if>
+            <if test="maxLoopCount != null">max_loop_count,</if>
+            <if test="currentLoopCount != null">current_loop_count,</if>
+            <if test="currentSeq != null">current_seq,</if>
+            <if test="nextSeq != null">next_seq,</if>
+            <if test="scheduleStyle != null">schedule_style,</if>
+            <if test="scheduleTimeNum != null">schedule_time_num,</if>
+            <if test="scheduleTimeUnit != null">schedule_time_unit,</if>
+            <if test="nextExecuteTime != null">next_execute_time,</if>
+            <if test="scheduleBenchmark != null">schedule_benchmark,</if>
+            <if test="scheduleBenchmarkCustomTime != null">schedule_benchmark_custom_time,</if>
+            <if test="remark != null">remark,</if>
+            <if test="delFlag != null">del_flag,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateBy != null">update_by,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="orgid != null">orgid,</if>
+            <if test="campusid != null">campusid,</if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="taskid != null">#{taskid},</if>
+            <if test="maxLoopCount != null">#{maxLoopCount},</if>
+            <if test="currentLoopCount != null">#{currentLoopCount},</if>
+            <if test="currentSeq != null">#{currentSeq},</if>
+            <if test="nextSeq != null">#{nextSeq},</if>
+            <if test="scheduleStyle != null">#{scheduleStyle},</if>
+            <if test="scheduleTimeNum != null">#{scheduleTimeNum},</if>
+            <if test="scheduleTimeUnit != null">#{scheduleTimeUnit},</if>
+            <if test="nextExecuteTime != null">#{nextExecuteTime},</if>
+            <if test="scheduleBenchmark != null">#{scheduleBenchmark},</if>
+            <if test="scheduleBenchmarkCustomTime != null">#{scheduleBenchmarkCustomTime},</if>
+            <if test="remark != null">#{remark},</if>
+            <if test="delFlag != null">#{delFlag},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateBy != null">#{updateBy},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="orgid != null">#{orgid},</if>
+            <if test="campusid != null">#{campusid},</if>
+        </trim>
+    </insert>
+
+    <update id="updateServiceTaskSchedule" parameterType="com.smartor.domain.ServiceTaskSchedule">
+        update service_task_schedule
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="taskid != null">taskid = #{taskid},</if>
+            <if test="maxLoopCount != null">max_loop_count = #{maxLoopCount},</if>
+            <if test="currentLoopCount != null">current_loop_count = #{currentLoopCount},</if>
+            <if test="currentSeq != null">current_seq = #{currentSeq},</if>
+            <if test="nextSeq != null">next_seq = #{nextSeq},</if>
+            <if test="scheduleStyle != null">schedule_style = #{scheduleStyle},</if>
+            <if test="scheduleTimeNum != null">schedule_time_num = #{scheduleTimeNum},</if>
+            <if test="scheduleTimeUnit != null">schedule_time_unit = #{scheduleTimeUnit},</if>
+            <if test="nextExecuteTime != null">next_execute_time = #{nextExecuteTime},</if>
+            <if test="scheduleBenchmark != null">schedule_benchmark = #{scheduleBenchmark},</if>
+            <if test="scheduleBenchmarkCustomTime != null">
+                schedule_benchmark_custom_time = #{scheduleBenchmarkCustomTime},
+            </if>
+            <if test="remark != null">remark = #{remark},</if>
+            <if test="delFlag != null">del_flag = #{delFlag},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="orgid != null">orgid = #{orgid},</if>
+            <if test="campusid != null">campusid = #{campusid},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <update id="deleteServiceTaskScheduleById" parameterType="Long">
+        update service_task_schedule
+        set del_flag = '1',
+            update_time = now()
+        where id = #{id}
+    </update>
+
+    <update id="deleteByTaskid">
+        update service_task_schedule
+        set del_flag = '1',
+            update_time = now()
+        where taskid = #{taskid}
+          and del_flag = '0'
+    </update>
+
+    <select id="selectActiveCycleSchedules" resultMap="ServiceTaskScheduleResult">
+        <include refid="selectServiceTaskScheduleVo"/>
+        where del_flag = '0'
+          and schedule_style = 1
+          and (max_loop_count is null
+                or max_loop_count = 0
+                or current_loop_count &lt; max_loop_count)
+        order by id asc
+        limit #{limit}
+    </select>
+
+</mapper>

--
Gitblit v1.9.3