From 1f66eb9a6353d638f5b9cb33b934bd4705e4efed Mon Sep 17 00:00:00 2001
From: 陈昶聿 <chychen@nbjetron.com>
Date: 星期三, 29 七月 2026 17:35:56 +0800
Subject: [PATCH] Merge branch 'master-任务组'

---
 smartor/src/main/java/com/smartor/domain/ServiceSubtask.java                       |   26 
 smartor/src/main/java/com/smartor/domain/PatMedOperationItem.java                  |   10 
 smartor/src/main/java/com/smartor/domain/ServiceTaskVO.java                        |   12 
 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            |  200 +++++
 smartor/src/main/java/com/smartor/domain/AppendPatientsReq.java                    |   21 
 smartor/src/main/java/com/smartor/domain/ServiceTaskSchedule.java                  |  166 ++++
 smartor/src/main/java/com/smartor/service/IPatMedInhospService.java                |    4 
 smartor/src/main/java/com/smartor/service/IServiceTaskScheduleService.java         |   60 +
 smartor/src/main/java/com/smartor/service/impl/PatMedInhospServiceImpl.java        |  265 ++++---
 smartor/src/main/resources/mapper/smartor/PatMedInhospMapper.xml                   |   14 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java                       |   69 +
 smartor/src/main/java/com/smartor/domain/PatMedInhosp.java                         |   12 
 smartor/src/main/java/com/smartor/domain/ServiceTaskScheduleVO.java                |   28 
 smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskServiceImpl.java      |   77 ++
 smartor/src/main/resources/mapper/smartor/PatMedOperationItemMapper.xml            |   21 
 smartor/src/main/java/com/smartor/domain/ServiceTaskScheduleDetail.java            |  102 +++
 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               |  192 +++++
 23 files changed, 1,869 insertions(+), 134 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 714587a..befb224 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
@@ -150,6 +150,9 @@
     IServiceSubtaskDetailTraceService serviceSubtaskDetailTraceService;
 
     @Autowired
+    IPatMedOuthospService patMedOuthospService;
+
+    @Autowired
     ISvyTaskTemplateService svyTaskTemplateService;
 
     @Autowired
@@ -181,6 +184,21 @@
     private String active;
     @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));
+    }
+
+    public void ryParams(String params) {
+        System.out.println("鎵ц鏈夊弬鏂规硶锛�" + params);
+    }
+
+    public void ryNoParams() {
+        System.out.println("鎵ц鏃犲弬鏂规硶");
+    }
 
     public void importData() {
         String filePath = "C:\\Users\\86176\\Desktop\\鏂板缓鏂囨湰鏂囨。 4.txt"; // 鏇挎崲涓轰綘鐨勬枃浠惰矾寰�
@@ -699,17 +717,17 @@
     }
 
     /**
-     * 澶勭悊鎮h�呬俊鎭紝杩涘叆瀛愪换鍔¤〃
+     * 澶勭悊鎮h�呬俊鎭紝杩涘叆瀛愪换鍔¤〃(浠诲姟缁�)
      */
-    public void dealOutHospInfoByDimension() {
+    public void dealOutHospInfoBySchedule() {
         String config = configService.selectConfigByKey("visit.early.day");
         if (port.equals("8095")) {
             //鍑洪櫌琛�
-            iPatMedInhospService.dealOutHospInfoByDimension(config);
+            iPatMedInhospService.dealOutHospInfoBySchedule(config);
             //闂ㄨ瘖琛�(鏂板崕鐨勬殏鏃朵笉鍋氶棬璇婇殢璁�)
-            if (!active.equals("xh")) {
-                //iPatMedOuthospService.dealOutpatientInfo();
-            }
+//            if (!active.equals("xh")) {
+//                //iPatMedOuthospService.dealOutpatientInfo();
+//            }
         }
     }
 
@@ -1603,4 +1621,43 @@
         int result = QwenLLMUtil.matchRegex(questionText, voiceText, value, regexText);
         return result;
     }
+
+    /**
+     * 浠诲姟缁勶紙appltype=5锛夐杞撼鍏ワ細鎵弿鏂板嚭闄㈡偅鑰咃紝鎸� schedule 缁戝畾鐨勭淮搴﹀尮閰嶄换鍔$粍锛�
+     * 涓哄懡涓殑鎮h�呮寜璁″垝鏄庣粏涓�娆℃�х敓鎴愮 0 杞� N 鏉� subtask锛屽苟缃� schedulecheck_flag=1銆�
+     * 鍚庣画杞鐢� {@link #rollTaskGroupCycle()} 璐熻矗銆�
+     * <p>
+     * 涓庝富鎵弿 dealOutHospInfo 鍒嗙锛屼簰涓嶅共鎵般�傚缓璁瘡 30 鍒嗛挓鎵ц涓�娆°��
+     */
+    public void dealTaskGroupOutHosp() {
+        log.info("銆恉ealTaskGroupOutHosp銆戝紑濮嬫壂鎻忎换鍔$粍鏂板嚭闄㈡偅鑰�");
+        if (!port.equals("8095")) {
+            return;
+        }
+        try {
+            String config = configService.selectConfigByKey("visit.early.day");
+            iPatMedInhospService.dealOutHospInfoBySchedule(config);
+            log.info("銆恉ealTaskGroupOutHosp銆戜换鍔$粍棣栬疆绾冲叆鎵弿瀹屾垚");
+        } catch (Exception e) {
+            log.error("銆恉ealTaskGroupOutHosp銆戜换鍔$粍鎵弿寮傚父", e);
+        }
+    }
+
+    /**
+     * 浠诲姟缁勶紙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..e281e01
--- /dev/null
+++ b/smartor/src/main/java/com/smartor/common/ScheduleSubtaskBuilder.java
@@ -0,0 +1,192 @@
+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,
+                                        List<Map<String, Object>> preachformList,
+                                        ServiceTaskSchedule schedule,
+                                        List<ServiceTaskScheduleDetail> scheduleDetailList,
+                                        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, scheduleDetailList, baseSubtask, preachformList);
+    }
+
+    /**
+     * 鏍规嵁 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 0bba67d..1e62b81 100644
--- a/smartor/src/main/java/com/smartor/domain/PatMedInhosp.java
+++ b/smartor/src/main/java/com/smartor/domain/PatMedInhosp.java
@@ -98,6 +98,11 @@
     private String icd10code;
 
     /**
+     * 鍏ラ櫌璇婃柇ICD鍊肩紪鍙烽泦鍚�
+     */
+    @ApiModelProperty(value = "鍏ラ櫌璇婃柇ICD鍊肩紪鍙烽泦鍚�")
+    private List<String> icd10codeList;
+    /**
      * 鍏ラ櫌璇婃柇鍚嶇О
      */
     @ApiModelProperty(value = "鍏ラ櫌璇婃柇鍚嶇О")
@@ -309,6 +314,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/PatMedOperationItem.java b/smartor/src/main/java/com/smartor/domain/PatMedOperationItem.java
index 6fd3835..326be38 100644
--- a/smartor/src/main/java/com/smartor/domain/PatMedOperationItem.java
+++ b/smartor/src/main/java/com/smartor/domain/PatMedOperationItem.java
@@ -8,6 +8,7 @@
 import lombok.Data;
 
 import java.util.Date;
+import java.util.List;
 
 /**
  * 鎮h�呮鏌ユ楠岃褰曞瓙瀵硅薄 pat_med_operation_item
@@ -216,5 +217,14 @@
     @ApiModelProperty("鎶ゅ+濮撳悕")
     @Excel(name = "鎶ゅ+濮撳悕")
     private String nurseName;
+
+    @ApiModelProperty(value = "鐥呭尯缂栫爜闆嗗悎")
+    private List<String> wardCodes;
+
+    @ApiModelProperty(value = "閮ㄩ棬缂栫爜闆嗗悎")
+    private List<String> deptCodes;
+
+    @ApiModelProperty(value = "鎵嬫湳绛夌骇闆嗗悎")
+    private List<String> opLevelCodes;
 }
 
diff --git a/smartor/src/main/java/com/smartor/domain/ServiceSubtask.java b/smartor/src/main/java/com/smartor/domain/ServiceSubtask.java
index dcee2ce..7d7024d 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..5290525
--- /dev/null
+++ b/smartor/src/main/java/com/smartor/domain/ServiceTaskSchedule.java
@@ -0,0 +1,166 @@
+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;
+
+    /**
+     * 缁戝畾绉戝浠g爜锛堥�楀彿鍒嗛殧锛夛紝浠诲姟缁勮嚜鍔ㄧ撼鍏ュ尮閰嶇敤
+     */
+    @ApiModelProperty("缁戝畾绉戝浠g爜(閫楀彿鍒嗛殧)锛岃嚜鍔ㄧ撼鍏ョ敤")
+    private String deptCodes;
+
+    /**
+     * 缁戝畾鐥呭尯浠g爜锛堥�楀彿鍒嗛殧锛夛紝浠诲姟缁勮嚜鍔ㄧ撼鍏ュ尮閰嶇敤
+     */
+    @ApiModelProperty("缁戝畾鐥呭尯浠g爜(閫楀彿鍒嗛殧)锛岃嚜鍔ㄧ撼鍏ョ敤")
+    private String wardCodes;
+
+    /**
+     * 缁戝畾鐤剧梾ICD10浠g爜锛堥�楀彿鍒嗛殧锛夛紝浠诲姟缁勮嚜鍔ㄧ撼鍏ュ尮閰嶇敤
+     */
+    @ApiModelProperty("缁戝畾鐤剧梾ICD10浠g爜(閫楀彿鍒嗛殧)锛岃嚜鍔ㄧ撼鍏ョ敤")
+    private String icd10Codes;
+
+    /**
+     * 缁戝畾鎵嬫湳绫诲瀷浠g爜锛堥�楀彿鍒嗛殧锛夛紝浠诲姟缁勮嚜鍔ㄧ撼鍏ュ尮閰嶇敤锛堟湰鏈熸殏涓嶇敤锛�
+     */
+    @ApiModelProperty("缁戝畾鎵嬫湳绫诲瀷浠g爜(閫楀彿鍒嗛殧)锛岃嚜鍔ㄧ撼鍏ョ敤(鏈湡鏆備笉鐢�)")
+    private String oplevelCodes;
+
+    /**
+     * 缁戝畾绉戝浠g爜
+     */
+    @ApiModelProperty("缁戝畾绉戝浠g爜")
+    private String deptCode;
+
+    /**
+     * 缁戝畾鐥呭尯浠g爜
+     */
+    @ApiModelProperty("缁戝畾鐥呭尯浠g爜")
+    private String wardCode;
+
+    /**
+     * 缁戝畾鐤剧梾ICD10浠g爜
+     */
+    @ApiModelProperty("缁戝畾鐤剧梾ICD10浠g爜")
+    private String icd10Code;
+
+    /**
+     * 缁戝畾鎵嬫湳绫诲瀷浠g爜
+     */
+    @ApiModelProperty("缁戝畾鎵嬫湳绫诲瀷浠g爜")
+    private String oplevelCode;
+}
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 174128d..ec6e65c 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/mapper/ServiceSubtaskMapper.java b/smartor/src/main/java/com/smartor/mapper/ServiceSubtaskMapper.java
index 8d6e806..ea93805 100644
--- a/smartor/src/main/java/com/smartor/mapper/ServiceSubtaskMapper.java
+++ b/smartor/src/main/java/com/smartor/mapper/ServiceSubtaskMapper.java
@@ -69,6 +69,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/IPatMedInhospService.java b/smartor/src/main/java/com/smartor/service/IPatMedInhospService.java
index 51e8aad..000928e 100644
--- a/smartor/src/main/java/com/smartor/service/IPatMedInhospService.java
+++ b/smartor/src/main/java/com/smartor/service/IPatMedInhospService.java
@@ -96,12 +96,12 @@
     public int dealOutHospInfo(String config);
 
     /**
-     * 澶勭悊浠巋is鍙栨潵鐨勫嚭闄㈡暟鎹�
+     * 澶勭悊浠巋is鍙栨潵鐨勫嚭闄㈡暟鎹� 浠诲姟缁勭敓鎴�
      *
      * @param
      * @return 缁撴灉
      */
-    public int dealOutHospInfoByDimension(String config);
+    public int dealOutHospInfoBySchedule(String config);
 
     /**
      * 鑾峰彇鍑洪櫌锛屼綇闄紝闂ㄨ瘖鏈嶅姟浜烘
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/PatMedInhospServiceImpl.java b/smartor/src/main/java/com/smartor/service/impl/PatMedInhospServiceImpl.java
index a8c7e00..46a1457 100644
--- a/smartor/src/main/java/com/smartor/service/impl/PatMedInhospServiceImpl.java
+++ b/smartor/src/main/java/com/smartor/service/impl/PatMedInhospServiceImpl.java
@@ -9,6 +9,7 @@
 import com.ruoyi.common.utils.DtoConversionUtils;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.StringUtils;
+import com.smartor.common.ScheduleSubtaskBuilder;
 import com.smartor.domain.*;
 import com.smartor.domain.DTO.PatMedInhospDTO;
 import com.smartor.domain.entity.ServiceSubtaskEntity;
@@ -65,6 +66,13 @@
     private String active;
     @Autowired
     private IConfigService iSysConfigService;
+    @Autowired
+    private ServiceTaskScheduleMapper serviceTaskScheduleMapper;
+
+    @Autowired
+    private ScheduleSubtaskBuilder scheduleSubtaskBuilder;
+    @Autowired
+    private ServiceTaskScheduleDetailMapper serviceTaskScheduleDetailMapper;
 
 
     /**
@@ -352,139 +360,152 @@
     }
 
     /**
-     * 澶氱淮搴﹂殢璁�
+     * 浠诲姟缁勯殢璁�
      *
      * @param config
      * @return
      */
-    public int dealOutHospInfoByDimension(String config) {
-        // 涓�娆℃�ф煡鍑烘墍鏈夐渶瑕佸鐞嗙殑鍑洪櫌鎮h�咃紙涓変釜缁村害浠绘剰涓�涓湭瀹屾垚锛�
-        List<PatMedInhosp> patList = patMedInhospMapper.selectNeedProcessList();
-        log.info("銆愮粺涓�鎵弿銆戝緟澶勭悊鎮h�呮暟锛歿}", patList.size());
+    public int dealOutHospInfoBySchedule(String config) {
 
-        for (PatMedInhosp patMedInhosp1 : patList) {
-            PatArchive patArchive = patArchiveMapper.selectPatArchiveByPatid(patMedInhosp1.getPatid());
-            if (Objects.isNull(patArchive)) {
-                // 涓変釜缁村害閮芥爣璁颁负澶辫触
-                markAllFlagsAsError(patMedInhosp1, "鎮h�呭熀鏈俊鎭负绌�");
-                continue;
-            }
+        ServiceTask serviceTaskVo = new ServiceTask();
+        serviceTaskVo.setLongTask(1);
+        serviceTaskVo.setAppltype("5");
+        List<ServiceTask> serviceTasks = serviceTaskMapper.selectServiceTaskList(serviceTaskVo);
 
-            // 鈹�鈹� 缁村害1锛氱瀹� 鈹�鈹�
-            if ("0".equals(patMedInhosp1.getDeptcheckFlag())) {
-                processTasksByDimension(patMedInhosp1, patArchive, config, 1);
-            }
+        if (CollectionUtils.isNotEmpty(serviceTasks)) {
+            for (ServiceTask serviceTask : serviceTasks) {
+                if(!ObjectUtils.isEmpty(serviceTask.getTaskid())){
+                    ServiceTaskSchedule serviceTaskSchedule = new ServiceTaskSchedule();
+                    ServiceTaskSchedule taskSchedule = new ServiceTaskSchedule();
+                    taskSchedule.setTaskid(serviceTask.getTaskid());
+                    List<ServiceTaskSchedule> serviceTaskSchedules = serviceTaskScheduleMapper.selectServiceTaskScheduleList(taskSchedule);
+                    if(CollectionUtils.isNotEmpty(serviceTaskSchedules)){
+                        serviceTaskSchedule = serviceTaskSchedules.get(0);
+                        String deptCodes = serviceTaskSchedule.getDeptCodes();
+                        String wardCodes = serviceTaskSchedule.getWardCodes();
+                        String icd10Codes = serviceTaskSchedule.getIcd10Codes();
+                        String opLevelCodes = serviceTaskSchedule.getOplevelCodes();
 
-            // 鈹�鈹� 缁村害2锛氱梾鍖� 鈹�鈹�锛堟柊鍗庡尰闄㈣烦杩囷級
-            if (!active.equals("xh") && "0".equals(patMedInhosp1.getWardcheckFlag())) {
-                processTasksByDimension(patMedInhosp1, patArchive, config, 2);
-            }
+                        List<String> icd10CodeList = new ArrayList<>();
+                        List<String> opLevelCodeList = new ArrayList<>();
+                        List<String> deptCodeList = new ArrayList<>();
+                        List<String> wardCodeList = new ArrayList<>();
 
-            // 鈹�鈹� 缁村害3锛氱柧鐥� 鈹�鈹�锛堟柊鍗庡尰闄㈣烦杩囷級
-            if (!active.equals("xh") && "0".equals(patMedInhosp1.getDiagcheckFlag())) {
-                processTasksByDimension(patMedInhosp1, patArchive, config, 3);
+                        /**
+                         * 闅忚绫诲瀷(1-绉戝锛�2-鐥呭尯锛�3-鐤剧梾 4-鍏ラ櫌绉戝 5-鍏ラ櫌鐥呭尯 6-鎵嬫湳)
+                         */
+                        Integer type = 1;
+                        if (StringUtils.isNotEmpty(deptCodes)) {
+                            type = 1;
+                            deptCodeList = Arrays.asList(deptCodes.split(","));
+                        }
+                        if(StringUtils.isNotEmpty(wardCodes)) {
+                            type = 2;
+                            wardCodeList = Arrays.asList(wardCodes.split(","));
+                        }
+                        if(StringUtils.isNotEmpty(icd10Codes)) {
+                            type = 3;
+                            icd10CodeList = Arrays.asList(icd10Codes.split(","));
+                        }
+                        if (StringUtils.isNotEmpty(opLevelCodes)) {
+                            type = 6;
+                            opLevelCodeList = Arrays.asList(opLevelCodes.split(","));
+                        }
+                        //闈炴墜鏈鐞嗘柟寮�
+                        if(type != 6){
+                            PatMedInhosp patMedInhospVo = new PatMedInhosp();
+                            patMedInhospVo.setScheduleCheckFlag("0");
+                            patMedInhospVo.setInhospstate("1");
+                            patMedInhospVo.setFuflag("1");
+//                            patMedInhospVo.setDeptcodeList(deptCodeList);
+                            patMedInhospVo.setLeaveldeptcodes(deptCodeList);
+                            patMedInhospVo.setLeavehospitaldistrictcodes(wardCodeList);
+                            patMedInhospVo.setIcd10codeList(icd10CodeList);
+                            List<PatMedInhosp> patList = patMedInhospMapper.selectPatMedInhospList(patMedInhospVo);
+                            log.info("dealOutHospInfoBySchedule 浠诲姟缁勯殢璁�, taskid = {}, 鎵弿鎮h�呬汉鏁� {}", serviceTask.getTaskid(), patList.size());
+
+                            for(PatMedInhosp patMedInhosp : patList){
+                                ServiceTaskScheduleDetail scheduleDetailVo = new ServiceTaskScheduleDetail();
+                                scheduleDetailVo.setTaskid(serviceTask.getTaskid());
+                                scheduleDetailVo.setScheduleid(serviceTaskSchedule.getId());
+                                List<ServiceTaskScheduleDetail> serviceTaskScheduleDetails = serviceTaskScheduleDetailMapper.selectServiceTaskScheduleDetailList(scheduleDetailVo);
+
+                                PatArchive patArchive = patArchiveMapper.selectPatArchiveByPatid(patMedInhosp.getPatid());
+                                if (Objects.isNull(patArchive)) {
+                                    continue;
+                                }
+                                //灏佽serviceSubtask
+
+                                ServiceSubtask serviceSubtask = boxedServiceSubtask(serviceTask, patMedInhosp, patArchive, config, type);
+                                List<Map<String, Object>> preachformList = new ArrayList<>();
+                                if(StringUtils.isNotEmpty(serviceTask.getPreachformDesc())){
+                                    try {
+                                        ObjectMapper objectMapper = new ObjectMapper();
+                                        preachformList = objectMapper.readValue(serviceTask.getPreachformDesc(), List.class);
+                                    }catch (Exception e){
+                                        log.error("浠诲姟缁刾reachformDesc瑙f瀽澶辫触,taskid={},inhospid={}", serviceTask.getTaskid(), patMedInhosp.getInhospid());
+                                    }
+                                }
+                                scheduleSubtaskBuilder.buildSubtasksForPatient(serviceTask, serviceTaskSchedule, serviceTaskScheduleDetails, serviceSubtask, preachformList);
+                                //缃杞撼鍏ユ爣璁帮紝閬垮厤涓嬫鎵弿閲嶅鐢熸垚锛堝悗缁疆娆$敱 rollTaskGroupCycle 璐熻矗锛�
+                                PatMedInhosp flagUpd = new PatMedInhosp();
+                                flagUpd.setInhospid(patMedInhosp.getInhospid());
+                                flagUpd.setScheduleCheckFlag("1");
+                                patMedInhospMapper.updatePatMedInhosp(flagUpd);
+                            }
+                        }else {
+                            //浠诲姟缁� - 鎵嬫湳闅忚
+                            if(CollectionUtils.isNotEmpty(opLevelCodeList)){
+                                PatMedOperationItem pmoi = new PatMedOperationItem();
+                                //鑾峰彇闇�瑕佸嚭闄㈢柧鐥呴殢璁匡紝鏈鐞嗙殑鏁版嵁
+                                pmoi.setOpercheckFlag("0");
+                                pmoi.setMainFlag("1");
+                                pmoi.setDeptCodes(deptCodeList);
+                                pmoi.setWardCodes(wardCodeList);
+                                pmoi.setOpLevelCodes(opLevelCodeList);
+                                List<PatMedOperationItem> patMedOperationItems = patMedOperationItemMapper.selectOperationItemList(pmoi);
+                                for(PatMedOperationItem patMedOperationItem : patMedOperationItems){
+                                    PatArchive patArchive = patArchiveMapper.selectPatArchiveByPatid(patMedOperationItem.getPatid());
+                                    if (Objects.isNull(patArchive)) {
+                                        continue;
+                                    }
+                                    PatMedInhosp patMedInhospVo = new PatMedInhosp();
+                                    patMedInhospVo.setPatid((patMedOperationItem.getPatid()));
+                                    patMedInhospVo.setScheduleCheckFlag("0");
+                                    patMedInhospVo.setInhospstate("1");
+                                    patMedInhospVo.setFuflag("1");
+                                    List<PatMedInhosp> patMedInhospList = patMedInhospMapper.selectPatMedInhospList(patMedInhospVo);
+                                    if(CollectionUtils.isNotEmpty(patMedInhospList)){
+                                        ServiceTaskScheduleDetail scheduleDetailVo = new ServiceTaskScheduleDetail();
+                                        scheduleDetailVo.setTaskid(serviceTask.getTaskid());
+                                        scheduleDetailVo.setScheduleid(serviceTaskSchedule.getId());
+                                        List<ServiceTaskScheduleDetail> serviceTaskScheduleDetails = serviceTaskScheduleDetailMapper.selectServiceTaskScheduleDetailList(scheduleDetailVo);
+                                        //灏佽serviceSubtask
+                                        ServiceSubtask serviceSubtask = boxedServiceSubtaskForOp(serviceTask, patMedInhospList.get(0), patMedOperationItem, patArchive, config);
+                                        List<Map<String, Object>> preachformList = new ArrayList<>();
+                                        if(StringUtils.isNotEmpty(serviceTask.getPreachformDesc())){
+                                            try {
+                                                ObjectMapper objectMapper = new ObjectMapper();
+                                                preachformList = objectMapper.readValue(serviceTask.getPreachformDesc(), List.class);
+                                            }catch (Exception e){
+                                                log.error("浠诲姟缁�(鎵嬫湳)preachformDesc瑙f瀽澶辫触,taskid={}", serviceTask.getTaskid());
+                                            }
+                                        }
+                                        scheduleSubtaskBuilder.buildSubtasksForPatient(serviceTask, serviceTaskSchedule, serviceTaskScheduleDetails, serviceSubtask, preachformList);
+                                        //缃杞撼鍏ユ爣璁帮紝閬垮厤閲嶅鐢熸垚
+                                        PatMedInhosp flagUpd = new PatMedInhosp();
+                                        flagUpd.setInhospid(patMedInhospList.get(0).getInhospid());
+                                        flagUpd.setScheduleCheckFlag("1");
+                                        patMedInhospMapper.updatePatMedInhosp(flagUpd);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
             }
         }
         return 1;
-    }
-
-    /**
-     * 鎸夌淮搴︽壘鍒版墍鏈夊尮閰嶉暱鏈熶换鍔★紝鎵归噺鐢熸垚 subtask
-     * type: 1=绉戝, 2=鐥呭尯, 3=鐤剧梾
-     */
-    private void processTasksByDimension(PatMedInhosp patMedInhosp1, PatArchive patArchive, String config, int type) {
-        List<Long> matchedTaskIds = findMatchingTaskIds(patMedInhosp1, type);
-
-        if (CollectionUtils.isEmpty(matchedTaskIds)) {
-            String reason = getDimensionReason(type);
-            markFlagAsNoConfig(patMedInhosp1, type, reason);
-            return;
-        }
-
-        // 涓�鏉� inhosp 璁板綍 鈫� 澶氫釜鍖归厤浠诲姟 鈫� 澶氭潯 subtask
-        for (Long taskId : matchedTaskIds) {
-            writeInSubTask(taskId, true, patMedInhosp1, patArchive, type, config);
-        }
-    }
-
-    private String getDimensionReason(int type) {
-        switch (type) {
-            case 1: { // 绉戝
-                return "鏈壘鍒扮瀹ゅ尮閰嶇殑闀挎湡浠诲姟";
-            }
-            case 2: { // 鐥呭尯
-                return "鏈壘鍒扮梾鍖哄尮閰嶇殑闀挎湡浠诲姟";
-            }
-            case 3: { // 鐤剧梾
-                return "鏈壘鍒扮柧鐥呭尮閰嶇殑闀挎湡浠诲姟";
-            }
-        }
-        return "澶氱淮闀挎湡浠诲姟-鍖归厤绫诲瀷涓虹┖";
-    }
-
-    private void markFlagAsNoConfig(PatMedInhosp patMedInhosp, int type, String reason) {
-        switch (type) {
-            case 1: { // 绉戝
-                patMedInhosp.setDeptcheckFlag("2");
-                patMedInhosp.setRemark(reason);
-                patMedInhospMapper.updatePatMedInhosp(patMedInhosp);
-                break;
-            }
-            case 2: { // 鐥呭尯
-                patMedInhosp.setWardcheckFlag("2");
-                patMedInhosp.setRemark(reason);
-                patMedInhospMapper.updatePatMedInhosp(patMedInhosp);
-                break;
-            }
-            case 3: { // 鐤剧梾
-                patMedInhosp.setDiagcheckFlag("2");
-                patMedInhosp.setRemark(reason);
-                patMedInhospMapper.updatePatMedInhosp(patMedInhosp);
-                break;
-            }
-        }
-
-    }
-
-    private void markAllFlagsAsError(PatMedInhosp patMedInhosp, String reason) {
-        patMedInhosp.setDeptcheckFlag("2");
-        patMedInhosp.setWardcheckFlag("2");
-        patMedInhosp.setDiagcheckFlag("2");
-        patMedInhosp.setRemark(reason);
-        patMedInhospMapper.updatePatMedInhosp(patMedInhosp);
-    }
-
-    /**
-     * 鏍规嵁缁村害绫诲瀷鏌ヨ鍖归厤鐨勯暱鏈熶换鍔� ID 鍒楄〃
-     */
-    private List<Long> findMatchingTaskIds(PatMedInhosp patMedInhosp1, int type) {
-        switch (type) {
-            case 1: { // 绉戝
-                ServiceTaskdept query = new ServiceTaskdept();
-                query.setLongtask(1L);
-                query.setDeptCode(patMedInhosp1.getLeaveldeptcode());
-                query.setDeptType("1");
-                query.setOrgid(patMedInhosp1.getOrgid());
-                return serviceTaskdeptMapper.selectServiceTaskdeptList(query).stream().map(ServiceTaskdept::getTaskId).collect(Collectors.toList());
-            }
-            case 2: { // 鐥呭尯
-                ServiceTaskdept query = new ServiceTaskdept();
-                query.setLongtask(1L);
-                query.setDeptCode(patMedInhosp1.getLeavehospitaldistrictcode());
-                query.setDeptType("2");
-                query.setOrgid(patMedInhosp1.getOrgid());
-                return serviceTaskdeptMapper.selectServiceTaskdeptList(query).stream().map(ServiceTaskdept::getTaskId).collect(Collectors.toList());
-            }
-            case 3: { // 鐤剧梾
-                if (StringUtils.isEmpty(patMedInhosp1.getLeaveicd10code())) return Collections.emptyList();
-                ServiceTaskdiag query = new ServiceTaskdiag();
-                query.setLongtask(1L);
-                query.setIcd10code(patMedInhosp1.getLeaveicd10code());
-                query.setOrgid(patMedInhosp1.getOrgid());
-                return serviceTaskdiagMapper.selectServiceTaskdiagList(query).stream().map(ServiceTaskdiag::getTaskId).collect(Collectors.toList());
-            }
-        }
-        return Collections.emptyList();
     }
 
 
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 95d1918..373bdb2 100644
--- a/smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskServiceImpl.java
+++ b/smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskServiceImpl.java
@@ -19,6 +19,7 @@
 import com.ruoyi.system.service.ISysConfigService;
 import com.smartor.common.FtpService;
 import com.smartor.common.MtSubmitSmUtil;
+import com.smartor.common.ScheduleSubtaskBuilder;
 import com.smartor.config.PhoneUtils;
 import com.smartor.domain.DTO.ServiceSubtaskDetailDTO;
 import com.smartor.domain.*;
@@ -118,6 +119,12 @@
 
     @Autowired
     private IServiceSubtaskDetailTraceService traceService;
+
+    @Autowired
+    private IServiceTaskScheduleService serviceTaskScheduleService;
+
+    @Autowired
+    private ScheduleSubtaskBuilder scheduleSubtaskBuilder;
 
     @Autowired
     private FtpService ftpService;
@@ -831,6 +838,29 @@
                             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());
+
+                    scheduleVO.setDeptCodes(serviceTaskVO.getDeptcode());
+                    scheduleVO.setWardCodes(serviceTaskVO.getLeavehospitaldistrictcode());
+                    scheduleVO.setIcd10Codes(serviceTaskVO.getIcd10code());
+                    scheduleVO.setOplevelCodes(serviceTaskVO.getOplevelcode());
+
+                    log.info("----serviceTaskSchedule鐨勫�间负锛歿}", scheduleVO);
+                    serviceTaskScheduleService.saveScheduleWithDetail(scheduleVO);
                 } else {
                     ServiceTaskdept serviceTaskdept = new ServiceTaskdept();
                     serviceTaskdept.setTaskId(serviceTask.getTaskid());
@@ -916,6 +946,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.getPreachformList(), schedule, detailList, patTaskRelevance, serviceSubtask);
+                        continue;
+                    }
+
                     serviceSubtaskMapper.insertServiceSubtask(serviceSubtask);
                     serviceSubtask.getId().intValue();
 
@@ -1130,6 +1174,24 @@
                             }
                         }
                     }
+                } 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.setDeptCodes(serviceTaskVO.getDeptcode());
+                    scheduleVO.setWardCodes(serviceTaskVO.getLeavehospitaldistrictcode());
+                    scheduleVO.setIcd10Codes(serviceTaskVO.getIcd10code());
+                    scheduleVO.setOplevelCodes(serviceTaskVO.getOplevelcode());
+                    scheduleVO.setCreateBy(serviceTask.getUpdateBy());
+                    serviceTaskScheduleService.saveScheduleWithDetail(scheduleVO);
                 }
             }
             if (ObjectUtils.isNotEmpty(serviceTaskVO.getIvrTaskTemplateVO())) {
@@ -1183,6 +1245,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.getPreachformList(), schedule, detailList, patTaskRelevance, serviceSubtask);
+                            continue;
+                        }
                         if (patTaskRelevance.getIsoperation() == 2) {
                             serviceSubtask.setUpdateBy(serviceTask.getUpdateBy());
                             serviceSubtaskMapper.updateServiceSubtaskByCondition(serviceSubtask);
@@ -1235,7 +1311,6 @@
         map.put("taskId", serviceTask.getTaskid().intValue());
         return map;
     }
-
 
     @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 7ac873f..95f1bcc 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"/>
@@ -111,6 +112,7 @@
                in_deptcheck_flag,
                in_wardcheck_flag,
                diagcheck_flag,
+               schedulecheck_flag,
                inhospstate,
                patno,
                long_task_reason,
@@ -172,6 +174,7 @@
         b.in_deptcheck_flag,
         b.in_wardcheck_flag,
         b.diagcheck_flag,
+        b.schedulecheck_flag,
         b.inhospstate,
         b.patno,
         b.inhospno,
@@ -266,6 +269,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>
@@ -328,6 +332,13 @@
                 #{deptcode}
             </foreach>
         </if>
+        <if test="icd10codeList != null and icd10codeList.size()>0">
+            AND b.leaveicd10code IN
+            <foreach collection="icd10codeList" item="icd10code" open="(" separator=","
+                     close=")">
+                #{icd10code}
+            </foreach>
+        </if>
         GROUP BY
         b.inhospid,
         a.age,
@@ -369,6 +380,7 @@
         b.in_deptcheck_flag,
         b.in_wardcheck_flag,
         b.diagcheck_flag,
+        b.schedulecheck_flag,
         b.inhospstate,
         b.patno,
         b.inhospno,
@@ -551,6 +563,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>
@@ -902,6 +915,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/PatMedOperationItemMapper.xml b/smartor/src/main/resources/mapper/smartor/PatMedOperationItemMapper.xml
index 329984e..75bccb7 100644
--- a/smartor/src/main/resources/mapper/smartor/PatMedOperationItemMapper.xml
+++ b/smartor/src/main/resources/mapper/smartor/PatMedOperationItemMapper.xml
@@ -486,6 +486,27 @@
         <if test="patid != null">
             and pmo.patid = #{patid}
         </if>
+        <if test="deptCodes != null and deptCodes.size()>0">
+            AND pmo.deptcode IN
+            <foreach collection="deptCodes" item="deptcode" open="(" separator=","
+                     close=")">
+                #{deptcode}
+            </foreach>
+        </if>
+        <if test="wardCodes != null and wardCodes.size()>0">
+            AND pmo.hospitaldistrictcode IN
+            <foreach collection="wardCodes" item="wardCode" open="(" separator=","
+                     close=")">
+                #{wardCode}
+            </foreach>
+        </if>
+        <if test="opLevelCodes != null and opLevelCodes.size()>0">
+            AND pmoi.oplevelcode IN
+            <foreach collection="opLevelCodes" item="levelCode" open="(" separator=","
+                     close=")">
+                #{levelCode}
+            </foreach>
+        </if>
     </select>
 
 </mapper>
diff --git a/smartor/src/main/resources/mapper/smartor/ServiceSubtaskMapper.xml b/smartor/src/main/resources/mapper/smartor/ServiceSubtaskMapper.xml
index 407dc13..49d4fda 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>
@@ -961,6 +969,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>
@@ -1053,6 +1065,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>
 
@@ -1150,6 +1166,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>
@@ -2918,6 +2938,25 @@
         </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>
+
     <select id="gethelibraryCountHyperlink" parameterType="com.smartor.domain.VO.HeLibraryCountVO"
             resultMap="ServiceSubtaskResult">
         select id,
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..a8980a6
--- /dev/null
+++ b/smartor/src/main/resources/mapper/smartor/ServiceTaskScheduleMapper.xml
@@ -0,0 +1,200 @@
+<?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="deptCodes" column="dept_codes"/>
+        <result property="wardCodes" column="ward_codes"/>
+        <result property="icd10Codes" column="icd10_codes"/>
+        <result property="oplevelCodes" column="oplevel_codes"/>
+        <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,
+               dept_codes,
+               ward_codes,
+               icd10_codes,
+               oplevel_codes,
+               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="deptCode != null">and INSTR(dept_codes,  #{deptCode}) > 0</if>
+        <if test="wardCode != null">and INSTR(ward_codes,  #{wardCode}) > 0</if>
+        <if test="icd10Code != null">and INSTR(icd10_codes,  #{icd10Code}) > 0</if>
+        <if test="oplevelCode != null">and INSTR(oplevel_codes,  #{oplevelCode}) > 0</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="deptCodes != null">dept_codes,</if>
+            <if test="wardCodes != null">ward_codes,</if>
+            <if test="icd10Codes != null">icd10_codes,</if>
+            <if test="oplevelCodes != null">oplevel_codes,</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="deptCodes != null">#{deptCodes},</if>
+            <if test="wardCodes != null">#{wardCodes},</if>
+            <if test="icd10Codes != null">#{icd10Codes},</if>
+            <if test="oplevelCodes != null">#{oplevelCodes},</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="deptCodes != null">dept_codes = #{deptCodes},</if>
+            <if test="wardCodes != null">ward_codes = #{wardCodes},</if>
+            <if test="icd10Codes != null">icd10_codes = #{icd10Codes},</if>
+            <if test="oplevelCodes != null">oplevel_codes = #{oplevelCodes},</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