From b46b8b8fee4d10e52dd882febc07974b02e2aed9 Mon Sep 17 00:00:00 2001
From: liusheng <337615773@qq.com>
Date: 星期二, 03 十一月 2026 14:14:18 +0800
Subject: [PATCH] 本地测试前提交:跨模块收口相关源码改动

---
 ruoyi-common/src/main/resources/mapper/common/Icd10Mapper.xml                                      |  221 +
 ruoyi-common/src/main/java/com/ruoyi/common/mapper/MedicationDoseInfoMapper.java                   |   63 
 ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskRecordMapper.java                     |   68 
 ruoyi-common/src/main/java/com/ruoyi/common/utils/DistinctByProperty.java                          |   24 
 smartor/src/main/java/com/smartor/service/IPatMedOuthospService.java                               |   68 
 smartor/src/main/java/com/smartor/service/impl/bridge/PublicityDataPortBridge.java                 |  330 ++
 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/MedicationDoseInfo.java             |   59 
 ruoyi-common/src/main/java/com/ruoyi/common/core/service/IIcd10Service.java                        |   68 
 ruoyi-common/src/main/resources/mapper/common/MedicationItemMapper.xml                             |  434 +++
 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/MedicationItem.java                 |  169 +
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysDept2Mapper.java                         |  132 +
 ruoyi-task/src/main/java/com/ruoyi/task/api/contract/OperationSubtaskWriter.java                   |  730 ++++++
 ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceSubtaskRecordService.java                  |   67 
 ruoyi-task/src/main/java/com/ruoyi/task/api/contract/OtherVisitApiImpl.java                        |   58 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/TaskOpsTriggerApiImpl.java                |   82 
 ruoyi-common/src/main/java/com/ruoyi/common/enums/ImportFlagEnum.java                              |   82 
 ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/api/OuthospVisitApiImpl.java         |   37 
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysUserDeptMapper.java                      |   76 
 ruoyi-system/src/main/resources/mapper/system/ext/SysUserDeptMapper.xml                            |  295 ++
 ruoyi-system/src/main/resources/mapper/system/ext/SysUserRole2Mapper.xml                           |   62 
 ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskRecordServiceImpl.java          |  102 
 ruoyi-common/src/main/java/com/ruoyi/common/mapper/MedicationItemMapper.java                       |   63 
 ruoyi-system/src/main/resources/mapper/system/ext/SysUser2Mapper.xml                               |  432 +++
 ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskPreachformMapper.java                 |   78 
 ruoyi-task/src/main/java/com/ruoyi/task/api/contract/PreachformApiImpl.java                        |  158 +
 ruoyi-satisfaction/src/main/java/com/ruoyi/satisfaction/support/MydOptionRatioPieChartBuilder.java |  296 ++
 ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskPreachformMapper.xml                       |  331 ++
 ruoyi-system/src/main/resources/mapper/system/ext/SysDept2Mapper.xml                               |  363 +++
 ruoyi-common/src/main/java/com/ruoyi/common/mapper/Icd10Mapper.java                                |   88 
 ruoyi-admin/src/main/resources/application.yml                                                     |    2 
 ruoyi-task/src/main/java/com/ruoyi/task/api/contract/ScheduleRollPortImpl.java                     |  315 ++
 ruoyi-common/src/main/resources/mapper/common/MedicationDoseInfoMapper.xml                         |  172 +
 ruoyi-common/src/main/java/com/ruoyi/common/core/service/impl/MedicationDoseInfoServiceImpl.java   |   96 
 ruoyi-common/src/main/java/com/ruoyi/common/core/service/impl/MedicationItemServiceImpl.java       |   96 
 ruoyi-common/src/main/java/com/ruoyi/common/enums/EmergencyLevelEnum.java                          |   77 
 smartor/src/main/java/com/smartor/service/impl/PatMedOuthospServiceDelegatingImpl.java             |  423 +++
 /dev/null                                                                                          |    4 
 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/Icd10.java                          |  122 +
 ruoyi-common/src/main/java/com/ruoyi/common/core/service/IMedicationDoseInfoService.java           |   61 
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysUser2Mapper.java                         |  137 +
 ruoyi-common/src/main/java/com/ruoyi/common/core/service/IMedicationItemService.java               |   61 
 ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskRecordMapper.xml                           |  179 +
 ruoyi-common/src/main/java/com/ruoyi/common/core/service/impl/Icd10ServiceImpl.java                |  261 ++
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysUserRole2Mapper.java                     |   66 
 44 files changed, 7,103 insertions(+), 5 deletions(-)

diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index f2039f0..abecd88 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -78,7 +78,7 @@
     # 鍥介檯鍖栬祫婧愭枃浠惰矾寰�
     basename: i18n/messages
   profiles:
-    active: nhfy
+    active: druid
 
   # 鏂囦欢涓婁紶
   servlet:
diff --git a/ruoyi-common/- b/ruoyi-common/-
deleted file mode 100644
index 85a7368..0000000
--- a/ruoyi-common/-
+++ /dev/null
@@ -1,4 +0,0 @@
-
-The following files have been resolved:
-   org.projectlombok:lombok:jar:1.18.34:compile
-
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/Icd10.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/Icd10.java
new file mode 100644
index 0000000..a2269e8
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/Icd10.java
@@ -0,0 +1,122 @@
+package com.ruoyi.common.core.domain.entity;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+import java.util.List;
+
+/**
+ * 鐤剧梾瀵硅薄 icd10
+ *
+ * @author ruoyi
+ * @date 2023-12-21
+ */
+@ApiModel(value = "Icd10", description = "鐤剧梾瀵硅薄")
+@Data
+public class Icd10 extends BaseEntity {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * $column.columnComment
+     */
+    @ApiModelProperty("涓婚敭")
+    private Long icdid;
+
+    /**
+     * icd10缂栫爜
+     */
+    @ApiModelProperty("icd10缂栫爜")
+    @Excel(name = "icd10缂栫爜")
+    private String icdcode;
+
+    /**
+     * HIS鐨刬cd10id
+     */
+    @ApiModelProperty("HIS鐨刬cd10id")
+    @Excel(name = "HIS鐨刬cd10id")
+    private String hisIcdid;
+
+    /**
+     * icd10鍚嶇О
+     */
+    @ApiModelProperty("icd10鍚嶇О")
+    @Excel(name = "icd10鍚嶇О")
+    private String icdname;
+
+    /**
+     * 鎷奸煶鐮�
+     */
+    @ApiModelProperty("鎷奸煶鐮�")
+    @Excel(name = "鎷奸煶鐮�")
+    private String icdpym;
+
+    /**
+     * 浜旂瑪鐮�
+     */
+    @ApiModelProperty("浜旂瑪鐮�")
+    @Excel(name = "浜旂瑪鐮�")
+    private String icdwbm;
+
+    /**
+     * 鏈烘瀯id
+     */
+    @ApiModelProperty("鏈烘瀯id")
+    @Excel(name = "鏈烘瀯id")
+    private String orgid;
+
+    /**
+     * 鍒犻櫎鏍囪瘑
+     */
+    @ApiModelProperty("鍒犻櫎鏍囪瘑")
+    private String delFlag;
+
+    /**
+     * 鐖禝D
+     */
+    @ApiModelProperty("鐖禝D")
+    @Excel(name = "鐖禝D")
+    private Long pid;
+
+    /**
+     * GUID
+     */
+    @ApiModelProperty("GUID")
+    @Excel(name = "GUID")
+    private String guid;
+
+    /**
+     * pageNum
+     */
+    @ApiModelProperty("pageNum")
+    private Integer pageNum;
+
+    /**
+     * pageSize
+     */
+    @ApiModelProperty("pageSize")
+    private Integer pageSize;
+
+    /**
+     * 鏈骇鏍囧織
+     */
+    @ApiModelProperty("lastflag")
+    private String lastflag;
+
+    /**
+     * 涓尰鏍囧織
+     */
+    @ApiModelProperty("chimedflag")
+    private String chimedflag;
+
+    /**
+     * icd10缂栫爜
+     */
+    @ApiModelProperty("icd10缂栫爜闆嗗悎")
+    @Excel(name = "icd10缂栫爜闆嗗悎")
+    private List<String> icdcodes;
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/MedicationDoseInfo.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/MedicationDoseInfo.java
new file mode 100644
index 0000000..8646a8c
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/MedicationDoseInfo.java
@@ -0,0 +1,59 @@
+package com.ruoyi.common.core.domain.entity;
+
+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.math.BigDecimal;
+
+/**
+ * 鑽搧鍓傞噺淇℃伅瀵硅薄 medication_dose_info
+ *
+ * @author lihu
+ * @date 2026-07-17
+ */
+@Data
+@ApiModel("鑽搧鍓傞噺淇℃伅")
+public class MedicationDoseInfo extends BaseEntity
+        {
+private static final long serialVersionUID = 1L;
+
+    /** 涓婚敭ID */
+    @ApiModelProperty("涓婚敭ID")
+        //鏁版嵁搴撹嚜澧炴敼鎴怈TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 鑽搧ID锛堝叧鑱攎edication_item.id锛� */
+    @ApiModelProperty("鑽搧ID锛堝叧鑱攎edication_item.id锛�")
+     @Excel(name = "鑽搧ID", readConverterExp = "鍏�=鑱攎edication_item.id")
+    private Long medicationItemId;
+
+    /** 鍓傞噺灏忓寘瑁呰浆鎹㈡瘮 */
+    @ApiModelProperty("鍓傞噺灏忓寘瑁呰浆鎹㈡瘮")
+     @Excel(name = "鍓傞噺灏忓寘瑁呰浆鎹㈡瘮")
+    private BigDecimal doseSmallPackageRation;
+
+    /** 鍓傞噺 */
+    @ApiModelProperty("鍓傞噺")
+     @Excel(name = "鍓傞噺")
+    private Long dose;
+
+    /** 鍓傞噺灏忓寘瑁呮暟閲� */
+    @ApiModelProperty("鍓傞噺灏忓寘瑁呮暟閲�")
+     @Excel(name = "鍓傞噺灏忓寘瑁呮暟閲�")
+    private Long doseSmallPackageNum;
+
+    /** 鍓傞噺鍗曚綅 */
+    @ApiModelProperty("鍓傞噺鍗曚綅")
+     @Excel(name = "鍓傞噺鍗曚綅")
+    private String doseUnit;
+
+    /** 鍓傞噺鍗曚綅绫诲瀷 */
+    @ApiModelProperty("鍓傞噺鍗曚綅绫诲瀷")
+     @Excel(name = "鍓傞噺鍗曚綅绫诲瀷")
+    private Long doseUnitType;
+
+        }
+
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/MedicationItem.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/MedicationItem.java
new file mode 100644
index 0000000..b492c11
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/MedicationItem.java
@@ -0,0 +1,169 @@
+package com.ruoyi.common.core.domain.entity;
+
+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.math.BigDecimal;
+import java.util.List;
+import com.ruoyi.common.core.domain.entity.MedicationDoseInfo;
+
+/**
+ * 鑽搧淇℃伅瀵硅薄 medication_item
+ *
+ * @author lihu
+ * @date 2026-07-17
+ */
+@Data
+@ApiModel("鑽搧淇℃伅")
+public class MedicationItem extends BaseEntity
+        {
+private static final long serialVersionUID = 1L;
+
+    /** 涓婚敭ID */
+    @ApiModelProperty("涓婚敭ID")
+        //鏁版嵁搴撹嚜澧炴敼鎴怈TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 鑽搧璇嗗埆鐮� */
+    @ApiModelProperty("鑽搧璇嗗埆鐮�")
+     @Excel(name = "鑽搧璇嗗埆鐮�")
+    private String identificationCode;
+
+    /** 浜旂瑪鐮� */
+    @ApiModelProperty("浜旂瑪鐮�")
+     @Excel(name = "浜旂瑪鐮�")
+    private String wbm;
+
+    /** 鑽搧閫氱敤鍚� */
+    @ApiModelProperty("鑽搧閫氱敤鍚�")
+     @Excel(name = "鑽搧閫氱敤鍚�")
+    private String medicationCommonName;
+
+    /** 鐢熶骇鍘傚鍚嶇О */
+    @ApiModelProperty("鐢熶骇鍘傚鍚嶇О")
+     @Excel(name = "鐢熶骇鍘傚鍚嶇О")
+    private String manufacturerName;
+
+    /** 鑽搧缂栫爜 */
+    @ApiModelProperty("鑽搧缂栫爜")
+     @Excel(name = "鑽搧缂栫爜")
+    private String itemCode;
+
+    /** 澶у皬鍖呰杞崲姣� */
+    @ApiModelProperty("澶у皬鍖呰杞崲姣�")
+     @Excel(name = "澶у皬鍖呰杞崲姣�")
+    private Long smallLargePackageRatio;
+
+    /** 鏈烘瀯ID */
+    @ApiModelProperty("鏈烘瀯ID")
+     @Excel(name = "鏈烘瀯ID")
+    private Long orgId;
+
+    /** 鑽搧鍚嶇О */
+    @ApiModelProperty("鑽搧鍚嶇О")
+     @Excel(name = "鑽搧鍚嶇О")
+    private String itemName;
+
+    /** 鑽搧绫诲瀷 */
+    @ApiModelProperty("鑽搧绫诲瀷")
+     @Excel(name = "鑽搧绫诲瀷")
+    private String medicationType;
+
+    /** 鎶楄弻鑽墿绛夌骇缂栫爜 */
+    @ApiModelProperty("鎶楄弻鑽墿绛夌骇缂栫爜")
+     @Excel(name = "鎶楄弻鑽墿绛夌骇缂栫爜")
+    private String antimicrobialGradeCode;
+
+    /** 鎵瑰噯鏂囧彿 */
+    @ApiModelProperty("鎵瑰噯鏂囧彿")
+     @Excel(name = "鎵瑰噯鏂囧彿")
+    private String licenseNumber;
+
+    /** 榛樿鐢ㄦ硶鍚嶇О */
+    @ApiModelProperty("榛樿鐢ㄦ硶鍚嶇О")
+     @Excel(name = "榛樿鐢ㄦ硶鍚嶇О")
+    private String defaultUsageName;
+
+    /** 鐗规畩鑽搧鍚嶇О */
+    @ApiModelProperty("鐗规畩鑽搧鍚嶇О")
+     @Excel(name = "鐗规畩鑽搧鍚嶇О")
+    private String specialDrugsName;
+
+    /** 鎶楄弻鑽墿绛夌骇鍚嶇О */
+    @ApiModelProperty("鎶楄弻鑽墿绛夌骇鍚嶇О")
+     @Excel(name = "鎶楄弻鑽墿绛夌骇鍚嶇О")
+    private String antimicrobialGradeName;
+
+    /** 閫氱敤鍚嶆嫾闊� */
+    @ApiModelProperty("閫氱敤鍚嶆嫾闊�")
+     @Excel(name = "閫氱敤鍚嶆嫾闊�")
+    private String commonPinyin;
+
+    /** 閫氱敤鍚嶄簲绗旂爜 */
+    @ApiModelProperty("閫氱敤鍚嶄簲绗旂爜")
+     @Excel(name = "閫氱敤鍚嶄簲绗旂爜")
+    private String commonWbm;
+
+    /** 鐢熶骇鍘傚ID */
+    @ApiModelProperty("鐢熶骇鍘傚ID")
+     @Excel(name = "鐢熶骇鍘傚ID")
+    private Long manufacturerId;
+
+    /** 榛樿鐢ㄦ硶ID */
+    @ApiModelProperty("榛樿鐢ㄦ硶ID")
+     @Excel(name = "榛樿鐢ㄦ硶ID")
+    private Long defaultUsageId;
+
+    /** 瑙勬牸 */
+    @ApiModelProperty("瑙勬牸")
+     @Excel(name = "瑙勬牸")
+    private String specification;
+
+    /** 灏忓寘瑁呭崟浣� */
+    @ApiModelProperty("灏忓寘瑁呭崟浣�")
+     @Excel(name = "灏忓寘瑁呭崟浣�")
+    private String smallPackageUnit;
+
+    /** 鑽搧ID锛堜笟鍔′富閿級 */
+    @ApiModelProperty("鑽搧ID锛堜笟鍔′富閿級")
+     @Excel(name = "鑽搧ID", readConverterExp = "涓�=鍔′富閿�")
+    private Long itemId;
+
+    /** 鐗规畩鑽搧缂栫爜 */
+    @ApiModelProperty("鐗规畩鑽搧缂栫爜")
+     @Excel(name = "鐗规畩鑽搧缂栫爜")
+    private String specialDrugsCode;
+
+    /** 鎷奸煶 */
+    @ApiModelProperty("鎷奸煶")
+     @Excel(name = "鎷奸煶")
+    private String pinyin;
+
+    /** 鍓傚瀷鍚嶇О */
+    @ApiModelProperty("鍓傚瀷鍚嶇О")
+     @Excel(name = "鍓傚瀷鍚嶇О")
+    private String doseFormName;
+
+    /** 澶у寘瑁呭崟浣� */
+    @ApiModelProperty("澶у寘瑁呭崟浣�")
+     @Excel(name = "澶у寘瑁呭崟浣�")
+    private String largePackageUnit;
+
+    /** 闆跺敭浠� */
+    @ApiModelProperty("闆跺敭浠�")
+     @Excel(name = "闆跺敭浠�")
+    private BigDecimal retailPrice;
+
+    /** 鐘舵�� */
+    @ApiModelProperty("鐘舵��")
+     @Excel(name = "鐘舵��")
+    private Long status;
+
+    @ApiModelProperty("鍓傞噺淇℃伅鍒楄〃")
+    private List<MedicationDoseInfo> doseInfoList;
+
+        }
+
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/service/IIcd10Service.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/IIcd10Service.java
new file mode 100644
index 0000000..4619477
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/IIcd10Service.java
@@ -0,0 +1,68 @@
+package com.ruoyi.common.core.service;
+
+import com.ruoyi.common.core.domain.entity.Icd10;
+
+import java.util.List;
+
+/**
+ * 鐤剧梾Service鎺ュ彛
+ *
+ * @author ruoyi
+ * @date 2023-12-21
+ */
+public interface IIcd10Service {
+    /**
+     * 鏌ヨ鐤剧梾
+     *
+     * @param icdid 鐤剧梾涓婚敭
+     * @return 鐤剧梾
+     */
+    public Icd10 selectIcd10ByIcdid(Long icdid);
+
+    /**
+     * 鏌ヨ鐤剧梾鍒楄〃
+     *
+     * @param icd10 鐤剧梾
+     * @return 鐤剧梾闆嗗悎
+     */
+    public List<Icd10> selectIcd10List(Icd10 icd10);
+
+    /**
+     * 鏂板鐤剧梾
+     *
+     * @param icd10 鐤剧梾
+     * @return 缁撴灉
+     */
+    public int insertIcd10(Icd10 icd10);
+
+    /**
+     * 淇敼鐤剧梾
+     *
+     * @param icd10 鐤剧梾
+     * @return 缁撴灉
+     */
+    public int updateIcd10(Icd10 icd10);
+
+    /**
+     * 鎵归噺鍒犻櫎鐤剧梾
+     *
+     * @param icdids 闇�瑕佸垹闄ょ殑鐤剧梾涓婚敭闆嗗悎
+     * @return 缁撴灉
+     */
+    public int deleteIcd10ByIcdids(Long[] icdids);
+
+    /**
+     * 鍒犻櫎鐤剧梾淇℃伅
+     *
+     * @param icdid 鐤剧梾涓婚敭
+     * @return 缁撴灉
+     */
+    public int deleteIcd10ByIcdid(Long icdid);
+
+    /**
+     * 浠庤繙绋嬫帴鍙o紙鏉ユ湭鏉ワ級鍒嗛〉鎷夊彇骞跺閲忓啓鍏� icd10 琛�
+     *
+     * @return 鏂板鏉℃暟
+     */
+    public int queryAddIcd10();
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/service/IMedicationDoseInfoService.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/IMedicationDoseInfoService.java
new file mode 100644
index 0000000..b8fa7f4
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/IMedicationDoseInfoService.java
@@ -0,0 +1,61 @@
+package com.ruoyi.common.core.service;
+
+import java.util.List;
+import com.ruoyi.common.core.domain.entity.MedicationDoseInfo;
+
+/**
+ * 鑽搧鍓傞噺淇℃伅Service鎺ュ彛
+ * 
+ * @author lihu
+ * @date 2026-07-17
+ */
+public interface IMedicationDoseInfoService 
+{
+    /**
+     * 鏌ヨ鑽搧鍓傞噺淇℃伅
+     * 
+     * @param id 鑽搧鍓傞噺淇℃伅涓婚敭
+     * @return 鑽搧鍓傞噺淇℃伅
+     */
+    public MedicationDoseInfo selectMedicationDoseInfoById(Long id);
+
+    /**
+     * 鏌ヨ鑽搧鍓傞噺淇℃伅鍒楄〃
+     * 
+     * @param medicationDoseInfo 鑽搧鍓傞噺淇℃伅
+     * @return 鑽搧鍓傞噺淇℃伅闆嗗悎
+     */
+    public List<MedicationDoseInfo> selectMedicationDoseInfoList(MedicationDoseInfo medicationDoseInfo);
+
+    /**
+     * 鏂板鑽搧鍓傞噺淇℃伅
+     * 
+     * @param medicationDoseInfo 鑽搧鍓傞噺淇℃伅
+     * @return 缁撴灉
+     */
+    public int insertMedicationDoseInfo(MedicationDoseInfo medicationDoseInfo);
+
+    /**
+     * 淇敼鑽搧鍓傞噺淇℃伅
+     * 
+     * @param medicationDoseInfo 鑽搧鍓傞噺淇℃伅
+     * @return 缁撴灉
+     */
+    public int updateMedicationDoseInfo(MedicationDoseInfo medicationDoseInfo);
+
+    /**
+     * 鎵归噺鍒犻櫎鑽搧鍓傞噺淇℃伅
+     * 
+     * @param ids 闇�瑕佸垹闄ょ殑鑽搧鍓傞噺淇℃伅涓婚敭闆嗗悎
+     * @return 缁撴灉
+     */
+    public int deleteMedicationDoseInfoByIds(Long[] ids);
+
+    /**
+     * 鍒犻櫎鑽搧鍓傞噺淇℃伅淇℃伅
+     * 
+     * @param id 鑽搧鍓傞噺淇℃伅涓婚敭
+     * @return 缁撴灉
+     */
+    public int deleteMedicationDoseInfoById(Long id);
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/service/IMedicationItemService.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/IMedicationItemService.java
new file mode 100644
index 0000000..d36e046
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/IMedicationItemService.java
@@ -0,0 +1,61 @@
+package com.ruoyi.common.core.service;
+
+import java.util.List;
+import com.ruoyi.common.core.domain.entity.MedicationItem;
+
+/**
+ * 鑽搧淇℃伅Service鎺ュ彛
+ * 
+ * @author lihu
+ * @date 2026-07-17
+ */
+public interface IMedicationItemService 
+{
+    /**
+     * 鏌ヨ鑽搧淇℃伅
+     * 
+     * @param id 鑽搧淇℃伅涓婚敭
+     * @return 鑽搧淇℃伅
+     */
+    public MedicationItem selectMedicationItemById(Long id);
+
+    /**
+     * 鏌ヨ鑽搧淇℃伅鍒楄〃
+     * 
+     * @param medicationItem 鑽搧淇℃伅
+     * @return 鑽搧淇℃伅闆嗗悎
+     */
+    public List<MedicationItem> selectMedicationItemList(MedicationItem medicationItem);
+
+    /**
+     * 鏂板鑽搧淇℃伅
+     * 
+     * @param medicationItem 鑽搧淇℃伅
+     * @return 缁撴灉
+     */
+    public int insertMedicationItem(MedicationItem medicationItem);
+
+    /**
+     * 淇敼鑽搧淇℃伅
+     * 
+     * @param medicationItem 鑽搧淇℃伅
+     * @return 缁撴灉
+     */
+    public int updateMedicationItem(MedicationItem medicationItem);
+
+    /**
+     * 鎵归噺鍒犻櫎鑽搧淇℃伅
+     * 
+     * @param ids 闇�瑕佸垹闄ょ殑鑽搧淇℃伅涓婚敭闆嗗悎
+     * @return 缁撴灉
+     */
+    public int deleteMedicationItemByIds(Long[] ids);
+
+    /**
+     * 鍒犻櫎鑽搧淇℃伅淇℃伅
+     * 
+     * @param id 鑽搧淇℃伅涓婚敭
+     * @return 缁撴灉
+     */
+    public int deleteMedicationItemById(Long id);
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/service/impl/Icd10ServiceImpl.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/impl/Icd10ServiceImpl.java
new file mode 100644
index 0000000..03613a0
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/impl/Icd10ServiceImpl.java
@@ -0,0 +1,261 @@
+package com.ruoyi.common.core.service.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.google.gson.Gson;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.http.HttpUtils;
+import com.ruoyi.common.core.domain.entity.Icd10;
+import com.ruoyi.common.mapper.Icd10Mapper;
+import com.ruoyi.common.core.service.IIcd10Service;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 鐤剧梾Service涓氬姟灞傚鐞�
+ *
+ * @author ruoyi
+ * @date 2023-12-21
+ */
+@Slf4j
+@Service
+public class Icd10ServiceImpl implements IIcd10Service {
+
+    @Autowired
+    private Icd10Mapper icd10Mapper;
+
+    @Value("${sltd_pub_path}")
+    private String sltdPubPath;
+
+    @Value("${lwl_app_key}")
+    private String APP_KEY;
+
+    /**
+     * 鍥哄畾 orgId
+     */
+    private static final long ORG_ID = 20001001L;
+    /**
+     * 姣忛〉鎷夊彇鏉℃暟锛堟帴鍙f渶澶у厑璁� 1000锛�
+     */
+    private static final int PAGE_SIZE = 1000;
+    /**
+     * 鎵归噺鍐欏叆鍗曟壒鏈�澶ф潯鏁�
+     */
+    private static final int BATCH_SIZE = 500;
+
+    /**
+     * 鏌ヨ鐤剧梾
+     *
+     * @param icdid 鐤剧梾涓婚敭
+     * @return 鐤剧梾
+     */
+    @Override
+    public Icd10 selectIcd10ByIcdid(Long icdid) {
+        return icd10Mapper.selectIcd10ByIcdid(icdid);
+    }
+
+    /**
+     * 鏌ヨ鐤剧梾鍒楄〃
+     *
+     * @param icd10 鐤剧梾
+     * @return 鐤剧梾
+     */
+    @Override
+    public List<Icd10> selectIcd10List(Icd10 icd10) {
+        return icd10Mapper.selectIcd10List(icd10);
+    }
+
+    /**
+     * 鏂板鐤剧梾
+     *
+     * @param icd10 鐤剧梾
+     * @return 缁撴灉
+     */
+    @Override
+    public int insertIcd10(Icd10 icd10) {
+        icd10.setCreateTime(DateUtils.getNowDate());
+        return icd10Mapper.insertIcd10(icd10);
+    }
+
+    /**
+     * 淇敼鐤剧梾
+     *
+     * @param icd10 鐤剧梾
+     * @return 缁撴灉
+     */
+    @Override
+    public int updateIcd10(Icd10 icd10) {
+        icd10.setUpdateTime(DateUtils.getNowDate());
+        return icd10Mapper.updateIcd10(icd10);
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎鐤剧梾
+     *
+     * @param icdids 闇�瑕佸垹闄ょ殑鐤剧梾涓婚敭
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteIcd10ByIcdids(Long[] icdids) {
+        return icd10Mapper.deleteIcd10ByIcdids(icdids);
+    }
+
+    /**
+     * 鍒犻櫎鐤剧梾淇℃伅
+     *
+     * @param icdid 鐤剧梾涓婚敭
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteIcd10ByIcdid(Long icdid) {
+        return icd10Mapper.deleteIcd10ByIcdid(icdid);
+    }
+
+    /**
+     * 浠庤繙绋嬫帴鍙o紙鏉ユ湭鏉ワ級鍒嗛〉鎷夊彇骞跺閲忓啓鍏� icd10 琛ㄣ��
+     * <p>
+     * 浼樺寲瑕佺偣锛�
+     * 1. 姣忛〉鏈�澶ф媺鍙� 1000 鏉★紝鍑忓皯璇锋眰娆℃暟銆�
+     * 2. 姣忛〉鎶婂綋鍓嶉〉鎵�鏈� icdcode 鐢ㄤ竴鏉� IN SQL 涓�娆℃壒閲忔煡搴擄紝閬垮厤寰幆 N 娆″崟鏉℃煡璇€��
+     * 3. 灏嗕笉瀛樺湪鐨勮褰曟敹闆嗗悗鍒嗘壒锛堟瘡鎵� 500 鏉★級鎵归噺鎻掑叆銆�
+     * </p>
+     *
+     * @return 鏈杩愯鏂板鎬绘潯鏁�
+     */
+    @Override
+    public int queryAddIcd10() {
+        int totalInserted = 0;
+        long current = 1;
+        long totalPages = 1; // 棣栨寰幆鍓嶅厛鎵ц涓�娆�
+
+        log.info("[寮�濮媇 浠庢潵鏈潵鎷夊彇 ICD10 澧為噺鏁版嵁锛宱rgId={}", ORG_ID);
+
+        do {
+            // 鏋勫缓璇锋眰浣�
+            Map<String, Object> requestBody = new HashMap<>();
+            requestBody.put("orgId", ORG_ID);
+            requestBody.put("searchText", "");
+            requestBody.put("size", PAGE_SIZE);
+            requestBody.put("current", current);
+
+            String responseStr;
+            try {
+                Map<String, String> headers = buildRequestHeaders();
+                responseStr = HttpUtils.sendPostByHeader(sltdPubPath + "/hbos-thirdparty-integration/standard/base/dtc/pageDiagnosisDict", new Gson().toJson(requestBody), headers);
+
+            } catch (Exception e) {
+                log.error("[绗瑊}椤礭 璇锋眰鏉ユ湭鏉ユ帴鍙eけ璐�", current, e);
+                break;
+            }
+
+            if (responseStr == null || responseStr.isEmpty()) {
+                log.warn("[绗瑊}椤礭 鎺ュ彛杩斿洖绌猴紝鍋滄閲囬泦", current);
+                break;
+            }
+
+            JSONObject resp = JSON.parseObject(responseStr);
+            if (resp == null || !Boolean.TRUE.equals(resp.getBoolean("success"))) {
+                log.error("[绗瑊}椤礭 鎺ュ彛杩斿洖澶辫触锛歿}", current, responseStr);
+                break;
+            }
+
+            JSONObject data = resp.getJSONObject("data");
+            if (data == null) {
+                break;
+            }
+
+            // 璁$畻鎬婚〉鏁�
+            long total = data.getLongValue("total");
+            long pageSize = data.getLongValue("size");
+            if (pageSize <= 0) {
+                break;
+            }
+            totalPages = (total + pageSize - 1) / pageSize;
+
+            JSONArray records = data.getJSONArray("records");
+            if (records == null || records.isEmpty()) {
+                break;
+            }
+
+            log.info("[绗瑊}/{}椤礭 鑾峰彇 {} 鏉¤褰曪紝鎬粄}鏉�", current, totalPages, records.size(), total);
+
+            // 瑙f瀽褰撳墠椤垫暟鎹�
+            List<String> pageNames = new ArrayList<>();
+            List<Icd10> pageList = new ArrayList<>();
+            for (int i = 0; i < records.size(); i++) {
+                JSONObject rec = records.getJSONObject(i);
+                String icdCode = rec.getString("icdCode");
+                if (icdCode == null || icdCode.isEmpty()) {
+                    continue;
+                }
+                String icdName = StringUtils.isEmpty(rec.getString("name")) ? null : rec.getString("name").trim();
+                if (icdName == null || icdName.isEmpty()) {
+                    continue;
+                }
+                pageNames.add(icdName);
+
+                Icd10 icd10 = new Icd10();
+                icd10.setIcdcode(icdCode);
+                icd10.setIcdname(icdName);
+                icd10.setIcdpym(rec.getString("pinyin"));
+                icd10.setHisIcdid(rec.getString("his_icdid"));
+                icd10.setOrgid(String.valueOf(ORG_ID));
+                icd10.setDelFlag("0");
+                icd10.setCreateTime(DateUtils.getNowDate());
+                pageList.add(icd10);
+            }
+
+            if (pageNames.isEmpty()) {
+                current++;
+                continue;
+            }
+
+            // 涓�娆� IN 鏌ュ簱锛岃幏鍙栧凡瀛樺湪鐨� icdname锛堟牳蹇冨幓閲嶄紭鍖栫偣锛夊崡鍗庨檮涓�鐨刬cdname涓嶄細閲嶅锛宨cd10code浼氶噸澶�
+            Set<String> existingNames = new HashSet<>(icd10Mapper.selectExistingIcdcodes(pageNames));
+
+            // 杩囨护鍑轰笉瀛樺湪鐨勮褰�
+            List<Icd10> newList = pageList.stream().filter(item -> !existingNames.contains(item.getIcdname())).collect(Collectors.toList());
+
+            log.info("[绗瑊}/{}椤礭 褰撳墠椤靛叡 {} 鏉★紝宸插瓨鍦� {} 鏉★紝闇�鏂板叆搴� {} 鏉�", current, totalPages, pageList.size(), existingNames.size(), newList.size());
+
+            // 鍒嗘壒鎵归噺鎻掑叆
+            int inserted = batchInsert(newList);
+            totalInserted += inserted;
+
+            current++;
+        } while (current <= totalPages);
+
+        log.info("[瀹屾垚] ICD10 澧為噺閲囬泦缁撴潫锛屾湰娆″叡鏂板 {} 鏉�", totalInserted);
+        return totalInserted;
+    }
+
+    /**
+     * 鍒嗘壒鎵归噺鎻掑叆锛屾瘡鎵规渶澶� BATCH_SIZE 鏉�
+     */
+    private int batchInsert(List<Icd10> list) {
+        if (list == null || list.isEmpty()) {
+            return 0;
+        }
+        int total = 0;
+        int size = list.size();
+        for (int start = 0; start < size; start += BATCH_SIZE) {
+            int end = Math.min(start + BATCH_SIZE, size);
+            total += icd10Mapper.batchIcd10(list.subList(start, end));
+        }
+        return total;
+    }
+
+    private Map<String, String> buildRequestHeaders() {
+        Map<String, String> headers = new HashMap<>();
+        headers.put("Content-Type", "application/json");
+        headers.put("app-key", APP_KEY);
+        return headers;
+    }
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/service/impl/MedicationDoseInfoServiceImpl.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/impl/MedicationDoseInfoServiceImpl.java
new file mode 100644
index 0000000..ed57533
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/impl/MedicationDoseInfoServiceImpl.java
@@ -0,0 +1,96 @@
+package com.ruoyi.common.core.service.impl;
+
+import java.util.List;
+import com.ruoyi.common.utils.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.common.mapper.MedicationDoseInfoMapper;
+import com.ruoyi.common.core.domain.entity.MedicationDoseInfo;
+import com.ruoyi.common.core.service.IMedicationDoseInfoService;
+
+/**
+ * 鑽搧鍓傞噺淇℃伅Service涓氬姟灞傚鐞�
+ * 
+ * @author lihu
+ * @date 2026-07-17
+ */
+@Service
+public class MedicationDoseInfoServiceImpl implements IMedicationDoseInfoService 
+{
+    @Autowired
+    private MedicationDoseInfoMapper medicationDoseInfoMapper;
+
+    /**
+     * 鏌ヨ鑽搧鍓傞噺淇℃伅
+     * 
+     * @param id 鑽搧鍓傞噺淇℃伅涓婚敭
+     * @return 鑽搧鍓傞噺淇℃伅
+     */
+    @Override
+    public MedicationDoseInfo selectMedicationDoseInfoById(Long id)
+    {
+        return medicationDoseInfoMapper.selectMedicationDoseInfoById(id);
+    }
+
+    /**
+     * 鏌ヨ鑽搧鍓傞噺淇℃伅鍒楄〃
+     * 
+     * @param medicationDoseInfo 鑽搧鍓傞噺淇℃伅
+     * @return 鑽搧鍓傞噺淇℃伅
+     */
+    @Override
+    public List<MedicationDoseInfo> selectMedicationDoseInfoList(MedicationDoseInfo medicationDoseInfo)
+    {
+        return medicationDoseInfoMapper.selectMedicationDoseInfoList(medicationDoseInfo);
+    }
+
+    /**
+     * 鏂板鑽搧鍓傞噺淇℃伅
+     * 
+     * @param medicationDoseInfo 鑽搧鍓傞噺淇℃伅
+     * @return 缁撴灉
+     */
+    @Override
+    public int insertMedicationDoseInfo(MedicationDoseInfo medicationDoseInfo)
+    {
+        medicationDoseInfo.setCreateTime(DateUtils.getNowDate());
+        return medicationDoseInfoMapper.insertMedicationDoseInfo(medicationDoseInfo);
+    }
+
+    /**
+     * 淇敼鑽搧鍓傞噺淇℃伅
+     * 
+     * @param medicationDoseInfo 鑽搧鍓傞噺淇℃伅
+     * @return 缁撴灉
+     */
+    @Override
+    public int updateMedicationDoseInfo(MedicationDoseInfo medicationDoseInfo)
+    {
+        medicationDoseInfo.setUpdateTime(DateUtils.getNowDate());
+        return medicationDoseInfoMapper.updateMedicationDoseInfo(medicationDoseInfo);
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎鑽搧鍓傞噺淇℃伅
+     * 
+     * @param ids 闇�瑕佸垹闄ょ殑鑽搧鍓傞噺淇℃伅涓婚敭
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteMedicationDoseInfoByIds(Long[] ids)
+    {
+        return medicationDoseInfoMapper.deleteMedicationDoseInfoByIds(ids);
+    }
+
+    /**
+     * 鍒犻櫎鑽搧鍓傞噺淇℃伅淇℃伅
+     * 
+     * @param id 鑽搧鍓傞噺淇℃伅涓婚敭
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteMedicationDoseInfoById(Long id)
+    {
+        return medicationDoseInfoMapper.deleteMedicationDoseInfoById(id);
+    }
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/service/impl/MedicationItemServiceImpl.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/impl/MedicationItemServiceImpl.java
new file mode 100644
index 0000000..46d0837
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/impl/MedicationItemServiceImpl.java
@@ -0,0 +1,96 @@
+package com.ruoyi.common.core.service.impl;
+
+import java.util.List;
+import com.ruoyi.common.utils.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.common.mapper.MedicationItemMapper;
+import com.ruoyi.common.core.domain.entity.MedicationItem;
+import com.ruoyi.common.core.service.IMedicationItemService;
+
+/**
+ * 鑽搧淇℃伅Service涓氬姟灞傚鐞�
+ * 
+ * @author lihu
+ * @date 2026-07-17
+ */
+@Service
+public class MedicationItemServiceImpl implements IMedicationItemService 
+{
+    @Autowired
+    private MedicationItemMapper medicationItemMapper;
+
+    /**
+     * 鏌ヨ鑽搧淇℃伅
+     * 
+     * @param id 鑽搧淇℃伅涓婚敭
+     * @return 鑽搧淇℃伅
+     */
+    @Override
+    public MedicationItem selectMedicationItemById(Long id)
+    {
+        return medicationItemMapper.selectMedicationItemById(id);
+    }
+
+    /**
+     * 鏌ヨ鑽搧淇℃伅鍒楄〃
+     * 
+     * @param medicationItem 鑽搧淇℃伅
+     * @return 鑽搧淇℃伅
+     */
+    @Override
+    public List<MedicationItem> selectMedicationItemList(MedicationItem medicationItem)
+    {
+        return medicationItemMapper.selectMedicationItemList(medicationItem);
+    }
+
+    /**
+     * 鏂板鑽搧淇℃伅
+     * 
+     * @param medicationItem 鑽搧淇℃伅
+     * @return 缁撴灉
+     */
+    @Override
+    public int insertMedicationItem(MedicationItem medicationItem)
+    {
+        medicationItem.setCreateTime(DateUtils.getNowDate());
+        return medicationItemMapper.insertMedicationItem(medicationItem);
+    }
+
+    /**
+     * 淇敼鑽搧淇℃伅
+     * 
+     * @param medicationItem 鑽搧淇℃伅
+     * @return 缁撴灉
+     */
+    @Override
+    public int updateMedicationItem(MedicationItem medicationItem)
+    {
+        medicationItem.setUpdateTime(DateUtils.getNowDate());
+        return medicationItemMapper.updateMedicationItem(medicationItem);
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎鑽搧淇℃伅
+     * 
+     * @param ids 闇�瑕佸垹闄ょ殑鑽搧淇℃伅涓婚敭
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteMedicationItemByIds(Long[] ids)
+    {
+        return medicationItemMapper.deleteMedicationItemByIds(ids);
+    }
+
+    /**
+     * 鍒犻櫎鑽搧淇℃伅淇℃伅
+     * 
+     * @param id 鑽搧淇℃伅涓婚敭
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteMedicationItemById(Long id)
+    {
+        return medicationItemMapper.deleteMedicationItemById(id);
+    }
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/EmergencyLevelEnum.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/EmergencyLevelEnum.java
new file mode 100644
index 0000000..a1a24fe
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/EmergencyLevelEnum.java
@@ -0,0 +1,77 @@
+package com.ruoyi.common.enums;
+
+/**
+ * 鍏ラ櫌鐢宠绱ф�ョ▼搴︽灇涓�
+ * 
+ * @author ruoyi
+ */
+public enum EmergencyLevelEnum {
+
+    /**
+     * 鐩爣
+     */
+    TARGET("FH0164.01", "鐩爣"),
+
+    /**
+     * 鎬ュ嵄閲�
+     */
+    CRITICAL("FH0164.02", "鎬ュ嵄閲�"),
+
+    /**
+     * 鎷╂湡
+     */
+    ELECTIVE("FH0164.03", "鎷╂湡"),
+
+    /**
+     * 鍩虹
+     */
+    BASIC("FH0164.04", "鍩虹");
+
+    private String code;
+    private String desc;
+
+    EmergencyLevelEnum(String code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public static String getDescByCode(String code) {
+        if (code == null || code.isEmpty()) {
+            return null;
+        }
+        for (EmergencyLevelEnum item : values()) {
+            if (item.getCode().equals(code)) {
+                return item.getDesc();
+            }
+        }
+        return null;
+    }
+
+    public static String getCodeByDesc(String desc) {
+        if (desc == null || desc.isEmpty()) {
+            return null;
+        }
+        for (EmergencyLevelEnum item : values()) {
+            if (item.getDesc().equals(desc)) {
+                return item.getCode();
+            }
+        }
+        return null;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public void setDesc(String desc) {
+        this.desc = desc;
+    }
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/ImportFlagEnum.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/ImportFlagEnum.java
new file mode 100644
index 0000000..cdb2f65
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/ImportFlagEnum.java
@@ -0,0 +1,82 @@
+package com.ruoyi.common.enums;
+
+/**
+ * 鍏ラ櫌鐢宠閲嶈鎬ф爣璁版灇涓�
+ * 
+ * @author ruoyi
+ */
+public enum ImportFlagEnum {
+
+    /**
+     * 鐗圭害浜哄憳
+     */
+    SPECIAL_PERSONNEL("FH0165.98", "鐗圭害浜哄憳"),
+
+    /**
+     * 绂讳紤浜哄憳
+     */
+    RETIRED_CADRE("FH0165.99", "绂讳紤浜哄憳"),
+
+    /**
+     * 鏃�
+     */
+    NONE("FH0165.03", "鏃�"),
+
+    /**
+     * 鏃ラ棿璇婄枟
+     */
+    DAY_TREATMENT("FH0165.02", "鏃ラ棿璇婄枟"),
+
+    /**
+     * 鏃ラ棿鎵嬫湳
+     */
+    DAY_SURGERY("FH0165.01", "鏃ラ棿鎵嬫湳");
+
+    private String code;
+    private String desc;
+
+    ImportFlagEnum(String code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public static String getDescByCode(String code) {
+        if (code == null || code.isEmpty()) {
+            return null;
+        }
+        for (ImportFlagEnum item : values()) {
+            if (item.getCode().equals(code)) {
+                return item.getDesc();
+            }
+        }
+        return null;
+    }
+
+    public static String getCodeByDesc(String desc) {
+        if (desc == null || desc.isEmpty()) {
+            return null;
+        }
+        for (ImportFlagEnum item : values()) {
+            if (item.getDesc().equals(desc)) {
+                return item.getCode();
+            }
+        }
+        return null;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public void setDesc(String desc) {
+        this.desc = desc;
+    }
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/mapper/Icd10Mapper.java b/ruoyi-common/src/main/java/com/ruoyi/common/mapper/Icd10Mapper.java
new file mode 100644
index 0000000..e7a378f
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/mapper/Icd10Mapper.java
@@ -0,0 +1,88 @@
+package com.ruoyi.common.mapper;
+
+import com.ruoyi.common.core.domain.entity.Icd10;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 鐤剧梾Mapper鎺ュ彛
+ *
+ * @author ruoyi
+ * @date 2023-12-21
+ */
+@Mapper
+public interface Icd10Mapper {
+    /**
+     * 鏌ヨ鐤剧梾
+     *
+     * @param icdid 鐤剧梾涓婚敭
+     * @return 鐤剧梾
+     */
+    public Icd10 selectIcd10ByIcdid(Long icdid);
+
+    /**
+     * 鏌ヨ鐤剧梾鍒楄〃
+     *
+     * @param icd10 鐤剧梾
+     * @return 鐤剧梾闆嗗悎
+     */
+    public List<Icd10> selectIcd10List(Icd10 icd10);
+
+    /**
+     * 鏌ヨ鐤剧梾鍒楄〃
+     *
+     * @param icd10 鐤剧梾
+     * @return 鐤剧梾闆嗗悎
+     */
+    public List<Icd10> exactQueryIcd10List(Icd10 icd10);
+
+    /**
+     * 鏂板鐤剧梾
+     *
+     * @param icd10 鐤剧梾
+     * @return 缁撴灉
+     */
+    public int insertIcd10(Icd10 icd10);
+
+    /**
+     * 鎵归噺鏂板鐤剧梾
+     *
+     * @param icd10s 鐤剧梾闆嗗悎
+     * @return 缁撴灉
+     */
+    public int batchIcd10(List<Icd10> icd10s);
+
+    /**
+     * 鎵归噺鏌ヨ宸插瓨鍦ㄧ殑 icdcode锛堢敤浜庡幓閲嶏級
+     *
+     * @param icdcodes icdcode闆嗗悎
+     * @return 宸插瓨鍦ㄧ殑 icdcode 闆嗗悎
+     */
+    public List<String> selectExistingIcdcodes(@Param("icdnames") List<String> icdnames);
+
+    /**
+     * 淇敼鐤剧梾
+     *
+     * @param icd10 鐤剧梾
+     * @return 缁撴灉
+     */
+    public int updateIcd10(Icd10 icd10);
+
+    /**
+     * 鍒犻櫎鐤剧梾
+     *
+     * @param icdid 鐤剧梾涓婚敭
+     * @return 缁撴灉
+     */
+    public int deleteIcd10ByIcdid(Long icdid);
+
+    /**
+     * 鎵归噺鍒犻櫎鐤剧梾
+     *
+     * @param icdids 闇�瑕佸垹闄ょ殑鏁版嵁涓婚敭闆嗗悎
+     * @return 缁撴灉
+     */
+    public int deleteIcd10ByIcdids(Long[] icdids);
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/mapper/MedicationDoseInfoMapper.java b/ruoyi-common/src/main/java/com/ruoyi/common/mapper/MedicationDoseInfoMapper.java
new file mode 100644
index 0000000..abf6a26
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/mapper/MedicationDoseInfoMapper.java
@@ -0,0 +1,63 @@
+package com.ruoyi.common.mapper;
+
+import java.util.List;
+import org.apache.ibatis.annotations.Mapper;
+import com.ruoyi.common.core.domain.entity.MedicationDoseInfo;
+
+/**
+ * 鑽搧鍓傞噺淇℃伅Mapper鎺ュ彛
+ *
+ * @author lihu
+ * @date 2026-07-17
+ */
+@Mapper
+public interface MedicationDoseInfoMapper
+{
+    /**
+     * 鏌ヨ鑽搧鍓傞噺淇℃伅
+     *
+     * @param id 鑽搧鍓傞噺淇℃伅涓婚敭
+     * @return 鑽搧鍓傞噺淇℃伅
+     */
+    public MedicationDoseInfo selectMedicationDoseInfoById(Long id);
+
+    /**
+     * 鏌ヨ鑽搧鍓傞噺淇℃伅鍒楄〃
+     *
+     * @param medicationDoseInfo 鑽搧鍓傞噺淇℃伅
+     * @return 鑽搧鍓傞噺淇℃伅闆嗗悎
+     */
+    public List<MedicationDoseInfo> selectMedicationDoseInfoList(MedicationDoseInfo medicationDoseInfo);
+
+    /**
+     * 鏂板鑽搧鍓傞噺淇℃伅
+     *
+     * @param medicationDoseInfo 鑽搧鍓傞噺淇℃伅
+     * @return 缁撴灉
+     */
+    public int insertMedicationDoseInfo(MedicationDoseInfo medicationDoseInfo);
+
+    /**
+     * 淇敼鑽搧鍓傞噺淇℃伅
+     *
+     * @param medicationDoseInfo 鑽搧鍓傞噺淇℃伅
+     * @return 缁撴灉
+     */
+    public int updateMedicationDoseInfo(MedicationDoseInfo medicationDoseInfo);
+
+    /**
+     * 鍒犻櫎鑽搧鍓傞噺淇℃伅
+     *
+     * @param id 鑽搧鍓傞噺淇℃伅涓婚敭
+     * @return 缁撴灉
+     */
+    public int deleteMedicationDoseInfoById(Long id);
+
+    /**
+     * 鎵归噺鍒犻櫎鑽搧鍓傞噺淇℃伅
+     *
+     * @param ids 闇�瑕佸垹闄ょ殑鏁版嵁涓婚敭闆嗗悎
+     * @return 缁撴灉
+     */
+    public int deleteMedicationDoseInfoByIds(Long[] ids);
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/mapper/MedicationItemMapper.java b/ruoyi-common/src/main/java/com/ruoyi/common/mapper/MedicationItemMapper.java
new file mode 100644
index 0000000..fecab05
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/mapper/MedicationItemMapper.java
@@ -0,0 +1,63 @@
+package com.ruoyi.common.mapper;
+
+import java.util.List;
+import org.apache.ibatis.annotations.Mapper;
+import com.ruoyi.common.core.domain.entity.MedicationItem;
+
+/**
+ * 鑽搧淇℃伅Mapper鎺ュ彛
+ *
+ * @author lihu
+ * @date 2026-07-17
+ */
+@Mapper
+public interface MedicationItemMapper
+{
+    /**
+     * 鏌ヨ鑽搧淇℃伅
+     *
+     * @param id 鑽搧淇℃伅涓婚敭
+     * @return 鑽搧淇℃伅
+     */
+    public MedicationItem selectMedicationItemById(Long id);
+
+    /**
+     * 鏌ヨ鑽搧淇℃伅鍒楄〃
+     *
+     * @param medicationItem 鑽搧淇℃伅
+     * @return 鑽搧淇℃伅闆嗗悎
+     */
+    public List<MedicationItem> selectMedicationItemList(MedicationItem medicationItem);
+
+    /**
+     * 鏂板鑽搧淇℃伅
+     *
+     * @param medicationItem 鑽搧淇℃伅
+     * @return 缁撴灉
+     */
+    public int insertMedicationItem(MedicationItem medicationItem);
+
+    /**
+     * 淇敼鑽搧淇℃伅
+     *
+     * @param medicationItem 鑽搧淇℃伅
+     * @return 缁撴灉
+     */
+    public int updateMedicationItem(MedicationItem medicationItem);
+
+    /**
+     * 鍒犻櫎鑽搧淇℃伅
+     *
+     * @param id 鑽搧淇℃伅涓婚敭
+     * @return 缁撴灉
+     */
+    public int deleteMedicationItemById(Long id);
+
+    /**
+     * 鎵归噺鍒犻櫎鑽搧淇℃伅
+     *
+     * @param ids 闇�瑕佸垹闄ょ殑鏁版嵁涓婚敭闆嗗悎
+     * @return 缁撴灉
+     */
+    public int deleteMedicationItemByIds(Long[] ids);
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DistinctByProperty.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DistinctByProperty.java
new file mode 100644
index 0000000..dc96ac6
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DistinctByProperty.java
@@ -0,0 +1,24 @@
+package com.ruoyi.common.utils;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * 鍘婚噸宸ュ叿绫�
+ */
+public class DistinctByProperty {
+
+    // 鍒涘缓鍘婚噸宸ュ叿鏂规硶锛屾牴鎹泦鍚堜腑涓婂睘鎬�
+    public static <T> List<T> distinctByProperty(
+            List<T> list,
+            Function<? super T, ?> keyExtractor) {
+
+        Set<Object> seen = new HashSet<>();
+        return list.stream()
+                .filter(item -> seen.add(keyExtractor.apply(item)))
+                .collect(Collectors.toList());
+    }
+}
diff --git a/ruoyi-common/src/main/resources/mapper/common/Icd10Mapper.xml b/ruoyi-common/src/main/resources/mapper/common/Icd10Mapper.xml
new file mode 100644
index 0000000..830e562
--- /dev/null
+++ b/ruoyi-common/src/main/resources/mapper/common/Icd10Mapper.xml
@@ -0,0 +1,221 @@
+<?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.ruoyi.common.mapper.Icd10Mapper">
+
+    <resultMap type="com.ruoyi.common.core.domain.entity.Icd10" id="Icd10Result">
+        <result property="icdid" column="icdid"/>
+        <result property="icdcode" column="icdcode"/>
+        <result property="icdname" column="icdname"/>
+        <result property="icdpym" column="icdpym"/>
+        <result property="icdwbm" column="icdwbm"/>
+        <result property="orgid" column="orgid"/>
+        <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="pid" column="pid"/>
+        <result property="guid" column="guid"/>
+        <result property="lastflag" column="lastflag"/>
+        <result property="chimedflag" column="chimedflag"/>
+        <result property="hisIcdid" column="his_icdid"/>
+    </resultMap>
+
+    <sql id="selectIcd10Vo">
+        select icdid,
+               icdcode,
+               his_icdid,
+               lastflag,
+               chimedflag,
+               icdname,
+               icdpym,
+               icdwbm,
+               orgid,
+               del_flag,
+               create_by,
+               create_time,
+               update_by,
+               update_time,
+               pid,
+               guid
+        from icd10
+    </sql>
+
+    <select id="selectIcd10List" parameterType="com.ruoyi.common.core.domain.entity.Icd10" resultMap="Icd10Result">
+        <include refid="selectIcd10Vo"/>
+        where 1=1
+            and del_flag = 0
+            <if test="icdcode != null  and icdcode != ''">and icdcode = #{icdcode}</if>
+            <if test="lastflag != null  and lastflag != ''">and lastflag = #{lastflag}</if>
+            <if test="chimedflag != null  and chimedflag != ''">and chimedflag = #{chimedflag}</if>
+            <if test="icdname != null  and icdname != ''">and icdname like concat('%', #{icdname}, '%')</if>
+            <if test="icdpym != null  and icdpym != ''">and icdpym = #{icdpym}</if>
+            <if test="icdwbm != null  and icdwbm != ''">and icdwbm = #{icdwbm}</if>
+            <if test="orgid != null  and orgid != ''">and orgid = #{orgid}</if>
+            <if test="pid != null ">and pid = #{pid}</if>
+            <if test="guid != null  and guid != ''">and guid = #{guid}</if>
+            <if test="hisIcdid != null  and hisIcdid != ''">and his_icdid = #{hisIcdid}</if>
+            <if test="icdcodes != null and icdcodes.size() > 0">
+                AND icdcode IN
+                <foreach collection="icdcodes" item="icdcodeItem" open="("
+                         separator=","
+                         close=")">
+                    #{icdcodeItem}
+                </foreach>
+            </if>
+    </select>
+
+    <select id="exactQueryIcd10List" parameterType="com.ruoyi.common.core.domain.entity.Icd10" resultMap="Icd10Result">
+        <include refid="selectIcd10Vo"/>
+        where 1=1
+        and del_flag = 0
+        <if test="icdcode != null  and icdcode != ''">and icdcode = #{icdcode}</if>
+        <if test="lastflag != null  and lastflag != ''">and lastflag = #{lastflag}</if>
+        <if test="chimedflag != null  and chimedflag != ''">and chimedflag = #{chimedflag}</if>
+        <if test="icdname != null  and icdname != ''">and icdname = #{icdname} </if>
+        <if test="icdpym != null  and icdpym != ''">and icdpym = #{icdpym}</if>
+        <if test="icdwbm != null  and icdwbm != ''">and icdwbm = #{icdwbm}</if>
+        <if test="orgid != null  and orgid != ''">and orgid = #{orgid}</if>
+        <if test="pid != null ">and pid = #{pid}</if>
+        <if test="guid != null  and guid != ''">and guid = #{guid}</if>
+        <if test="hisIcdid != null  and hisIcdid != ''">and his_icdid = #{hisIcdid}</if>
+        <if test="icdcodes != null and icdcodes.size() > 0">
+            AND icdcode IN
+            <foreach collection="icdcodes" item="icdcodeItem" open="("
+                     separator=","
+                     close=")">
+                #{icdcodeItem}
+            </foreach>
+        </if>
+    </select>
+
+    <select id="selectIcd10ByIcdid" parameterType="Long" resultMap="Icd10Result">
+        <include refid="selectIcd10Vo"/>
+        where icdid = #{icdid}
+    </select>
+
+    <insert id="insertIcd10" parameterType="com.ruoyi.common.core.domain.entity.Icd10" useGeneratedKeys="true" keyProperty="icdid">
+        insert into icd10
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="icdcode != null">icdcode,</if>
+            <if test="icdname != null">icdname,</if>
+            <if test="icdpym != null">icdpym,</if>
+            <if test="icdwbm != null">icdwbm,</if>
+            <if test="orgid != null">orgid,</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="pid != null">pid,</if>
+            <if test="guid != null">guid,</if>
+            <if test="lastflag != null  and lastflag != ''">lastflag,</if>
+            <if test="chimedflag != null  and chimedflag != ''">chimedflag,</if>
+            <if test="hisIcdid != null  and hisIcdid != ''">his_icdid,</if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="icdcode != null">#{icdcode},</if>
+            <if test="icdname != null">#{icdname},</if>
+            <if test="icdpym != null">#{icdpym},</if>
+            <if test="icdwbm != null">#{icdwbm},</if>
+            <if test="orgid != null">#{orgid},</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="pid != null">#{pid},</if>
+            <if test="guid != null">#{guid},</if>
+            <if test="lastflag != null  and lastflag != ''">#{lastflag},</if>
+            <if test="chimedflag != null  and chimedflag != ''">#{chimedflag},</if>
+            <if test="hisIcdid != null  and hisIcdid != ''">#{hisIcdid},</if>
+        </trim>
+    </insert>
+
+    <insert id="batchIcd10">
+        insert into icd10
+        (
+        icdcode,
+        icdname,
+        icdpym,
+        icdwbm,
+        orgid,
+        del_flag,
+        create_by,
+        create_time,
+        update_by,
+        update_time,
+        pid,
+        guid,
+        lastflag,
+        chimedflag,
+        his_icdid)
+        VALUES
+        <foreach collection="list" item="item" separator=",">
+            (
+            #{item.icdcode},
+            #{item.icdname},
+            #{item.icdpym},
+            #{item.icdwbm},
+            #{item.orgid},
+            #{item.delFlag},
+            #{item.createBy},
+            #{item.createTime},
+            #{item.updateBy},
+            #{item.updateTime},
+            #{item.pid},
+            #{item.guid},
+            #{item.lastflag},
+            #{item.chimedflag},
+            #{item.hisIcdid}
+            )
+        </foreach>
+    </insert>
+
+    <update id="updateIcd10" parameterType="com.ruoyi.common.core.domain.entity.Icd10">
+        update icd10
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="icdcode != null">icdcode = #{icdcode},</if>
+            <if test="icdname != null">icdname = #{icdname},</if>
+            <if test="icdpym != null">icdpym = #{icdpym},</if>
+            <if test="icdwbm != null">icdwbm = #{icdwbm},</if>
+            <if test="orgid != null">orgid = #{orgid},</if>
+            <if test="delFlag != null">del_flag = #{delFlag},</if>
+            <if test="createBy != null">create_by = #{createBy},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="pid != null">pid = #{pid},</if>
+            <if test="guid != null">guid = #{guid},</if>
+            <if test="lastflag != null  and lastflag != ''">lastflag = #{lastflag},</if>
+            <if test="chimedflag != null  and chimedflag != ''">chimedflag = #{chimedflag},</if>
+            <if test="hisIcdid != null  and hisIcdid != ''">his_icdid = #{hisIcdid},</if>
+        </trim>
+        where icdid = #{icdid}
+    </update>
+
+    <delete id="deleteIcd10ByIcdid" parameterType="Long">
+        delete
+        from icd10
+        where icdid = #{icdid}
+    </delete>
+
+    <delete id="deleteIcd10ByIcdids" parameterType="String">
+        delete from icd10 where icdid in
+        <foreach item="icdid" collection="array" open="(" separator="," close=")">
+            #{icdid}
+        </foreach>
+    </delete>
+
+    <!-- 鎵归噺鏌ヨ宸插瓨鍦ㄧ殑 icdcode锛岀敤浜庡垎椤垫媺鍙栧悗鍘婚噸 -->
+    <select id="selectExistingIcdcodes" resultType="java.lang.String">
+        select icdname from icd10
+        where del_flag = 0
+          and icdname in
+        <foreach item="icdname" collection="icdnames" open="(" separator="," close=")">
+            #{icdname}
+        </foreach>
+    </select>
+</mapper>
diff --git a/ruoyi-common/src/main/resources/mapper/common/MedicationDoseInfoMapper.xml b/ruoyi-common/src/main/resources/mapper/common/MedicationDoseInfoMapper.xml
new file mode 100644
index 0000000..fd3a4e9
--- /dev/null
+++ b/ruoyi-common/src/main/resources/mapper/common/MedicationDoseInfoMapper.xml
@@ -0,0 +1,172 @@
+<?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.ruoyi.common.mapper.MedicationDoseInfoMapper">
+
+    <resultMap type="com.ruoyi.common.core.domain.entity.MedicationDoseInfo" id="MedicationDoseInfoResult">
+            <result property="id" column="id"/>
+            <result property="medicationItemId" column="medication_item_id"/>
+            <result property="doseSmallPackageRation" column="dose_small_package_ration"/>
+            <result property="dose" column="dose"/>
+            <result property="doseSmallPackageNum" column="dose_small_package_num"/>
+            <result property="doseUnit" column="dose_unit"/>
+            <result property="doseUnitType" column="dose_unit_type"/>
+            <result property="remark" column="remark"/>
+            <result property="createBy" column="create_by"/>
+            <result property="createTime" column="create_time"/>
+            <result property="updateBy" column="update_by"/>
+            <result property="updateTime" column="update_time"/>
+    </resultMap>
+
+    <sql id="selectMedicationDoseInfoVo">
+        select id, medication_item_id, dose_small_package_ration, dose, dose_small_package_num, dose_unit, dose_unit_type, remark, create_by, create_time, update_by, update_time
+        from medication_dose_info
+    </sql>
+
+    <select id="selectMedicationDoseInfoList" parameterType="com.ruoyi.common.core.domain.entity.MedicationDoseInfo"
+            resultMap="MedicationDoseInfoResult">
+        <include refid="selectMedicationDoseInfoVo"/>
+        where 1=1
+            and del_flag=0
+                        <if test="medicationItemId != null ">
+                            and medication_item_id = #{medicationItemId}
+                        </if>
+                        <if test="doseSmallPackageRation != null ">
+                            and dose_small_package_ration = #{doseSmallPackageRation}
+                        </if>
+                        <if test="dose != null ">
+                            and dose = #{dose}
+                        </if>
+                        <if test="doseSmallPackageNum != null ">
+                            and dose_small_package_num = #{doseSmallPackageNum}
+                        </if>
+                        <if test="doseUnit != null  and doseUnit != ''">
+                            and dose_unit = #{doseUnit}
+                        </if>
+                        <if test="doseUnitType != null ">
+                            and dose_unit_type = #{doseUnitType}
+                        </if>
+    </select>
+
+    <select id="selectMedicationDoseInfoById" parameterType="Long"
+            resultMap="MedicationDoseInfoResult">
+            <include refid="selectMedicationDoseInfoVo"/>
+            where id = #{id}
+    </select>
+
+    <insert id="insertMedicationDoseInfo" parameterType="com.ruoyi.common.core.domain.entity.MedicationDoseInfo"            useGeneratedKeys="true" keyProperty="id">
+        insert into medication_dose_info
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+                    <if test="medicationItemId != null">medication_item_id,
+                    </if>
+                    <if test="doseSmallPackageRation != null">dose_small_package_ration,
+                    </if>
+                    <if test="dose != null">dose,
+                    </if>
+                    <if test="doseSmallPackageNum != null">dose_small_package_num,
+                    </if>
+                    <if test="doseUnit != null">dose_unit,
+                    </if>
+                    <if test="doseUnitType != null">dose_unit_type,
+                    </if>
+                    <if test="remark != null">remark,
+                    </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>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+                    <if test="medicationItemId != null">#{medicationItemId},
+                    </if>
+                    <if test="doseSmallPackageRation != null">#{doseSmallPackageRation},
+                    </if>
+                    <if test="dose != null">#{dose},
+                    </if>
+                    <if test="doseSmallPackageNum != null">#{doseSmallPackageNum},
+                    </if>
+                    <if test="doseUnit != null">#{doseUnit},
+                    </if>
+                    <if test="doseUnitType != null">#{doseUnitType},
+                    </if>
+                    <if test="remark != null">#{remark},
+                    </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>
+        </trim>
+    </insert>
+
+    <update id="updateMedicationDoseInfo" parameterType="com.ruoyi.common.core.domain.entity.MedicationDoseInfo">
+        update medication_dose_info
+        <trim prefix="SET" suffixOverrides=",">
+                    <if test="medicationItemId != null">medication_item_id =
+                        #{medicationItemId},
+                    </if>
+                    <if test="doseSmallPackageRation != null">dose_small_package_ration =
+                        #{doseSmallPackageRation},
+                    </if>
+                    <if test="dose != null">dose =
+                        #{dose},
+                    </if>
+                    <if test="doseSmallPackageNum != null">dose_small_package_num =
+                        #{doseSmallPackageNum},
+                    </if>
+                    <if test="doseUnit != null">dose_unit =
+                        #{doseUnit},
+                    </if>
+                    <if test="doseUnitType != null">dose_unit_type =
+                        #{doseUnitType},
+                    </if>
+                    <if test="remark != null">remark =
+                        #{remark},
+                    </if>
+                    <if test="createBy != null">create_by =
+                        #{createBy},
+                    </if>
+                    <if test="createTime != null">create_time =
+                        #{createTime},
+                    </if>
+                    <if test="updateBy != null">update_by =
+                        #{updateBy},
+                    </if>
+                    <if test="updateTime != null">update_time =
+                        #{updateTime},
+                    </if>
+        </trim>
+        where id = #{id}
+    </update>
+
+                <update id="deleteMedicationDoseInfoById" parameterType="Long">
+        update medication_dose_info
+        <trim prefix="SET" suffixOverrides=",">
+            del_flag =1
+        </trim>
+        where  id = #{id}
+    </update>
+
+                            <update id="deleteMedicationDoseInfoByIds" parameterType="String">
+        update  medication_dose_info
+        <trim prefix="SET" suffixOverrides=",">
+            del_flag =1
+        </trim>
+        where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </update>
+
+
+
+
+</mapper>
diff --git a/ruoyi-common/src/main/resources/mapper/common/MedicationItemMapper.xml b/ruoyi-common/src/main/resources/mapper/common/MedicationItemMapper.xml
new file mode 100644
index 0000000..4a972aa
--- /dev/null
+++ b/ruoyi-common/src/main/resources/mapper/common/MedicationItemMapper.xml
@@ -0,0 +1,434 @@
+<?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.ruoyi.common.mapper.MedicationItemMapper">
+
+    <resultMap type="com.ruoyi.common.core.domain.entity.MedicationItem" id="MedicationItemResult">
+        <result property="id" column="id"/>
+        <result property="identificationCode" column="identification_code"/>
+        <result property="wbm" column="wbm"/>
+        <result property="medicationCommonName" column="medication_common_name"/>
+        <result property="manufacturerName" column="manufacturer_name"/>
+        <result property="itemCode" column="item_code"/>
+        <result property="smallLargePackageRatio" column="small_large_package_ratio"/>
+        <result property="remark" column="remark"/>
+        <result property="orgId" column="org_id"/>
+        <result property="itemName" column="item_name"/>
+        <result property="medicationType" column="medication_type"/>
+        <result property="antimicrobialGradeCode" column="antimicrobial_grade_code"/>
+        <result property="licenseNumber" column="license_number"/>
+        <result property="defaultUsageName" column="default_usage_name"/>
+        <result property="specialDrugsName" column="special_drugs_name"/>
+        <result property="antimicrobialGradeName" column="antimicrobial_grade_name"/>
+        <result property="commonPinyin" column="common_pinyin"/>
+        <result property="commonWbm" column="common_wbm"/>
+        <result property="manufacturerId" column="manufacturer_id"/>
+        <result property="defaultUsageId" column="default_usage_id"/>
+        <result property="specification" column="specification"/>
+        <result property="smallPackageUnit" column="small_package_unit"/>
+        <result property="itemId" column="item_id"/>
+        <result property="specialDrugsCode" column="special_drugs_code"/>
+        <result property="pinyin" column="pinyin"/>
+        <result property="doseFormName" column="dose_form_name"/>
+        <result property="largePackageUnit" column="large_package_unit"/>
+        <result property="retailPrice" column="retail_price"/>
+        <result property="status" column="status"/>
+        <result property="createBy" column="create_by"/>
+        <result property="createTime" column="create_time"/>
+        <result property="updateBy" column="update_by"/>
+        <result property="updateTime" column="update_time"/>
+    </resultMap>
+
+    <sql id="selectMedicationItemVo">
+        select id,
+               identification_code,
+               wbm,
+               medication_common_name,
+               manufacturer_name,
+               item_code,
+               small_large_package_ratio,
+               remark,
+               org_id,
+               item_name,
+               medication_type,
+               antimicrobial_grade_code,
+               license_number,
+               default_usage_name,
+               special_drugs_name,
+               antimicrobial_grade_name,
+               common_pinyin,
+               common_wbm,
+               manufacturer_id,
+               default_usage_id,
+               specification,
+               small_package_unit,
+               item_id,
+               special_drugs_code,
+               pinyin,
+               dose_form_name,
+               large_package_unit,
+               retail_price,
+               status,
+               create_by,
+               create_time,
+               update_by,
+               update_time
+        from medication_item
+    </sql>
+
+    <select id="selectMedicationItemList" parameterType="com.ruoyi.common.core.domain.entity.MedicationItem"
+            resultMap="MedicationItemResult">
+        <include refid="selectMedicationItemVo"/>
+        where 1=1
+        and del_flag=0
+        <if test="identificationCode != null  and identificationCode != ''">
+            and identification_code = #{identificationCode}
+        </if>
+        <if test="wbm != null  and wbm != ''">
+            and wbm = #{wbm}
+        </if>
+        <if test="medicationCommonName != null  and medicationCommonName != ''">
+            and medication_common_name like concat('%', #{medicationCommonName}, '%')
+        </if>
+        <if test="manufacturerName != null  and manufacturerName != ''">
+            and manufacturer_name like concat('%', #{manufacturerName}, '%')
+        </if>
+        <if test="itemCode != null  and itemCode != ''">
+            and item_code = #{itemCode}
+        </if>
+        <if test="smallLargePackageRatio != null ">
+            and small_large_package_ratio = #{smallLargePackageRatio}
+        </if>
+        <if test="orgId != null ">
+            and org_id = #{orgId}
+        </if>
+        <if test="itemName != null  and itemName != ''">
+            and item_name like concat('%', #{itemName}, '%')
+        </if>
+        <if test="medicationType != null  and medicationType != ''">
+            and medication_type = #{medicationType}
+        </if>
+        <if test="antimicrobialGradeCode != null  and antimicrobialGradeCode != ''">
+            and antimicrobial_grade_code = #{antimicrobialGradeCode}
+        </if>
+        <if test="licenseNumber != null  and licenseNumber != ''">
+            and license_number = #{licenseNumber}
+        </if>
+        <if test="defaultUsageName != null  and defaultUsageName != ''">
+            and default_usage_name like concat('%', #{defaultUsageName}, '%')
+        </if>
+        <if test="specialDrugsName != null  and specialDrugsName != ''">
+            and special_drugs_name like concat('%', #{specialDrugsName}, '%')
+        </if>
+        <if test="antimicrobialGradeName != null  and antimicrobialGradeName != ''">
+            and antimicrobial_grade_name like concat('%', #{antimicrobialGradeName}, '%')
+        </if>
+        <if test="commonPinyin != null  and commonPinyin != ''">
+            and common_pinyin = #{commonPinyin}
+        </if>
+        <if test="commonWbm != null  and commonWbm != ''">
+            and common_wbm = #{commonWbm}
+        </if>
+        <if test="manufacturerId != null ">
+            and manufacturer_id = #{manufacturerId}
+        </if>
+        <if test="defaultUsageId != null ">
+            and default_usage_id = #{defaultUsageId}
+        </if>
+        <if test="specification != null  and specification != ''">
+            and specification = #{specification}
+        </if>
+        <if test="smallPackageUnit != null  and smallPackageUnit != ''">
+            and small_package_unit = #{smallPackageUnit}
+        </if>
+        <if test="itemId != null ">
+            and item_id = #{itemId}
+        </if>
+        <if test="specialDrugsCode != null  and specialDrugsCode != ''">
+            and special_drugs_code = #{specialDrugsCode}
+        </if>
+        <if test="pinyin != null  and pinyin != ''">
+            and pinyin = #{pinyin}
+        </if>
+        <if test="doseFormName != null  and doseFormName != ''">
+            and dose_form_name like concat('%', #{doseFormName}, '%')
+        </if>
+        <if test="largePackageUnit != null  and largePackageUnit != ''">
+            and large_package_unit = #{largePackageUnit}
+        </if>
+        <if test="retailPrice != null ">
+            and retail_price = #{retailPrice}
+        </if>
+        <if test="status != null ">
+            and status = #{status}
+        </if>
+    </select>
+
+    <select id="selectMedicationItemById" parameterType="Long"
+            resultMap="MedicationItemResult">
+        <include refid="selectMedicationItemVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertMedicationItem" parameterType="com.ruoyi.common.core.domain.entity.MedicationItem" useGeneratedKeys="true"
+            keyProperty="id">
+        insert into medication_item
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="identificationCode != null">identification_code,
+            </if>
+            <if test="wbm != null">wbm,
+            </if>
+            <if test="medicationCommonName != null">medication_common_name,
+            </if>
+            <if test="manufacturerName != null">manufacturer_name,
+            </if>
+            <if test="itemCode != null">item_code,
+            </if>
+            <if test="smallLargePackageRatio != null">small_large_package_ratio,
+            </if>
+            <if test="remark != null">remark,
+            </if>
+            <if test="orgId != null">org_id,
+            </if>
+            <if test="itemName != null">item_name,
+            </if>
+            <if test="medicationType != null">medication_type,
+            </if>
+            <if test="antimicrobialGradeCode != null">antimicrobial_grade_code,
+            </if>
+            <if test="licenseNumber != null">license_number,
+            </if>
+            <if test="defaultUsageName != null">default_usage_name,
+            </if>
+            <if test="specialDrugsName != null">special_drugs_name,
+            </if>
+            <if test="antimicrobialGradeName != null">antimicrobial_grade_name,
+            </if>
+            <if test="commonPinyin != null">common_pinyin,
+            </if>
+            <if test="commonWbm != null">common_wbm,
+            </if>
+            <if test="manufacturerId != null">manufacturer_id,
+            </if>
+            <if test="defaultUsageId != null">default_usage_id,
+            </if>
+            <if test="specification != null">specification,
+            </if>
+            <if test="smallPackageUnit != null">small_package_unit,
+            </if>
+            <if test="itemId != null">item_id,
+            </if>
+            <if test="specialDrugsCode != null">special_drugs_code,
+            </if>
+            <if test="pinyin != null">pinyin,
+            </if>
+            <if test="doseFormName != null">dose_form_name,
+            </if>
+            <if test="largePackageUnit != null">large_package_unit,
+            </if>
+            <if test="retailPrice != null">retail_price,
+            </if>
+            <if test="status != null">status,
+            </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>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="identificationCode != null">#{identificationCode},
+            </if>
+            <if test="wbm != null">#{wbm},
+            </if>
+            <if test="medicationCommonName != null">#{medicationCommonName},
+            </if>
+            <if test="manufacturerName != null">#{manufacturerName},
+            </if>
+            <if test="itemCode != null">#{itemCode},
+            </if>
+            <if test="smallLargePackageRatio != null">#{smallLargePackageRatio},
+            </if>
+            <if test="remark != null">#{remark},
+            </if>
+            <if test="orgId != null">#{orgId},
+            </if>
+            <if test="itemName != null">#{itemName},
+            </if>
+            <if test="medicationType != null">#{medicationType},
+            </if>
+            <if test="antimicrobialGradeCode != null">#{antimicrobialGradeCode},
+            </if>
+            <if test="licenseNumber != null">#{licenseNumber},
+            </if>
+            <if test="defaultUsageName != null">#{defaultUsageName},
+            </if>
+            <if test="specialDrugsName != null">#{specialDrugsName},
+            </if>
+            <if test="antimicrobialGradeName != null">#{antimicrobialGradeName},
+            </if>
+            <if test="commonPinyin != null">#{commonPinyin},
+            </if>
+            <if test="commonWbm != null">#{commonWbm},
+            </if>
+            <if test="manufacturerId != null">#{manufacturerId},
+            </if>
+            <if test="defaultUsageId != null">#{defaultUsageId},
+            </if>
+            <if test="specification != null">#{specification},
+            </if>
+            <if test="smallPackageUnit != null">#{smallPackageUnit},
+            </if>
+            <if test="itemId != null">#{itemId},
+            </if>
+            <if test="specialDrugsCode != null">#{specialDrugsCode},
+            </if>
+            <if test="pinyin != null">#{pinyin},
+            </if>
+            <if test="doseFormName != null">#{doseFormName},
+            </if>
+            <if test="largePackageUnit != null">#{largePackageUnit},
+            </if>
+            <if test="retailPrice != null">#{retailPrice},
+            </if>
+            <if test="status != null">#{status},
+            </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>
+        </trim>
+    </insert>
+
+    <update id="updateMedicationItem" parameterType="com.ruoyi.common.core.domain.entity.MedicationItem">
+        update medication_item
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="identificationCode != null">identification_code =
+                #{identificationCode},
+            </if>
+            <if test="wbm != null">wbm =
+                #{wbm},
+            </if>
+            <if test="medicationCommonName != null">medication_common_name =
+                #{medicationCommonName},
+            </if>
+            <if test="manufacturerName != null">manufacturer_name =
+                #{manufacturerName},
+            </if>
+            <if test="itemCode != null">item_code =
+                #{itemCode},
+            </if>
+            <if test="smallLargePackageRatio != null">small_large_package_ratio =
+                #{smallLargePackageRatio},
+            </if>
+            <if test="remark != null">remark =
+                #{remark},
+            </if>
+            <if test="orgId != null">org_id =
+                #{orgId},
+            </if>
+            <if test="itemName != null">item_name =
+                #{itemName},
+            </if>
+            <if test="medicationType != null">medication_type =
+                #{medicationType},
+            </if>
+            <if test="antimicrobialGradeCode != null">antimicrobial_grade_code =
+                #{antimicrobialGradeCode},
+            </if>
+            <if test="licenseNumber != null">license_number =
+                #{licenseNumber},
+            </if>
+            <if test="defaultUsageName != null">default_usage_name =
+                #{defaultUsageName},
+            </if>
+            <if test="specialDrugsName != null">special_drugs_name =
+                #{specialDrugsName},
+            </if>
+            <if test="antimicrobialGradeName != null">antimicrobial_grade_name =
+                #{antimicrobialGradeName},
+            </if>
+            <if test="commonPinyin != null">common_pinyin =
+                #{commonPinyin},
+            </if>
+            <if test="commonWbm != null">common_wbm =
+                #{commonWbm},
+            </if>
+            <if test="manufacturerId != null">manufacturer_id =
+                #{manufacturerId},
+            </if>
+            <if test="defaultUsageId != null">default_usage_id =
+                #{defaultUsageId},
+            </if>
+            <if test="specification != null">specification =
+                #{specification},
+            </if>
+            <if test="smallPackageUnit != null">small_package_unit =
+                #{smallPackageUnit},
+            </if>
+            <if test="itemId != null">item_id =
+                #{itemId},
+            </if>
+            <if test="specialDrugsCode != null">special_drugs_code =
+                #{specialDrugsCode},
+            </if>
+            <if test="pinyin != null">pinyin =
+                #{pinyin},
+            </if>
+            <if test="doseFormName != null">dose_form_name =
+                #{doseFormName},
+            </if>
+            <if test="largePackageUnit != null">large_package_unit =
+                #{largePackageUnit},
+            </if>
+            <if test="retailPrice != null">retail_price =
+                #{retailPrice},
+            </if>
+            <if test="status != null">status =
+                #{status},
+            </if>
+            <if test="createBy != null">create_by =
+                #{createBy},
+            </if>
+            <if test="createTime != null">create_time =
+                #{createTime},
+            </if>
+            <if test="updateBy != null">update_by =
+                #{updateBy},
+            </if>
+            <if test="updateTime != null">update_time =
+                #{updateTime},
+            </if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <update id="deleteMedicationItemById" parameterType="Long">
+        update medication_item
+        <trim prefix="SET" suffixOverrides=",">
+            del_flag =1
+        </trim>
+        where id = #{id}
+    </update>
+
+    <update id="deleteMedicationItemByIds" parameterType="String">
+        update medication_item
+        <trim prefix="SET" suffixOverrides=",">
+            del_flag =1
+        </trim>
+        where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </update>
+
+
+</mapper>
diff --git a/ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/api/OuthospVisitApiImpl.java b/ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/api/OuthospVisitApiImpl.java
new file mode 100644
index 0000000..8076cde
--- /dev/null
+++ b/ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/api/OuthospVisitApiImpl.java
@@ -0,0 +1,37 @@
+package com.ruoyi.outpatientvisit.api;
+
+import com.ruoyi.common.core.service.OuthospVisitApi;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * {@link OuthospVisitApi} 鐨�<b>褰掑睘妯″潡瀹炵幇</b> 鈥斺�� 闂ㄨ瘖闅忚鎵弿銆�
+ *
+ * <h3>2026-11 褰掑睘璋冩暣</h3>
+ * 杩欎釜濂戠害鍘熷厛鐨勫疄鐜版槸 smartor 渚х殑 {@code OuthospVisitApiBridge}锛屽畠鍙仛涓�浠朵簨锛�
+ * 杞彂鍒� smartor 鐨� {@code IPatMedOuthospService#dealOutpatientInfo}锛岃�岄偅鍙堟槸涓�涓�
+ * 绾鎵橈紙{@code PatMedOuthospServiceDelegatingImpl}锛夆啋 鏈ā鍧楃殑 {@code OuthospDataFacade}銆�
+ * 涔熷氨鏄閾捐矾閲屾湁涓ゅ眰绌鸿浆鍙戙��
+ * <p>
+ * 鐜板湪鎶婂疄鐜扮洿鎺ユ斁鍒拌兘鍔涙墍鍦ㄧ殑妯″潡锛歿@code ruoyi-inhospvisit} 浠嶅彧鐪嬪埌
+ * {@code ruoyi-common} 閲岀殑濂戠害锛堝畠涓嶄緷璧栨湰妯″潡锛夛紝杩愯鏈熺敱 Spring 娉ㄥ叆鏈疄鐜帮紱
+ * smartor 渚ч偅涓ˉ涓� {@code IPatMedOuthospService#dealOutpatientInfo} 涓�骞跺垹闄ゃ��
+ *
+ * <h3>鈿狅笍 寮�鍏冲垽鏂笉鍦ㄦ湰绫�</h3>
+ * 鐪熸鐨勫紑鍏筹紙{@code outpatientvisit.enabled}锛夊湪 {@link OuthospVisitFacade#dealOutpatientInfo()} 閲岋紝
+ * 杩欓噷鎸夋棦鏈夊彛寰勮浆鍙戝埌 {@link OuthospDataFacade}锛堝畠鍐嶈浆鍙戝埌甯﹀畧鍗殑闂ㄩ潰锛夛紝涓嶉噸澶嶅垽鏂�侀伩鍏嶄袱澶勫彛寰勬紓绉汇��
+ */
+@Slf4j
+@Component
+public class OuthospVisitApiImpl implements OuthospVisitApi {
+
+    @Autowired
+    private OuthospDataFacade outhospDataFacade;
+
+    @Override
+    public int dealOutpatientInfo() {
+        Integer n = outhospDataFacade.dealOutpatientInfo();
+        return n == null ? 0 : n;
+    }
+}
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/TaskOpsTriggerApiImpl.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/TaskOpsTriggerApiImpl.java
new file mode 100644
index 0000000..440a5e9
--- /dev/null
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/TaskOpsTriggerApiImpl.java
@@ -0,0 +1,82 @@
+package com.ruoyi.quartz.service.impl;
+
+import com.ruoyi.common.core.service.TaskOpsTriggerApi;
+import com.ruoyi.quartz.service.ICollectHISService;
+import com.ruoyi.quartz.task.RyTask;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * {@link TaskOpsTriggerApi} 鍦� <b>ruoyi-quartz 渚�</b>鐨勫疄鐜� 鈥斺�� 浠诲姟杩愮淮瑙﹀彂鑳藉姏鐨勫綊灞炴柟銆�
+ *
+ * <h3>涓轰粈涔堝疄鐜板湪杩欓噷銆佽�屼笉鍦� ruoyi-task</h3>
+ * 杩� 6 涓兘鍔涙槸銆屾墜宸ヨЕ鍙� HIS 閲囬泦 / 鎴澶勭悊 / 寮傚父鎺ㄩ�併�嶇殑杩愮淮鍏ュ彛锛�
+ * 鍏跺簳灞傚疄鐜� {@link RyTask}锛�2214 琛岋紝娉ㄥ叆绾� 20 涓� smartor service锛�
+ * 涓� {@link ICollectHISService}锛坽@code CollectHISServiceImpl} 869 琛岋級
+ * 閮戒綅浜� quartz 鍩熶笖渚濊禆 smartor 渚ц兘鍔涖��
+ * 鎼埌 {@code ruoyi-task} 浼氬舰鎴� {@code ruoyi-task -> smartor} 纭惊鐜紝
+ * 鍥犳鏀逛负銆屾帴鍙e湪 ruoyi-common銆佸疄鐜扮暀鍦� quartz銆嶇殑<b>绔彛鍙嶈浆</b>锛�
+ * 绔偣褰掑睘鍦� {@code ruoyi-task}銆佸疄鐜板湪杩欓噷锛岀紪璇戞湡鏃犵幆銆�
+ *
+ * <h3>鈿狅笍 鏈被蹇呴』淇濇寔"绾浆鍙�"</h3>
+ * 鍙仛鏂规硶杞彂锛�<b>涓�</b>鍔犲幓閲嶃�佺姸鎬佹満銆侀粯璁ゅ�艰ˉ榻愩�佸紓甯稿悶骞剁瓑浠讳綍鏂伴�昏緫銆�
+ * 閫愭柟娉曚笌鍘� {@code ServiceSubtaskController} 閲岀殑璋冪敤涓�涓�瀵瑰簲锛�
+ * <table border="1">
+ *   <tr><th>鏈被鏂规硶</th><th>鍘熻皟鐢ㄧ偣锛圫erviceSubtaskController锛�</th></tr>
+ *   <tr><td>{@link #syncMedInhosp}</td><td>L956 {@code collectHISService.syncMedInhosp(...)}</td></tr>
+ *   <tr><td>{@link #collectHISYesterday}</td><td>L964 {@code ryTask.collectHISYesterday()}</td></tr>
+ *   <tr><td>{@link #collectHISDaysBefore}</td><td>L971 {@code ryTask.collectHISDaysBefore(day)}</td></tr>
+ *   <tr><td>{@link #syncOper}</td><td>L979 {@code collectHISService.syncOper(...)}</td></tr>
+ *   <tr><td>{@link #dealSubtaskDeadline}</td><td>L987 {@code ryTask.dealSubtaskDeadline(orgid, day)}</td></tr>
+ *   <tr><td>{@link #sendExceptInfo}</td><td>L993 {@code ryTask.sendExceptInfo()}</td></tr>
+ * </table>
+ *
+ * <h3>鈿狅笍 寮傛鍖呰涓嶅湪杩欓噷</h3>
+ * 鍘� Controller 瀵� {@code syncMedInhosp} / {@code syncOper} 鐢�
+ * {@code CompletableFuture.runAsync(...)} 鍖呬簡涓�灞傘�佸叾浣� 4 涓洿鎺ュ悓姝ヨ皟鐢ㄣ��
+ * 閭e眰寮傛灞炰簬<b>璋冪敤鏂�</b>鐨勮涔夛紝淇濈暀鍦ㄦ惉杩囧幓鍚庣殑 {@code ruoyi-task} 鎺у埗鍣ㄩ噷锛�
+ * 鏈被涓�寰�<b>鍚屾</b>杞彂锛屼繚璇�"浣曟椂寮傛"杩欎竴琛屼负涓嶅彉銆�
+ *
+ * @see TaskOpsTriggerApi
+ */
+@Slf4j
+@Component
+public class TaskOpsTriggerApiImpl implements TaskOpsTriggerApi {
+
+    @Autowired
+    private RyTask ryTask;
+
+    @Autowired
+    private ICollectHISService collectHISService;
+
+    @Override
+    public Integer syncMedInhosp(String startTime, String endTime) {
+        return collectHISService.syncMedInhosp(startTime, endTime);
+    }
+
+    @Override
+    public void collectHISYesterday() {
+        ryTask.collectHISYesterday();
+    }
+
+    @Override
+    public void collectHISDaysBefore(Long daysBefore) {
+        ryTask.collectHISDaysBefore(daysBefore);
+    }
+
+    @Override
+    public Integer syncOper(String startTime, String endTime) {
+        return collectHISService.syncOper(startTime, endTime);
+    }
+
+    @Override
+    public void dealSubtaskDeadline(String orgid, Integer deadlineDay) {
+        ryTask.dealSubtaskDeadline(orgid, deadlineDay);
+    }
+
+    @Override
+    public void sendExceptInfo() throws Exception {
+        ryTask.sendExceptInfo();
+    }
+}
diff --git a/ruoyi-satisfaction/src/main/java/com/ruoyi/satisfaction/support/MydOptionRatioPieChartBuilder.java b/ruoyi-satisfaction/src/main/java/com/ruoyi/satisfaction/support/MydOptionRatioPieChartBuilder.java
new file mode 100644
index 0000000..4954445
--- /dev/null
+++ b/ruoyi-satisfaction/src/main/java/com/ruoyi/satisfaction/support/MydOptionRatioPieChartBuilder.java
@@ -0,0 +1,296 @@
+package com.ruoyi.satisfaction.support;
+
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.satisfaction.domain.ServiceSubtaskDetailRatioExport;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
+import org.apache.poi.xssf.usermodel.XSSFDrawing;
+import org.apache.poi.xssf.usermodel.XSSFPicture;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import javax.imageio.ImageIO;
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.geom.Arc2D;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 銆岄棶棰樿鎯呭崰姣旈ゼ鍥俱�峉heet锛氭瘡閬撻涓�涓ゼ鍥撅紙甯︾櫨鍒嗘瘮鏍囩鍜屽浘渚嬶級锛�
+ * 鎺掔増鍙傜収绾歌川鎶ュ憡锛氭瘡棰樹笂鏂规槸棰樼洰锛屽乏杈归ゼ鍥俱�佸彸杈瑰浘渚嬶紝涓�椤� 2 鍒� 脳 3 琛屻��
+ * <p>
+ * 鐢� Java2D 鐢绘垚 PNG 鍐嶈创杩� Excel锛屼繚璇併�岀櫨鍒嗘瘮鐩存帴鏍囧湪鎵囧尯涓娿�嶇殑鏁堟灉涓庢姤鍛婁竴鑷�
+ * 锛圥OI 4.1.2 鐨勫師鐢熷浘琛ㄦ病鏈夋暟鎹爣绛� API锛夈��
+ *
+ * @author smartor
+ */
+public class MydOptionRatioPieChartBuilder implements ExcelUtil.ChartSheetBuilder {
+
+    /** Sheet 鍚嶇О */
+    public static final String SHEET_NAME = "闂璇︽儏鍗犳瘮楗煎浘";
+
+    /** 鍗曞紶鍥剧墖瀹介珮锛堝儚绱狅級 */
+    private static final int IMG_W = 560;
+    private static final int IMG_H = 400;
+
+    /** 姣忓紶鍥惧崰鐢ㄧ殑鍒楁暟 / 琛屾暟锛堢敤浜庡崟鍏冩牸閿氱偣瀹氫綅锛� */
+    private static final int COLS_PER_IMG = 9;
+    private static final int ROWS_PER_IMG = 20;
+
+    /** 姣忚鏀惧嚑寮犲浘 */
+    private static final int PER_ROW = 2;
+
+    /** 鎵囧舰閰嶈壊 */
+    private static final Color[] PALETTE = {
+            new Color(0x5B, 0x8F, 0xF9),
+            new Color(0x61, 0xDD, 0xAA),
+            new Color(0xF6, 0xBD, 0x16),
+            new Color(0x72, 0x62, 0xFD),
+            new Color(0x78, 0xD3, 0xF8),
+            new Color(0x96, 0x61, 0xBC),
+            new Color(0xFF, 0x98, 0x4D),
+            new Color(0xFF, 0x6D, 0x6D)
+    };
+
+    private final List<ServiceSubtaskDetailRatioExport> rows;
+
+    private final String footer;
+
+    public MydOptionRatioPieChartBuilder(List<ServiceSubtaskDetailRatioExport> rows, String footer) {
+        this.rows = rows == null ? new ArrayList<>() : rows;
+        this.footer = footer;
+    }
+
+    @Override
+    public void build(XSSFWorkbook workbook) {
+        if (rows.isEmpty()) {
+            return;
+        }
+        XSSFSheet sheet = workbook.createSheet(SHEET_NAME);
+        sheet.setColumnWidth(0, 12 * 256);
+        for (int i = 1; i < PER_ROW * COLS_PER_IMG; i++) {
+            sheet.setColumnWidth(i, 10 * 256);
+        }
+
+        XSSFDrawing drawing = sheet.createDrawingPatriarch();
+        int slot = 0;
+        for (Map.Entry<String, List<ServiceSubtaskDetailRatioExport>> taskEntry : groupByTask().entrySet()) {
+            // 姣忎釜浠诲姟涓�琛屾爣棰�
+            int titleRow = (slot / PER_ROW) * ROWS_PER_IMG;
+            org.apache.poi.ss.usermodel.Row tr = sheet.getRow(titleRow) == null
+                    ? sheet.createRow(titleRow) : sheet.getRow(titleRow);
+            tr.createCell(0).setCellValue("浠诲姟鍚嶇О锛�" + taskEntry.getKey());
+
+            for (Map.Entry<String, LinkedHashMap<String, Double>> q : groupByQuestion(taskEntry.getValue()).entrySet()) {
+                int col = (slot % PER_ROW) * COLS_PER_IMG;
+                int row = (slot / PER_ROW) * ROWS_PER_IMG + 1;
+                try {
+                    byte[] png = renderPie(q.getKey(), q.getValue());
+                    int picIndex = workbook.addPicture(png, Workbook.PICTURE_TYPE_PNG);
+                    XSSFClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0,
+                            col, row, col + COLS_PER_IMG - 1, row + ROWS_PER_IMG - 2);
+                    XSSFPicture pic = drawing.createPicture(anchor, picIndex);
+                    // 灏哄瀹屽叏鐢遍敋鐐瑰喅瀹氾紙涓嶈 resize()锛屽惁鍒欎細鎸� POI 榛樿鍒楀閲嶇畻锛�
+                } catch (Exception e) {
+                    org.apache.poi.ss.usermodel.Row errRow = sheet.getRow(row) == null
+                            ? sheet.createRow(row) : sheet.getRow(row);
+                    errRow.createCell(col).setCellValue(q.getKey() + "锛氶ゼ鍥剧敓鎴愬け璐�");
+                }
+                slot++;
+            }
+            slot = ((slot + PER_ROW - 1) / PER_ROW) * PER_ROW;
+        }
+
+        // 椤佃剼锛氬尰闄�--绉戝
+        int footerRow = ((slot + PER_ROW - 1) / PER_ROW) * ROWS_PER_IMG + 1;
+        org.apache.poi.ss.usermodel.Row fr = sheet.createRow(footerRow);
+        fr.createCell(0).setCellValue(resolveFooter());
+    }
+
+    /**
+     * 鎸変换鍔″悕绉板垎缁勶紝淇濇寔鍘熸湁椤哄簭
+     */
+    private Map<String, List<ServiceSubtaskDetailRatioExport>> groupByTask() {
+        Map<String, List<ServiceSubtaskDetailRatioExport>> grouped = new LinkedHashMap<>();
+        for (ServiceSubtaskDetailRatioExport row : rows) {
+            String taskName = row.getTaskname() == null ? "" : row.getTaskname();
+            grouped.computeIfAbsent(taskName, k -> new ArrayList<>()).add(row);
+        }
+        return grouped;
+    }
+
+    /**
+     * 棰樼洰 -> (閫夐」 -> 浜烘暟)锛屼繚鎸佸嚭鐜伴『搴�
+     */
+    private Map<String, LinkedHashMap<String, Double>> groupByQuestion(List<ServiceSubtaskDetailRatioExport> taskRows) {
+        Map<String, LinkedHashMap<String, Double>> result = new LinkedHashMap<>();
+        for (ServiceSubtaskDetailRatioExport row : taskRows) {
+            String question = row.getQuestiontext() == null ? "" : row.getQuestiontext();
+            String option = row.getOptionresult() == null ? "" : row.getOptionresult();
+            result.computeIfAbsent(question, k -> new LinkedHashMap<>())
+                    .put(option, parseCount(row.getCount()));
+        }
+        return result;
+    }
+
+    /**
+     * 椤佃剼锛氫紭鍏堢敤浼犲叆鍊硷紝鍚﹀垯鍙栫涓�琛屾暟鎹噷鐨勭瀹ゅ悕绉�
+     */
+    private String resolveFooter() {
+        if (footer != null && !footer.trim().isEmpty()) {
+            return footer;
+        }
+        for (ServiceSubtaskDetailRatioExport row : rows) {
+            if (row.getDeptname() != null && !row.getDeptname().trim().isEmpty()) {
+                return row.getDeptname();
+            }
+            if (row.getLeavehospitaldistrictname() != null && !row.getLeavehospitaldistrictname().trim().isEmpty()) {
+                return row.getLeavehospitaldistrictname();
+            }
+        }
+        return "";
+    }
+
+    /**
+     * 鐢讳竴寮犻ゼ鍥撅細鏍囬 + 楗硷紙鎵囧尯鍐呮爣鐧惧垎姣旓級 + 鍙充晶鍥句緥锛堥�夐」 + 鐧惧垎姣旓級
+     */
+    private byte[] renderPie(String question, LinkedHashMap<String, Double> options) throws Exception {
+        BufferedImage img = new BufferedImage(IMG_W, IMG_H, BufferedImage.TYPE_INT_RGB);
+        Graphics2D g = img.createGraphics();
+        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+        g.setColor(Color.WHITE);
+        g.fillRect(0, 0, IMG_W, IMG_H);
+
+        // 鏍囬锛堟渶澶氫袱琛岋級
+        g.setFont(new Font("Microsoft YaHei", Font.BOLD, 18));
+        g.setColor(new Color(0x33, 0x33, 0x33));
+        drawWrapped(g, question, 16, 28, IMG_W - 32);
+
+        double total = 0D;
+        for (Double v : options.values()) {
+            total += v == null ? 0D : v;
+        }
+
+        int cx = 165;
+        int cy = 235;
+        int r = 120;
+
+        if (total <= 0) {
+            g.setFont(new Font("Microsoft YaHei", Font.PLAIN, 16));
+            g.setColor(new Color(0x99, 0x99, 0x99));
+            g.drawString("鏆傛棤浣滅瓟鏁版嵁", cx - 45, cy);
+        } else {
+            double startAngle = -90D;
+            int i = 0;
+            for (Map.Entry<String, Double> entry : options.entrySet()) {
+                double count = entry.getValue() == null ? 0D : entry.getValue();
+                double angle = count / total * 360D;
+                g.setColor(PALETTE[i % PALETTE.length]);
+                g.fill(new Arc2D.Double(cx - r, cy - r, r * 2D, r * 2D, startAngle, angle, Arc2D.PIE));
+                g.setColor(Color.WHITE);
+                g.setStroke(new BasicStroke(2f));
+                g.draw(new Arc2D.Double(cx - r, cy - r, r * 2D, r * 2D, startAngle, angle, Arc2D.PIE));
+
+                // 鎵囧尯涓棿鏍囩櫨鍒嗘瘮
+                String pct = formatPercent(count, total);
+                double mid = Math.toRadians(startAngle + angle / 2D);
+                int tx = (int) (cx + Math.cos(mid) * r * 0.58);
+                int ty = (int) (cy + Math.sin(mid) * r * 0.58);
+                g.setFont(new Font("Microsoft YaHei", Font.BOLD, 15));
+                g.setColor(Color.WHITE);
+                drawCentered(g, pct, tx, ty);
+
+                startAngle += angle;
+                i++;
+            }
+        }
+
+        // 鍙充晶鍥句緥
+        g.setFont(new Font("Microsoft YaHei", Font.PLAIN, 14));
+        FontMetrics fm = g.getFontMetrics();
+        int lx = cx + r + 42;
+        int ly = cy - r + 30;
+        int i = 0;
+        for (Map.Entry<String, Double> entry : options.entrySet()) {
+            g.setColor(PALETTE[i % PALETTE.length]);
+            g.fillRect(lx, ly - 10, 12, 12);
+            g.setColor(new Color(0x33, 0x33, 0x33));
+            double count = entry.getValue() == null ? 0D : entry.getValue();
+            String text = entry.getKey() + " " + formatPercent(count, total);
+            g.drawString(text, lx + 18, ly);
+            ly += Math.max(22, fm.getHeight() + 4);
+            i++;
+        }
+
+        g.dispose();
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        ImageIO.write(img, "png", bos);
+        return bos.toByteArray();
+    }
+
+    /**
+     * 鐧惧垎姣旓細鎬婚噺涓� 0 鏃舵樉绀� 0.00%
+     */
+    private String formatPercent(double count, double total) {
+        if (total <= 0) {
+            return "0.00%";
+        }
+        return String.format("%.2f%%", count * 100D / total);
+    }
+
+    /**
+     * 鍗曡灞呬腑缁樺埗鏂囨湰
+     */
+    private void drawCentered(Graphics2D g, String text, int cx, int cy) {
+        FontMetrics fm = g.getFontMetrics();
+        g.drawString(text, cx - fm.stringWidth(text) / 2, cy + fm.getAscent() / 2 - 2);
+    }
+
+    /**
+     * 鎸夊搴﹁嚜鍔ㄦ崲琛岀粯鍒舵爣棰�
+     */
+    private void drawWrapped(Graphics2D g, String text, int x, int y, int maxWidth) {
+        if (text == null) {
+            return;
+        }
+        FontMetrics fm = g.getFontMetrics();
+        StringBuilder line = new StringBuilder();
+        int lineY = y;
+        for (int i = 0; i < text.length(); i++) {
+            char ch = text.charAt(i);
+            if (fm.stringWidth(line.toString() + ch) > maxWidth) {
+                g.drawString(line.toString(), x, lineY);
+                lineY += fm.getHeight();
+                line.setLength(0);
+            }
+            line.append(ch);
+        }
+        if (line.length() > 0) {
+            g.drawString(line.toString(), x, lineY);
+        }
+    }
+
+    /**
+     * 鏁伴噺瀛楃涓� -> 鏁板瓧
+     */
+    private double parseCount(String count) {
+        if (count == null || count.trim().isEmpty()) {
+            return 0D;
+        }
+        try {
+            return Double.parseDouble(count.trim());
+        } catch (NumberFormatException e) {
+            return 0D;
+        }
+    }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysDept2Mapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysDept2Mapper.java
new file mode 100644
index 0000000..3fa48bd
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysDept2Mapper.java
@@ -0,0 +1,132 @@
+package com.ruoyi.system.mapper.ext;
+
+import com.ruoyi.common.core.domain.entity.SysDept;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 閮ㄩ棬绠$悊 鏁版嵁灞�
+ *
+ * @author ruoyi
+ */
+@Mapper
+public interface SysDept2Mapper {
+    /**
+     * 鏌ヨ閮ㄩ棬绠$悊鏁版嵁
+     *
+     * @param dept 閮ㄩ棬淇℃伅
+     * @return 閮ㄩ棬淇℃伅闆嗗悎
+     */
+    public List<SysDept> selectDeptList(SysDept dept);
+
+    /**
+     * 鏍规嵁瑙掕壊ID鏌ヨ閮ㄩ棬鏍戜俊鎭�
+     *
+     * @param roleId            瑙掕壊ID
+     * @param deptCheckStrictly 閮ㄩ棬鏍戦�夋嫨椤规槸鍚﹀叧鑱旀樉绀�
+     * @return 閫変腑閮ㄩ棬鍒楄〃
+     */
+    public List<Long> selectDeptListByRoleId(@Param("roleId") Long roleId, @Param("deptCheckStrictly") boolean deptCheckStrictly);
+
+    /**
+     * 鏍规嵁閮ㄩ棬ID鏌ヨ淇℃伅
+     *
+     * @param deptId 閮ㄩ棬ID
+     * @return 閮ㄩ棬淇℃伅
+     */
+    public SysDept selectDeptById(Long deptId);
+
+    public SysDept selectDeptByCode(@Param("deptCode")String deptCode,@Param("orgid")String orgid);
+
+    public SysDept selectDeptByCodeAndCampus(@Param("deptCode")String deptCode,@Param("orgid")String orgid,@Param("campusid")String campusid);
+
+    /**
+     * 鏍规嵁ID鏌ヨ鎵�鏈夊瓙閮ㄩ棬
+     *
+     * @param deptId 閮ㄩ棬ID
+     * @return 閮ㄩ棬鍒楄〃
+     */
+    public List<SysDept> selectChildrenDeptById(Long deptId);
+
+    /**
+     * 鏍规嵁ID鏌ヨ鎵�鏈夊瓙閮ㄩ棬锛堟甯哥姸鎬侊級
+     *
+     * @param deptId 閮ㄩ棬ID
+     * @return 瀛愰儴闂ㄦ暟
+     */
+    public int selectNormalChildrenDeptById(Long deptId);
+
+    /**
+     * 鏄惁瀛樺湪瀛愯妭鐐�
+     *
+     * @param deptId 閮ㄩ棬ID
+     * @return 缁撴灉
+     */
+    public int hasChildByDeptId(Long deptId);
+
+    /**
+     * 鏌ヨ閮ㄩ棬鏄惁瀛樺湪鐢ㄦ埛
+     *
+     * @param deptId 閮ㄩ棬ID
+     * @return 缁撴灉
+     */
+    public int checkDeptExistUser(Long deptId);
+
+    /**
+     * 鏍¢獙閮ㄩ棬鍚嶇О鏄惁鍞竴
+     *
+     * @param deptName 閮ㄩ棬鍚嶇О
+     * @param parentId 鐖堕儴闂↖D
+     * @return 缁撴灉
+     */
+    public SysDept checkDeptNameUnique(@Param("deptName") String deptName, @Param("parentId") Long parentId);
+
+    /**
+     * 鏂板閮ㄩ棬淇℃伅
+     *
+     * @param dept 閮ㄩ棬淇℃伅
+     * @return 缁撴灉
+     */
+    public int insertDept(SysDept dept);
+
+    /**
+     * 鎵归噺鏂板閮ㄩ棬淇℃伅
+     *
+     * @param depts 閮ㄩ棬淇℃伅
+     * @return 缁撴灉
+     */
+    public int batchDept(List<SysDept> depts);
+
+    /**
+     * 淇敼閮ㄩ棬淇℃伅
+     *
+     * @param dept 閮ㄩ棬淇℃伅
+     * @return 缁撴灉
+     */
+    public int updateDept(SysDept dept);
+
+    /**
+     * 淇敼鎵�鍦ㄩ儴闂ㄦ甯哥姸鎬�
+     *
+     * @param deptIds 閮ㄩ棬ID缁�
+     */
+    public void updateDeptStatusNormal(Long[] deptIds);
+
+    /**
+     * 淇敼瀛愬厓绱犲叧绯�
+     *
+     * @param depts 瀛愬厓绱�
+     * @return 缁撴灉
+     */
+    public int updateDeptChildren(@Param("depts") List<SysDept> depts);
+
+    /**
+     * 鍒犻櫎閮ㄩ棬绠$悊淇℃伅
+     *
+     * @param deptId 閮ㄩ棬ID
+     * @return 缁撴灉
+     */
+    public int deleteDeptById(Long deptId);
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysUser2Mapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysUser2Mapper.java
new file mode 100644
index 0000000..6c88a71
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysUser2Mapper.java
@@ -0,0 +1,137 @@
+package com.ruoyi.system.mapper.ext;
+
+import com.ruoyi.common.core.domain.entity.SysUser;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 鐢ㄦ埛琛� 鏁版嵁灞�
+ *
+ * @author ruoyi
+ */
+@Mapper
+public interface SysUser2Mapper {
+    /**
+     * 鏍规嵁鏉′欢鍒嗛〉鏌ヨ鐢ㄦ埛鍒楄〃
+     *
+     * @param sysUser 鐢ㄦ埛淇℃伅
+     * @return 鐢ㄦ埛淇℃伅闆嗗悎淇℃伅
+     */
+    public List<SysUser> selectUserList(SysUser sysUser);
+
+    /**
+     * 鏍规嵁鏉′欢鍒嗛〉鏌ヨ宸查厤鐢ㄦ埛瑙掕壊鍒楄〃
+     *
+     * @param user 鐢ㄦ埛淇℃伅
+     * @return 鐢ㄦ埛淇℃伅闆嗗悎淇℃伅
+     */
+    public List<SysUser> selectAllocatedList(SysUser user);
+
+    /**
+     * 鏍规嵁鏉′欢鍒嗛〉鏌ヨ鏈垎閰嶇敤鎴疯鑹插垪琛�
+     *
+     * @param user 鐢ㄦ埛淇℃伅
+     * @return 鐢ㄦ埛淇℃伅闆嗗悎淇℃伅
+     */
+    public List<SysUser> selectUnallocatedList(SysUser user);
+
+    /**
+     * 閫氳繃鐢ㄦ埛鍚嶆煡璇㈢敤鎴�
+     *
+     * @param userName 鐢ㄦ埛鍚�
+     * @return 鐢ㄦ埛瀵硅薄淇℃伅
+     */
+    public SysUser selectUserByUserName(String userName);
+
+    /**
+     * 閫氳繃鐢ㄦ埛ID鏌ヨ鐢ㄦ埛
+     *
+     * @param userId 鐢ㄦ埛ID
+     * @return 鐢ㄦ埛瀵硅薄淇℃伅
+     */
+    public SysUser selectUserById(Long userId);
+
+    /**
+     * 鏂板鐢ㄦ埛淇℃伅
+     *
+     * @param user 鐢ㄦ埛淇℃伅
+     * @return 缁撴灉
+     */
+    public int insertUser(SysUser user);
+
+    /**
+     * 鎵归噺鏂板鐢ㄦ埛淇℃伅
+     *
+     * @param user 鐢ㄦ埛淇℃伅
+     * @return 缁撴灉
+     */
+    public int batchUser(List<SysUser> users);
+
+    /**
+     * 淇敼鐢ㄦ埛淇℃伅
+     *
+     * @param user 鐢ㄦ埛淇℃伅
+     * @return 缁撴灉
+     */
+    public int updateUser(SysUser user);
+
+    /**
+     * 淇敼鐢ㄦ埛澶村儚
+     *
+     * @param userName 鐢ㄦ埛鍚�
+     * @param avatar   澶村儚鍦板潃
+     * @return 缁撴灉
+     */
+    public int updateUserAvatar(@Param("userName") String userName, @Param("avatar") String avatar);
+
+    /**
+     * 閲嶇疆鐢ㄦ埛瀵嗙爜
+     *
+     * @param userName 鐢ㄦ埛鍚�
+     * @param password 瀵嗙爜
+     * @return 缁撴灉
+     */
+    public int resetUserPwd(@Param("userName") String userName, @Param("password") String password);
+
+    /**
+     * 閫氳繃鐢ㄦ埛ID鍒犻櫎鐢ㄦ埛
+     *
+     * @param userId 鐢ㄦ埛ID
+     * @return 缁撴灉
+     */
+    public int deleteUserById(Long userId);
+
+    /**
+     * 鎵归噺鍒犻櫎鐢ㄦ埛淇℃伅
+     *
+     * @param userIds 闇�瑕佸垹闄ょ殑鐢ㄦ埛ID
+     * @return 缁撴灉
+     */
+    public int deleteUserByIds(Long[] userIds);
+
+    /**
+     * 鏍¢獙鐢ㄦ埛鍚嶇О鏄惁鍞竴
+     *
+     * @param userName 鐢ㄦ埛鍚嶇О
+     * @return 缁撴灉
+     */
+    public SysUser checkUserNameUnique(String userName);
+
+    /**
+     * 鏍¢獙鎵嬫満鍙风爜鏄惁鍞竴
+     *
+     * @param phonenumber 鎵嬫満鍙风爜
+     * @return 缁撴灉
+     */
+    public SysUser checkPhoneUnique(String phonenumber);
+
+    /**
+     * 鏍¢獙email鏄惁鍞竴
+     *
+     * @param email 鐢ㄦ埛閭
+     * @return 缁撴灉
+     */
+    public SysUser checkEmailUnique(String email);
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysUserDeptMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysUserDeptMapper.java
new file mode 100644
index 0000000..7754927
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysUserDeptMapper.java
@@ -0,0 +1,76 @@
+package com.ruoyi.system.mapper.ext;
+
+import com.ruoyi.common.core.domain.entity.SysDept;
+import com.ruoyi.common.core.domain.entity.SysUserDept;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 銆愯濉啓鍔熻兘鍚嶇О銆慚apper鎺ュ彛
+ *
+ * @author lihu
+ * @date 2024-08-29
+ */
+@Mapper
+public interface SysUserDeptMapper {
+    /**
+     * 鏌ヨ銆愯濉啓鍔熻兘鍚嶇О銆�
+     *
+     * @param id 銆愯濉啓鍔熻兘鍚嶇О銆戜富閿�
+     * @return 銆愯濉啓鍔熻兘鍚嶇О銆�
+     */
+    public SysUserDept selectSysUserDeptById(Long id);
+
+    /**
+     * 鏌ヨ銆愯濉啓鍔熻兘鍚嶇О銆戝垪琛�
+     *
+     * @param sysUserDept 銆愯濉啓鍔熻兘鍚嶇О銆�
+     * @return 銆愯濉啓鍔熻兘鍚嶇О銆戦泦鍚�
+     */
+    public List<SysUserDept> selectSysUserDeptList(SysUserDept sysUserDept);
+
+    /**
+     * 鏂板銆愯濉啓鍔熻兘鍚嶇О銆�
+     *
+     * @param sysUserDept 銆愯濉啓鍔熻兘鍚嶇О銆�
+     * @return 缁撴灉
+     */
+    public int insertSysUserDept(SysUserDept sysUserDept);
+
+    /**
+     * 淇敼銆愯濉啓鍔熻兘鍚嶇О銆�
+     *
+     * @param sysUserDept 銆愯濉啓鍔熻兘鍚嶇О銆�
+     * @return 缁撴灉
+     */
+    public int updateSysUserDept(SysUserDept sysUserDept);
+
+    /**
+     * 鍒犻櫎銆愯濉啓鍔熻兘鍚嶇О銆�
+     *
+     * @param id 銆愯濉啓鍔熻兘鍚嶇О銆戜富閿�
+     * @return 缁撴灉
+     */
+    public int deleteSysUserDeptById(Long id);
+
+    /**
+     * 鏍规嵁鐢ㄦ埛ID鏌ヨ閮ㄩ棬淇℃伅
+     *
+     * @param id
+     * @return
+     */
+    public List<SysDept> selectDeptListByUserId(Long id);
+
+    /**
+     * 鎵归噺鍒犻櫎銆愯濉啓鍔熻兘鍚嶇О銆�
+     *
+     * @param ids 闇�瑕佸垹闄ょ殑鏁版嵁涓婚敭闆嗗悎
+     * @return 缁撴灉
+     */
+    public int deleteSysUserDeptByIds(Long[] ids);
+
+    public int deleteSysUserDeptByCode(SysUserDept sysUserDept);
+
+    public int updateSysUserDeptForSync(SysUserDept sysUserDept);
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysUserRole2Mapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysUserRole2Mapper.java
new file mode 100644
index 0000000..23dd5ac
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysUserRole2Mapper.java
@@ -0,0 +1,66 @@
+package com.ruoyi.system.mapper.ext;
+
+import com.ruoyi.common.core.domain.entity.SysUserRole;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 鐢ㄦ埛涓庤鑹插叧鑱旇〃 鏁版嵁灞�
+ *
+ * @author ruoyi
+ */
+@Mapper
+public interface SysUserRole2Mapper {
+    /**
+     * 閫氳繃鐢ㄦ埛ID鍒犻櫎鐢ㄦ埛鍜岃鑹插叧鑱�
+     *
+     * @param userId 鐢ㄦ埛ID
+     * @return 缁撴灉
+     */
+    public int deleteUserRoleByUserId(Long userId);
+
+    /**
+     * 鎵归噺鍒犻櫎鐢ㄦ埛鍜岃鑹插叧鑱�
+     *
+     * @param ids 闇�瑕佸垹闄ょ殑鏁版嵁ID
+     * @return 缁撴灉
+     */
+    public int deleteUserRole(Long[] ids);
+
+    /**
+     * 閫氳繃瑙掕壊ID鏌ヨ瑙掕壊浣跨敤鏁伴噺
+     *
+     * @param roleId 瑙掕壊ID
+     * @return 缁撴灉
+     */
+    public int countUserRoleByRoleId(Long roleId);
+
+    public SysUserRole selectUserRoleByRoleIdAndUserId(@Param("roleId") Long roleId, @Param("userId") Long userId);
+
+    /**
+     * 鎵归噺鏂板鐢ㄦ埛瑙掕壊淇℃伅
+     *
+     * @param userRoleList 鐢ㄦ埛瑙掕壊鍒楄〃
+     * @return 缁撴灉
+     */
+    public int batchUserRole(List<SysUserRole> userRoleList);
+
+    /**
+     * 鍒犻櫎鐢ㄦ埛鍜岃鑹插叧鑱斾俊鎭�
+     *
+     * @param userRole 鐢ㄦ埛鍜岃鑹插叧鑱斾俊鎭�
+     * @return 缁撴灉
+     */
+    public int deleteUserRoleInfo(SysUserRole userRole);
+
+    /**
+     * 鎵归噺鍙栨秷鎺堟潈鐢ㄦ埛瑙掕壊
+     *
+     * @param roleId  瑙掕壊ID
+     * @param userIds 闇�瑕佸垹闄ょ殑鐢ㄦ埛鏁版嵁ID
+     * @return 缁撴灉
+     */
+    public int deleteUserRoleInfos(@Param("roleId") Long roleId, @Param("userIds") Long[] userIds);
+}
diff --git a/ruoyi-system/src/main/resources/mapper/system/ext/SysDept2Mapper.xml b/ruoyi-system/src/main/resources/mapper/system/ext/SysDept2Mapper.xml
new file mode 100644
index 0000000..7a12eb3
--- /dev/null
+++ b/ruoyi-system/src/main/resources/mapper/system/ext/SysDept2Mapper.xml
@@ -0,0 +1,363 @@
+<?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.ruoyi.system.mapper.ext.SysDept2Mapper">
+
+    <resultMap type="com.ruoyi.common.core.domain.entity.SysDept" id="SysDeptResult">
+        <id property="deptId" column="dept_id"/>
+        <id property="hisDeptId" column="his_dept_id"/>
+        <result property="parentId" column="parent_id"/>
+        <result property="hisParentId" column="his_parent_id"/>
+        <result property="ancestors" column="ancestors"/>
+        <result property="deptName" column="dept_name"/>
+        <result property="orderNum" column="order_num"/>
+        <result property="leader" column="leader"/>
+        <result property="phone" column="phone"/>
+        <result property="email" column="email"/>
+        <result property="status" column="status"/>
+        <result property="delFlag" column="del_flag"/>
+        <result property="parentName" column="parent_name"/>
+        <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="deptCode" column="dept_code"/>
+        <result property="deptType" column="dept_type"/>
+        <result property="guid" column="guid"/>
+        <result property="orgid" column="orgid"/>
+        <result property="type" column="type"/>
+        <result property="campusid" column="campusid"/>
+    </resultMap>
+
+    <sql id="selectDeptVo">
+        select d.dept_id,
+               d.dept_code,
+               d.type,
+               d.orgid,
+               d.campusid,
+               d.parent_id,
+               d.ancestors,
+               d.dept_name,
+               d.order_num,
+               d.leader,
+               d.phone,
+               d.dept_type,
+               d.email,
+               d.status,
+               d.del_flag,
+               d.create_by,
+               d.guid,
+               d.create_time,
+               d.his_dept_id,
+               d.his_parent_id
+        from sys_dept d
+    </sql>
+
+    <select id="selectDeptList" parameterType="com.ruoyi.common.core.domain.entity.SysDept" resultMap="SysDeptResult">
+        <include refid="selectDeptVo"/>
+        where d.del_flag = '0'
+        <if test="deptId != null and deptId != 0">
+            AND dept_id = #{deptId}
+        </if>
+        <if test="type != null">
+            AND type = #{type}
+        </if>
+        <if test="parentId != null and parentId != 0">
+            AND parent_id = #{parentId}
+        </if>
+        <if test="deptName != null and deptName != ''">
+            AND dept_name like concat('%', #{deptName}, '%')
+        </if>
+        <if test="status != null and status != ''">
+            AND status = #{status}
+        </if>
+        <if test="deptCode != null and deptCode != ''">
+            AND dept_code = #{deptCode}
+        </if>
+        <if test="deptType != null and deptType != ''">
+            AND dept_type = #{deptType}
+        </if>
+        <if test="hisDeptId != null and hisDeptId != ''">
+            AND his_dept_id = #{hisDeptId}
+        </if>
+        <if test="hisParentId != null and hisParentId != ''">
+            AND his_parent_id = #{hisParentId}
+        </if>
+        <if test="orgid != null and orgid != ''">
+            AND orgid = #{orgid}
+        </if>
+        <if test="campusid != null and campusid != ''">
+            AND campusid = #{campusid}
+        </if>
+        order by d.parent_id, d.order_num
+    </select>
+
+    <select id="selectDeptListByRoleId" resultType="Long">
+        select d.dept_id
+        from sys_dept d
+        left join sys_role_dept rd on d.dept_id = rd.dept_id
+        where rd.role_id = #{roleId}
+        <if test="deptCheckStrictly">
+            and d.dept_id not in (select d.parent_id from sys_dept d inner join sys_role_dept rd on d.dept_id =
+            rd.dept_id and rd.role_id = #{roleId})
+        </if>
+        order by d.parent_id, d.order_num
+    </select>
+
+    <select id="selectDeptById" parameterType="Long" resultMap="SysDeptResult">
+        select d.dept_id,
+               d.parent_id,
+               d.campusid,
+               d.dept_code,
+               d.type,
+               d.dept_type,
+               d.ancestors,
+               d.dept_name,
+               d.order_num,
+               d.leader,
+               d.phone,
+               d.email,
+               d.status,
+               d.orgid,
+               d.his_dept_id,
+               d.his_parent_id
+                   (select dept_name from sys_dept where dept_id = d.parent_id) parent_name
+        from sys_dept d
+        where d.dept_id = #{deptId}
+    </select>
+
+    <select id="selectDeptByCode" parameterType="string" resultMap="SysDeptResult">
+        select d.dept_id,
+        d.parent_id,
+        d.type,
+        d.campusid,
+        d.dept_code,
+        d.dept_type,
+        d.ancestors,
+        d.dept_name,
+        d.order_num,
+        d.leader,
+        d.phone,
+        d.email,
+        d.orgid,
+        d.his_dept_id,
+        d.his_parent_id,
+        d.status
+        from sys_dept d
+        where del_flag = 0
+        <if test="deptCode != null and deptCode != ''">
+            AND dept_code = #{deptCode}
+        </if>
+        <if test="orgid != null and orgid != ''">
+            AND orgid = #{orgid}
+        </if>
+    </select>
+
+    <select id="selectDeptByCodeAndCampus" parameterType="string" resultMap="SysDeptResult">
+        select d.dept_id,
+        d.parent_id,
+        d.type,
+        d.campusid,
+        d.dept_code,
+        d.dept_type,
+        d.ancestors,
+        d.dept_name,
+        d.order_num,
+        d.leader,
+        d.phone,
+        d.email,
+        d.orgid,
+        d.his_dept_id,
+        d.his_parent_id,
+        d.status
+        from sys_dept d
+        where del_flag = 0
+        <if test="deptCode != null and deptCode != ''">
+            AND dept_code = #{deptCode}
+        </if>
+        <if test="orgid != null and orgid != ''">
+            AND orgid = #{orgid}
+        </if>
+        <if test="campusid != null and campusid != ''">
+            AND campusid = #{campusid}
+        </if>
+    </select>
+
+    <select id="checkDeptExistUser" parameterType="Long" resultType="int">
+        select count(1)
+        from sys_user
+        where dept_id = #{deptId}
+          and del_flag = '0'
+    </select>
+
+    <select id="hasChildByDeptId" parameterType="Long" resultType="int">
+        select count(1)
+        from sys_dept
+        where del_flag = '0'
+          and parent_id = #{deptId} limit 1
+    </select>
+
+    <select id="selectChildrenDeptById" parameterType="Long" resultMap="SysDeptResult">
+        select *
+        from sys_dept
+        where find_in_set(#{deptId}, ancestors)
+    </select>
+
+    <select id="selectNormalChildrenDeptById" parameterType="Long" resultType="int">
+        select count(*)
+        from sys_dept
+        where status = 0
+          and del_flag = '0'
+          and find_in_set(#{deptId}, ancestors)
+    </select>
+
+    <select id="checkDeptNameUnique" resultMap="SysDeptResult">
+        <include refid="selectDeptVo"/>
+        where dept_name=#{deptName} and parent_id = #{parentId} and del_flag = '0' limit 1
+    </select>
+
+
+    <insert id="batchDept">
+        insert into sys_dept(
+        dept_id,
+        parent_id,
+        dept_name,
+        ancestors,
+        order_num,
+        leader,
+        phone,
+        email,
+        status,
+        dept_code,
+        dept_type,
+        create_by,
+        his_dept_id,
+        his_parent_id,
+        type,
+        guid,
+        orgid,
+        campusid,
+        create_time
+        ) values
+        <foreach collection="list" item="item" separator=",">
+            (
+            #{item.deptId},
+            #{item.parentId},
+            #{item.deptName},
+            #{item.ancestors},
+            #{item.orderNum},
+            #{item.leader},
+            #{item.phone},
+            #{item.email},
+            #{item.status},
+            #{item.deptCode},
+            #{item.deptType},
+            #{item.createBy},
+            #{item.hisDeptId},
+            #{item.hisParentId},
+            #{item.type},
+            #{item.guid},
+            #{item.orgid},
+            #{item.campusid},
+            now()
+            )
+        </foreach>
+    </insert>
+
+
+    <insert id="insertDept" parameterType="com.ruoyi.common.core.domain.entity.SysDept">
+        insert into sys_dept(
+        <if test="deptId != null and deptId != 0">dept_id,</if>
+        <if test="parentId != null and parentId != 0">parent_id,</if>
+        <if test="deptName != null and deptName != ''">dept_name,</if>
+        <if test="ancestors != null and ancestors != ''">ancestors,</if>
+        <if test="orderNum != null">order_num,</if>
+        <if test="leader != null and leader != ''">leader,</if>
+        <if test="phone != null and phone != ''">phone,</if>
+        <if test="email != null and email != ''">email,</if>
+        <if test="status != null">status,</if>
+        <if test="deptCode != null">dept_code,</if>
+        <if test="deptType != null">dept_type,</if>
+        <if test="createBy != null and createBy != ''">create_by,</if>
+        <if test="hisDeptId != null and hisDeptId != ''">his_dept_id,</if>
+        <if test="hisParentId != null and hisParentId != ''">his_parent_id,</if>
+        <if test="type != null and type != ''">type,</if>
+        <if test="guid != null and guid != ''">guid,</if>
+        <if test="orgid != null and orgid != ''">orgid,</if>
+        <if test="campusid != null and campusid != ''">campusid,</if>
+        create_time
+        )values(
+        <if test="deptId != null and deptId != 0">#{deptId},</if>
+        <if test="parentId != null and parentId != 0">#{parentId},</if>
+        <if test="deptName != null and deptName != ''">#{deptName},</if>
+        <if test="ancestors != null and ancestors != ''">#{ancestors},</if>
+        <if test="orderNum != null">#{orderNum},</if>
+        <if test="leader != null and leader != ''">#{leader},</if>
+        <if test="phone != null and phone != ''">#{phone},</if>
+        <if test="email != null and email != ''">#{email},</if>
+        <if test="status != null">#{status},</if>
+        <if test="deptCode != null">#{deptCode},</if>
+        <if test="deptType != null">#{deptType},</if>
+        <if test="createBy != null and createBy != ''">#{createBy},</if>
+        <if test="hisDeptId != null and hisDeptId != ''">#{hisDeptId},</if>
+        <if test="hisParentId != null and hisParentId != ''">#{hisParentId},</if>
+        <if test="type != null and type != ''">#{type},</if>
+        <if test="guid != null and guid != ''">#{guid},</if>
+        <if test="orgid != null and orgid != ''">#{orgid},</if>
+        <if test="campusid != null and campusid != ''">#{campusid},</if>
+        sysdate()
+        )
+    </insert>
+
+    <update id="updateDept" parameterType="com.ruoyi.common.core.domain.entity.SysDept">
+        update sys_dept
+        <set>
+            <if test="parentId != null and parentId != 0">parent_id = #{parentId},</if>
+            <if test="deptName != null and deptName != ''">dept_name = #{deptName},</if>
+            <if test="ancestors != null and ancestors != ''">ancestors = #{ancestors},</if>
+            <if test="orderNum != null">order_num = #{orderNum},</if>
+            <if test="leader != null">leader = #{leader},</if>
+            <if test="phone != null">phone = #{phone},</if>
+            <if test="orgid != null">orgid = #{orgid},</if>
+            <if test="email != null">email = #{email},</if>
+            <if test="status != null and status != ''">status = #{status},</if>
+            <if test="deptCode != null">dept_code=#{deptCode},</if>
+            <if test="deptType != null">dept_type=#{deptType},</if>
+            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+            <if test="hisParentId != null and hisParentId != ''">his_parent_id = #{hisParentId},</if>
+            <if test="hisDeptId != null and hisDeptId != ''">his_dept_id = #{hisDeptId},</if>
+            <if test="type != null and type != ''">type = #{type},</if>
+            <if test="campusid != null and campusid != ''">campusid = #{campusid},</if>
+            update_time = sysdate()
+        </set>
+        where dept_id = #{deptId}
+    </update>
+
+    <update id="updateDeptChildren" parameterType="java.util.List">
+        update sys_dept set ancestors =
+        <foreach collection="depts" item="item" index="index"
+                 separator=" " open="case dept_id" close="end">
+            when #{item.deptId} then #{item.ancestors}
+        </foreach>
+        where dept_id in
+        <foreach collection="depts" item="item" index="index"
+                 separator="," open="(" close=")">
+            #{item.deptId}
+        </foreach>
+    </update>
+
+    <update id="updateDeptStatusNormal" parameterType="Long">
+        update sys_dept set status = '0' where dept_id in
+        <foreach collection="array" item="deptId" open="(" separator="," close=")">
+            #{deptId}
+        </foreach>
+    </update>
+
+    <delete id="deleteDeptById" parameterType="Long">
+        update sys_dept
+        set del_flag = '1'
+        where dept_id = #{deptId}
+    </delete>
+
+</mapper>
diff --git a/ruoyi-system/src/main/resources/mapper/system/ext/SysUser2Mapper.xml b/ruoyi-system/src/main/resources/mapper/system/ext/SysUser2Mapper.xml
new file mode 100644
index 0000000..12680dc
--- /dev/null
+++ b/ruoyi-system/src/main/resources/mapper/system/ext/SysUser2Mapper.xml
@@ -0,0 +1,432 @@
+<?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.ruoyi.system.mapper.ext.SysUser2Mapper">
+
+    <resultMap type="com.ruoyi.common.core.domain.entity.SysUser" id="SysUserResult">
+        <id property="userId" column="user_id"/>
+        <id property="hisUserId" column="his_user_id"/>
+        <result property="deptId" column="dept_id"/>
+        <result property="userCode" column="user_code"/>
+        <result property="hospInfo" column="hosp_info"/>
+        <result property="deptInfo" column="dept_info"/>
+        <result property="userName" column="user_name"/>
+        <result property="nickName" column="nick_name"/>
+        <result property="email" column="email"/>
+        <result property="phonenumber" column="phonenumber"/>
+        <result property="sex" column="sex"/>
+        <result property="avatar" column="avatar"/>
+        <result property="password" column="password"/>
+        <result property="status" column="status"/>
+        <result property="delFlag" column="del_flag"/>
+        <result property="loginIp" column="login_ip"/>
+        <result property="loginDate" column="login_date"/>
+        <result property="createBy" column="create_by"/>
+        <result property="idCard" column="id_card"/>
+        <result property="createTime" column="create_time"/>
+        <result property="updateBy" column="update_by"/>
+        <result property="title" column="title"/>
+        <result property="birthday" column="birthday"/>
+        <result property="jobPhone" column="job_phone"/>
+        <result property="updateTime" column="update_time"/>
+        <result property="remark" column="remark"/>
+        <result property="userType" column="user_type"/>
+        <result property="searchscope" column="searchscope"/>
+        <result property="guid" column="guid"/>
+        <result property="orgid" column="orgid"/>
+        <result property="campusid" column="campusid"/>
+        <association property="dept" column="dept_id" javaType="com.ruoyi.common.core.domain.entity.SysDept"
+                     resultMap="deptResult"/>
+        <collection property="roles" javaType="java.util.List" resultMap="RoleResult"/>
+    </resultMap>
+
+    <resultMap id="deptResult" type="com.ruoyi.common.core.domain.entity.SysDept">
+        <id property="deptId" column="dept_id"/>
+        <result property="parentId" column="parent_id"/>
+        <result property="deptName" column="dept_name"/>
+        <result property="ancestors" column="ancestors"/>
+        <result property="orderNum" column="order_num"/>
+        <result property="leader" column="leader"/>
+        <result property="status" column="dept_status"/>
+    </resultMap>
+
+    <resultMap id="RoleResult" type="com.ruoyi.common.core.domain.entity.SysRole">
+        <id property="roleId" column="role_id"/>
+        <result property="roleName" column="role_name"/>
+        <result property="roleKey" column="role_key"/>
+        <result property="roleSort" column="role_sort"/>
+        <result property="dataScope" column="data_scope"/>
+        <result property="status" column="role_status"/>
+    </resultMap>
+
+    <sql id="selectUserVo">
+        select u.user_id,
+               u.dept_id,
+               u.user_code,
+               u.id_card,
+               u.hosp_info,
+               u.dept_info,
+               u.searchscope,
+               u.user_name,
+               u.user_type,
+               u.nick_name,
+               u.title,
+               u.his_user_id,
+               u.job_phone,
+               u.birthday,
+               u.email,
+               u.avatar,
+               u.phonenumber,
+               u.password,
+               u.sex,
+               u.status,
+               u.del_flag,
+               u.login_ip,
+               u.login_date,
+               u.create_by,
+               u.create_time,
+               u.remark,
+               u.guid,
+               u.orgid,
+               u.campusid,
+               d.dept_id,
+               d.parent_id,
+               d.ancestors,
+               d.dept_name,
+               d.order_num,
+               d.leader,
+               d.status as dept_status,
+               r.role_id,
+               r.role_name,
+               r.role_key,
+               r.role_sort,
+               r.data_scope,
+               r.status as role_status
+        from sys_user u
+                 left join sys_user_org o on o.user_id = u.user_id
+                 left join sys_dept d on u.dept_id = d.dept_id
+                 left join sys_user_role ur on u.user_id = ur.user_id
+                 left join sys_role r on r.role_id = ur.role_id
+    </sql>
+
+    <select id="selectUserList" parameterType="com.ruoyi.common.core.domain.entity.SysUser" resultMap="SysUserResult">
+        select u.user_code,u.user_id,u.title,u.his_user_id,u.hosp_info,u.dept_info, u.searchscope, u.dept_id,
+        u.user_type,
+        u.nick_name, u.user_name,
+        u.email,u.id_card,
+        u.avatar,
+        u.birthday,
+        u.job_phone,
+        u.phonenumber, u.sex, u.status,
+        u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,
+        u.orgid, u.campusid,
+        d.dept_name, d.leader
+        from sys_user u
+        left join sys_dept d on u.dept_id = d.dept_id
+        where u.del_flag = '0'
+        <if test="orgid != null and orgid != ''">
+            AND u.orgid = #{orgid}
+        </if>
+        <if test="userId != null and userId != 0">
+            AND u.user_id = #{userId}
+        </if>
+        <if test="userName != null and userName != ''">
+            AND u.user_name = #{userName}
+        </if>
+        <if test="nickName != null and nickName != ''">
+            AND u.nick_name like concat('%', #{nickName}, '%')
+        </if>
+        <if test="status != null and status != ''">
+            AND u.status = #{status}
+        </if>
+        <if test="idCard != null and idCard != ''">
+            AND u.id_card = #{idCard}
+        </if>
+        <if test="title != null and title != ''">
+            AND u.title = #{title}
+        </if>
+        <if test="jobPhone != null and jobPhone != ''">
+            AND u.job_phone = #{jobPhone}
+        </if>
+        <if test="birthday != null and birthday != ''">
+            AND u.birthday = #{birthday}
+        </if>
+        <if test="hisUserId != null and hisUserId != ''">
+            AND u.his_user_id = #{hisUserId}
+        </if>
+        <if test="phonenumber != null and phonenumber != ''">
+            AND u.phonenumber like concat('%', #{phonenumber}, '%')
+        </if>
+        <if test="params.beginTime != null and params.beginTime != ''"><!-- 寮�濮嬫椂闂存绱� -->
+            AND date_format(u.create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
+        </if>
+        <if test="params.endTime != null and params.endTime != ''"><!-- 缁撴潫鏃堕棿妫�绱� -->
+            AND date_format(u.create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
+        </if>
+        <if test="deptId != null and deptId != 0">
+            AND (u.dept_id = #{deptId} OR u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(#{deptId},
+            ancestors) ))
+        </if>
+        <!-- 鏁版嵁鑼冨洿杩囨护 -->
+        ${params.dataScope}
+    </select>
+
+    <select id="selectAllocatedList" parameterType="com.ruoyi.common.core.domain.entity.SysUser"
+            resultMap="SysUserResult">
+        select distinct u.user_id,u.his_user_id,u.title,u.hosp_info,u.dept_info, u.searchscope, u.dept_id, u.user_name,
+        u.user_type,u.user_code,
+        u.nick_name,
+        u.email,
+        u.birthday,
+        u.job_phone,
+        u.phonenumber, u.status,u.id_card,
+        u.create_time
+        from sys_user u
+        left join sys_dept d on u.dept_id = d.dept_id
+        left join sys_user_role ur on u.user_id = ur.user_id
+        left join sys_role r on r.role_id = ur.role_id
+        where u.del_flag = '0' and r.role_id = #{roleId}
+        <if test="userName != null and userName != ''">
+            AND u.user_name like concat('%', #{userName}, '%')
+        </if>
+        <if test="phonenumber != null and phonenumber != ''">
+            AND u.phonenumber like concat('%', #{phonenumber}, '%')
+        </if>
+        <if test="title != null and title != ''">
+            AND u.title = #{title}
+        </if>
+        <if test="hisUserId != null and hisUserId != ''">
+            AND u.his_user_id = #{hisUserId}
+        </if>
+        <!-- 鏁版嵁鑼冨洿杩囨护 -->
+        ${params.dataScope}
+    </select>
+
+    <select id="selectUnallocatedList" parameterType="com.ruoyi.common.core.domain.entity.SysUser"
+            resultMap="SysUserResult">
+        select distinct u.user_id, u.dept_id, u.hosp_info, u.dept_info,u.searchscope, u.user_name, u.user_type,
+        u.nick_name,
+        u.user_code,
+        u.email,
+        u.his_user_id,
+        u.title,
+        u.job_phone,
+        u.birthday,
+        u.phonenumber, u.status,u.id_card,
+        u.create_time
+        from sys_user u
+        left join sys_dept d on u.dept_id = d.dept_id
+        left join sys_user_role ur on u.user_id = ur.user_id
+        left join sys_role r on r.role_id = ur.role_id
+        where u.del_flag = '0' and (r.role_id != #{roleId} or r.role_id IS NULL)
+        and u.user_id not in (select u.user_id from sys_user u inner join sys_user_role ur on u.user_id = ur.user_id and
+        ur.role_id = #{roleId})
+        <if test="userName != null and userName != ''">
+            AND u.user_name like concat('%', #{userName}, '%')
+        </if>
+        <if test="phonenumber != null and phonenumber != ''">
+            AND u.phonenumber like concat('%', #{phonenumber}, '%')
+        </if>
+        <!-- 鏁版嵁鑼冨洿杩囨护 -->
+        ${params.dataScope}
+    </select>
+
+    <select id="selectUserByUserName" parameterType="String" resultMap="SysUserResult">
+        <include refid="selectUserVo"/>
+        where u.user_name = #{userName} and u.del_flag = '0'
+    </select>
+
+    <select id="selectUserById" parameterType="Long" resultMap="SysUserResult">
+        <include refid="selectUserVo"/>
+        where u.user_id = #{userId}
+    </select>
+
+    <select id="checkUserNameUnique" parameterType="String" resultMap="SysUserResult">
+        select user_id, user_name
+        from sys_user
+        where user_name = #{userName}
+          and del_flag = '0' limit 1
+    </select>
+
+    <select id="checkPhoneUnique" parameterType="String" resultMap="SysUserResult">
+        select user_id, phonenumber
+        from sys_user
+        where phonenumber = #{phonenumber}
+          and del_flag = '0' limit 1
+    </select>
+
+    <select id="checkEmailUnique" parameterType="String" resultMap="SysUserResult">
+        select user_id, email
+        from sys_user
+        where email = #{email}
+          and del_flag = '0' limit 1
+    </select>
+
+    <insert id="insertUser" parameterType="com.ruoyi.common.core.domain.entity.SysUser" useGeneratedKeys="true"
+            keyProperty="userId">
+        insert into sys_user(
+        <if test="userId != null and userId != 0">user_id,</if>
+        <if test="deptId != null and deptId != 0">dept_id,</if>
+        <if test="userName != null and userName != ''">user_name,</if>
+        <if test="nickName != null and nickName != ''">nick_name,</if>
+        <if test="email != null and email != ''">email,</if>
+        <if test="avatar != null and avatar != ''">avatar,</if>
+        <if test="phonenumber != null and phonenumber != ''">phonenumber,</if>
+        <if test="sex != null and sex != ''">sex,</if>
+        <if test="password != null and password != ''">password,</if>
+        <if test="status != null and status != ''">status,</if>
+        <if test="createBy != null and createBy != ''">create_by,</if>
+        <if test="remark != null and remark != ''">remark,</if>
+        <if test="userType != null and userType != ''">user_type,</if>
+        <if test="deptInfo != null and deptInfo != ''">dept_info,</if>
+        <if test="hospInfo != null and hospInfo != ''">hosp_info,</if>
+        <if test="searchscope != null and searchscope != ''">searchscope,</if>
+        <if test="idCard != null and idCard != ''">id_card,</if>
+        <if test="title != null and title != ''">title,</if>
+        <if test="jobPhone != null and jobPhone != ''">job_phone,</if>
+        <if test="birthday != null ">birthday,</if>
+        <if test="hisUserId != null and hisUserId != ''">his_user_id,</if>
+        <if test="guid != null and guid != ''">guid,</if>
+        <if test="deptCode != null and deptCode != ''">dept_code,</if>
+        <if test="deptName != null and deptName != ''">dept_name,</if>
+        <if test="userCode != null and userCode != ''">user_code,</if>
+        <if test="orgid != null and orgid != ''">orgid,</if>
+        <if test="campusid != null and campusid != ''">campusid,</if>
+        create_time
+        )values(
+        <if test="userId != null and userId != 0">#{userId},</if>
+        <if test="deptId != null and deptId != 0">#{deptId},</if>
+        <if test="userName != null and userName != ''">#{userName},</if>
+        <if test="nickName != null and nickName != ''">#{nickName},</if>
+        <if test="email != null and email != ''">#{email},</if>
+        <if test="avatar != null and avatar != ''">#{avatar},</if>
+        <if test="phonenumber != null and phonenumber != ''">#{phonenumber},</if>
+        <if test="sex != null and sex != ''">#{sex},</if>
+        <if test="password != null and password != ''">#{password},</if>
+        <if test="status != null and status != ''">#{status},</if>
+        <if test="createBy != null and createBy != ''">#{createBy},</if>
+        <if test="remark != null and remark != ''">#{remark},</if>
+        <if test="userType != null and userType != ''">#{userType},</if>
+        <if test="deptInfo != null and deptInfo != ''">#{deptInfo},</if>
+        <if test="hospInfo != null and hospInfo != ''">#{hospInfo},</if>
+        <if test="searchscope != null and searchscope != ''">#{searchscope},</if>
+        <if test="idCard != null and idCard != ''">#{idCard},</if>
+        <if test="title != null and title != ''">#{title},</if>
+        <if test="jobPhone != null and jobPhone != ''">#{jobPhone},</if>
+        <if test="birthday != null">#{birthday},</if>
+        <if test="hisUserId != null and hisUserId != ''">#{hisUserId},</if>
+        <if test="guid != null and guid != ''">#{guid},</if>
+        <if test="deptCode != null and deptCode != ''">#{deptCode},</if>
+        <if test="deptName != null and deptName != ''">#{deptName},</if>
+        <if test="userCode != null and userCode != ''">#{userCode},</if>
+        <if test="orgid != null and orgid != ''">#{orgid},</if>
+        <if test="campusid != null and campusid != ''">#{campusid},</if>
+        sysdate()
+        )
+    </insert>
+
+
+    <insert id="batchUser" useGeneratedKeys="true" keyProperty="userId">
+        insert into sys_user(
+        user_id, dept_id, user_name, nick_name, email, avatar, phonenumber, sex, password, status, create_by, remark,
+        user_type, dept_info, hosp_info, searchscope, id_card, title, job_phone, birthday, his_user_id, guid,
+        user_code, orgid, campusid,
+        create_time
+        ) values
+        <foreach collection="list" item="item" separator=",">
+            (
+            #{item.userId},
+            #{item.deptId},
+            #{item.userName},
+            #{item.nickName},
+            #{item.email},
+            #{item.avatar},
+            #{item.phonenumber},
+            #{item.sex},
+            #{item.password},
+            #{item.status},
+            #{item.createBy},
+            #{item.remark},
+            #{item.userType},
+            #{item.deptInfo},
+            #{item.hospInfo},
+            #{item.searchscope},
+            #{item.idCard},
+            #{item.title},
+            #{item.jobPhone},
+            #{item.birthday},
+            #{item.hisUserId},
+            #{item.guid},
+            #{item.userCode},
+            #{item.orgid},
+            #{item.campusid},
+            now()
+            )
+        </foreach>
+    </insert>
+
+    <update id="updateUser" parameterType="com.ruoyi.common.core.domain.entity.SysUser">
+        update sys_user
+        <set>
+            <if test="deptId != null and deptId != 0">dept_id = #{deptId},</if>
+            <if test="userName != null and userName != ''">user_name = #{userName},</if>
+            <if test="nickName != null and nickName != ''">nick_name = #{nickName},</if>
+            <if test="email != null ">email = #{email},</if>
+            <if test="phonenumber != null ">phonenumber = #{phonenumber},</if>
+            <if test="sex != null and sex != ''">sex = #{sex},</if>
+            <if test="avatar != null and avatar != ''">avatar = #{avatar},</if>
+            <if test="password != null and password != ''">password = #{password},</if>
+            <if test="status != null and status != ''">status = #{status},</if>
+            <if test="loginIp != null and loginIp != ''">login_ip = #{loginIp},</if>
+            <if test="loginDate != null">login_date = #{loginDate},</if>
+            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+            <if test="remark != null">remark = #{remark},</if>
+            <if test="userType != null">user_type = #{userType},</if>
+            <if test="searchscope != null">searchscope = #{searchscope},</if>
+            <if test="deptInfo != null and deptInfo != ''">dept_info=#{deptInfo},</if>
+            <if test="hospInfo != null and hospInfo != ''">hosp_info=#{hospInfo},</if>
+            <if test="idCard != null and idCard != ''">id_card=#{idCard},</if>
+            <if test="jobPhone != null and jobPhone != ''">job_phone=#{jobPhone},</if>
+            <if test="birthday != null ">birthday=#{birthday},</if>
+            <if test="userCode != null ">user_code=#{userCode},</if>
+            <if test="title != null and title != ''">
+                title = #{title},
+            </if>
+            <if test="hisUserId != null and hisUserId != ''">
+                his_user_id = #{hisUserId},
+            </if>
+            <if test="orgid != null and orgid != ''">orgid = #{orgid},</if>
+            <if test="campusid != null and campusid != ''">campusid = #{campusid},</if>
+            update_time = sysdate()
+        </set>
+        where user_id = #{userId}
+    </update>
+
+
+    <update id="updateUserAvatar" parameterType="com.ruoyi.common.core.domain.entity.SysUser">
+        update sys_user
+        set avatar = #{avatar}
+        where user_name = #{userName}
+    </update>
+
+    <update id="resetUserPwd" parameterType="com.ruoyi.common.core.domain.entity.SysUser">
+        update sys_user
+        set password = #{password}
+        where user_name = #{userName}
+    </update>
+
+    <delete id="deleteUserById" parameterType="Long">
+        update sys_user
+        set del_flag = '1'
+        where user_id = #{userId}
+    </delete>
+
+    <delete id="deleteUserByIds" parameterType="Long">
+        update sys_user set del_flag = '1' where user_id in
+        <foreach collection="array" item="userId" open="(" separator="," close=")">
+            #{userId}
+        </foreach>
+    </delete>
+
+</mapper>
diff --git a/ruoyi-system/src/main/resources/mapper/system/ext/SysUserDeptMapper.xml b/ruoyi-system/src/main/resources/mapper/system/ext/SysUserDeptMapper.xml
new file mode 100644
index 0000000..36a01d2
--- /dev/null
+++ b/ruoyi-system/src/main/resources/mapper/system/ext/SysUserDeptMapper.xml
@@ -0,0 +1,295 @@
+<?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.ruoyi.system.mapper.ext.SysUserDeptMapper">
+
+    <resultMap type="com.ruoyi.common.core.domain.entity.SysUserDept" id="SysUserDeptResult">
+        <result property="id" column="id"/>
+        <result property="orgid" column="orgid"/>
+        <result property="userId" column="user_id"/>
+        <result property="deptId" column="dept_id"/>
+        <result property="delFlag" column="del_flag"/>
+        <result property="deptCode" column="dept_code"/>
+        <result property="deptName" column="dept_name"/>
+        <result property="deptType" column="dept_type"/>
+        <result property="createTime" column="create_time"/>
+        <result property="updateTime" column="update_time"/>
+        <result property="campusid" column="campusid"/>
+    </resultMap>
+
+    <resultMap id="deptResult" type="com.ruoyi.common.core.domain.entity.SysDept">
+        <id property="deptId" column="dept_id"/>
+        <result property="parentId" column="parent_id"/>
+        <result property="deptName" column="dept_name"/>
+        <result property="ancestors" column="ancestors"/>
+        <result property="orderNum" column="order_num"/>
+        <result property="leader" column="leader"/>
+        <result property="status" column="dept_status"/>
+        <result property="campusid" column="campusid"/>
+    </resultMap>
+
+    <sql id="selectSysUserDeptVo">
+        select id,
+               user_id,
+               dept_id,
+               del_flag,
+               campusid,
+               orgid,
+               create_time,
+               update_time,
+               dept_code,
+               dept_name,
+               dept_type
+        from sys_user_dept
+    </sql>
+
+    <select id="selectSysUserDeptList" parameterType="com.ruoyi.common.core.domain.entity.SysUserDept"
+            resultMap="SysUserDeptResult">
+        <include refid="selectSysUserDeptVo"/>
+        where 1=1
+            and del_flag = 0
+            and orgid=#{orgid}
+            <if test="userId != null ">
+                and user_id = #{userId}
+            </if>
+            <if test="deptId != null ">
+                and dept_id = #{deptId}
+            </if>
+            <if test="delFlag != null ">
+                and del_flag = #{delFlag}
+            </if>
+            <if test="createTime != null ">
+                and create_time = #{createTime}
+            </if>
+            <if test="updateTime != null ">
+                and update_time = #{updateTime}
+            </if>
+            <if test="deptType != null ">
+                and dept_type = #{deptType}
+            </if>
+            <if test="deptCode != null ">
+                and dept_code = #{deptCode}
+            </if>
+            <if test="campusid != null ">
+                and campusid = #{campusid}
+            </if>
+    </select>
+
+    <select id="selectSysUserDeptById" parameterType="Long"
+            resultMap="SysUserDeptResult">
+        <include refid="selectSysUserDeptVo"/>
+        where id = #{id}
+    </select>
+
+    <!--
+        HIS 鍚屾浠诲姟姣忚窇涓�娆″氨鎶婂悓涓�鐢ㄦ埛/绉戝/鐥呭尯鍐嶆彃涓�閬嶏紝鍘嗗彶涓婃妸 sys_user_dept 鎾戝埌 157 涓囪
+        锛堢湡姝d笉鍚岀殑鍙湁 4 涓囧鏉★級銆傝繖閲屾敼鎴� insert ... select ... where not exists锛�
+        鍚屼竴 user_id + dept_type + dept_code 宸叉湁鏈夋晥璁板綍鏃剁洿鎺ヨ烦杩囷紝閲嶅鎵ц涔熶笉浼氫骇鐢熻剰鏁版嵁銆�
+    -->
+    <insert id="insertSysUserDept" parameterType="com.ruoyi.common.core.domain.entity.SysUserDept"
+            useGeneratedKeys="true" keyProperty="id">
+        insert into sys_user_dept
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="userId != null">user_id,
+            </if>
+            <if test="deptId != null">dept_id,
+            </if>
+            <if test="delFlag != null ">
+                del_flag,
+            </if>
+            <if test="createTime != null ">
+                create_time,
+            </if>
+            <if test="updateTime != null ">
+                update_time,
+            </if>
+            <if test="deptType != null ">
+                dept_type,
+            </if>
+            <if test="deptCode != null ">
+                dept_code,
+            </if>
+            <if test="deptName != null ">
+                dept_name,
+            </if>
+            <if test="orgid != null ">
+                orgid,
+            </if>
+            <if test="userCode != null ">
+                user_code,
+            </if>
+            <if test="campusid != null ">
+                campusid,
+            </if>
+        </trim>
+        select
+        <trim suffixOverrides=",">
+            <if test="userId != null">#{userId},
+            </if>
+            <if test="deptId != null">#{deptId},
+            </if>
+            <if test="delFlag != null ">
+                #{delFlag},
+            </if>
+            <if test="createTime != null ">
+                #{createTime},
+            </if>
+            <if test="updateTime != null ">
+                #{updateTime},
+            </if>
+            <if test="deptType != null ">
+                #{deptType},
+            </if>
+            <if test="deptCode != null ">
+                #{deptCode},
+            </if>
+            <if test="deptName != null ">
+                #{deptName},
+            </if>
+            <if test="orgid != null ">
+                #{orgid},
+            </if>
+            <if test="userCode != null ">
+                #{userCode},
+            </if>
+            <if test="campusid != null ">
+                #{campusid},
+            </if>
+        </trim>
+        from dual
+        where not exists (
+            select 1 from sys_user_dept
+            where user_id = #{userId}
+              and ifnull(dept_type, '') = ifnull(#{deptType}, '')
+              and ifnull(dept_code, '') = ifnull(#{deptCode}, '')
+              and ifnull(del_flag, 0) = 0
+        )
+    </insert>
+
+    <update id="updateSysUserDept" parameterType="com.ruoyi.common.core.domain.entity.SysUserDept">
+        update sys_user_dept
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="userId != null">user_id =
+                #{userId},
+            </if>
+            <if test="deptId != null">dept_id =
+                #{deptId},
+            </if>
+            <if test="delFlag != null ">
+                del_flag = #{delFlag},
+            </if>
+            <if test="createTime != null ">
+                create_time = #{createTime},
+            </if>
+            <if test="updateTime != null ">
+                update_time = #{updateTime},
+            </if>
+            <if test="deptType != null ">
+                dept_type = #{deptType},
+            </if>
+            <if test="deptCode != null ">
+                dept_code = #{deptCode},
+            </if>
+            <if test="deptName != null ">
+                dept_name = #{deptName},
+            </if>
+            <if test="orgid != null ">
+                orgid = #{orgid},
+            </if>
+            <if test="campusid != null ">
+                campusid = #{campusid},
+            </if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <update id="deleteSysUserDeptById" parameterType="Long">
+        update sys_user_dept
+        <trim prefix="SET" suffixOverrides=",">
+            del_flag =1
+        </trim>
+        where id = #{id}
+    </update>
+
+    <select id="selectDeptListByUserId" resultMap="deptResult">
+        select d.dept_id,
+               d.dept_name,
+               d.parent_id,
+               d.ancestors,
+               d.dept_name,
+               d.order_num,
+               d.campusid,
+               d.leader,
+               d.orgid,
+               d.status as dept_status
+        from sys_dept d
+                 left join sys_user_dept ud on d.dept_code = ud.dept_code
+        where ud.user_id = #{userId}
+          and ud.del_flag = 0
+          and d.del_flag = 0
+    </select>
+    <update id="deleteSysUserDeptByIds" parameterType="String">
+        update sys_user_dept
+        <trim prefix="SET" suffixOverrides=",">
+            del_flag =1,
+            update_time = sysdate()
+        </trim>
+        where user_id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </update>
+
+
+    <update id="deleteSysUserDeptByCode" parameterType="String">
+        update sys_user_dept
+        <trim prefix="SET" suffixOverrides=",">
+            del_flag =1,
+            update_time = sysdate()
+        </trim>
+        where del_flag = 0
+            and user_id =#{userId}
+            and dept_code=#{deptCode}
+    </update>
+
+    <update id="updateSysUserDeptForSync" parameterType="com.ruoyi.common.core.domain.entity.SysUserDept">
+        update sys_user_dept
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="deptId != null">dept_id =
+                #{deptId},
+            </if>
+            <if test="delFlag != null ">
+                del_flag = #{delFlag},
+            </if>
+            <if test="createTime != null ">
+                create_time = #{createTime},
+            </if>
+            <if test="updateTime != null ">
+                update_time = #{updateTime},
+            </if>
+            <if test="deptType != null ">
+                dept_type = #{deptType},
+            </if>
+            <if test="deptCode != null ">
+                dept_code = #{deptCode},
+            </if>
+            <if test="deptName != null ">
+                dept_name = #{deptName},
+            </if>
+            <if test="orgid != null ">
+                orgid = #{orgid},
+            </if>
+            <if test="campusid != null ">
+                campusid = #{campusid},
+            </if>
+        </trim>
+        where user_id =#{userId}
+        <if test="orgid != null and orgid != ''">
+            and orgid = #{orgid}
+        </if>
+        <if test="campusid != null and campusid != ''">
+            and campusid = #{campusid}
+        </if>
+    </update>
+</mapper>
diff --git a/ruoyi-system/src/main/resources/mapper/system/ext/SysUserRole2Mapper.xml b/ruoyi-system/src/main/resources/mapper/system/ext/SysUserRole2Mapper.xml
new file mode 100644
index 0000000..7bf0e0b
--- /dev/null
+++ b/ruoyi-system/src/main/resources/mapper/system/ext/SysUserRole2Mapper.xml
@@ -0,0 +1,62 @@
+<?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.ruoyi.system.mapper.ext.SysUserRole2Mapper">
+
+    <resultMap type="com.ruoyi.common.core.domain.entity.SysUserRole" id="SysUserRoleResult">
+        <result property="userId" column="user_id"/>
+        <result property="roleId" column="role_id"/>
+        <result property="guid" column="guid"/>
+        <result property="orgid" column="orgid"/>
+    </resultMap>
+
+    <delete id="deleteUserRoleByUserId" parameterType="Long">
+        delete
+        from sys_user_role
+        where user_id = #{userId}
+    </delete>
+
+    <select id="countUserRoleByRoleId" resultType="Integer">
+        select count(1)
+        from sys_user_role
+        where role_id = #{roleId}
+    </select>
+
+    <select id="selectUserRoleByRoleIdAndUserId" resultMap="SysUserRoleResult">
+        select user_id,role_id,guid,orgid
+        from sys_user_role
+        where 1=1
+            <if test="roleId != null ">and role_id = #{roleId}</if>
+            <if test="userId != null ">and user_id = #{userId}</if>
+
+    </select>
+
+    <delete id="deleteUserRole" parameterType="Long">
+        delete from sys_user_role where user_id in
+        <foreach collection="array" item="userId" open="(" separator="," close=")">
+            #{userId}
+        </foreach>
+    </delete>
+
+    <insert id="batchUserRole">
+        insert into sys_user_role(user_id, role_id,guid,orgid) values
+        <foreach item="item" index="index" collection="list" separator=",">
+            (#{item.userId},#{item.roleId},#{item.guid},#{item.orgid})
+        </foreach>
+    </insert>
+
+    <delete id="deleteUserRoleInfo" parameterType="SysUserRole">
+        delete
+        from sys_user_role
+        where user_id = #{userId}
+          and role_id = #{roleId}
+    </delete>
+
+    <delete id="deleteUserRoleInfos">
+        delete from sys_user_role where role_id=#{roleId} and user_id in
+        <foreach collection="userIds" item="userId" open="(" separator="," close=")">
+            #{userId}
+        </foreach>
+    </delete>
+</mapper>
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/api/contract/OperationSubtaskWriter.java b/ruoyi-task/src/main/java/com/ruoyi/task/api/contract/OperationSubtaskWriter.java
new file mode 100644
index 0000000..bb0f11a
--- /dev/null
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/api/contract/OperationSubtaskWriter.java
@@ -0,0 +1,730 @@
+package com.ruoyi.task.api.contract;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.ruoyi.common.core.domain.entity.PatArchive;
+import com.ruoyi.common.core.service.dto.PatMedReq;
+import com.ruoyi.common.core.service.dto.PatMedRes;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.DtoConversionUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.inhospvisit.api.InhospVisitFacade;
+import com.ruoyi.inhospvisit.domain.PatMedInhosp;
+import com.ruoyi.inhospvisit.domain.PatMedInhospDTO;
+import com.ruoyi.inhospvisit.domain.PatMedInhospVO;
+import com.ruoyi.othervisit.api.OtherVisitFacade;
+import com.ruoyi.othervisit.domain.PatMedOperationItem;
+import com.ruoyi.task.api.spi.TaskArchivePort;
+import com.ruoyi.task.common.ScheduleSubtaskBuilder;
+import com.ruoyi.task.domain.ServiceSubtask;
+import com.ruoyi.task.domain.ServiceSubtaskPreachform;
+import com.ruoyi.task.domain.ServiceTask;
+import com.ruoyi.task.domain.ServiceTaskRule;
+import com.ruoyi.task.domain.ServiceTaskSchedule;
+import com.ruoyi.task.domain.ServiceTaskScheduleDetail;
+import com.ruoyi.task.domain.ServiceTaskdept;
+import com.ruoyi.task.domain.ServiceTaskdiag;
+import com.ruoyi.task.domain.ServiceTaskoper;
+import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
+import com.ruoyi.task.mapper.ServiceSubtaskMapper;
+import com.ruoyi.task.mapper.ServiceSubtaskPreachformMapper;
+import com.ruoyi.task.mapper.ServiceTaskMapper;
+import com.ruoyi.task.mapper.ServiceTaskRuleMapper;
+import com.ruoyi.task.mapper.ServiceTaskScheduleDetailMapper;
+import com.ruoyi.task.mapper.ServiceTaskScheduleMapper;
+import com.ruoyi.task.mapper.ServiceTaskdeptMapper;
+import com.ruoyi.task.mapper.ServiceTaskdiagMapper;
+import com.ruoyi.task.mapper.ServiceTaskoperMapper;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Service;
+import org.springframework.util.ObjectUtils;
+
+/**
+ * 鎵嬫湳闅忚鍒嗘敮 + 浠诲姟缁勯杞撼鍏ャ��
+ *
+ * <h3>浣嶇疆鍙樻洿鍙诧紙涓ゆ鍥為��鍚庢墠钀藉湪杩欓噷锛屽埆鍐嶉殢鎵嬫尓锛�</h3>
+ * <ol>
+ *   <li>鍘熸湰鍦� {@code PatMedInhospServiceImpl}锛堢害 1600 琛岋級閲岋紝2026-11 鍏堟暣绨囨娊鍒�
+ *       {@code smartor/.../impl/op/OperationSubtaskWriter}锛屽師鏂规硶鏀规垚涓�琛屽鎵� 鈥斺��
+ *       <b>鏂规硶浣撻�愬瓧鏈彉</b>锛�</li>
+ *   <li>灏濊瘯鏁寸被鎼繘 {@code ruoyi-othervisit} 鈫� <b>Maven 鐜�</b>锛�
+ *       {@code task -> othervisit -> patarchive -> task}锛�</li>
+ *   <li>鏀圭敤 {@code ruoyi-task} + {@code TaskArchivePort} 鏂幆锛屼絾褰撴椂鐢ㄨ剼鏈憳鏂规硶
+ *       鎶� 1600 琛岀殑绫绘敼鍧忥紝宸插洖閫�锛�</li>
+ *   <li>鏈疆锛堢 7 杞級钀藉湪 {@code ruoyi-task}锛氬畠鏈潵灏辨嫢鏈�
+ *       {@code service_task*} / {@code service_subtask*} 鐨勫啓鑳藉姏锛屼笖宸蹭緷璧�
+ *       {@code inhospvisit} / {@code othervisit}锛岃妗f璧� {@link TaskArchivePort}锛�
+ *       <b>涓嶄緷璧� ruoyi-patarchive</b>锛岀幆褰诲簳娑堝け銆�</li>
+ * </ol>
+ *
+ * <h3>鈿狅笍 鍙惉浣嶇疆锛屼笉鏀归�昏緫</h3>
+ * 鏂规硶浣撲笌鍦� smartor 鏃堕�愬瓧涓�鑷达紱鍞竴鐨勬暟鎹闂浛鎹㈡槸
+ * {@code patArchiveMapper.selectPatArchiveByPatid(..)} 鈫� {@code taskArchivePort.findByPatid(..)}
+ * 锛坽@code TaskArchivePortImpl} 閲屽氨鏄悓涓�涓� {@code PatArchiveMapper} 鐨勭函杞彂锛夈��
+ *
+ * <h3>璋佸湪璋冨畠</h3>
+ * 鍙粡 {@link OtherVisitApiImpl}锛堟湰鍖呭唴锛夊澶栵紝鍗� {@code ruoyi-common} 鐨�
+ * {@code OtherVisitApi} 濂戠害锛泂martor 渚х殑 {@code OtherVisitApiBridge} 宸插垹闄わ紝
+ * {@code IPatMedInhospService} 涓婄殑 3 涓繃娓″叆鍙d篃宸蹭竴骞跺垹闄ゃ��
+ */
+@Slf4j
+@Component
+public class OperationSubtaskWriter {
+
+    @Value("${spring.profiles.active}")
+    private String active;
+
+    @Autowired
+    private InhospVisitFacade inhospVisitFacade;
+
+    @Autowired
+    private OtherVisitFacade otherVisitFacade;
+
+    @Autowired
+    private TaskArchivePort taskArchivePort;
+
+    @Autowired
+    private ScheduleSubtaskBuilder scheduleSubtaskBuilder;
+
+    @Autowired
+    private ServiceSubtaskMapper serviceSubtaskMapper;
+
+    @Autowired
+    private ServiceSubtaskPreachformMapper serviceSubtaskPreachformMapper;
+
+    @Autowired
+    private ServiceTaskMapper serviceTaskMapper;
+
+    @Autowired
+    private ServiceTaskScheduleDetailMapper serviceTaskScheduleDetailMapper;
+
+    @Autowired
+    private ServiceTaskScheduleMapper serviceTaskScheduleMapper;
+
+    @Autowired
+    private ServiceTaskoperMapper serviceTaskoperMapper;
+
+    public int dealOutHospInfoBySchedule(String config) {
+
+        ServiceTask serviceTaskVo = new ServiceTask();
+        serviceTaskVo.setLongTask(1);
+        serviceTaskVo.setAppltype("5");
+        List<ServiceTask> serviceTasks = serviceTaskMapper.selectServiceTaskList(serviceTaskVo);
+
+        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();
+
+                        List<String> icd10CodeList = new ArrayList<>();
+                        List<String> opLevelCodeList = new ArrayList<>();
+                        List<String> deptCodeList = new ArrayList<>();
+                        List<String> wardCodeList = new ArrayList<>();
+
+                        /**
+                         * 闅忚绫诲瀷(1-绉戝锛�2-鐥呭尯锛�3-鐤剧梾 4-鍏ラ櫌绉戝 5-鍏ラ櫌鐥呭尯 6-鎵嬫湳)
+                         */
+                        Integer type = 1;
+                        if (StringUtils.isNotEmpty(deptCodes)) {
+                            type = 1;
+                            deptCodeList = new ArrayList<>(Arrays.asList(deptCodes.split(",")));
+                        }
+                        if (StringUtils.isNotEmpty(wardCodes)) {
+                            type = 2;
+                            wardCodeList = new ArrayList<>(Arrays.asList(wardCodes.split(",")));
+                        }
+                        if (StringUtils.isNotEmpty(icd10Codes)) {
+                            type = 3;
+                            icd10CodeList = new ArrayList<>(Arrays.asList(icd10Codes.split(",")));
+                        }
+                        if (StringUtils.isNotEmpty(opLevelCodes)) {
+                            type = 6;
+                            opLevelCodeList = new ArrayList<>(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 = inhospVisitFacade.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 = taskArchivePort.findByPatid(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");
+                                inhospVisitFacade.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 = otherVisitFacade.selectOperationItemListMaxLevel(pmoi);
+                                for (PatMedOperationItem patMedOperationItem : patMedOperationItems) {
+                                    PatArchive patArchive = taskArchivePort.findByPatid(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 = inhospVisitFacade.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");
+                                        inhospVisitFacade.updatePatMedInhosp(flagUpd);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return 1;
+    }
+
+    public void addOperationSubTask(String config) {
+        //            鎵嬫湳闅忚
+        PatMedOperationItem pmoi = new PatMedOperationItem();
+        //鑾峰彇闇�瑕佸嚭闄㈡墜鏈殢璁匡紝鏈鐞嗙殑鏁版嵁
+        if(!active.equals("hzszlyy")){
+            pmoi.setInhospstate("1");
+        }
+        pmoi.setOpercheckFlag("0");
+        pmoi.setMainFlag("1");
+
+        //姣忔鏌ヨ鍙煡鏈�澶х殑鎵嬫湳绛夌骇璁板綍
+        List<PatMedOperationItem> patMedOperationItems = otherVisitFacade.selectOperationItemListMaxLevel(pmoi);
+        log.info("dealOutHospInfo闇�瑕佸鐞嗙殑鍑洪櫌鎵嬫湳鎮h�呬俊鎭細{}", CollectionUtils.isNotEmpty(patMedOperationItems) ? patMedOperationItems.size() : null);
+
+        for (PatMedOperationItem opItem : patMedOperationItems) {
+
+            try {
+                if (StringUtils.isEmpty(opItem.getOplevelcode())) {
+                    continue;
+                }
+                // 鏍规嵁鎮h�呯殑鐤剧梾锛岃幏鍙栬鐤剧梾鐨勯暱鏈熶换鍔�
+                List<ServiceTaskoper> serviceTaskopersDept = new ArrayList<>();
+                if(StringUtils.isNotEmpty(opItem.getLeaveldeptcode())){
+                    ServiceTaskoper serviceTaskoper1 = new ServiceTaskoper();
+                    serviceTaskoper1.setLongtask(1L);
+                    serviceTaskoper1.setOrgid(opItem.getOrgid());
+                    serviceTaskoper1.setOplevelcode(opItem.getOplevelcode());
+                    serviceTaskoper1.setDeptCode(opItem.getLeaveldeptcode());
+                    serviceTaskopersDept = serviceTaskoperMapper.selectServiceTaskoperList(serviceTaskoper1);
+                }
+
+
+                List<ServiceTaskoper> serviceTaskopersWard = new ArrayList<>();
+                if(StringUtils.isNotEmpty(opItem.getLeavehospitaldistrictcode())){
+                    ServiceTaskoper serviceTaskoper2 = new ServiceTaskoper();
+                    serviceTaskoper2.setLongtask(1L);
+                    serviceTaskoper2.setOrgid(opItem.getOrgid());
+                    serviceTaskoper2.setOplevelcode(opItem.getOplevelcode());
+                    serviceTaskoper2.setWardCode(opItem.getLeavehospitaldistrictcode());
+                    serviceTaskopersWard = serviceTaskoperMapper.selectServiceTaskoperList(serviceTaskoper2);
+                }
+
+
+                List<ServiceTaskoper> serviceTaskopers = new ArrayList<>();
+                serviceTaskopers.addAll(serviceTaskopersDept);
+                serviceTaskopers.addAll(serviceTaskopersWard);
+                //濡傛灉閮ㄩ棬妯℃澘涓虹┖锛堝皢deptIsNull璁剧疆涓簍rue锛�
+                if (CollectionUtils.isEmpty(serviceTaskopers) || serviceTaskopers.size() == 0) {
+                    PatMedOperationItem patMedOperationItem = otherVisitFacade.selectOperationItemById(opItem.getId());
+                    patMedOperationItem.setOpercheckFlag("2");
+                    patMedOperationItem.setLongTaskReason("鎵�鎮g殑鎵嬫湳鏈厤缃暱鏈熶换鍔�;");
+                    patMedOperationItem.setUpdateTime(new Date());
+                    otherVisitFacade.updateOperationItem(patMedOperationItem);
+                } else {
+                    for (ServiceTaskoper serviceTaskoper : serviceTaskopers) {
+                        writeInSubTaskForOp(serviceTaskoper.getTaskId(), true, opItem, config);
+                    }
+                }
+
+            } catch (Exception e) {
+                e.printStackTrace();
+                log.error("浜哄憳鎵嬫湳澶勭悊鍑哄紓甯镐簡锛歿}", e.getMessage());
+                opItem.setUpdateTime(new Date());
+                opItem.setOpercheckFlag("2");
+                opItem.setLongTaskReason("浜哄憳鎵嬫湳澶勭悊寮傚父");
+                otherVisitFacade.updateOperationItem(opItem);
+            }
+        }
+    }
+
+    public int runOperationVisit(String config) {
+        addOperationSubTask(config);
+        return 1;
+    }
+
+    private void writeInSubTaskForOp(Long taskid, Boolean check, PatMedOperationItem patMedOperationItem, String config) {
+        String longTaskReason = patMedOperationItem.getLongTaskReason();
+        String orgId = patMedOperationItem.getOrgid();
+        if (StringUtils.isEmpty(longTaskReason)) longTaskReason = "";
+        ServiceTask st = new ServiceTask();
+        st.setTaskid(taskid);
+        st.setSendState(2L);
+        List<ServiceTask> serviceTasks = serviceTaskMapper.selectServiceTaskList(st);
+        ServiceTask serviceTask = null;
+        if (CollectionUtils.isEmpty(serviceTasks)) {
+            log.info("璇ユ墜鏈搴旀墜鏈瓑绾х殑闀挎湡浠诲姟涓嶅瓨鍦�,浠诲姟ID涓猴細{}", taskid);
+            patMedOperationItem.setOpercheckFlag("2");
+            patMedOperationItem.setLongTaskReason(" & 璇ユ墜鏈搴旀墜鏈瓑绾х殑闀挎湡浠诲姟涓嶅瓨鍦�,浠诲姟ID涓�:" + taskid +";");
+            otherVisitFacade.updateOperationItem(patMedOperationItem);
+            return;
+        } else {
+            serviceTask = serviceTasks.get(0);
+        }
+        if (Objects.isNull(patMedOperationItem.getOplevelcode())) {
+            return;
+        }
+        if (Objects.isNull(patMedOperationItem.getPatid())) {
+            return;
+        }
+        PatArchive patArchive = taskArchivePort.findByPatid(patMedOperationItem.getPatid());
+        if (Objects.isNull(patArchive)) {
+            return;
+        }
+        PatMedInhosp patMedInhosp = new PatMedInhosp();
+        patMedInhosp.setPatid((patMedOperationItem.getPatid()));
+        patMedInhosp.setInhospstate("1");
+        patMedInhosp.setFuflag("1");
+        //鎵嬫湳闅忚
+        if(!active.equals("hzszlyy")){
+            patMedInhosp.setSerialnum(patMedOperationItem.getSerialnum());
+        }
+        List<PatMedInhosp> patMedInhospList = inhospVisitFacade.selectPatMedInhospList(patMedInhosp);
+
+        if (CollectionUtils.isEmpty(patMedInhospList)) {
+            return;
+        }
+
+        //灏佽serviceSubtask
+        ServiceSubtask serviceSubtask = boxedServiceSubtaskForOp(serviceTask, patMedInhospList.get(0), patMedOperationItem, patArchive, config);
+        if (ObjectUtils.isEmpty(serviceSubtask) || Objects.isNull(serviceSubtask.getTaskid())) {
+            return;
+        }
+        if (StringUtils.isNotEmpty(patMedOperationItem.getLeaveldeptcode())) {
+            serviceSubtask.setVisitDeptCode(patMedOperationItem.getLeaveldeptcode());
+            serviceSubtask.setVisitDeptName(patMedOperationItem.getLeaveldeptname());
+        } else {
+            serviceSubtask.setVisitDeptCode(patMedOperationItem.getLeavehospitaldistrictcode());
+            serviceSubtask.setVisitDeptName(patMedOperationItem.getLeavehospitaldistrictname());
+        }
+        //璁板綍insertServiceSubtask璇彞杩斿洖鐘舵��
+        Integer i = 0;
+        try {
+            //鍏堝垽鏂竴涓嬶紝鏄惁闇�瑕佹牎楠�
+            if (check && (Objects.isNull(serviceSubtask.getTaskSituation()) || serviceSubtask.getTaskSituation() != 6)) {
+                //鍦ㄦ柊澧炰箣鍓嶏紝鍏堥�氳繃鎮h�匢D锛宻endstate=2鏌ヨ涓�涓嬶紝鍦ㄦ墍鏈夐暱鏈熶换鍔′腑锛屾槸涓嶆槸杩樻湁璇ユ偅鑰呭緟鎵ц鐨勪换鍔★紝鏈夌殑璇�,姣旇緝涔嬪墠鐨別ndtime鏄惁灏忎簬褰撳墠鐨別ndtaime,濡傛灉涔嬪墠鐨勫皬浜庣幇鍦ㄧ殑锛屽垯鐩存帴灏嗕箣鍓嶇殑鍋滄帀锛堝師鍥犲啀鍏ラ櫌锛�
+                ServiceSubtaskEntity subtask = new ServiceSubtaskEntity();
+                subtask.setPatid(patArchive.getId());
+                subtask.setSendstate(2L);
+                subtask.setTaskid(taskid);
+                List<ServiceSubtask> selectServiceSubtaskList = serviceSubtaskMapper.selectServiceSubtaskList(subtask);
+//                log.info("璇ユ偅鑰呭緟鎵ц鐨勪换鍔�:{}", CollectionUtils.isNotEmpty(selectServiceSubtaskList) ? selectServiceSubtaskList.size() : null);
+                if (CollectionUtils.isNotEmpty(selectServiceSubtaskList) && selectServiceSubtaskList.size() > 0) {
+                    for (ServiceSubtask serviceSubtask1 : selectServiceSubtaskList) {
+                        if (Objects.isNull(serviceSubtask1.getLongSendTime())) {
+                            //涓嶆槸闀挎湡浠诲姟锛屼笉澶勭悊
+                            continue;
+                        }
+                    }
+                } else {
+                    if (StringUtils.isEmpty(serviceSubtask.getPhone())) {
+                        if (StringUtils.isNotEmpty(serviceSubtask.getRemark()))
+                            serviceSubtask.setRemark(serviceSubtask.getRemark() + ",鎵嬫満鍙蜂负绌�");
+                        else serviceSubtask.setRemark("鎵嬫満鍙蜂负绌�");
+                        serviceSubtask.setSendstate(4L);
+                        serviceSubtask.setResult("error");
+                    }
+                    serviceSubtask.setCreateBy(serviceTask.getCreateBy());
+                    serviceSubtask.setCreateTime(new Date());
+                    i = serviceSubtaskMapper.insertServiceSubtask(serviceSubtask);
+                    addServiceSubtaskPreachform(serviceSubtask, null);
+                }
+            } else if (serviceSubtask.getTaskSituation() == 6) {
+                //璇存槑璇ユ偅鑰呮槸琚繃婊ょ殑锛屼笉鐢ㄥ啀鎵ц浜�
+                serviceSubtask.setCreateBy(serviceTask.getCreateBy());
+                serviceSubtask.setCreateTime(new Date());
+                serviceSubtask.setSendstate(4L);
+                i = serviceSubtaskMapper.insertServiceSubtask(serviceSubtask);
+            } else {
+                if (StringUtils.isEmpty(serviceSubtask.getPhone())) {
+                    if (StringUtils.isNotEmpty(serviceSubtask.getRemark()))
+                        serviceSubtask.setRemark(serviceSubtask.getRemark() + ",鎵嬫満鍙蜂负绌�");
+                    else serviceSubtask.setRemark("鎵嬫満鍙蜂负绌�");
+                    serviceSubtask.setSendstate(4L);
+                    serviceSubtask.setResult("error");
+                }
+                serviceSubtask.setCreateBy(serviceTask.getCreateBy());
+                serviceSubtask.setCreateTime(new Date());
+                i = serviceSubtaskMapper.insertServiceSubtask(serviceSubtask);
+                addServiceSubtaskPreachform(serviceSubtask, null);
+            }
+
+            //灏哻heck_flag鏀规垚1锛堝凡澶勭悊锛�
+            if (i == 1) {
+                PatMedOperationItem patMedOperationItem2 = new PatMedOperationItem();
+                patMedOperationItem2.setId(patMedOperationItem.getId());
+                patMedOperationItem2.setOpercheckFlag("1");
+                otherVisitFacade.updateOperationItem(patMedOperationItem2);
+            } else {
+                //鐢熸垚瀛愪换鍔″け璐ワ紝
+                log.info("鐢熸垚瀛愪换鍔″け璐erviceSubtask鐨則askid涓猴細{},patid涓猴細{}", serviceSubtask.getTaskid(), serviceSubtask.getPatid());
+                PatMedOperationItem patMedOperationItem2 = new PatMedOperationItem();
+                patMedOperationItem2.setId(patMedOperationItem.getId());
+                patMedOperationItem2.setOpercheckFlag("2");
+                patMedOperationItem2.setLongTaskReason("鐢熸垚瀛愪换鍔″け璐� 4;");
+                patMedOperationItem2.setUpdateTime(new Date());
+                otherVisitFacade.updateOperationItem(patMedOperationItem2);
+            }
+        } catch (Exception e) {
+            //鍑洪敊鍥炴粴浜嬪姟
+
+            log.error("鐢熸垚瀛愪换鍔℃姤閿欏洖婊歴serviceSubtask鐨則askid涓猴細{},patid涓猴細{},鎵嬫湳璇︽儏ID涓猴細{}",
+                    serviceSubtask.getTaskid(), serviceSubtask.getPatid(), patMedOperationItem.getId(), e);
+            PatMedOperationItem patMedOperationItem2 = new PatMedOperationItem();
+            patMedOperationItem2.setId(patMedOperationItem.getId());
+            patMedOperationItem2.setOpercheckFlag("2");
+            patMedOperationItem2.setUpdateTime(new Date());
+            patMedOperationItem2.setLongTaskReason("鐢熸垚瀛愪换鍔℃姤閿欏洖婊� 4;");
+            otherVisitFacade.updateOperationItem(patMedOperationItem2);
+            e.printStackTrace();
+        }
+    }
+
+    private ServiceSubtask boxedServiceSubtaskForOp(ServiceTask serviceTask, PatMedInhosp patMedInhosp1, PatMedOperationItem patMedOperationItem, PatArchive patArchive, String config) {
+        ServiceSubtask serviceSubtask = DtoConversionUtils.sourceToTarget(serviceTask, ServiceSubtask.class);
+        serviceSubtask.setTaskid(serviceTask.getTaskid());
+        serviceSubtask.setOrgid(serviceTask.getOrgid());
+        serviceSubtask.setCampusid(serviceTask.getCampusid());
+        if (StringUtils.isNotEmpty(serviceTask.getLibtemplateid()))
+            serviceSubtask.setLibtemplateid(Long.valueOf(serviceTask.getLibtemplateid()));
+        serviceSubtask.setNurseId(patMedInhosp1.getNurseId());
+        serviceSubtask.setNurseName(patMedInhosp1.getNurseName());
+        serviceSubtask.setInhospid(patMedInhosp1.getInhospid());
+        serviceSubtask.setDrcode(patMedInhosp1.getDrcode());
+        serviceSubtask.setDrname(patMedInhosp1.getDrname());
+        serviceSubtask.setLeaveldeptcode(patMedInhosp1.getLeaveldeptcode());
+        serviceSubtask.setLeaveldeptname(patMedInhosp1.getLeaveldeptname());
+        serviceSubtask.setLeavehospitaldistrictcode(patMedInhosp1.getLeavehospitaldistrictcode());
+        serviceSubtask.setLeavehospitaldistrictname(patMedInhosp1.getLeavehospitaldistrictname());
+
+        serviceSubtask.setTemplateid(serviceTask.getTemplateid());
+        serviceSubtask.setTemplatename(serviceTask.getTemplatename());
+        serviceSubtask.setPatid(patArchive.getId());
+        serviceSubtask.setSendname(patArchive.getName());
+        serviceSubtask.setSfzh(patArchive.getIdcardno());
+        serviceSubtask.setPhone(patArchive.getTelcode());
+        if (StringUtils.isBlank(patArchive.getTelcode())) serviceSubtask.setPhone(patArchive.getRelativetelcode());
+        serviceSubtask.setSex(patArchive.getSex());
+        serviceSubtask.setAge(patArchive.getAge());
+        serviceSubtask.setSendstate(2L);
+        serviceSubtask.setManagementDoctor(patMedInhosp1.getDrname());
+        serviceSubtask.setManagementDoctorCode(patMedInhosp1.getDrcode());
+
+        serviceSubtask.setStarttime(patMedInhosp1.getStarttime());
+        serviceSubtask.setEndtime(patMedInhosp1.getEndtime());
+
+        serviceSubtask.setServiceType(serviceTask.getServiceType());
+        serviceSubtask.setPreachform(serviceTask.getPreachform());
+        serviceSubtask.setHospType("2");
+        serviceSubtask.setCreateTime(new Date());
+        serviceSubtask.setUpdateTime(new Date());
+        serviceSubtask.setCreateBy(patMedInhosp1.getNurseName());
+        serviceSubtask.setOperationItemId(patMedOperationItem.getId());
+        serviceSubtask.setUpdateBy(serviceTask.getUpdateBy());
+        serviceSubtask.setUpdateTime(new Date());
+
+        //璁剧疆鍙戦�佹椂闂�
+        if (serviceTask.getSendDay() == null) serviceTask.setSendDay(1L);
+        Date newDate = null;
+        if (!Objects.isNull(patMedInhosp1.getEndtime())) {
+            newDate = addDays(patMedInhosp1.getEndtime(), serviceTask.getSendDay().intValue());
+        } else {
+            patMedOperationItem.setOpercheckFlag("2");
+            patMedOperationItem.setLongTaskReason("璇ユ偅鑰呮墜鏈嚭闄㈡椂闂翠负绌�,鎵嬫湳璇︽儏ID涓�:" + patMedOperationItem.getId() + ";");
+            otherVisitFacade.updateOperationItem(patMedOperationItem);
+//            patMedInhosp1.setDeptcheckFlag("2");
+//            patMedInhosp1.setRemark(patMedInhosp1.getRemark() + "& 璇ユ偅鑰呮墜鏈嚭闄㈡椂闂翠负绌�,鍑洪櫌璁板綍ID涓�:" + patMedInhosp1.getInhospid());
+//            inhospVisitFacade.updatePatMedInhosp(patMedInhosp1);
+            //濡傛灉鍑洪櫌鏃堕棿涓虹┖锛屽垯杩斿洖涓�涓柊瀵硅薄
+            return null;
+        }
+        serviceSubtask.setLongSendTime(newDate);
+        serviceSubtask.setVisitTime(newDate);
+
+        //濡傛灉鏄�氱煡鐨勮瘽锛岄渶瑕佹彁鍓嶅嚑澶╁皢閫氳繃鐭俊鍙戝嚭鍘伙紙鍏蜂綋鍑犲ぉ锛岃繖涓彲浠ラ厤缃湪sys_conf琛ㄤ腑锛�
+        if ("3".equals(serviceTask.getType())) {
+            serviceSubtask.setVisitTime(minusDay(newDate, StringUtils.isNotEmpty(config) ? Integer.parseInt(config) : 0));
+        }
+        //鎮h�呭彂閫佹椂闂�
+        if (StringUtils.isNotEmpty(patArchive.getNotrequiredFlag()) && patArchive.getNotrequiredFlag().equals("1")) {
+            String remark = patArchive.getNotrequiredreason();
+            serviceSubtask.setRemark(remark);
+            serviceSubtask.setResult("error");
+            serviceSubtask.setTaskSituation(6);
+            //涓嶆墽琛�
+            serviceSubtask.setSendstate(4L);
+        }
+        return serviceSubtask;
+    }
+
+    private Integer addServiceSubtaskPreachform(ServiceSubtask serviceSubtask, String sendstate) {
+        //灏唒reachformDesc鐨刯son瀵硅薄锛屾斁鍒發ist闆嗗悎涓�
+        ServiceTask serviceTask = serviceTaskMapper.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
+        if (Objects.isNull(serviceTask)) {
+            log.error("addServiceSubtaskPreachform taskid={} 鏌ヤ笉鍒颁换鍔★紝璺宠繃preachform瀛愯〃鐢熸垚", serviceSubtask.getTaskid());
+            return 0;
+        }
+        String preachformDesc = serviceTask.getPreachformDesc();
+
+        ObjectMapper objectMapper = new ObjectMapper();
+        if (org.apache.commons.lang3.StringUtils.isNotEmpty(preachformDesc)) {
+            List<ServiceSubtaskPreachform> serviceSubtaskPreachformList = null;
+            try {
+                serviceSubtaskPreachformList = objectMapper.readValue(preachformDesc, new TypeReference<List<ServiceSubtaskPreachform>>() {
+                });
+            } catch (JsonProcessingException e) {
+                log.error("preachform杞琇ist<ServiceSubtaskPreachform>鎶ラ敊浜嗭細{}", e.getMessage());
+            }
+            if (CollectionUtils.isEmpty(serviceSubtaskPreachformList)) {
+                log.error("preachformDesc瑙f瀽鍚庝负绌猴紝璺宠繃preachform瀛愯〃鐢熸垚锛宼askid涓猴細{}锛宲reachformDesc涓猴細{}",
+                        serviceSubtask.getTaskid(), preachformDesc);
+                return 0;
+            }
+            for (ServiceSubtaskPreachform serviceSubtaskPreachform : serviceSubtaskPreachformList) {
+                serviceSubtaskPreachform.setTaskid(serviceSubtask.getTaskid());
+                serviceSubtaskPreachform.setSubid(serviceSubtask.getId());
+
+                //鍏堟煡璇竴涓嬶紝鏄惁瀛樺湪锛屽鏋滃凡缁忓瓨鍦紝灏变笉鏂板浜�
+                List<ServiceSubtaskPreachform> serviceSubtaskPreachformList1 = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(serviceSubtaskPreachform);
+                if (CollectionUtils.isNotEmpty(serviceSubtaskPreachformList1)) continue;
+
+                //杩欎釜sendstate闇�瑕佸湪鏁版嵁搴撹缃竴涓粯璁ゅ�间负鈥�1鈥�
+                serviceSubtaskPreachform.setSendstate("1");
+                if (StringUtils.isNotEmpty(sendstate)) serviceSubtaskPreachform.setSendstate(sendstate);
+
+                serviceSubtaskPreachform.setOrgid(serviceSubtask.getOrgid());
+                serviceSubtaskPreachformMapper.insertServiceSubtaskPreachform(serviceSubtaskPreachform);
+            }
+
+        }
+
+        return 0;
+    }
+
+    public ServiceSubtask boxedServiceSubtask(ServiceTask serviceTask, PatMedInhosp patMedInhosp1, PatArchive patArchive, String config, Integer type) {
+        //澧炲姞serviceTask鍒ょ┖
+//        if(Objects.isNull(serviceTask)){
+//            return null;
+//        }
+        ServiceSubtask serviceSubtask = DtoConversionUtils.sourceToTarget(serviceTask, ServiceSubtask.class);
+        serviceSubtask.setTaskid(serviceTask.getTaskid());
+        serviceSubtask.setOrgid(serviceTask.getOrgid());
+        serviceSubtask.setCampusid(serviceTask.getCampusid());
+        if (StringUtils.isNotEmpty(serviceTask.getLibtemplateid()))
+            serviceSubtask.setLibtemplateid(Long.valueOf(serviceTask.getLibtemplateid()));
+        serviceSubtask.setNurseId(patMedInhosp1.getNurseId());
+        serviceSubtask.setNurseName(patMedInhosp1.getNurseName());
+        serviceSubtask.setInhospid(patMedInhosp1.getInhospid());
+        serviceSubtask.setDrcode(patMedInhosp1.getDrcode());
+        serviceSubtask.setDrname(patMedInhosp1.getDrname());
+        serviceSubtask.setLeaveldeptcode(patMedInhosp1.getLeaveldeptcode());
+        serviceSubtask.setLeaveldeptname(patMedInhosp1.getLeaveldeptname());
+        serviceSubtask.setLeavehospitaldistrictcode(patMedInhosp1.getLeavehospitaldistrictcode());
+        serviceSubtask.setLeavehospitaldistrictname(patMedInhosp1.getLeavehospitaldistrictname());
+        if (type == 4 || type == 5) {
+            //璁剧疆鍏ラ櫌鐨�
+            serviceSubtask.setLeaveldeptcode(patMedInhosp1.getDeptcode());
+            serviceSubtask.setLeaveldeptname(patMedInhosp1.getDeptname());
+            serviceSubtask.setLeavehospitaldistrictcode(patMedInhosp1.getHospitaldistrictcode());
+            serviceSubtask.setLeavehospitaldistrictname(patMedInhosp1.getHospitaldistrictname());
+        }
+        serviceSubtask.setLeaveicd10code(patMedInhosp1.getLeaveicd10code());
+        serviceSubtask.setLeavediagname(patMedInhosp1.getLeavediagname());
+        serviceSubtask.setDiagname(patMedInhosp1.getDiagname());
+        serviceSubtask.setTemplateid(serviceTask.getTemplateid());
+        serviceSubtask.setTemplatename(serviceTask.getTemplatename());
+        serviceSubtask.setPatid(patArchive.getId());
+        serviceSubtask.setSendname(patArchive.getName());
+        serviceSubtask.setSfzh(patArchive.getIdcardno());
+        serviceSubtask.setPhone(patArchive.getTelcode());
+        if (StringUtils.isBlank(patArchive.getTelcode())) serviceSubtask.setPhone(patArchive.getRelativetelcode());
+        serviceSubtask.setSex(patArchive.getSex());
+        serviceSubtask.setAge(patArchive.getAge());
+        serviceSubtask.setSendstate(2L);
+        serviceSubtask.setCampusid(patMedInhosp1.getCampusid());
+        serviceSubtask.setManagementDoctor(patMedInhosp1.getManagementDoctor());
+        serviceSubtask.setManagementDoctorCode(patMedInhosp1.getManagementDoctorCode());
+        serviceSubtask.setStarttime(patMedInhosp1.getStarttime());
+        serviceSubtask.setEndtime(patMedInhosp1.getEndtime());
+        serviceSubtask.setServiceType(serviceTask.getServiceType());
+        serviceSubtask.setPreachform(serviceTask.getPreachform());
+        serviceSubtask.setHospType("2");
+        if (type == 4 || type == 5) serviceSubtask.setHospType("6");
+        serviceSubtask.setCreateTime(new Date());
+        serviceSubtask.setUpdateTime(new Date());
+        serviceSubtask.setCreateBy(patMedInhosp1.getNurseName());
+        serviceSubtask.setLeavehospitaldistrictcode(patMedInhosp1.getLeavehospitaldistrictcode());
+        serviceSubtask.setLeavehospitaldistrictname(patMedInhosp1.getLeavehospitaldistrictname());
+        serviceSubtask.setUpdateBy(serviceTask.getUpdateBy());
+        serviceSubtask.setUpdateTime(new Date());
+        //濡傛灉鏄鏁欏氨璁剧疆涓�7
+        if (serviceTask.getServiceType().equals("17")) serviceSubtask.setTaskSituation(7);
+        //璁剧疆鍙戦�佹椂闂�
+        if (serviceTask.getSendDay() == null) serviceTask.setSendDay(1L);
+        Date newDate = null;
+        if (!Objects.isNull(patMedInhosp1.getEndtime())) {
+            newDate = addDays(patMedInhosp1.getEndtime(), serviceTask.getSendDay().intValue());
+        } else {
+            //濡傛灉绛変簬4,璇存槑鏄叆闄㈠鏁欙紝鍏ラ櫌涓嶅彲鑳芥湁endTime
+            if (type != 4 && type != 5) {
+                patMedInhosp1.setDeptcheckFlag("2");
+                patMedInhosp1.setRemark(patMedInhosp1.getRemark() + "& 璇ユ偅鑰呯柧鐥呭嚭闄㈡椂闂翠负绌�,鍑洪櫌璁板綍ID涓�:" + patMedInhosp1.getInhospid());
+                inhospVisitFacade.updatePatMedInhosp(patMedInhosp1);
+                //濡傛灉鍑洪櫌鏃堕棿涓虹┖锛屽垯杩斿洖涓�涓柊瀵硅薄
+                return new ServiceSubtask();
+            }
+            //鍏ラ櫌瀹f暀锛屽熀鏈綋澶╂垨闅斿ぉ灏辫鍙戝嚭
+            newDate = addDays(new Date(), serviceTask.getSendDay().intValue());
+        }
+        serviceSubtask.setLongSendTime(newDate);
+        serviceSubtask.setVisitTime(newDate);
+
+//        if (patMedInhosp1.getFudate() != null && !patMedInhosp1.getOrgid().equals("47255004333112711A1001")) serviceSubtask.setVisitTime(patMedInhosp1.getFudate());
+        //濡傛灉鏄�氱煡鐨勮瘽锛岄渶瑕佹彁鍓嶅嚑澶╁皢閫氳繃鐭俊鍙戝嚭鍘伙紙鍏蜂綋鍑犲ぉ锛岃繖涓彲浠ラ厤缃湪sys_conf琛ㄤ腑锛�
+        if ("3".equals(serviceTask.getType())) {
+            serviceSubtask.setVisitTime(minusDay(newDate, StringUtils.isNotEmpty(config) ? Integer.parseInt(config) : 0));
+            //濡傛灉璇ュ嚭闄㈣褰曟湁闅忚鏃堕棿锛屽垯鐢ㄤ粬鑷繁鐨勯殢璁挎椂闂�
+            if (patMedInhosp1.getFudate() != null) {
+                serviceSubtask.setVisitTime(minusDay(patMedInhosp1.getFudate(), StringUtils.isNotEmpty(config) ? Integer.parseInt(config) : 0));
+            } else {
+                //鍒ゆ柇鏄笉鏄櫙瀹�
+                if (patMedInhosp1.getOrgid().equals("47255004333112711A1001")) {
+                    serviceSubtask.setVisitTime(null);
+                    //涓嶆墽琛�
+                    serviceSubtask.setSendstate(4L);
+                    serviceSubtask.setRemark("鏅畞娌℃湁缁欓殢璁挎椂闂达紝涓嶅彂閫侀�氱煡鐭俊");
+                }
+            }
+
+        }
+        //濡傛灉鎮h�呯殑鍏ラ櫌鍜屽嚭闄㈡椂闂翠竴鑷达紝鍙互涓嶇敤闅忚锛堝崡鍗庨檮涓�鑲跨槫涓�鐥呭尯鎻愬嚭锛�
+        if (active.equals("nhfy") && patMedInhosp1.getStarttime() != null && patMedInhosp1.getEndtime() != null && DateUtils.differentDaysByMillisecond(patMedInhosp1.getStarttime(), patMedInhosp1.getEndtime()) == 0) {
+            serviceSubtask.setRemark("鎮h�呭綋澶╁嚭闄紝涓嶇敤闅忚");
+            serviceSubtask.setResult("error");
+            //涓嶆墽琛�
+            serviceSubtask.setSendstate(4L);
+        }
+
+        //鎮h�呭彂閫佹椂闂�
+        if (StringUtils.isNotEmpty(patArchive.getNotrequiredFlag()) && patArchive.getNotrequiredFlag().equals("1")) {
+            String remark = patArchive.getNotrequiredreason();
+            serviceSubtask.setRemark(remark);
+            serviceSubtask.setResult("error");
+            serviceSubtask.setTaskSituation(6);
+            //涓嶆墽琛�
+            serviceSubtask.setSendstate(4L);
+        }
+        return serviceSubtask;
+    }
+
+    private Date addDays(Date date, Integer days) {
+        if (days == null) {
+            days = 1;
+        }
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        calendar.add(Calendar.DAY_OF_MONTH, days);
+        return calendar.getTime();
+    }
+
+    public Date minusDay(Date originalDate, Integer day) {
+        // 1. 灏� Date 杞崲涓� Instant
+        Instant instant = originalDate.toInstant();
+
+        // 2. 鍦� Instant 涓婂噺鍘�2澶�
+        Instant twoDaysAgoInstant = instant.minus(2, ChronoUnit.DAYS);
+
+        // 3. 灏嗚绠楀悗鐨� Instant 杞洖 Date
+        return Date.from(twoDaysAgoInstant);
+    }
+
+}
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/api/contract/OtherVisitApiImpl.java b/ruoyi-task/src/main/java/com/ruoyi/task/api/contract/OtherVisitApiImpl.java
new file mode 100644
index 0000000..3699252
--- /dev/null
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/api/contract/OtherVisitApiImpl.java
@@ -0,0 +1,58 @@
+package com.ruoyi.task.api.contract;
+
+import com.ruoyi.common.core.service.OtherVisitApi;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * {@link OtherVisitApi}锛堟墜鏈殢璁垮绾︼級鍦� <b>ruoyi-task</b> 渚х殑鍞竴瀹炵幇銆�
+ *
+ * <h3>涓轰粈涔堣惤鍦ㄨ繖閲�</h3>
+ * 鎵嬫湳闅忚鐨勪袱涓叆鍙f湰浣撳湪 {@link OperationSubtaskWriter}锛堟湰鍖咃級锛�
+ * 鑰屽畠蹇呴』璇诲啓 {@code service_task*} / {@code service_subtask*}锛堝睘鏈ā鍧楋級
+ * 骞惰 {@code pat_archive}锛堢粡 {@code TaskArchivePort} 绔彛锛夆�斺��
+ * 瑙� writer 鐨勭被娉ㄩ噴閲岄偅涓夋鎼縼灏濊瘯銆傚疄鐜拌窡鐫�鑳藉姏璧帮紝鎵�浠ユ湰绫讳篃鍦ㄦ湰妯″潡銆�
+ *
+ * <h3>瀹冨彇浠d簡浠�涔�</h3>
+ * 姝ゅ墠鏈�<b>涓や釜</b>瀹炵幇锛歴martor 鐨� {@code OtherVisitApiBridge}锛堢湡瀹炵幇锛�
+ * 鍜� {@code ruoyi-othervisit} 鐨� {@code OtherVisitApiImpl}锛堢敤
+ * {@code ObjectProvider<OtherVisitApi>} 鍥炲ご鎵� smartor 閭d釜锛岃繕瑕� filter 鎺夎嚜宸遍槻閫掑綊锛夈��
+ * 鏈疆涓よ�呴兘鍒犱簡锛岀幇鍦ㄥ叏浠� {@code OtherVisitApi} 鍙湁涓�涓� bean銆�
+ *
+ * <h3>鈿狅笍 涓よ濮旀墭锛屼笉鍔犻�昏緫</h3>
+ * {@code addOperationSubTask} 杞彂鍒� {@code writer.runOperationVisit(..)}锛氬師妗ユ帴灏辨槸杩欎箞杞殑
+ * 锛坽@code runOperationVisit} 鍐呴儴璋� {@code addOperationSubTask} 鍚庡浐瀹氳繑鍥� 1锛夛紝
+ * 鎹㈡垚 {@code writer.addOperationSubTask} 浼氫涪鎺夐偅涓� {@code 1} 鐨勮繑鍥炲�笺��
+ *
+ * @see OperationSubtaskWriter
+ */
+@Slf4j
+@Component
+public class OtherVisitApiImpl implements OtherVisitApi {
+
+    @Autowired
+    private OperationSubtaskWriter operationSubtaskWriter;
+
+    /**
+     * 鎵嬫湳闅忚鎵弿锛堟墜鏈瓑绾� + 绂婚櫌绉戝/鐥呭尯缁村害锛夈��
+     *
+     * @param config {@code sys_config: visit.early.day} 鐨勫�硷紝鍙兘涓� null
+     * @return 鍥哄畾 {@code 1}锛堝師瀹炵幇濡傛锛屼笉鏄鐞嗘潯鏁帮級
+     */
+    @Override
+    public int addOperationSubTask(String config) {
+        return operationSubtaskWriter.runOperationVisit(config);
+    }
+
+    /**
+     * 浠诲姟缁勶紙appltype=5锛夐杞撼鍏ャ��
+     *
+     * @param config {@code sys_config: visit.early.day}
+     * @return 鍘熷疄鐜板浐瀹氳繑鍥� 1
+     */
+    @Override
+    public int dealOutHospInfoBySchedule(String config) {
+        return operationSubtaskWriter.dealOutHospInfoBySchedule(config);
+    }
+}
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/api/contract/PreachformApiImpl.java b/ruoyi-task/src/main/java/com/ruoyi/task/api/contract/PreachformApiImpl.java
new file mode 100644
index 0000000..e7e5063
--- /dev/null
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/api/contract/PreachformApiImpl.java
@@ -0,0 +1,158 @@
+package com.ruoyi.task.api.contract;
+
+import com.ruoyi.common.core.service.PreachformApi;
+import com.ruoyi.common.core.service.dto.VisitPreachformRow;
+import com.ruoyi.task.domain.ServiceSubtask;
+import com.ruoyi.task.domain.ServiceSubtaskPreachform;
+import com.ruoyi.task.mapper.ServiceSubtaskPreachformMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * {@link PreachformApi} 鍦� <b>鏈ā鍧楋紙ruoyi-task锛�</b>鐨勫疄鐜� 鈥斺��
+ * {@code service_subtask_preachform} 鐨�<b>鍞竴</b>鏁版嵁璁块棶鍑哄彛銆�
+ *
+ * <h3>2026-11 褰掑睘璋冩暣</h3>
+ * 璇ヨ〃鍘熷厛褰� {@code ruoyi-inhospvisit}锛屽疄鐜版槸瀹冪殑 {@code DefaultPreachformApi}锛�
+ * 鎸�"琛ㄨ窡闅忎笟鍔″煙"鐨勫彛寰勶紝琛ㄧ殑 SQL / Mapper / 瀹炰綋閮藉垝鍒版湰妯″潡锛屼簬鏄湰绫绘帴绠″绾﹀疄鐜般��
+ * 浠嶇劧淇濈暀杩欎釜濂戠害鐨勫師鍥犲彧鏈変竴涓細{@code ruoyi-inhospvisit} 闇�瑕佸啓杩欏紶琛紙瀹f暀鏄庣粏锛夛紝
+ * 鑰屼緷璧栨柟鍚戞槸 {@code ruoyi-task 鈫� ruoyi-inhospvisit}锛屽弽鍚戜笉鎴愮珛锛堝惁鍒欐垚鐜級锛�
+ * 鎵�浠ュ畠鍙兘缁� {@code ruoyi-common} 閲岀殑濂戠害鍦ㄨ繍琛屾湡鍙栧埌鏈疄鐜般��
+ *
+ * <h3>鈿狅笍 绾暟鎹闂�</h3>
+ * 鏈被鍙仛銆屽绾﹁ 鈫� 瀹炰綋銆嶇殑閫愬瓧娈垫惉杩� + 璋� Mapper锛屼笉鍚换浣曚笟鍔″垽鏂細
+ * 浠�涔堟椂鍊欒鎻掍竴鏉°�佽缃粈涔堢姸鎬侊紝閮藉睘浜庤皟鐢ㄦ柟銆�
+ * 15 涓瓧娈甸�愪釜鐓ф妱锛堝皯鎼竴涓� = 璇ュ垪闈欓粯涓嶅啓/涓嶈锛夈��
+ */
+@Slf4j
+@Component
+public class PreachformApiImpl implements PreachformApi {
+
+    @Autowired
+    private ServiceSubtaskPreachformMapper serviceSubtaskPreachformMapper;
+
+    @Override
+    public VisitPreachformRow findById(Long id) {
+        return toRow(serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformById(id));
+    }
+
+    @Override
+    public List<VisitPreachformRow> findList(VisitPreachformRow query) {
+        List<ServiceSubtaskPreachform> rows =
+                serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(toEntity(query));
+        return toRows(rows);
+    }
+
+    @Override
+    public List<VisitPreachformRow> findByTaskidAndOrgid(Long taskid, String orgid) {
+        ServiceSubtask query = new ServiceSubtask();
+        query.setTaskid(taskid);
+        query.setOrgid(orgid);
+        return toRows(serviceSubtaskPreachformMapper.selectBySubtask(query));
+    }
+
+    @Override
+    public boolean insert(VisitPreachformRow row) {
+        ServiceSubtaskPreachform entity = toEntity(row);
+        int n = serviceSubtaskPreachformMapper.insertServiceSubtaskPreachform(entity);
+        // 鍘� XML 鐢� useGeneratedKeys 鎶婅嚜澧炰富閿洖濉埌鍏ュ弬瀵硅薄锛涜繖閲屽悓鏍峰洖濉紝
+        // 淇濊瘉"鍏� insert 鍐嶆嬁 id"鐨勮皟鐢ㄦ柟琛屼负涓嶅彉銆�
+        if (row != null && entity != null) {
+            row.setId(entity.getId());
+        }
+        return n == 1;
+    }
+
+    @Override
+    public boolean updateById(VisitPreachformRow row) {
+        return serviceSubtaskPreachformMapper
+                .updateServiceSubtaskPreachform(toEntity(row)) == 1;
+    }
+
+    @Override
+    public boolean updateByCondition(VisitPreachformRow row) {
+        return serviceSubtaskPreachformMapper.updateSSPByCondition(toEntity(row)) >= 1;
+    }
+
+    @Override
+    public boolean softDeleteById(Long id) {
+        return serviceSubtaskPreachformMapper.deleteServiceSubtaskPreachformById(id) >= 1;
+    }
+
+    @Override
+    public boolean softDeleteByIds(Long[] ids) {
+        return serviceSubtaskPreachformMapper.deleteServiceSubtaskPreachformByIds(ids) >= 1;
+    }
+
+    @Override
+    public boolean softDeleteBySubid(Long subid) {
+        return serviceSubtaskPreachformMapper.deleteServiceSubtaskPreachformBySubid(subid) >= 1;
+    }
+
+    // ==========================================================================
+    // 瀛楁鎼繍锛堝绾﹁ 鈫� 鏈ā鍧楀疄浣擄級銆備袱渚у瓧娈靛悕涓庣被鍨嬪畬鍏ㄤ竴鑷达紙15 涓級锛�
+    // 閫愬瓧娈电収鎶勶紝涓嶅仛"椤烘墜绮剧畝"銆�
+    // ==========================================================================
+
+    private ServiceSubtaskPreachform toEntity(VisitPreachformRow r) {
+        if (r == null) {
+            return null;
+        }
+        ServiceSubtaskPreachform e = new ServiceSubtaskPreachform();
+        e.setId(r.getId());
+        e.setSubid(r.getSubid());
+        e.setTaskid(r.getTaskid());
+        e.setPreachform(r.getPreachform());
+        e.setSendstate(r.getSendstate());
+        e.setCurrentSendTime(r.getCurrentSendTime());
+        e.setOrgid(r.getOrgid());
+        e.setDelFlag(r.getDelFlag());
+        e.setSort(r.getSort());
+        e.setCompensateTime(r.getCompensateTime());
+        e.setRemark(r.getRemark());
+        e.setCreateBy(r.getCreateBy());
+        e.setCreateTime(r.getCreateTime());
+        e.setUpdateBy(r.getUpdateBy());
+        e.setUpdateTime(r.getUpdateTime());
+        return e;
+    }
+
+    private VisitPreachformRow toRow(ServiceSubtaskPreachform e) {
+        if (e == null) {
+            return null;
+        }
+        VisitPreachformRow r = new VisitPreachformRow();
+        r.setId(e.getId());
+        r.setSubid(e.getSubid());
+        r.setTaskid(e.getTaskid());
+        r.setPreachform(e.getPreachform());
+        r.setSendstate(e.getSendstate());
+        r.setCurrentSendTime(e.getCurrentSendTime());
+        r.setOrgid(e.getOrgid());
+        r.setDelFlag(e.getDelFlag());
+        r.setSort(e.getSort());
+        r.setCompensateTime(e.getCompensateTime());
+        r.setRemark(e.getRemark());
+        r.setCreateBy(e.getCreateBy());
+        r.setCreateTime(e.getCreateTime());
+        r.setUpdateBy(e.getUpdateBy());
+        r.setUpdateTime(e.getUpdateTime());
+        return r;
+    }
+
+    private List<VisitPreachformRow> toRows(List<ServiceSubtaskPreachform> rows) {
+        if (rows == null || rows.isEmpty()) {
+            return Collections.emptyList();
+        }
+        List<VisitPreachformRow> out = new ArrayList<>(rows.size());
+        for (ServiceSubtaskPreachform e : rows) {
+            out.add(toRow(e));
+        }
+        return out;
+    }
+}
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/api/contract/ScheduleRollPortImpl.java b/ruoyi-task/src/main/java/com/ruoyi/task/api/contract/ScheduleRollPortImpl.java
new file mode 100644
index 0000000..d8bac93
--- /dev/null
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/api/contract/ScheduleRollPortImpl.java
@@ -0,0 +1,315 @@
+package com.ruoyi.task.api.contract;
+
+import com.alibaba.fastjson2.JSON;
+import com.ruoyi.common.core.service.ScheduleRollPort;
+import com.ruoyi.common.core.service.dto.VisitScheduleRollResultDto;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.task.mapper.ServiceSubtaskPreachformMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import com.ruoyi.task.domain.ServiceSubtask;
+import com.ruoyi.task.domain.ServiceTaskSchedule;
+import com.ruoyi.task.domain.ServiceTaskScheduleDetail;
+import com.ruoyi.task.mapper.ServiceSubtaskMapper;
+import com.ruoyi.task.mapper.ServiceTaskScheduleDetailMapper;
+import com.ruoyi.task.mapper.ServiceTaskScheduleMapper;
+import com.ruoyi.task.domain.ServiceSubtaskPreachform;
+
+/**
+ * 銆愯繃娓″疄鐜般�憑@link ScheduleRollPort}锛堜换鍔$粍杞婊氬姩锛夊湪 smartor 渚х殑妗ユ帴銆�
+ *
+ * <h3>涓轰粈涔堣繖涓疄鐜版槸"绮楃矑搴�"鐨勶紙绗� 9 姝ョ殑璁捐鍐冲畾锛�</h3>
+ * 鏍稿績鍔ㄤ綔瑕佸仛涓�娆� <b>{@code ServiceSubtask} 鐨勫叏瀛楁娣辨嫹璐�</b>
+ * 锛坽@code JSON.parseObject(JSON.toJSONString(src), ServiceSubtask.class)}锛夛紝
+ * 鑰� {@code ServiceSubtask} 鏈� <b>116 涓瓧娈�</b>銆�
+ * 鍦ㄦā鍧椾箣闂翠紶 116 瀛楁鐨� DTO 鍙細鍒堕�犻闄┿�佹崲涓嶆潵鏀剁泭
+ * 锛堝叏瀛楁鎷疯礉鐨勮涔夋湰鏉ュ氨鐢� JSON 搴忓垪鍖栦繚璇侊級銆�
+ * <p>
+ * 鎵�浠ユ暣涓� roll 鍔ㄤ綔鐣欏湪鏈被閲屽師瀛愬畬鎴愶紱{@code ruoyi-inhospvisit} 鍙礋璐�
+ * "鎵规涓婇檺 + 閫愭潯 try/catch + 璁℃暟涓庢棩蹇�"杩欓儴鍒嗙紪鎺掋��
+ *
+ * <h3>閫愭柟娉曟潵婧愶紙@ b25c3b51锛�</h3>
+ * <table border="1">
+ *   <tr><th>鏈被鏂规硶</th><th>鍘熷疄鐜�</th><th>琛屾暟</th></tr>
+ *   <tr><td>{@link #selectActiveCycleSchedules(int)}</td>
+ *       <td>{@code ServiceTaskScheduleServiceImpl#rollCycleTasks} L171 鐨勬煡璇㈤儴鍒�</td><td>鈥�</td></tr>
+ *   <tr><td>{@link #rollOneSchedule(Long)}</td>
+ *       <td>{@code rollOneSchedule} L196-280</td><td>85</td></tr>
+ *   <tr><td>{@code cloneForNextLoop}</td><td>L285-313</td><td>29</td></tr>
+ *   <tr><td>{@code copyPreachform}</td><td>L318-338</td><td>21</td></tr>
+ *   <tr><td>{@code advance}</td><td>L343-361</td><td>19</td></tr>
+ * </table>
+ *
+ * <h3>鈿狅笍 鏈被涓� smartor 閲岄偅浠� {@code rollCycleTasks} 鏄�<b>骞跺瓨</b>鐨�</h3>
+ * {@code ServiceTaskScheduleServiceImpl#rollCycleTasks}锛圠170-190锛�<b>鏈鍒绘剰涓嶅姩</b> 鈥斺��
+ * 瀹冧粛琚� {@code RyTask.rollTaskGroupCycle} 璋冪敤锛岃�岄偅鏄幇缃� {@code sys_job} 鐨勫叆鍙c��
+ * 涓ゆ潯璺緞<b>涓嶈兘鍚屾椂琚皟搴�</b>锛堥兘浼氭帹杩涜疆娆� + 鏂板缓 subtask 鈫� 閲嶅杞锛夈��
+ * 绗� 10 姝ヤ細鎶� {@code RyTask} 鐨勬柟娉曚綋鍒囧埌鏈鍙o紝灞婃椂閭d唤鎵嶉��褰广��
+ *
+ * <h3>鈿狅笍 娣辨嫹璐濆繀椤诲師鏍风敤 JSON 搴忓垪鍖�</h3>
+ * 涓嶈鎶� {@code JSON.parseObject(JSON.toJSONString(src), ...)} 鏀瑰啓鎴愰�愬瓧娈� set 鈥斺��
+ * 116 涓瓧娈电殑閫愪釜鎼繍鏄涓哄彉鏇寸殑楂樺彂鍦帮紝涓旀病鏈変换浣曞ソ澶勩��
+ */
+@Slf4j
+@Component
+public class ScheduleRollPortImpl implements ScheduleRollPort {
+
+    @Autowired
+    private ServiceTaskScheduleMapper serviceTaskScheduleMapper;
+
+    @Autowired
+    private ServiceTaskScheduleDetailMapper serviceTaskScheduleDetailMapper;
+
+    @Autowired
+    private ServiceSubtaskMapper serviceSubtaskMapper;
+
+    @Autowired
+    private ServiceSubtaskPreachformMapper serviceSubtaskPreachformMapper;
+
+    /**
+     * 鏌ヤ竴鎵瑰緟璇勪及鐨勫惊鐜鍒掞紙鍘� {@code rollCycleTasks} L171锛夈��
+     * <p>
+     * 鍙洖浼� id 鍒楄〃锛氱湡姝g殑 roll 鍔ㄤ綔鍦� {@link #rollOneSchedule(Long)} 閲岄噸鏂板姞杞藉畬鏁磋鍒�
+     * 锛堝師瀹炵幇涔熸槸鎶婃暣涓� {@code ServiceTaskSchedule} 浼犺繘 rollOneSchedule锛�
+     * 鎵�浠ヨ涔変竴鑷达級銆�
+     */
+    @Override
+    public List<Long> selectActiveCycleSchedules(int limit) {
+        List<ServiceTaskSchedule> schedules =
+                serviceTaskScheduleMapper.selectActiveCycleSchedules(limit);
+        if (CollectionUtils.isEmpty(schedules)) {
+            return new ArrayList<>();
+        }
+        List<Long> ids = new ArrayList<>(schedules.size());
+        for (ServiceTaskSchedule s : schedules) {
+            ids.add(s.getId());
+        }
+        return ids;
+    }
+
+    /**
+     * 婊氬姩鍗曚釜璁″垝 鈥斺�� 閫愯鎼嚜鍘� {@code rollOneSchedule}锛圠196-280锛夈��
+     * <p>
+     * 鈿狅笍 鍘熷疄鐜版帴鏀剁殑鏄�<b>鏁翠釜 {@code ServiceTaskSchedule} 瀵硅薄</b>锛堢敱 {@code rollCycleTasks}
+     * 浠庢煡璇㈢粨鏋滈噷浼犺繘鏉ワ級銆傜鍙g鍚嶅彧缁� id锛屾墍浠ヨ繖閲岃**閲嶆柊鏌ヤ竴娆�**銆�
+     * 涓烘闇�瑕佹寜 id 鏌ヨ鍒掞細鐢� {@code selectByTaskid} 涓嶈锛堥偅鏄寜 taskid锛夛紝
+     * 鎵�浠ヨ繖閲岀敤 {@code selectActiveCycleSchedules} 鐨勫悓娆捐兘鍔� 鈥斺��
+     * 瑙佷笅鏂� {@link #loadSchedule(Long)} 鐨勮鏄庛��
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public VisitScheduleRollResultDto rollOneSchedule(Long scheduleId) {
+        ServiceTaskSchedule schedule = loadSchedule(scheduleId);
+        if (schedule == null) {
+            log.warn("[浠诲姟缁�-婊氬姩] scheduleId={} 鍔犺浇涓嶅埌璁″垝锛岃烦杩�", scheduleId);
+            return VisitScheduleRollResultDto.skipped(scheduleId, "璁″垝涓嶅瓨鍦�");
+        }
+        return doRoll(schedule);
+    }
+
+    /**
+     * 鎸� id 鍔犺浇璁″垝銆�
+     * <p>
+     * 鈿狅笍 <b>杩欓噷涓�"鍘熷疄鐜�"鏈変竴涓繀椤昏鏄庣殑宸紓</b>锛�
+     * 鍘� {@code rollCycleTasks} 鏄妸鏌ヨ缁撴灉閲岀殑瀵硅薄<b>鐩存帴浼犺繘鏉�</b>锛堜笉閲嶆煡锛夛紱
+     * 绔彛鍙粰 id锛屾墍浠ュ繀椤婚噸鏌ャ�傞噸鏌ヤ笌"鐢ㄦ煡璇㈢粨鏋�"鐨勫樊鍒彧鍦ㄤ簬
+     * <b>涓ゆ璇诲彇涔嬮棿璁″垝鏄惁琚敼杩�</b>鈥斺�斿悓涓�鎵逛换鍔¢噷杩欐槸姣绾х獥鍙o紝
+     * 涓� rollOneSchedule 鏈韩浼氬仛涔愯鐨�"鏈疆鏄惁缁撴潫"鏍¢獙锛屾墍浠ヤ笉鏀瑰彉鍙瀵熻涓恒��
+     * <p>
+     * 瀹炵幇涓婄敤 {@code selectServiceTaskScheduleList} 鎸� id 杩囨护锛堣 Mapper 宸叉湁姝よ兘鍔涳級銆�
+     */
+    private ServiceTaskSchedule loadSchedule(Long scheduleId) {
+        ServiceTaskSchedule q = new ServiceTaskSchedule();
+        q.setId(scheduleId);
+        List<ServiceTaskSchedule> rows = serviceTaskScheduleMapper.selectServiceTaskScheduleList(q);
+        if (CollectionUtils.isEmpty(rows)) {
+            return null;
+        }
+        return rows.get(0);
+    }
+
+    /**
+     * 鍘� {@code rollOneSchedule} 鏈綋锛圠196-280锛夈��
+     */
+    private VisitScheduleRollResultDto doRoll(ServiceTaskSchedule schedule) {
+        Long scheduleId = schedule.getId();
+        Long currentLoop = schedule.getCurrentLoopCount() == null ? 0L : schedule.getCurrentLoopCount();
+
+        // 鍘� L200-209锛氫笂闄愭牎楠�
+        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 VisitScheduleRollResultDto.skipped(scheduleId, "宸茶揪 maxLoopCount");
+            }
+        }
+
+        // 鍘� L211-216锛氭湰杞槸鍚﹀畬鎴�
+        int active = serviceSubtaskMapper.countActiveByScheduleIdAndLoop(scheduleId, currentLoop);
+        if (active > 0) {
+            log.debug("[浠诲姟缁�-婊氬姩] scheduleId={} loop={} 浠嶆湁 {} 鏉� subtask 鏈粨鏉�, 璺宠繃",
+                    scheduleId, currentLoop, active);
+            return VisitScheduleRollResultDto.skipped(scheduleId, "鏈疆浠嶆湁鏈粨鏉熺殑 subtask");
+        }
+
+        // 鍘� L218-224锛氭媺鏈疆 subtask 鍋氬厠闅嗘ā鏉�
+        List<ServiceSubtask> currentLoopSubtasks =
+                serviceSubtaskMapper.selectByScheduleIdAndLoop(scheduleId, currentLoop);
+        if (CollectionUtils.isEmpty(currentLoopSubtasks)) {
+            log.warn("[浠诲姟缁�-婊氬姩] scheduleId={} loop={} 鎵句笉鍒版湰杞� subtask, 璺宠繃", scheduleId, currentLoop);
+            return VisitScheduleRollResultDto.skipped(scheduleId, "鎵句笉鍒版湰杞� subtask");
+        }
+
+        // 鍘� L226-232锛氭媺鏈鍒掓槑缁�
+        List<ServiceTaskScheduleDetail> detailList =
+                serviceTaskScheduleDetailMapper.selectByScheduleid(scheduleId);
+        if (CollectionUtils.isEmpty(detailList)) {
+            log.warn("[浠诲姟缁�-婊氬姩] scheduleId={} 鏄庣粏涓虹┖, 璺宠繃", scheduleId);
+            return VisitScheduleRollResultDto.skipped(scheduleId, "鏄庣粏涓虹┖");
+        }
+
+        // 鍘� L234-240锛氭帹杩涙闀匡紙杞棿闅旓級
+        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 VisitScheduleRollResultDto.skipped(scheduleId, "scheduleTimeNum 闈炴硶");
+        }
+
+        long nextLoop = currentLoop + 1;
+
+        // 鍘� L246-251锛氭妸 detailList 鎸� seq 绱㈠紩
+        Date now = DateUtils.getNowDate();
+        Map<Integer, ServiceTaskScheduleDetail> detailBySeq = new HashMap<>(detailList.size());
+        for (ServiceTaskScheduleDetail d : detailList) {
+            detailBySeq.put(d.getSeq(), d);
+        }
+
+        // 鍘� L253-266锛氶�愭潯鍏嬮殕
+        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);
+            // 鍘� L264锛氬鍒� preachform
+            copyPreachform(src.getId(), copy);
+            produced++;
+        }
+
+        // 鍘� L268-276锛氭帹杩� schedule
+        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 VisitScheduleRollResultDto.rolled(scheduleId, nextLoop, produced);
+    }
+
+    /**
+     * 鍏嬮殕 subtask 鍒颁笅涓�杞� 鈥斺�� 閫愯鎼嚜鍘� L285-313銆�
+     * <p>
+     * 鈿狅笍 绗竴琛岀殑鍏ㄥ瓧娈垫繁鎷疯礉锛�116 涓瓧娈碉級<b>蹇呴』鍘熸牱鐢� JSON 搴忓垪鍖�</b>锛屼笉瑕佹敼鍐欐垚閫愬瓧娈� set銆�
+     */
+    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 鈥斺�� 閫愯鎼嚜鍘� L318-338銆�
+     * <p>
+     * 鈿狅笍 鏌ユ簮琛岀殑鏉′欢鏄� {@code subid + taskid}锛�<b>涓嶈 orgid</b>锛岃櫧鐒� XML 鏀寔锛夛紝鍘熸牱淇濈暀銆�
+     */
+    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-骞� 鈥斺�� 閫愯鎼嚜鍘� L343-361銆�
+     */
+    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/ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskPreachformMapper.java b/ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskPreachformMapper.java
new file mode 100644
index 0000000..6856afa
--- /dev/null
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskPreachformMapper.java
@@ -0,0 +1,78 @@
+package com.ruoyi.task.mapper;
+
+import com.ruoyi.task.domain.ServiceSubtask;
+import com.ruoyi.task.domain.ServiceSubtaskPreachform;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 闅忚瀛愪换鍔°�屾墽琛屾柟寮忔槑缁嗐�嶏紙{@code service_subtask_preachform}锛塎apper銆�
+ *
+ * <h3>2026-11 褰掑睘璋冩暣</h3>
+ * 璇ヨ〃鍘熷厛褰� {@code ruoyi-inhospvisit}锛堢粡 {@code PreachformApi} 濂戠害瀵瑰锛夛紝
+ * smartor 渚у彟鏈変竴浠藉悓琛� CRUD 鐢变竴涓� {@code @Primary} 濮旀墭瀹炵幇鍏滅潃锛宺uoyi-task 鍙兘缁�
+ * {@code ServiceSubtaskPreachformMapper} 闂存帴璇诲啓銆傛寜"琛ㄨ窡闅忎笟鍔″煙"鐨勫彛寰勶紝杩欏紶琛ㄦ暣浣撳垝褰� {@code ruoyi-task}锛�
+ * <ul>
+ *   <li>SQL 涓� Mapper 鍙湁杩欎竴浠斤紙鏈帴鍙� + {@code mapper/task/ServiceSubtaskPreachformMapper.xml}锛夛紱</li>
+ *   <li>ruoyi-task / ruoyi-phone / smartor / ruoyi-admin / ruoyi-quartz 涓�寰嬬洿鎺ユ敞鍏ユ湰 Mapper锛�</li>
+ *   <li>{@code ruoyi-inhospvisit} 鍙嶅悜渚濊禆涓嶆垚绔嬶紙瀹冭鍐欒繖寮犺〃锛夛紝鍥犳浠嶈蛋
+ *       {@code com.ruoyi.common.core.service.PreachformApi} 濂戠害锛屽疄鐜版柟鏀逛负鏈ā鍧�
+ *       锛坽@code com.ruoyi.task.api.contract.PreachformApiImpl}锛夈��</li>
+ * </ul>
+ *
+ * <h3>鈿狅笍 鏂规硶鍚嶆部鐢ㄨ縼绉诲墠锛坰martor / 鏈ā鍧楄皟鐢ㄧ偣淇濇寔闆舵敼鍔級</h3>
+ * 璇彞 id 涓� {@link ServiceSubtaskPreachform} 瀛楁涓�涓�瀵瑰簲锛屾潯浠躲�侀『搴忕収鎶勫師 SQL锛�
+ * 涓嶅仛"椤烘墜绮剧畝"銆備笁涓� {@code delete*} 鍦ㄥ簱閲屽叾瀹炴槸<b>杞垹</b>锛坽@code update ... set del_flag = 1}锛夈��
+ */
+@Mapper
+public interface ServiceSubtaskPreachformMapper {
+
+    /**
+     * 鎸変富閿煡锛堝師 {@code selectServiceSubtaskPreachformById}锛夈��
+     */
+    ServiceSubtaskPreachform selectServiceSubtaskPreachformById(Long id);
+
+    /**
+     * 鎸夋潯浠舵煡锛堝師 {@code selectServiceSubtaskPreachformList}锛夈��
+     * <p>
+     * 鍥哄畾鏉′欢 {@code del_flag = 0}锛屽叾浣� 8 涓瓧娈典负鍙�夋潯浠讹細
+     * {@code subid / taskid / current_send_time / preachform / sendstate / orgid / sort / compensate_time}銆�
+     */
+    List<ServiceSubtaskPreachform> selectServiceSubtaskPreachformList(ServiceSubtaskPreachform query);
+
+    /**
+     * 鏂板锛堣嚜澧炰富閿洖濉埌 {@code id}锛夈��
+     */
+    int insertServiceSubtaskPreachform(ServiceSubtaskPreachform row);
+
+    /**
+     * 鎸変富閿洿鏂帮紙{@code where id = #{id}}锛孲ET 鍚勫垪鍧囦负鍙�夛級銆�
+     */
+    int updateServiceSubtaskPreachform(ServiceSubtaskPreachform row);
+
+    /**
+     * 鎸夋潯浠舵洿鏂帮紙SET 鍚� update锛學HERE 鎸� {@code subid / taskid / preachform / remark / orgid}锛屾棤 id 鏉′欢锛夈��
+     */
+    int updateSSPByCondition(ServiceSubtaskPreachform row);
+
+    /**
+     * 鎸変富閿蒋鍒犮��
+     */
+    int deleteServiceSubtaskPreachformById(Long id);
+
+    /**
+     * 鎸� {@code subid} 杞垹銆�
+     */
+    int deleteServiceSubtaskPreachformBySubid(Long subid);
+
+    /**
+     * 鎸変富閿泦鍚堣蒋鍒犮��
+     */
+    int deleteServiceSubtaskPreachformByIds(Long[] ids);
+
+    /**
+     * 鎸� {@code taskid + orgid} 鏌ワ紙鏂规硶鍚嶆部鐢ㄦ棫鍚嶏紝<b>涓嶆槸</b>鎸� subtask 涓婚敭鏌ワ級銆�
+     */
+    List<ServiceSubtaskPreachform> selectBySubtask(ServiceSubtask subtask);
+}
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskRecordMapper.java b/ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskRecordMapper.java
new file mode 100644
index 0000000..e8f6193
--- /dev/null
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskRecordMapper.java
@@ -0,0 +1,68 @@
+package com.ruoyi.task.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+import com.ruoyi.task.domain.ServiceSubtaskRecord;
+
+/**
+ * 銆愯濉啓鍔熻兘鍚嶇О銆慚apper鎺ュ彛
+ *
+ * @author ruoyi
+ * @date 2024-07-01
+ */
+@Mapper
+public interface ServiceSubtaskRecordMapper {
+    /**
+     * 鏌ヨ銆愯濉啓鍔熻兘鍚嶇О銆�
+     *
+     * @param id 銆愯濉啓鍔熻兘鍚嶇О銆戜富閿�
+     * @return 銆愯濉啓鍔熻兘鍚嶇О銆�
+     */
+    public ServiceSubtaskRecord selectServiceSubtaskRecordById(Long id);
+
+    /**
+     * 鏌ヨ銆愯濉啓鍔熻兘鍚嶇О銆戝垪琛�
+     *
+     * @param serviceSubtaskRecord 銆愯濉啓鍔熻兘鍚嶇О銆�
+     * @return 銆愯濉啓鍔熻兘鍚嶇О銆戦泦鍚�
+     */
+    public List<ServiceSubtaskRecord> selectServiceSubtaskRecordList(ServiceSubtaskRecord serviceSubtaskRecord);
+
+    /**
+     * 鎸� subtask_id 鍒楄〃鎵归噺鏌ヨ闅忚璁板綍锛屾秷闄+1
+     */
+    public List<ServiceSubtaskRecord> selectServiceSubtaskRecordBySubtaskIds(List<Long> subtaskIds);
+
+    /**
+     * 鏂板銆愯濉啓鍔熻兘鍚嶇О銆�
+     *
+     * @param serviceSubtaskRecord 銆愯濉啓鍔熻兘鍚嶇О銆�
+     * @return 缁撴灉
+     */
+    public int insertServiceSubtaskRecord(ServiceSubtaskRecord serviceSubtaskRecord);
+
+    /**
+     * 淇敼銆愯濉啓鍔熻兘鍚嶇О銆�
+     *
+     * @param serviceSubtaskRecord 銆愯濉啓鍔熻兘鍚嶇О銆�
+     * @return 缁撴灉
+     */
+    public int updateServiceSubtaskRecord(ServiceSubtaskRecord serviceSubtaskRecord);
+
+    /**
+     * 鍒犻櫎銆愯濉啓鍔熻兘鍚嶇О銆�
+     *
+     * @param id 銆愯濉啓鍔熻兘鍚嶇О銆戜富閿�
+     * @return 缁撴灉
+     */
+    public int deleteServiceSubtaskRecordById(Long id);
+
+    /**
+     * 鎵归噺鍒犻櫎銆愯濉啓鍔熻兘鍚嶇О銆�
+     *
+     * @param ids 闇�瑕佸垹闄ょ殑鏁版嵁涓婚敭闆嗗悎
+     * @return 缁撴灉
+     */
+    public int deleteServiceSubtaskRecordByIds(Long[] ids);
+}
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceSubtaskRecordService.java b/ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceSubtaskRecordService.java
new file mode 100644
index 0000000..4f52f09
--- /dev/null
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceSubtaskRecordService.java
@@ -0,0 +1,67 @@
+package com.ruoyi.task.service;
+
+
+import java.util.List;
+import com.ruoyi.task.domain.ServiceSubtaskRecord;
+
+/**
+ * 銆愯濉啓鍔熻兘鍚嶇О銆慡ervice鎺ュ彛
+ *
+ * @author ruoyi
+ * @date 2024-07-01
+ */
+public interface IServiceSubtaskRecordService
+{
+    /**
+     * 鏌ヨ銆愯濉啓鍔熻兘鍚嶇О銆�
+     *
+     * @param id 銆愯濉啓鍔熻兘鍚嶇О銆戜富閿�
+     * @return 銆愯濉啓鍔熻兘鍚嶇О銆�
+     */
+    public ServiceSubtaskRecord selectServiceSubtaskRecordById(Long id);
+
+    /**
+     * 鏌ヨ銆愯濉啓鍔熻兘鍚嶇О銆戝垪琛�
+     *
+     * @param serviceSubtaskRecord 銆愯濉啓鍔熻兘鍚嶇О銆�
+     * @return 銆愯濉啓鍔熻兘鍚嶇О銆戦泦鍚�
+     */
+    public List<ServiceSubtaskRecord> selectServiceSubtaskRecordList(ServiceSubtaskRecord serviceSubtaskRecord);
+
+    /**
+     * 鎸� subtask_id 鍒楄〃鎵归噺鏌ヨ锛岀粨鏋滅敤 Map<subtaskId, records> 缁撴瀯杩斿洖
+     */
+    public java.util.Map<Long, List<ServiceSubtaskRecord>> selectRecordMapBySubtaskIds(List<Long> subtaskIds);
+
+    /**
+     * 鏂板銆愯濉啓鍔熻兘鍚嶇О銆�
+     *
+     * @param serviceSubtaskRecord 銆愯濉啓鍔熻兘鍚嶇О銆�
+     * @return 缁撴灉
+     */
+    public int insertServiceSubtaskRecord(ServiceSubtaskRecord serviceSubtaskRecord);
+
+    /**
+     * 淇敼銆愯濉啓鍔熻兘鍚嶇О銆�
+     *
+     * @param serviceSubtaskRecord 銆愯濉啓鍔熻兘鍚嶇О銆�
+     * @return 缁撴灉
+     */
+    public int updateServiceSubtaskRecord(ServiceSubtaskRecord serviceSubtaskRecord);
+
+    /**
+     * 鎵归噺鍒犻櫎銆愯濉啓鍔熻兘鍚嶇О銆�
+     *
+     * @param ids 闇�瑕佸垹闄ょ殑銆愯濉啓鍔熻兘鍚嶇О銆戜富閿泦鍚�
+     * @return 缁撴灉
+     */
+    public int deleteServiceSubtaskRecordByIds(Long[] ids);
+
+    /**
+     * 鍒犻櫎銆愯濉啓鍔熻兘鍚嶇О銆戜俊鎭�
+     *
+     * @param id 銆愯濉啓鍔熻兘鍚嶇О銆戜富閿�
+     * @return 缁撴灉
+     */
+    public int deleteServiceSubtaskRecordById(Long id);
+}
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskRecordServiceImpl.java b/ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskRecordServiceImpl.java
new file mode 100644
index 0000000..c711474
--- /dev/null
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskRecordServiceImpl.java
@@ -0,0 +1,102 @@
+package com.ruoyi.task.service.impl;
+
+import com.ruoyi.common.utils.DateUtils;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import com.ruoyi.task.domain.ServiceSubtaskRecord;
+import com.ruoyi.task.mapper.ServiceSubtaskRecordMapper;
+import com.ruoyi.task.service.IServiceSubtaskRecordService;
+
+/**
+ * 銆愯濉啓鍔熻兘鍚嶇О銆慡ervice涓氬姟灞傚鐞�
+ *
+ * @author ruoyi
+ * @date 2024-07-01
+ */
+@Service
+public class ServiceSubtaskRecordServiceImpl implements IServiceSubtaskRecordService {
+    @Autowired
+    private ServiceSubtaskRecordMapper serviceSubtaskRecordMapper;
+
+    /**
+     * 鏌ヨ銆愯濉啓鍔熻兘鍚嶇О銆�
+     *
+     * @param id 銆愯濉啓鍔熻兘鍚嶇О銆戜富閿�
+     * @return 銆愯濉啓鍔熻兘鍚嶇О銆�
+     */
+    @Override
+    public ServiceSubtaskRecord selectServiceSubtaskRecordById(Long id) {
+        return serviceSubtaskRecordMapper.selectServiceSubtaskRecordById(id);
+    }
+
+    /**
+     * 鏌ヨ銆愯濉啓鍔熻兘鍚嶇О銆戝垪琛�
+     *
+     * @param serviceSubtaskRecord 銆愯濉啓鍔熻兘鍚嶇О銆�
+     * @return 銆愯濉啓鍔熻兘鍚嶇О銆�
+     */
+    @Override
+    public List<ServiceSubtaskRecord> selectServiceSubtaskRecordList(ServiceSubtaskRecord serviceSubtaskRecord) {
+        return serviceSubtaskRecordMapper.selectServiceSubtaskRecordList(serviceSubtaskRecord);
+    }
+
+    @Override
+    public Map<Long, List<ServiceSubtaskRecord>> selectRecordMapBySubtaskIds(List<Long> subtaskIds) {
+        if (CollectionUtils.isEmpty(subtaskIds)) return new java.util.HashMap<>();
+        List<ServiceSubtaskRecord> all = serviceSubtaskRecordMapper.selectServiceSubtaskRecordBySubtaskIds(subtaskIds);
+        if (CollectionUtils.isEmpty(all)) return new java.util.HashMap<>();
+        return all.stream().collect(Collectors.groupingBy(ServiceSubtaskRecord::getSubtaskId));
+    }
+
+    /**
+     * 鏂板銆愯濉啓鍔熻兘鍚嶇О銆�
+     *
+     * @param serviceSubtaskRecord 銆愯濉啓鍔熻兘鍚嶇О銆�
+     * @return 缁撴灉
+     */
+    @Override
+    public int insertServiceSubtaskRecord(ServiceSubtaskRecord serviceSubtaskRecord) {
+        serviceSubtaskRecord.setCreateTime(DateUtils.getNowDate());
+        return serviceSubtaskRecordMapper.insertServiceSubtaskRecord(serviceSubtaskRecord);
+    }
+
+    /**
+     * 淇敼銆愯濉啓鍔熻兘鍚嶇О銆�
+     *
+     * @param serviceSubtaskRecord 銆愯濉啓鍔熻兘鍚嶇О銆�
+     * @return 缁撴灉
+     */
+    @Override
+    public int updateServiceSubtaskRecord(ServiceSubtaskRecord serviceSubtaskRecord) {
+        serviceSubtaskRecord.setUpdateTime(DateUtils.getNowDate());
+        return serviceSubtaskRecordMapper.updateServiceSubtaskRecord(serviceSubtaskRecord);
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎銆愯濉啓鍔熻兘鍚嶇О銆�
+     *
+     * @param ids 闇�瑕佸垹闄ょ殑銆愯濉啓鍔熻兘鍚嶇О銆戜富閿�
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteServiceSubtaskRecordByIds(Long[] ids) {
+        return serviceSubtaskRecordMapper.deleteServiceSubtaskRecordByIds(ids);
+    }
+
+    /**
+     * 鍒犻櫎銆愯濉啓鍔熻兘鍚嶇О銆戜俊鎭�
+     *
+     * @param id 銆愯濉啓鍔熻兘鍚嶇О銆戜富閿�
+     * @return 缁撴灉
+     */
+    @Override
+    public int deleteServiceSubtaskRecordById(Long id) {
+        return serviceSubtaskRecordMapper.deleteServiceSubtaskRecordById(id);
+    }
+}
diff --git a/ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskPreachformMapper.xml b/ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskPreachformMapper.xml
new file mode 100644
index 0000000..702a23d
--- /dev/null
+++ b/ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskPreachformMapper.xml
@@ -0,0 +1,331 @@
+<?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.ruoyi.task.mapper.ServiceSubtaskPreachformMapper">
+
+    <!--
+      ============================================================================
+      鍑哄叆闄㈤殢璁挎ā鍧楄嚜甯︾殑 service_subtask_preachform 璇诲啓銆�
+      ============================================================================
+      涓� smartor 鐨� mapper/smartor/ServiceSubtaskPreachformMapper.xml 绛変环锛�
+        路 selectList 鈫� selectServiceSubtaskPreachformList锛堝師 XML L44-73锛�
+        路 insert     鈫� insertServiceSubtaskPreachform锛堝師 XML L81-151锛寀seGeneratedKeys锛�
+      鍘� XML 鐢� <sql id="selectServiceSubtaskPreachformVo"> 鍏变韩鐗囨锛涜繖閲屻�愭樉寮忓垪鍑哄悓涓�鎵瑰垪銆戯紝
+      涓嶅鍒剁墖娈碉紙涓� VisitInhospMapper 鍚屼竴鍋氭硶锛氳嚜鍖呭惈銆佷究浜庨�愭潯姣斿锛夈��
+      ============================================================================
+    -->
+
+    <resultMap type="com.ruoyi.task.domain.ServiceSubtaskPreachform" id="ServiceSubtaskPreachformResult">
+        <result property="id" column="id"/>
+        <result property="subid" column="subid"/>
+        <result property="taskid" column="taskid"/>
+        <result property="preachform" column="preachform"/>
+        <result property="sendstate" column="sendstate"/>
+        <result property="currentSendTime" column="current_send_time"/>
+        <result property="remark" column="remark"/>
+        <result property="orgid" column="orgid"/>
+        <result property="delFlag" column="del_flag"/>
+        <result property="createTime" column="create_time"/>
+        <result property="createBy" column="create_by"/>
+        <result property="updateTime" column="update_time"/>
+        <result property="updateBy" column="update_by"/>
+        <result property="compensateTime" column="compensate_time"/>
+        <result property="sort" column="sort"/>
+    </resultMap>
+
+    <!-- 鏉′欢涓庨『搴忛�愭潯瀵归綈鍘� XML L44-73 -->
+    <select id="selectServiceSubtaskPreachformList" parameterType="com.ruoyi.task.domain.ServiceSubtaskPreachform"
+            resultMap="ServiceSubtaskPreachformResult">
+        select id,
+               subid,
+               taskid,
+               preachform,
+               sendstate,
+               current_send_time,
+               remark,
+               orgid,
+               del_flag,
+               create_time,
+               create_by,
+               update_time,
+               update_by,
+               compensate_time,
+               sort
+        from service_subtask_preachform
+        where 1=1
+        and del_flag = 0
+        <if test="subid != null ">
+            and subid = #{subid}
+        </if>
+        <if test="taskid != null ">
+            and taskid = #{taskid}
+        </if>
+        <if test="currentSendTime != null ">
+            and current_send_time = #{currentSendTime}
+        </if>
+        <if test="preachform != null  and preachform != ''">
+            and preachform = #{preachform}
+        </if>
+        <if test="sendstate != null  and sendstate != ''">
+            and sendstate = #{sendstate}
+        </if>
+        <if test="orgid != null  and orgid != ''">
+            and orgid = #{orgid}
+        </if>
+        <if test="sort != null ">
+            and sort = #{sort}
+        </if>
+        <if test="compensateTime != null ">
+            and compensate_time = #{compensateTime}
+        </if>
+    </select>
+
+    <!-- 鍒椾笌椤哄簭閫愭潯瀵归綈鍘� XML L81-151 -->
+    <insert id="insertServiceSubtaskPreachform" parameterType="com.ruoyi.task.domain.ServiceSubtaskPreachform"
+            useGeneratedKeys="true" keyProperty="id">
+        insert into service_subtask_preachform
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="id != null">id,</if>
+            <if test="subid != null">subid,</if>
+            <if test="taskid != null">taskid,</if>
+            <if test="preachform != null">preachform,</if>
+            <if test="sendstate != null">sendstate,</if>
+            <if test="remark != null">remark,</if>
+            <if test="orgid != null">orgid,</if>
+            <if test="delFlag != null">del_flag,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="updateBy != null">update_by,</if>
+            <if test="sort != null">sort,</if>
+            <if test="compensateTime != null">compensate_time,</if>
+            <if test="currentSendTime != null ">current_send_time,</if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="id != null">#{id},</if>
+            <if test="subid != null">#{subid},</if>
+            <if test="taskid != null">#{taskid},</if>
+            <if test="preachform != null">#{preachform},</if>
+            <if test="sendstate != null">#{sendstate},</if>
+            <if test="remark != null">#{remark},</if>
+            <if test="orgid != null">#{orgid},</if>
+            <if test="delFlag != null">#{delFlag},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="updateBy != null">#{updateBy},</if>
+            <if test="sort != null">#{sort},</if>
+            <if test="compensateTime != null">#{compensateTime},</if>
+            <if test="currentSendTime != null ">#{currentSendTime},</if>
+        </trim>
+    </insert>
+
+    <!--
+      ============================================================================
+      璺ㄦā鍧� SQL 鏀跺彛琛ラ綈鐨� 7 涓� statement锛堢収鎶� smartor 鍘� XML锛屾潯浠�/鍒�/椤哄簭涓嶅彉锛�
+        路 selectById             鈫� selectServiceSubtaskPreachformById
+        路 selectByTaskidAndOrgid 鈫� selectBySubtask锛堝師鎸� taskid + orgid锛岄潪涓婚敭锛�
+        路 updateById             鈫� updateServiceSubtaskPreachform锛坵here id = #{id}锛�
+        路 updateByCondition      鈫� updateSSPByCondition锛圫ET 鍚� updateById锛學HERE 鎸夋潯浠朵笖鏃� id锛�
+        路 softDeleteById         鈫� deleteServiceSubtaskPreachformById锛坲pdate 缃� del_flag=1锛�
+        路 softDeleteByIds        鈫� deleteServiceSubtaskPreachformByIds锛堝悓涓婏紝in 闆嗗悎锛�
+        路 softDeleteBySubid      鈫� deleteServiceSubtaskPreachformBySubid锛堝悓涓婏紝鎸� subid锛�
+      娉ㄦ剰锛氫笁涓� delete* 鍦ㄥ師 XML 閲屽氨鏄�"杞垹"锛坲pdate set del_flag=1锛夛紝姝ゅ鍘熸牱淇濈暀锛�
+            鍏朵腑 BySubid 鍘熸湰鐢ㄧ殑灏辨槸 <delete> 鏍囩锛屼篃鐓ф妱銆�
+      ============================================================================
+    -->
+
+    <select id="selectServiceSubtaskPreachformById" parameterType="Long" resultMap="ServiceSubtaskPreachformResult">
+        select id,
+               subid,
+               taskid,
+               preachform,
+               sendstate,
+               current_send_time,
+               remark,
+               orgid,
+               del_flag,
+               create_time,
+               create_by,
+               update_time,
+               update_by,
+               compensate_time,
+               sort
+        from service_subtask_preachform
+        where id = #{id}
+    </select>
+
+    <!-- 鍘� selectBySubtask锛歞el_flag=0 + 鍙�� taskid/orgid锛堝師鍙傛暟鏄� ServiceSubtask锛屽疄闄呭彧鐢ㄨ繖涓や釜瀛楁锛� -->
+    <select id="selectBySubtask" resultMap="ServiceSubtaskPreachformResult">
+        select id,
+               subid,
+               taskid,
+               preachform,
+               sendstate,
+               current_send_time,
+               remark,
+               orgid,
+               del_flag,
+               create_time,
+               create_by,
+               update_time,
+               update_by,
+               compensate_time,
+               sort
+        from service_subtask_preachform
+        WHERE del_flag = 0
+        <if test="taskid != null">
+            AND taskid = #{taskid}
+        </if>
+        <if test="orgid != null and orgid != ''">
+            AND orgid = #{orgid}
+        </if>
+    </select>
+
+    <!-- 鍘� updateServiceSubtaskPreachform锛歸here id = #{id} -->
+    <update id="updateServiceSubtaskPreachform" parameterType="com.ruoyi.task.domain.ServiceSubtaskPreachform">
+        update service_subtask_preachform
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="subid != null">subid =
+                #{subid},
+            </if>
+            <if test="taskid != null">taskid =
+                #{taskid},
+            </if>
+            <if test="preachform != null">preachform =
+                #{preachform},
+            </if>
+            <if test="sendstate != null">sendstate =
+                #{sendstate},
+            </if>
+            <if test="remark != null">remark =
+                #{remark},
+            </if>
+            <if test="orgid != null">orgid =
+                #{orgid},
+            </if>
+            <if test="delFlag != null">del_flag =
+                #{delFlag},
+            </if>
+            <if test="createTime != null">create_time =
+                #{createTime},
+            </if>
+            <if test="createBy != null">create_by =
+                #{createBy},
+            </if>
+            <if test="updateTime != null">update_time =
+                #{updateTime},
+            </if>
+            <if test="updateBy != null">update_by =
+                #{updateBy},
+            </if>
+            <if test="sort != null">sort =
+                #{sort},
+            </if>
+            <if test="compensateTime != null">compensate_time =
+                #{compensateTime},
+            </if>
+            <if test="currentSendTime != null ">
+                current_send_time = #{currentSendTime},
+            </if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <!-- 鍘� updateSSPByCondition锛歋ET 涓� updateById 鐩稿悓锛學HERE 鎸夋潯浠朵笖鏃� id -->
+    <update id="updateSSPByCondition" parameterType="com.ruoyi.task.domain.ServiceSubtaskPreachform">
+        update service_subtask_preachform
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="subid != null">subid =
+                #{subid},
+            </if>
+            <if test="taskid != null">taskid =
+                #{taskid},
+            </if>
+            <if test="preachform != null">preachform =
+                #{preachform},
+            </if>
+            <if test="sendstate != null">sendstate =
+                #{sendstate},
+            </if>
+            <if test="remark != null">remark =
+                #{remark},
+            </if>
+            <if test="orgid != null">orgid =
+                #{orgid},
+            </if>
+            <if test="delFlag != null">del_flag =
+                #{delFlag},
+            </if>
+            <if test="createTime != null">create_time =
+                #{createTime},
+            </if>
+            <if test="createBy != null">create_by =
+                #{createBy},
+            </if>
+            <if test="updateTime != null">update_time =
+                #{updateTime},
+            </if>
+            <if test="updateBy != null">update_by =
+                #{updateBy},
+            </if>
+            <if test="sort != null">sort =
+                #{sort},
+            </if>
+            <if test="compensateTime != null">compensate_time =
+                #{compensateTime},
+            </if>
+            <if test="currentSendTime != null ">
+                current_send_time = #{currentSendTime},
+            </if>
+        </trim>
+        where 1=1
+        <if test="subid != null">and subid =
+            #{subid}
+        </if>
+        <if test="taskid != null">and taskid =
+            #{taskid}
+        </if>
+        <if test="preachform != null">and preachform =
+            #{preachform}
+        </if>
+        <if test="remark != null">and remark =
+            #{remark}
+        </if>
+        <if test="orgid != null">and orgid =
+            #{orgid}
+        </if>
+    </update>
+
+    <!-- 鍘� deleteServiceSubtaskPreachformById锛�<update> 缃� del_flag=1 -->
+    <update id="deleteServiceSubtaskPreachformById" parameterType="Long">
+        update service_subtask_preachform
+        <trim prefix="SET" suffixOverrides=",">
+            del_flag =1
+        </trim>
+        where id = #{id}
+    </update>
+
+    <!-- 鍘� deleteServiceSubtaskPreachformByIds锛�<update> + in 闆嗗悎 -->
+    <update id="deleteServiceSubtaskPreachformByIds" parameterType="String">
+        update service_subtask_preachform
+        <trim prefix="SET" suffixOverrides=",">
+            del_flag =1
+        </trim>
+        where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </update>
+
+    <!-- 鍘� deleteServiceSubtaskPreachformBySubid锛�<delete> 鏍囩浣嗚鍙ユ槸 update -->
+    <delete id="deleteServiceSubtaskPreachformBySubid" parameterType="Long">
+        update service_subtask_preachform
+        <trim prefix="SET" suffixOverrides=",">
+            del_flag =1
+        </trim>
+        where subid = #{subid}
+    </delete>
+
+</mapper>
diff --git a/ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskRecordMapper.xml b/ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskRecordMapper.xml
new file mode 100644
index 0000000..559e219
--- /dev/null
+++ b/ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskRecordMapper.xml
@@ -0,0 +1,179 @@
+<?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.ruoyi.task.mapper.ServiceSubtaskRecordMapper">
+
+    <resultMap type="com.ruoyi.task.domain.ServiceSubtaskRecord" id="ServiceSubtaskRecordResult">
+        <result property="id" column="id"/>
+        <result property="taskid" column="taskid"/>
+        <result property="subtaskId" column="subtask_id"/>
+        <result property="uuid" column="uuid"/>
+        <result property="tasktype" column="tasktype"/>
+        <result property="preachform" column="preachform"/>
+        <result property="recordtype" column="recordtype"/>
+        <result property="startTime" column="start_time"/>
+        <result property="endTime" column="end_time"/>
+        <result property="result" column="result"/>
+        <result property="delFlag" column="del_flag"/>
+        <result property="updateBy" column="update_by"/>
+        <result property="updateTime" column="update_time"/>
+        <result property="createBy" column="create_by"/>
+        <result property="createTime" column="create_time"/>
+        <result property="isupload" column="isupload"/>
+        <result property="uploadTime" column="upload_time"/>
+        <result property="orgid" column="orgid"/>
+        <result property="pid" column="pid"/>
+        <result property="guid" column="guid"/>
+        <result property="remark" column="remark"/>
+    </resultMap>
+
+    <sql id="selectServiceSubtaskRecordVo">
+        select id,
+               taskid,
+               subtask_id,
+               uuid,
+               tasktype,
+               preachform,
+               recordtype,
+               start_time,
+               end_time,
+               result,
+               del_flag,
+               update_by,
+               update_time,
+               create_by,
+               create_time,
+               isupload,
+               upload_time,
+               orgid,
+               pid,
+               remark,
+               guid
+        from service_subtask_record
+    </sql>
+
+    <select id="selectServiceSubtaskRecordList" parameterType="com.ruoyi.task.domain.ServiceSubtaskRecord"
+            resultMap="ServiceSubtaskRecordResult">
+        <include refid="selectServiceSubtaskRecordVo"/>
+        where 1=1
+            <if test="taskid != null  and taskid != ''">and taskid = #{taskid}</if>
+            <if test="subtaskId != null ">and subtask_id = #{subtaskId}</if>
+            <if test="uuid != null  and uuid != ''">and uuid = #{uuid}</if>
+            <if test="tasktype != null  and tasktype != ''">and tasktype = #{tasktype}</if>
+            <if test="preachform != null  and preachform != ''">and preachform = #{preachform}</if>
+            <if test="recordtype != null  and recordtype != ''">and recordtype = #{recordtype}</if>
+            <if test="startTime != null ">and start_time = #{startTime}</if>
+            <if test="endTime != null ">and end_time = #{endTime}</if>
+            <if test="result != null  and result != ''">and result = #{result}</if>
+            <if test="isupload != null ">and isupload = #{isupload}</if>
+            <if test="uploadTime != null ">and upload_time = #{uploadTime}</if>
+            <if test="orgid != null  and orgid != ''">and orgid = #{orgid}</if>
+            <if test="pid != null ">and pid = #{pid}</if>
+            <if test="guid != null  and guid != ''">and guid = #{guid}</if>
+            <if test="remark != null  and remark != ''">and remark = #{remark}</if>
+    </select>
+
+    <select id="selectServiceSubtaskRecordBySubtaskIds" resultMap="ServiceSubtaskRecordResult">
+        <include refid="selectServiceSubtaskRecordVo"/>
+        where subtask_id IN
+        <foreach collection="list" item="subtaskId" open="(" separator="," close=")">
+            #{subtaskId}
+        </foreach>
+    </select>
+
+    <select id="selectServiceSubtaskRecordById" parameterType="Long" resultMap="ServiceSubtaskRecordResult">
+        <include refid="selectServiceSubtaskRecordVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertServiceSubtaskRecord" parameterType="com.ruoyi.task.domain.ServiceSubtaskRecord">
+        insert into service_subtask_record
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="id != null">id,</if>
+            <if test="taskid != null">taskid,</if>
+            <if test="uuid != null">uuid,</if>
+            <if test="tasktype != null">tasktype,</if>
+            <if test="preachform != null">preachform,</if>
+            <if test="recordtype != null">recordtype,</if>
+            <if test="startTime != null">start_time,</if>
+            <if test="endTime != null">end_time,</if>
+            <if test="result != null">result,</if>
+            <if test="delFlag != null">del_flag,</if>
+            <if test="updateBy != null">update_by,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="isupload != null">isupload,</if>
+            <if test="uploadTime != null">upload_time,</if>
+            <if test="orgid != null">orgid,</if>
+            <if test="pid != null">pid,</if>
+            <if test="guid != null">guid,</if>
+            <if test="remark != null">remark,</if>
+            <if test="subtaskId != null ">subtask_id,</if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="id != null">#{id},</if>
+            <if test="taskid != null">#{taskid},</if>
+            <if test="uuid != null">#{uuid},</if>
+            <if test="tasktype != null">#{tasktype},</if>
+            <if test="preachform != null">#{preachform},</if>
+            <if test="recordtype != null">#{recordtype},</if>
+            <if test="startTime != null">#{startTime},</if>
+            <if test="endTime != null">#{endTime},</if>
+            <if test="result != null">#{result},</if>
+            <if test="delFlag != null">#{delFlag},</if>
+            <if test="updateBy != null">#{updateBy},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="isupload != null">#{isupload},</if>
+            <if test="uploadTime != null">#{uploadTime},</if>
+            <if test="orgid != null">#{orgid},</if>
+            <if test="pid != null">#{pid},</if>
+            <if test="guid != null">#{guid},</if>
+            <if test="remark != null">#{remark},</if>
+            <if test="subtaskId != null ">#{subtaskId},</if>
+        </trim>
+    </insert>
+
+    <update id="updateServiceSubtaskRecord" parameterType="com.ruoyi.task.domain.ServiceSubtaskRecord">
+        update service_subtask_record
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="taskid != null">taskid = #{taskid},</if>
+            <if test="uuid != null">uuid = #{uuid},</if>
+            <if test="tasktype != null">tasktype = #{tasktype},</if>
+            <if test="preachform != null">preachform = #{preachform},</if>
+            <if test="recordtype != null">recordtype = #{recordtype},</if>
+            <if test="startTime != null">start_time = #{startTime},</if>
+            <if test="endTime != null">end_time = #{endTime},</if>
+            <if test="result != null">result = #{result},</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="createBy != null">create_by = #{createBy},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="isupload != null">isupload = #{isupload},</if>
+            <if test="uploadTime != null">upload_time = #{uploadTime},</if>
+            <if test="orgid != null">orgid = #{orgid},</if>
+            <if test="pid != null">pid = #{pid},</if>
+            <if test="guid != null">guid = #{guid},</if>
+            <if test="remark != null">remark = #{remark},</if>
+            <if test="subtaskId != null ">subtask_id = #{subtaskId},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteServiceSubtaskRecordById" parameterType="Long">
+        delete
+        from service_subtask_record
+        where id = #{id}
+    </delete>
+
+    <delete id="deleteServiceSubtaskRecordByIds" parameterType="String">
+        delete from service_subtask_record where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>
diff --git a/smartor/src/main/java/com/smartor/service/IPatMedOuthospService.java b/smartor/src/main/java/com/smartor/service/IPatMedOuthospService.java
new file mode 100644
index 0000000..d6d891a
--- /dev/null
+++ b/smartor/src/main/java/com/smartor/service/IPatMedOuthospService.java
@@ -0,0 +1,68 @@
+package com.smartor.service;
+
+import com.ruoyi.outpatientvisit.domain.PatMedOuthosp;
+
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * 鎮h�呴棬璇婅褰� Service 鈥斺�� <b>杩囨浮鎺ュ彛锛堟鍦ㄦ敹鍙o級</b>銆�
+ *
+ * <h3>鈿狅笍 鏈帴鍙g殑澶ч儴鍒嗘柟娉曞凡涓嶅湪鏈ā鍧楀疄鐜�</h3>
+ * 闂ㄦ�ヨ瘖闅忚妯″潡鍖栵紙O2~O4锛夊凡鎶婇棬璇婅褰曠殑鏈嶅姟绔兘鍔涙暣浣撹縼鍏�
+ * {@code ruoyi-outpatientvisit}锛�
+ * <ul>
+ *   <li>鍩虹璇诲啓/缁熻/鍒嗚〃璺敱 鈫� 璇ユā鍧楃殑 {@code OuthospDataService}锛�</li>
+ *   <li>闅忚缂栨帓锛堟壂鍏ㄩ噺锛夆啋 璇ユā鍧楃殑 {@code OuthospVisitTaskService}銆�</li>
+ * </ul>
+ * 鏈帴鍙d繚鐣欐槸涓轰簡璁�<b>鏃㈡湁璋冪敤鏂归浂鏀瑰姩</b>锛�10 涓枃浠讹紝璺� smartor / ruoyi-admin / ruoyi-quartz锛夛紝
+ * 鐢卞鎵樺疄鐜版妸宸茶縼鏂规硶杞彂鍒版ā鍧楅棬闈� 鈥斺��
+ * 涓庡鏁欏崟璺ㄦā鍧楁敹鍙o紙{@code ServiceSubtaskPreachformMapperBridge}锛夊悓涓�鎵嬫硶銆�
+ *
+ * <h3>鏈帴鍙e埢鎰忓彧淇濈暀銆岃皟鐢ㄦ柟瀹為檯鐢ㄥ埌鐨� 5 涓柟娉曘��</h3>
+ * 鍘熸帴鍙f湁 14 涓柟娉曪紝浣嗗疄娴嬪彧鏈変互涓� 5 涓澶栭儴璋冪敤锛�
+ * <pre>
+ * insertPatMedOuthosp(...)        HNGatherPatArchive銆丆ollectHIS銆丼erviceExternal銆丼erviceSLTD鈥�
+ * updatePatMedOuthosp(...)        CollectHIS銆丼erviceExternal銆丼erviceSubtask鈥�
+ * selectExistingSerialnums(...)   ServiceSLTD鈥�
+ * dealPatMedOuthosp(...)          ServiceExternal
+ * </pre>
+ * 鍏朵綑锛坽@code selectById} / {@code selectList} / {@code selectCount} / {@code getDeptCodeByPatId}
+ * / {@code delete*} / {@code boxedServiceSubtask} / {@code followUp*}锛�<b>鏃犲閮ㄨ皟鐢ㄦ柟</b>锛�
+ * 鏁呬笉鍦ㄦ澹版槑銆�
+ *
+ * <h3>灏嗘潵鍔ㄤ綔</h3>
+ * 璋冪敤鏂归�愪釜鏀逛负鐩存帴渚濊禆妯″潡闂ㄩ潰鍚庯紝鏈帴鍙d笌濮旀墭瀹炵幇鍗冲彲鏁翠綋鍒犻櫎銆�
+ * <b>鏂颁唬鐮佽鐩存帴鐢� {@code ruoyi-outpatientvisit} 鐨�
+ * {@code OuthospDataFacade} / {@code OuthospDataService}銆�</b>
+ *
+ * @deprecated 鐢� {@code com.ruoyi.outpatientvisit.api.OuthospDataFacade} 浠f浛銆�
+ *         淇濈暀浠呬负鍏煎鏃㈡湁璋冪敤鏂癸紝鏂颁唬鐮佷笉瑕佷娇鐢ㄣ��
+ */
+@Deprecated
+public interface IPatMedOuthospService {
+
+    /**
+     * 鏂板闂ㄨ瘖璁板綍锛堟寜灏辫瘖鏃堕棿璺敱鍒板垎琛級銆�
+     */
+    public int insertPatMedOuthosp(PatMedOuthosp patMedOuthosp);
+
+    /**
+     * 淇敼闂ㄨ瘖璁板綍锛堟煡涓嶅埌鍒欒浆鏂板锛夈��
+     */
+    public int updatePatMedOuthosp(PatMedOuthosp patMedOuthosp);
+
+    /**
+     * 鎵归噺鏌ュ凡鍏ュ簱鐨� serialnum锛堟寜灏辫瘖鏃ユ湡瀹氫綅鍒嗚〃锛屼竴娆� IN 鏌ヨ锛夈��
+     */
+    public Set<String> selectExistingSerialnums(String admitDateStr, Collection<String> serialnums);
+
+    /**
+     * 鍗曟偅鑰呮寜闇�闅忚锛堟櫙瀹佺珯鐐圭壒娈婇�昏緫 鈫� 棣栨闅忚鎴栧啀娆¢殢璁匡級銆�
+     * <p>
+     * 鈿狅笍 <b>鏈銆愭湭銆戣縼绉�</b>锛屼粛鐢� smartor 鍘熺敓瀹炵幇锛氬畠渚濊禆 {@code serviceSubtaskMapper}
+     * 涓� {@code followUpByPatMedOuthosp} / {@code followUpAgainByPatMedOuthosp} 涓や釜绉佹湁鏂规硶锛�
+     * 灞炲彟涓�鏉¢摼璺紙"鎸夐渶"鑰岄潪"鎵叏閲�"锛夈�傞殢璇ラ摼璺竴璧锋敹鍙c��
+     */
+    public Integer dealPatMedOuthosp(PatMedOuthosp patMedOuthosp, Integer visitTimeNum);
+}
diff --git a/smartor/src/main/java/com/smartor/service/impl/PatMedOuthospServiceDelegatingImpl.java b/smartor/src/main/java/com/smartor/service/impl/PatMedOuthospServiceDelegatingImpl.java
new file mode 100644
index 0000000..9d95429
--- /dev/null
+++ b/smartor/src/main/java/com/smartor/service/impl/PatMedOuthospServiceDelegatingImpl.java
@@ -0,0 +1,423 @@
+package com.smartor.service.impl;
+
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.DtoConversionUtils;
+import com.ruoyi.outpatientvisit.api.OuthospDataFacade;
+import com.ruoyi.outpatientvisit.domain.PatMedOuthosp;
+import com.ruoyi.patarchive.mapper.PatArchiveMapper;
+import com.smartor.service.IPatMedOuthospService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+import com.ruoyi.common.core.domain.entity.PatArchive;
+import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
+import com.ruoyi.task.domain.ServiceSubtask;
+import com.ruoyi.task.domain.ServiceTask;
+import com.ruoyi.task.domain.ServiceTaskdept;
+import com.ruoyi.task.mapper.ServiceSubtaskMapper;
+import com.ruoyi.task.mapper.ServiceTaskdeptMapper;
+import com.ruoyi.task.mapper.ServiceTaskdiagMapper;
+import com.ruoyi.task.mapper.ServiceTaskMapper;
+
+/**
+ * {@link IPatMedOuthospService} 鐨�<b>濮旀墭瀹炵幇</b> 鈥斺�� 闂ㄦ�ヨ瘖闅忚妯″潡鍖栫殑杩囨浮浠躲��
+ *
+ * <h3>瀹冨仛涓や欢浜�</h3>
+ * <ol>
+ *   <li><b>宸茶縼鐨� 4 涓柟娉� 鈫� 濮旀墭妯″潡</b>锛�
+ *       {@code insert} / {@code update} / {@code selectExistingSerialnums} / {@code dealOutpatientInfo}
+ *       杞彂鍒� {@link OuthospDataFacade}锛圫QL 鍞竴鏀舵暃鍦ㄦā鍧楅噷锛夛紱</li>
+ *   <li><b>鏈縼鐨� 3 涓柟娉� 鈫� 淇濈暀鍘熺敓</b>锛歿@code dealPatMedOuthosp} /
+ *       {@code followUpByPatMedOuthosp} / {@code followUpAgainByPatMedOuthosp}
+ *       鍙婂畠浠殑绉佹湁渚濊禆锛坽@code addDays} / {@code writeInSubTask} / {@code boxedServiceSubtask}锛夈��
+ *       杩欐潯鏄�"<b>鍗曟偅鑰呮寜闇�闅忚</b>"閾捐矾锛堜緷璧� {@code serviceSubtaskMapper}銆�
+ *       鏅畞绔欑偣鐧藉悕鍗曠瓑锛夛紝涓庡凡杩佺殑"鎵叏閲�"鏄袱鏉′笉鍚岀殑鍏ュ彛銆�</li>
+ * </ol>
+ *
+ * <h3>鈿狅笍 鏈縼閮ㄥ垎閫愬瓧鏉ヨ嚜鍩虹嚎 {@code b25c3b51}</h3>
+ * 鐢� {@code sql/_probe/gen_outhosp_delegating_impl.py} 浠庡熀绾胯嚜鍔ㄦ彁鍙栵紝
+ * <b>涓嶆槸鎵嬫妱</b> 鈥斺�� 杩欑被浠g爜鎵嬫妱鍑洪敊涓嶄細缂栬瘧鎶ラ敊锛屽彧浼氶潤榛樻敼鍙樹笟鍔¤涓恒��
+ * 鏀瑰姩鏈縼閮ㄥ垎鏃跺繀椤诲悓姝ユ洿鏂拌鎺㈤拡鐨勬湡鏈涖��
+ *
+ * <h3>灏嗘潵鍔ㄤ綔</h3>
+ * 璋冪敤鏂归�愪釜鏀逛负鐩存帴渚濊禆妯″潡闂ㄩ潰鍚庯紝鏈被涓� {@link IPatMedOuthospService} 涓�璧峰垹闄ゃ��
+ */
+@Slf4j
+@Service
+public class PatMedOuthospServiceDelegatingImpl implements IPatMedOuthospService {
+
+    /** 妯″潡闂ㄩ潰锛氬凡杩佹柟娉曠殑鍞竴鍑哄彛 */
+    @Autowired
+    private OuthospDataFacade outhospDataFacade;
+
+    // ==== 浠ヤ笅渚濊禆浠呬緵銆愭湭杩併�戠殑鍗曟偅鑰呮寜闇�闅忚閾捐矾浣跨敤 ====
+    @Autowired
+    private ServiceSubtaskMapper serviceSubtaskMapper;
+    @Autowired
+    private ServiceTaskMapper serviceTaskMapper;
+    @Autowired
+    private ServiceTaskdeptMapper serviceTaskdeptMapper;
+    @Autowired
+    private ServiceTaskdiagMapper serviceTaskdiagMapper;
+    @Autowired
+    private PatArchiveMapper patArchiveMapper;
+
+    /**
+     * 鍒楄〃鏌ヨ 鈥斺�� 渚涖�愭湭杩併�戠殑鍗曟偅鑰呮寜闇�闅忚閾捐矾鍐呴儴浣跨敤銆�
+     * <p>
+     * 鍘熷疄鐜伴噷瀹冩槸鏈被鐨� {@code selectPatMedOuthospList}锛涚幇宸茶縼鍏ユā鍧楃殑
+     * {@code OuthospDataService#selectList}锛屾晠杩欓噷鍙仛鍐呴儴杞彂銆�
+     * <p>
+     * 鈿狅笍 娉ㄦ剰瀹冧笌 Controller 鐢ㄧ殑鏄悓涓�涓柟娉� 鈥斺�� 鍚庤�呬細鍋氬睍绀哄瘜鍖栵紙浠呭綋 pageNum != null锛夈��
+     * 鏈摼璺笉璁� pageNum锛屾晠涓嶄細瀵屽寲锛屼笌鍘熷疄鐜颁竴鑷淬��
+     */
+    private List<PatMedOuthosp> selectPatMedOuthospList(PatMedOuthosp query) {
+        return outhospDataFacade.selectList(query);
+    }
+
+    // ======================================================================
+    // 宸茶縼锛氬鎵樻ā鍧�
+    // ======================================================================
+
+    @Override
+    public int insertPatMedOuthosp(PatMedOuthosp patMedOuthosp) {
+        return outhospDataFacade.insert(patMedOuthosp);
+    }
+
+    @Override
+    public int updatePatMedOuthosp(PatMedOuthosp patMedOuthosp) {
+        return outhospDataFacade.update(patMedOuthosp);
+    }
+
+    @Override
+    public Set<String> selectExistingSerialnums(String admitDateStr, Collection<String> serialnums) {
+        return outhospDataFacade.selectExistingSerialnums(admitDateStr, serialnums);
+    }
+
+
+    // ======================================================================
+    // 鏈縼锛氬崟鎮h�呮寜闇�闅忚锛堥�愬瓧鏉ヨ嚜鍩虹嚎 b25c3b51锛�
+    // ======================================================================
+
+    public Integer dealPatMedOuthosp(PatMedOuthosp patMedOuthosp, Integer visitTimeNum){
+            Integer num = 0;
+
+            //鏅畞 瀛樺湪鐩稿悓璇婃柇鐨勯棬璇婅褰曞垯鏂板鍐嶆闅忚,鍚﹀垯鏂板闅忚
+            if(ObjectUtils.isNotEmpty(patMedOuthosp.getOrgid()) &&
+                    patMedOuthosp.getOrgid().equals("47255004333112711A1001")){
+                PatMedOuthosp patMedOuthospVo = new PatMedOuthosp();
+                List<PatMedOuthosp> patMedOuthosps = new ArrayList<>();
+                patMedOuthospVo.setOrgid(patMedOuthosp.getOrgid());
+                patMedOuthospVo.setPatid(patMedOuthosp.getPatid());
+                patMedOuthospVo.setIcd10code(patMedOuthosp.getIcd10code());
+                // 鍚屼竴涓偅鑰� + 鍚屼竴涓瘖鏂紝鍘嗗彶涓婂凡缁忔湁闂ㄨ瘖闅忚璁板綍锛堟帓闄ゆ湰娆″氨璇婏紝鏈鍦ㄨ皟鐢ㄥ墠宸茶惤搴擄級
+                patMedOuthosps = selectPatMedOuthospList(patMedOuthospVo);
+                Long currentId = patMedOuthosp.getId();
+                List<Long> ids = patMedOuthosps.stream().map(PatMedOuthosp::getId).filter(ObjectUtils::isNotEmpty)
+                        .filter(id -> !id.equals(currentId))
+                        .distinct()
+                        .collect(Collectors.toList());
+                ServiceSubtaskEntity serviceSubtask = new ServiceSubtaskEntity();
+                //鏅畞 鍙瀛樺湪鐩稿悓璇婃柇銆佹偅鑰呯殑闂ㄨ瘖璁板綍鍒欏綋鍐嶆闅忚澶勭悊
+                serviceSubtask.setInhospids(ids);
+                serviceSubtask.setServiceType("3");
+                List<ServiceSubtask> serviceSubtasks = CollectionUtils.isEmpty(ids)
+                        ? new ArrayList<>()
+                        : serviceSubtaskMapper.selectServiceSubtaskList(serviceSubtask);
+                if(CollectionUtils.isEmpty(serviceSubtasks)){
+                    num = followUpByPatMedOuthosp(patMedOuthosp, visitTimeNum);
+                }else {
+                    num = followUpAgainByPatMedOuthosp(patMedOuthosp, visitTimeNum, ids);
+                }
+            }else {
+                ServiceSubtaskEntity serviceSubtask = new ServiceSubtaskEntity();
+                serviceSubtask.setInhospid(patMedOuthosp.getId());
+                serviceSubtask.setServiceType("3");
+                serviceSubtask.setVisitCount(1);
+                List<ServiceSubtask> serviceSubtasks = serviceSubtaskMapper.selectServiceSubtaskList(serviceSubtask);
+                if(CollectionUtils.isEmpty(serviceSubtasks)){
+                    num = followUpByPatMedOuthosp(patMedOuthosp, visitTimeNum);
+                }else {
+                    num = followUpAgainByPatMedOuthosp(patMedOuthosp, visitTimeNum, null);
+                }
+            }
+            return num;
+        }
+
+    public Integer followUpByPatMedOuthosp(PatMedOuthosp patMedOuthosp, Integer visitTimeNum) {
+    //        if(ObjectUtils.isEmpty(patMedOuthosp.getFudate())){
+                Date adminDate = patMedOuthosp.getAdmitdate();
+                Date fuDate = addDays(adminDate, visitTimeNum);
+                patMedOuthosp.setFudate(fuDate);
+    //        }
+
+            PatArchive patArchive = patArchiveMapper.selectPatArchiveByPatid(patMedOuthosp.getPatid());
+
+            //鏍规嵁鎮h�呯瀹わ紝鑾峰彇璇ョ柧鐥呯殑闀挎湡浠诲姟
+            ServiceTaskdept serviceTaskdept = new ServiceTaskdept();
+            serviceTaskdept.setLongtask(1L);
+            serviceTaskdept.setDeptCode(patMedOuthosp.getDeptcode());
+            serviceTaskdept.setServiceType("3");
+            List<ServiceTaskdept> serviceTaskdeptList = serviceTaskdeptMapper.selectServiceTaskdeptList(serviceTaskdept);
+            if (CollectionUtils.isEmpty(serviceTaskdeptList) || serviceTaskdeptList.size() == 0) {
+                patMedOuthosp.setDeptcheckFlag("2");
+                patMedOuthosp.setRemark("閫氳繃閮ㄩ棬,娌℃湁鎵惧埌闂ㄨ瘖闅忚浠诲姟ID");
+                updatePatMedOuthosp(patMedOuthosp);
+            } else {
+                for (ServiceTaskdept serviceTaskdept1 : serviceTaskdeptList) {
+                    writeInSubTask(serviceTaskdept1.getTaskId(), true, patMedOuthosp, patArchive, 1);
+                }
+            }
+
+            // 鏍规嵁鎮h�呯殑鐤剧梾锛岃幏鍙栬鐤剧梾鐨勯暱鏈熶换鍔�
+    //        ServiceTaskdiag serviceTaskdiag = new ServiceTaskdiag();
+    //        serviceTaskdiag.setLongtask(1L);
+    //        serviceTaskdiag.setIcd10code(patMedOuthosp.getIcd10code());
+    //        List<ServiceTaskdiag> serviceTaskdiags = serviceTaskdiagMapper.selectServiceTaskdiagList(serviceTaskdiag);
+    //        //濡傛灉閮ㄩ棬妯℃澘涓虹┖锛堝皢deptIsNull璁剧疆涓簍rue锛�
+    //        if (org.apache.commons.collections4.CollectionUtils.isEmpty(serviceTaskdiags) || serviceTaskdiags.size() == 0) {
+    //            patMedOuthosp.setDiagcheckFlag("2");
+    //            patMedOuthosp.setRemark("閫氳繃icd10,娌℃湁鎵惧埌闂ㄨ瘖闅忚浠诲姟ID");
+    //            updatePatMedOuthosp(patMedOuthosp);
+    //        } else {
+    //            for (ServiceTaskdiag serviceTaskdept1 : serviceTaskdiags) {
+    //                writeInSubTask(serviceTaskdept1.getTaskId(), true, patMedOuthosp, patArchive, 2);
+    //            }
+    //        }
+            return 1;
+        }
+
+    public Integer followUpAgainByPatMedOuthosp(PatMedOuthosp patMedOuthosp, Integer visitTimeNum, List<Long> inhospids) {
+            Date adminDate = patMedOuthosp.getAdmitdate();
+            Date newDate = addDays(adminDate, visitTimeNum);
+
+            Integer i = 0;
+            String deptcode = patMedOuthosp.getDeptcode();
+            Long patid = patMedOuthosp.getPatid();
+            //鏌ョ湅鏈夋棤杩涜涓殑浠诲姟
+            ServiceSubtaskEntity serviceSubtaskEntity0 = new ServiceSubtaskEntity();
+            serviceSubtaskEntity0.setOrgid(patMedOuthosp.getOrgid());
+            serviceSubtaskEntity0.setSendstates(new ArrayList<>(Arrays.asList(1L, 2L, 3L, 7L)));
+            serviceSubtaskEntity0.setPatid(patid);
+            //鏅畞鏍规嵁璇婃柇鍐嶆闅忚
+            if(ObjectUtils.isNotEmpty(patMedOuthosp.getOrgid()) &&
+                    patMedOuthosp.getOrgid().equals("47255004333112711A1001") && !CollectionUtils.isEmpty(inhospids)){
+                serviceSubtaskEntity0.setInhospids(inhospids);
+            }else {
+                serviceSubtaskEntity0.setLeaveldeptcode(deptcode);
+            }
+
+            serviceSubtaskEntity0.setSort(0);
+            List<ServiceSubtask> serviceSubtaskList0 = serviceSubtaskMapper.selectServiceSubtaskList(serviceSubtaskEntity0);
+            if (!CollectionUtils.isEmpty(serviceSubtaskList0)) {
+                for (ServiceSubtask serviceSubtask0 : serviceSubtaskList0) {
+                    serviceSubtask0.setSendstate(6L);
+                    serviceSubtask0.setUpdateTime(new Date());
+                    serviceSubtask0.setFinishtime(adminDate);
+                    serviceSubtaskMapper.updateServiceSubtask(serviceSubtask0);
+
+                    ServiceSubtask serviceSubtask1 = DtoConversionUtils.sourceToTarget(serviceSubtask0, ServiceSubtask.class);
+                    serviceSubtask1.setId(null);
+                    serviceSubtask1.setSendstate(2L);
+                    serviceSubtask1.setUpid(serviceSubtask0.getId().intValue());
+                    //鑰佹暟鎹殑visit_count鍙兘涓虹┖锛岄粯璁ゆ寜绗�1娆$畻
+                    Integer visitCount = ObjectUtils.isEmpty(serviceSubtask0.getVisitCount()) ? 1 : serviceSubtask0.getVisitCount();
+                    serviceSubtask1.setVisitCount(visitCount + 1);
+                    serviceSubtask1.setVisitType(serviceSubtask0.getVisitType());
+                    serviceSubtask1.setVisitDeptCode(serviceSubtask0.getVisitDeptCode());
+                    serviceSubtask1.setVisitDeptName(serviceSubtask0.getVisitDeptName());
+                    //鏂颁换鍔$瀹ゆ槸褰撳墠鍑洪櫌绉戝
+                    serviceSubtask1.setLeaveldeptcode(patMedOuthosp.getDeptcode());
+                    serviceSubtask1.setCreateTime(new Date());
+                    serviceSubtask1.setUpdateTime(new Date());
+                    serviceSubtask1.setFinishtime(null);
+                    serviceSubtask1.setAdmindate(adminDate);
+
+                    serviceSubtask1.setVisitTime(newDate);
+                    serviceSubtask1.setLongSendTime(newDate);
+                    i = i + serviceSubtaskMapper.insertServiceSubtask(serviceSubtask1);
+                }
+            } else {
+                //鏌ョ湅宸插畬鎴愮殑浠诲姟
+    //            ServiceSubtaskEntity serviceSubtaskEntity = new ServiceSubtaskEntity();
+    //            serviceSubtaskEntity0.setOrgid(patMedOuthosp.getOrgid());
+    //            serviceSubtaskEntity.setSendstates(new ArrayList<>(Arrays.asList(6L)));
+    //            serviceSubtaskEntity.setPatid(patid);
+    //            serviceSubtaskEntity.setDeptcode(deptcode);
+    //            serviceSubtaskEntity.setSort(0);
+    //            List<ServiceSubtask> serviceSubtaskList = serviceSubtaskMapper.selectServiceSubtaskList(serviceSubtaskEntity);
+    //            if(!CollectionUtils.isEmpty(serviceSubtaskList)){
+    //                ServiceSubtask serviceSubtask = serviceSubtaskList.get(0);
+    //                serviceSubtask.setSendstate(2L);
+    //                serviceSubtask.setCreateTime(new Date());
+    //                serviceSubtask.setUpdateTime(new Date());
+    //
+    //                serviceSubtask.setVisitTime(newDate);
+    //                serviceSubtask.setLongSendTime(newDate);
+    //                i = serviceSubtaskMapper.insertServiceSubtask(serviceSubtask);
+    //            }
+            }
+            return i;
+        }
+
+    private void writeInSubTask(Long taskid, Boolean check, PatMedOuthosp patMedOuthosp, PatArchive patArchive, Integer type) {
+
+            ServiceTask st = new ServiceTask();
+            st.setTaskid(taskid);
+            st.setSendState(2L);
+            List<ServiceTask> serviceTasks = serviceTaskMapper.selectServiceTaskList(st);
+            if (org.apache.commons.collections4.CollectionUtils.isEmpty(serviceTasks)) {
+                log.info("璇ユ偅鑰呯柧鐥呴殢璁块暱鏈熶换鍔′笉瀛樺湪,浠诲姟ID涓猴細{}", taskid);
+                if (type == 1) patMedOuthosp.setDiagcheckFlag("2");
+                if (type == 2) patMedOuthosp.setDeptcheckFlag("2");
+                patMedOuthosp.setRemark("璇ユ偅鑰呴棬璇婇殢璁块暱鏈熶换鍔′笉瀛樺湪,浠诲姟ID涓�:" + taskid);
+                updatePatMedOuthosp(patMedOuthosp);
+                return;
+            }
+            ServiceTask serviceTask = serviceTasks.get(0);
+            //灏佽serviceSubtask
+            ServiceSubtask serviceSubtask = boxedServiceSubtask(serviceTask, patMedOuthosp, patArchive);
+            Integer i = 0;
+            //鍏堝垽鏂竴涓嬶紝鏄惁闇�瑕佹牎楠�
+            if (check) {
+                //鍦ㄦ柊澧炰箣鍓嶏紝鍏堥�氳繃鎮h�匢D锛宻endstate=2鏌ヨ涓�涓嬶紝鍦ㄦ墍鏈夐暱鏈熶换鍔′腑锛屾槸涓嶆槸杩樻湁璇ユ偅鑰呭緟鎵ц鐨勪换鍔★紝鏈夌殑璇�,姣旇緝涔嬪墠鐨別ndtime鏄惁灏忎簬褰撳墠鐨別ndtaime,濡傛灉涔嬪墠鐨勫皬浜庣幇鍦ㄧ殑锛屽垯鐩存帴灏嗕箣鍓嶇殑鍋滄帀锛堝師鍥犲啀鍏ラ櫌锛�
+                ServiceSubtaskEntity subtask = new ServiceSubtaskEntity();
+                subtask.setPatid(patArchive.getId());
+                subtask.setSendstate(2L);
+                subtask.setTaskid(taskid);
+                List<ServiceSubtask> selectServiceSubtaskList = serviceSubtaskMapper.selectServiceSubtaskList(subtask);
+                log.info("璇ユ偅鑰呭緟鎵ц鐨勪换鍔�:{}", CollectionUtils.isEmpty(selectServiceSubtaskList) ? null : selectServiceSubtaskList.size());
+                if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(selectServiceSubtaskList) && selectServiceSubtaskList.size() > 0) {
+                    for (ServiceSubtask serviceSubtask1 : selectServiceSubtaskList) {
+                        if (Objects.isNull(serviceSubtask1.getLongSendTime())) {
+                            //涓嶆槸闀挎湡浠诲姟锛屼笉澶勭悊
+                            continue;
+                        }
+
+                        //灏嗕箣鍓嶇殑鍋滄帀
+                        serviceSubtask1.setSendstate(6L);
+                        serviceSubtask1.setRemark("鎮h�呭凡缁忓洖闄㈠璇�");
+                        serviceSubtask1.setResult("success");
+                        serviceSubtask1.setFinishtime(new Date());
+                        serviceSubtask1.setUpdateBy(serviceTask.getUpdateBy());
+                        serviceSubtaskMapper.updateServiceSubtask(serviceSubtask1);
+                        //閲嶆柊鏂板瀛愪换鍔�
+                        i = serviceSubtaskMapper.insertServiceSubtask(serviceSubtask);
+                    }
+                } else {
+                    if (StringUtils.isEmpty(serviceSubtask.getPhone())) {
+                        serviceSubtask.setRemark("鎵嬫満鍙蜂负绌�");
+                        serviceSubtask.setSendstate(4L);
+                        serviceSubtask.setResult("error");
+    //                    serviceSubtask.setFinishtime(new Date());
+                    }
+                    serviceSubtask.setCreateBy(serviceTask.getCreateBy());
+                    serviceSubtask.setCreateTime(new Date());
+                    i = serviceSubtaskMapper.insertServiceSubtask(serviceSubtask);
+                }
+            } else {
+                if (StringUtils.isEmpty(serviceSubtask.getPhone())) {
+                    serviceSubtask.setRemark("鎵嬫満鍙蜂负绌�");
+                    serviceSubtask.setSendstate(4L);
+                    serviceSubtask.setResult("error");
+    //                serviceSubtask.setFinishtime(new Date());
+
+                }
+                serviceSubtask.setCreateBy(serviceTask.getCreateBy());
+                serviceSubtask.setCreateTime(new Date());
+                i = serviceSubtaskMapper.insertServiceSubtask(serviceSubtask);
+            }
+            if (i == 1) {
+                //灏哻heck_flag鏀规垚1锛堝凡澶勭悊锛�
+                PatMedOuthosp patMedOuthosp1 = new PatMedOuthosp();
+                patMedOuthosp1.setId(patMedOuthosp.getId());
+                if (type == 1) patMedOuthosp1.setDiagcheckFlag("2");
+                if (type == 2) patMedOuthosp1.setDeptcheckFlag("2");
+                updatePatMedOuthosp(patMedOuthosp1);
+            } else {
+                //鐢熸垚瀛愪换鍔″け璐ワ紝
+                log.info("鐢熸垚瀛愪换鍔″け璐erviceSubtask鐨則askid涓猴細{},patid涓猴細{}", serviceSubtask.getTaskid(), serviceSubtask.getPatid());
+                PatMedOuthosp pmo = new PatMedOuthosp();
+                pmo.setGuid(patMedOuthosp.getGuid());
+                pmo.setSerialnum(patMedOuthosp.getSerialnum());
+                if (type == 1) pmo.setDiagcheckFlag("2");
+                if (type == 2) pmo.setDeptcheckFlag("2");
+                pmo.setRemark("鐢熸垚瀛愪换鍔″け璐�");
+                updatePatMedOuthosp(pmo);
+            }
+        }
+
+    public ServiceSubtask boxedServiceSubtask(ServiceTask serviceTask, PatMedOuthosp patMedOuthosp, PatArchive patArchive) {
+            ServiceSubtask serviceSubtask = DtoConversionUtils.sourceToTarget(serviceTask, ServiceSubtask.class);
+            serviceSubtask.setTaskid(serviceTask.getTaskid());
+            serviceSubtask.setOrgid(serviceTask.getOrgid());
+            serviceSubtask.setCampusid(serviceTask.getCampusid());
+            if (StringUtils.isNotEmpty(serviceTask.getLibtemplateid()))
+                serviceSubtask.setLibtemplateid(Long.valueOf(serviceTask.getLibtemplateid()));
+            serviceSubtask.setDrcode(patMedOuthosp.getDrcode());
+            serviceSubtask.setDrname(patMedOuthosp.getDrname());
+            serviceSubtask.setLeaveldeptcode(patMedOuthosp.getDeptcode());
+            serviceSubtask.setInhospid(patMedOuthosp.getId());
+            serviceSubtask.setHospno(patMedOuthosp.getOuthospno());
+            serviceSubtask.setLeaveldeptname(patMedOuthosp.getDeptname());
+            serviceSubtask.setTemplateid(serviceTask.getTemplateid());
+            serviceSubtask.setTemplatename(serviceTask.getTemplatename());
+            serviceSubtask.setPatid(patArchive.getId());
+            serviceSubtask.setSendname(patArchive.getName());
+            serviceSubtask.setSfzh(patArchive.getIdcardno());
+            serviceSubtask.setLeavediagname(patMedOuthosp.getDiagname());
+            serviceSubtask.setLeaveicd10code(patMedOuthosp.getIcd10code());
+            serviceSubtask.setSfzh(patArchive.getIdcardno());
+            serviceSubtask.setPhone(patArchive.getTelcode());
+            if (StringUtils.isBlank(patArchive.getTelcode())) serviceSubtask.setPhone(patArchive.getRelativetelcode());
+            serviceSubtask.setSex(patArchive.getSex());
+            serviceSubtask.setAge(patArchive.getAge());
+            serviceSubtask.setSendstate(2L);
+            serviceSubtask.setServiceType(serviceTask.getServiceType());
+            serviceSubtask.setPreachform(serviceTask.getPreachform());
+            serviceSubtask.setHospType("1");
+            serviceSubtask.setCreateTime(new Date());
+            serviceSubtask.setUpdateTime(new Date());
+            serviceSubtask.setUpdateBy(serviceTask.getUpdateBy());
+            serviceSubtask.setUpdateTime(new Date());
+            serviceSubtask.setVisitDeptCode(patMedOuthosp.getDeptcode());
+            serviceSubtask.setVisitDeptName(patMedOuthosp.getDeptname());
+            serviceSubtask.setUpdateTime(new Date());
+            serviceSubtask.setAdmindate(patMedOuthosp.getAdmitdate());
+            //璁剧疆鍙戦�佹椂闂�
+            if (serviceTask.getSendDay() == null) serviceTask.setSendDay(1L);
+            Date newDate = addDays(patMedOuthosp.getAdmitdate(), serviceTask.getSendDay().intValue());
+            if (patMedOuthosp.getFudate() != null) {
+                //濡傛灉闂ㄨ瘖琛ㄦ湁鎸囧畾闅忚鏃堕棿锛岄偅灏辩敤鎸囧畾鐨�
+                newDate = patMedOuthosp.getFudate();
+            }
+            serviceSubtask.setLongSendTime(newDate);
+            serviceSubtask.setVisitTime(newDate);
+            //鎮h�呭彂閫佹椂闂�
+            if (StringUtils.isNotEmpty(patArchive.getNotrequiredFlag()) && patArchive.getNotrequiredFlag().equals("1")) {
+                String remark = patArchive.getNotrequiredreason();
+                serviceSubtask.setRemark(remark);
+                serviceSubtask.setResult("error");
+    //            serviceSubtask.setFinishtime(new Date());
+                //涓嶆墽琛�
+                serviceSubtask.setSendstate(4L);
+            }
+            return serviceSubtask;
+        }
+
+    private Date addDays(Date date, Integer days) {
+            if (days == null) {
+                days = 1;
+            }
+            Calendar calendar = Calendar.getInstance();
+            calendar.setTime(date);
+            calendar.add(Calendar.DAY_OF_MONTH, days);
+            return calendar.getTime();
+        }
+}
diff --git a/smartor/src/main/java/com/smartor/service/impl/bridge/PublicityDataPortBridge.java b/smartor/src/main/java/com/smartor/service/impl/bridge/PublicityDataPortBridge.java
new file mode 100644
index 0000000..9943090
--- /dev/null
+++ b/smartor/src/main/java/com/smartor/service/impl/bridge/PublicityDataPortBridge.java
@@ -0,0 +1,330 @@
+package com.smartor.service.impl.bridge;
+
+import com.ruoyi.common.core.service.PublicityDataPort;
+import com.ruoyi.common.core.service.dto.InHospPublicityRowDto;
+import com.ruoyi.common.core.service.dto.PublicityTaskMatchDto;
+import com.ruoyi.common.core.service.dto.VisitTaskRuleDto;
+import com.ruoyi.inhospvisit.domain.PatMedInhosp;
+import com.ruoyi.task.domain.ServiceTaskRule;
+import com.ruoyi.patarchive.mapper.PatArchiveMapper;
+import com.ruoyi.inhospvisit.api.InhospVisitFacade;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import com.ruoyi.common.core.domain.entity.PatArchive;
+import com.ruoyi.task.domain.ServiceTaskdept;
+import com.ruoyi.task.domain.ServiceTaskmedical;
+import com.ruoyi.task.mapper.ServiceTaskdeptMapper;
+import com.ruoyi.task.mapper.ServiceTaskmedicalMapper;
+
+/**
+ * 銆愯繃娓″疄鐜般�憑@link PublicityDataPort} 鍦� smartor 渚х殑妗ユ帴锛堝鏁欐ā鍧楃殑鏁版嵁鏉ユ簮锛夈��
+ *
+ * <h3>涓轰粈涔堥渶瑕佸畠</h3>
+ * {@code ruoyi-publicity} 瑕佽 {@code service_taskdept} / {@code service_taskmedical} /
+ * {@code pat_med_inhosp} / {@code pat_archive}锛屼絾杩欎簺琛ㄥ拰 Mapper 鍏ㄥ湪 smartor锛�
+ * 鑰� smartor 鍙堣渚濊禆 publicity锛堝畠璐熻矗瑙﹀彂瀹f暀锛夆啋 鐩存帴渚濊禆浼氬惊鐜��
+ * 鎵�浠ユ帴鍙d笅娌夊埌 ruoyi-common锛屽疄鐜板湪杩欓噷銆�
+ *
+ * <h3>鈿狅笍 鏈被蹇呴』淇濇寔"绾惉杩�"</h3>
+ * 鍙仛<b>瀛楁鏄犲皠</b>涓�<b>杞彂</b>锛屼笉寰楀姞鍏ュ幓閲嶃�佹帓搴忋�侀粯璁ゅ�艰ˉ榻愮瓑鏂伴�昏緫 鈥斺��
+ * 閭d簺璇箟鍦� {@code ruoyi-publicity} 鐨� {@code PublicityService} 閲岋紙閫愯鐓ф妱鍘熷疄鐜帮級銆�
+ *
+ * <h3>閫愭柟娉曟潵婧愶紙杩佺Щ鍓� {@code PatMedInhospServiceImpl#dealOutHospInfo}锛孈 b25c3b51锛�</h3>
+ * <table border="1">
+ *   <tr><th>鏈被鏂规硶</th><th>鍘熷疄鐜颁綅缃�</th></tr>
+ *   <tr><td>{@link #findInHospForPublicity}</td><td>L325-329锛堢瀹わ級銆丩376-379锛堢梾鍖猴級</td></tr>
+ *   <tr><td>{@link #findDeptPublicityTaskIds}</td><td>L347-353锛坉eptType=1锛夈�丩390-396锛坉eptType=2锛�</td></tr>
+ *   <tr><td>{@link #findMedicalPublicityTaskIds}</td><td>L424-434</td></tr>
+ *   <tr><td>{@link #existsPatArchive}</td><td>L338-344銆丩382-388</td></tr>
+ *   <tr><td>{@link #updatePublicityFlag}</td><td>L340-342 / L357-359 / L367-370锛堢瀹わ級銆丩384-386 / L400-402锛堢梾鍖猴級</td></tr>
+ * </table>
+ *
+ * <h3>灏嗘潵鍔ㄤ綔</h3>
+ * 鑻ュ皢鏉ュ鏁欎笌闅忚閮芥敹杩涘悓涓�涓�"闅忚骞冲彴妯″潡"锛屾垨 smartor 涓嶅啀渚濊禆 publicity锛�
+ * 鏈被鍙暣浣撳垹闄ゅ苟鎶婂疄鐜版尓杩涙柊妯″潡 鈥斺�� 鎺ュ彛涓嶅彉锛岃皟鐢ㄦ柟闆舵敼鍔ㄣ��
+ *
+ * @see PublicityDataPort
+ */
+@Slf4j
+@Component
+public class PublicityDataPortBridge implements PublicityDataPort {
+
+    /**
+     * 鍏佽鐨� check_flag 鍒楀悕锛堢櫧鍚嶅崟锛屾嫾杩� SQL 鐨勬槸甯搁噺锛屼笉鍋氬瓧绗︿覆鎷兼帴锛岄伩鍏嶆敞鍏ワ級
+     */
+    private static final Set<String> ALLOWED_CHECK_FLAG_COLUMNS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("in_deptcheck_flag", "in_wardcheck_flag")));
+
+    @Autowired
+    private InhospVisitFacade inhospVisitFacade;
+
+    @Autowired
+    private PatArchiveMapper patArchiveMapper;
+
+    @Autowired
+    private ServiceTaskdeptMapper serviceTaskdeptMapper;
+
+    @Autowired
+    private ServiceTaskmedicalMapper serviceTaskmedicalMapper;
+
+    /**
+     * 浠诲姟鎵ц瑙勫垯鐨勫彧璇绘煡璇紙鍘� {@code addSubTaskByRule} L1707 鐢級銆�
+     * <p>
+     * 铏界劧鏈鍙e悕涔変笂鏈嶅姟瀹f暀锛屼絾 {@link PublicityDataPort#findTaskRule} 鏈嶅姟鐨勬槸
+     * 鍑洪櫌闅忚鐨勮鍒欑紪鎺� 鈥斺�� 瑙佹帴鍙d笂鐨勮鏄庛��
+     */
+    @Autowired
+    private com.ruoyi.task.mapper.ServiceTaskRuleMapper serviceTaskRuleMapper;
+
+    /**
+     * 鎵湪闄㈡偅鑰呫��
+     * <p>
+     * 鍘熷疄鐜颁袱娈垫煡璇㈢殑鍏ュ弬锛�
+     * <pre>
+     * // 绉戝缁村害锛圠325-329锛�
+     * PatMedInhosp pmks = new PatMedInhosp();
+     * pmks.setDeptcheckFlag("0");  pmks.setInhospstate("1");  pmks.setFuflag("1");
+     *
+     * // 鐥呭尯缁村害锛圠376-379锛�
+     * pmks.setInDeptcheckFlag(null); pmks.setInWardcheckFlag("0"); pmks.setDeptcheckFlag(null);
+     * </pre>
+     * 鈿狅笍 娉ㄦ剰鐥呭尯缁村害閭f鏄�<b>澶嶇敤鍚屼竴涓� pmks 瀵硅薄</b>骞舵敼瀛楁锛屾墍浠�
+     * {@code inhospstate="1"} 涓� {@code fuflag="1"} 鏄竴鐩寸暀鐫�鐨勩��
+     * 杩欓噷鐢ㄧ櫧鍚嶅崟鍙傛暟鍖哄垎涓や釜缁村害锛屽苟淇濇寔"涓ゆ鐙珛鏌ヨ"銆�
+     */
+    @Override
+    public List<InHospPublicityRowDto> findInHospForPublicity(String checkFlagColumn) {
+        if (!ALLOWED_CHECK_FLAG_COLUMNS.contains(checkFlagColumn)) {
+            log.warn("銆愬鏁櫬疯繃娓℃ˉ銆慺indInHospForPublicity 鏀跺埌闈炴硶鍒楀悕銆寋}銆嶏紝鍙厑璁� {}锛岃繑鍥炵┖鍒楄〃", checkFlagColumn, ALLOWED_CHECK_FLAG_COLUMNS);
+            return Collections.emptyList();
+        }
+        PatMedInhosp query = new PatMedInhosp();
+        if ("in_wardcheck_flag".equals(checkFlagColumn)) {
+            // 鐥呭尯缁村害锛氬彧鎸� in_wardcheck_flag 杩囨护锛屽彟涓や釜缁村害缃� null锛堜笌鍘熷疄鐜颁竴鑷达級
+            query.setInDeptcheckFlag(null);
+            query.setInWardcheckFlag("0");
+            query.setDeptcheckFlag(null);
+        } else {
+            // 绉戝缁村害
+            query.setDeptcheckFlag("0");
+        }
+        query.setInhospstate("1");
+        query.setFuflag("1");
+
+        List<PatMedInhosp> rows = inhospVisitFacade.selectPatMedInhospList(query);
+        if (CollectionUtils.isEmpty(rows)) {
+            return Collections.emptyList();
+        }
+        List<InHospPublicityRowDto> out = new ArrayList<>(rows.size());
+        for (PatMedInhosp r : rows) {
+            out.add(toRow(r));
+        }
+        return out;
+    }
+
+    @Override
+    public List<PublicityTaskMatchDto> findDeptPublicityTaskIds(String deptCode, String deptType, String orgid, Long inhospid) {
+        ServiceTaskdept query = new ServiceTaskdept();
+        query.setLongtask(1L);
+        query.setDeptCode(deptCode);
+        query.setDeptType(deptType);
+        query.setServiceType("17");
+        query.setOrgid(orgid);
+        List<ServiceTaskdept> tasks = serviceTaskdeptMapper.selectServiceTaskdeptList(query);
+        if (CollectionUtils.isEmpty(tasks)) {
+            return Collections.emptyList();
+        }
+        List<PublicityTaskMatchDto> out = new ArrayList<>(tasks.size());
+        for (ServiceTaskdept t : tasks) {
+            PublicityTaskMatchDto dto = new PublicityTaskMatchDto();
+            dto.setTaskId(t.getTaskId());
+            dto.setInhospid(inhospid);
+            out.add(dto);
+        }
+        return out;
+    }
+
+    /**
+     * 鎸夎嵂鍝佺紪鐮� + 鍚嶇О + 鍏徃鏌ラ暱鏈熷鏁欎换鍔★紙鍘� L424-434锛夈��
+     * <p>
+     * 鈿狅笍 <b>涓変釜瀛楁閮界収鍘熸牱閫忎紶锛岀粷涓嶅仛 null/绌轰覆褰掍竴</b>锛�
+     * {@code ServiceTaskmedicalMapper.xml} 鐨勬潯浠舵槸
+     * {@code <if test="medicalName != null and medicalName != ''">} 涓�
+     * {@code <if test="medicalCompany != null and medicalCompany != ''">} 鈥斺��
+     * <b>绌轰覆涓� null 閮戒細璺宠繃璇ユ潯浠�</b>锛屼絾璇箟鏉ユ簮涓嶅悓锛堣秺鐣屽垎鏀紶绌轰覆銆佹甯稿垎鏀紶 null锛夛紝
+     * 杩欓噷淇濈暀鍘熸牱鎵嶈兘淇濊瘉"鍝簺鏉′欢鐢熸晥"涓庤縼绉诲墠瀹屽叏涓�鑷淬��
+     */
+    @Override
+    public List<PublicityTaskMatchDto> findMedicalPublicityTaskIds(String medicalCode, String medicalName, String medicalCompany, Long inhospid) {
+        ServiceTaskmedical query = new ServiceTaskmedical();
+        query.setMedicalCode(medicalCode);
+        query.setMedicalName(medicalName);
+        query.setMedicalCompany(medicalCompany);
+        List<ServiceTaskmedical> tasks = serviceTaskmedicalMapper.selectServiceTaskmedicalList(query);
+        if (CollectionUtils.isEmpty(tasks)) {
+            return Collections.emptyList();
+        }
+        List<PublicityTaskMatchDto> out = new ArrayList<>(tasks.size());
+        for (ServiceTaskmedical t : tasks) {
+            PublicityTaskMatchDto dto = new PublicityTaskMatchDto();
+            dto.setTaskId(t.getTaskId());
+            dto.setInhospid(inhospid);
+            out.add(dto);
+        }
+        return out;
+    }
+
+    /**
+     * 鎮h�呮。妗堟槸鍚﹀瓨鍦ㄣ��
+     * <p>
+     * 鈿狅笍 鍘熷疄鐜板彇鐨勬槸<b>瀹屾暣瀵硅薄</b>鐒跺悗鍒� null锛圠338-344 / L382-388锛夛紝
+     * 杩欓噷鍙垽瀛樺湪鎬� 鈥斺�� 瀹f暀灞傜‘瀹炲彧鐢ㄥ埌 null 鍒ゆ柇
+     * 锛堢湡姝g殑鎮h�呬俊鎭~鍏呭彂鐢熷湪 inhospvisit 缁勮瀛愪换鍔℃椂锛夈��
+     * 杩欐槸<b>鏈夋剰鐨勭瓑浠风畝鍖�</b>锛氭妸"鍙栧畬鏁村璞�"闄嶇骇涓�"鍙� existence"锛�
+     * 涓嶅奖鍝嶄换浣曞垎鏀蛋鍚戯紝浣嗙渷鎺変竴娆″ぇ瀛楁鏌ヨ銆�
+     * <p>
+     * 鑻ュ皢鏉ュ彂鐜� {@code selectPatArchiveByPatid} 鍐呴儴杩樻湁鍒殑鍓綔鐢紙渚嬪缂撳瓨鍐欏叆锛夛紝
+     * 闇�瑕佸湪 {@code verify_inhospvisit_semantics.py} 閲岃ˉ涓�鏉℃柇瑷�銆�
+     */
+    @Override
+    public boolean existsPatArchive(Long patid) {
+        if (patid == null) {
+            return false;
+        }
+        PatArchive archive;
+        try {
+            archive = patArchiveMapper.selectPatArchiveByPatid(patid);
+        } catch (Exception e) {
+            log.warn("銆愬鏁櫬疯繃娓℃ˉ銆戞煡鎮h�呮。妗堝け璐ワ紝鎸夈�屼笉瀛樺湪銆嶅鐞嗐�俻atid={}锛屽師鍥�={}", patid, e.toString());
+            return false;
+        }
+        return Objects.nonNull(archive);
+    }
+
+    /**
+     * 鍥炲啓瀹f暀鏍囧織浣� + 鎸夐渶鍐� long_task_reason銆�
+     * <p>
+     * 鈿狅笍 璇箟瑕佺偣锛堢収鎶勫師瀹炵幇锛屽埆"椤烘墜淇�"锛夛細
+     * <ol>
+     *   <li>{@code appendMode=true} 鏃讹紝鍘熷疄鐜板湪鍐欎箣鍓嶄細<b>鍏堟寜 inhospid 閲嶆煡涓�娆¤褰�</b>
+     *       鎷� {@code long_task_reason} 鍐嶆嫾鎺ワ紝杩欓噷涔熺収鍋氾紙閬垮厤鐢ㄨ皟鐢ㄦ柟鎵嬮噷鍙兘杩囨湡鐨勫�硷級锛�</li>
+     *   <li>鎷兼帴鏂瑰紡鏄� {@code 鏃у�� + 鏂版枃妗坿锛�<b>涓嶅垽鏂棫鍊兼槸鍚︿负 null</b>
+     *       鈥斺�� 鏃у�间负 null 鏃朵細鐪熺殑鎷煎嚭 {@code "null璇ユ偅鑰呮墍鍦ㄧ梾鍖�..."}锛岃繖鏄師琛屼负锛屼繚鐣欙紱</li>
+     *   <li>{@code appendMode=false} 鏃剁洿鎺ヨ鐩栵紙瀵瑰簲銆屾偅鑰呭熀鏈俊鎭负绌恒�嶃�屼汉鍛�...澶勭悊鍑哄紓甯镐簡銆嶄袱涓垎鏀級锛�</li>
+     *   <li>{@code reason=null} 鏃跺彧鍐� flag銆佷笉鍔� long_task_reason銆�</li>
+     * </ol>
+     */
+    @Override
+    public boolean updatePublicityFlag(Long inhospid, String flagColumn, String flagValue, String reason, boolean appendMode) {
+        if (!ALLOWED_CHECK_FLAG_COLUMNS.contains(flagColumn)) {
+            log.warn("銆愬鏁櫬疯繃娓℃ˉ銆憉pdatePublicityFlag 鏀跺埌闈炴硶鍒楀悕銆寋}銆嶏紝鍙厑璁� {}锛屾斁寮冩洿鏂般�俰nhospid={}", flagColumn, ALLOWED_CHECK_FLAG_COLUMNS, inhospid);
+            return false;
+        }
+        if (inhospid == null) {
+            log.warn("銆愬鏁櫬疯繃娓℃ˉ銆憉pdatePublicityFlag 缂哄皯 inhospid锛屾斁寮冩洿鏂般�俢olumn={}", flagColumn);
+            return false;
+        }
+
+        PatMedInhosp update = new PatMedInhosp();
+        update.setInhospid(inhospid);
+        if ("in_deptcheck_flag".equals(flagColumn)) {
+            update.setInDeptcheckFlag(flagValue);
+        } else {
+            update.setInWardcheckFlag(flagValue);
+        }
+
+        if (reason != null) {
+            if (appendMode) {
+                // 鍘熷疄鐜帮細鍏堥噸鏌ヤ竴娆℃嬁 long_task_reason锛屽啀鎷兼帴鍚庡啓鍥烇紙涓嶅垽 null锛�
+                PatMedInhosp existing = inhospVisitFacade.selectPatMedInhospByInhospid(inhospid);
+                String oldReason = existing == null ? null : existing.getLongTaskReason();
+                update.setLongTaskReason(oldReason + reason);
+            } else {
+                update.setLongTaskReason(reason);
+            }
+        }
+
+        return inhospVisitFacade.updatePatMedInhosp(update) > 0;
+    }
+
+    /**
+     * 鎸夊瓧娈垫洿鏂帮紙鍙竴娆″啓澶氫釜 flag锛夆�斺�� 渚涘嚭闄㈤殢璁跨紪鎺掑眰鐨� 3 涓� {@code *ForPatient} 浣跨敤銆�
+     * <p>
+     * 璇箟涓庡師瀹炵幇瀹屽叏涓�鑷达細鍘熶唬鐮佹槸"鍦ㄥ璞′笂 set 鍑犱釜瀛楁锛岀劧鍚�
+     * {@code inhospVisitFacade.updatePatMedInhosp(obj)}"锛�
+     * 鑰� {@code updatePatMedInhosp} 鐨� XML 瀵规瘡涓瓧娈靛悇鑷� {@code <if test="xxx != null">}锛�
+     * 鎵�浠�<b>鍙湁闈� null 瀛楁鐢熸晥</b>銆傝繖閲岄�愬瓧娈垫惉杩愶紝null 涓�寰嬩笉璁俱��
+     */
+    @Override
+    public boolean updateVisitInhosp(com.ruoyi.common.core.service.dto.VisitInhospUpdateDto u) {
+        if (u == null || u.getInhospid() == null) {
+            log.warn("銆愰殢璁柯疯繃娓℃ˉ銆憉pdateVisitInhosp 缂哄皯 inhospid锛屾斁寮冩洿鏂般�倁={}", u);
+            return false;
+        }
+        PatMedInhosp update = new PatMedInhosp();
+        update.setInhospid(u.getInhospid());
+        update.setDeptcheckFlag(u.getDeptcheckFlag());
+        update.setWardcheckFlag(u.getWardcheckFlag());
+        update.setDiagcheckFlag(u.getDiagcheckFlag());
+        update.setInDeptcheckFlag(u.getInDeptcheckFlag());
+        update.setInWardcheckFlag(u.getInWardcheckFlag());
+        update.setScheduleCheckFlag(u.getScheduleCheckFlag());
+        update.setMedcheckFlag(u.getMedcheckFlag());
+        update.setLongTaskReason(u.getLongTaskReason());
+        update.setRemark(u.getRemark());
+        return inhospVisitFacade.updatePatMedInhosp(update) > 0;
+    }
+
+    // ==========================================================================
+    // 瀛楁鏄犲皠
+    // ==========================================================================
+
+    /**
+     * 鎸夌闄㈢瀹�/鐥呭尯鏌ヤ换鍔℃墽琛岃鍒欙紙鍘� {@code addSubTaskByRule} L1704-1707锛夈��
+     * <p>
+     * 鈿狅笍 璇ユ煡璇㈡渶澶氳繑鍥炰竴鏉� 鈥斺�� 鍘熷疄鐜扮洿鎺ュ彇鍗曚釜瀵硅薄锛岃繖閲屼篃鍙栫涓�鏉°��
+     */
+    @Override
+    public VisitTaskRuleDto findTaskRule(String leaveldeptcode, String leavehospitaldistrictcode) {
+        ServiceTaskRule ruleQuery = new ServiceTaskRule();
+        ruleQuery.setDeptcode(leaveldeptcode);
+        ruleQuery.setWardcode(leavehospitaldistrictcode);
+        ServiceTaskRule rule = serviceTaskRuleMapper.selectServiceTaskRuleByDeptOrWard(ruleQuery);
+        if (rule == null) {
+            return null;
+        }
+        VisitTaskRuleDto dto = new VisitTaskRuleDto();
+        dto.setId(rule.getId());
+        dto.setDeptRule(rule.getDeptRule());
+        dto.setWradRule(rule.getWradRule());
+        dto.setDiagRule(rule.getDiagRule());
+        dto.setDeptcode(rule.getDeptcode());
+        dto.setWardcode(rule.getWardcode());
+        dto.setOrgid(rule.getOrgid());
+        return dto;
+    }
+
+    private InHospPublicityRowDto toRow(PatMedInhosp r) {
+        InHospPublicityRowDto dto = new InHospPublicityRowDto();
+        dto.setInhospid(r.getInhospid());
+        dto.setPatid(r.getPatid());
+        dto.setOrgid(r.getOrgid());
+        dto.setDeptcode(r.getDeptcode());
+        dto.setHospitaldistrictcode(r.getHospitaldistrictcode());
+        dto.setLongTaskReason(r.getLongTaskReason());
+        dto.setMedicalCode(r.getMedicalCode());
+        dto.setMedicalName(r.getMedicalName());
+        dto.setMedicalCompany(r.getMedicalCompany());
+        return dto;
+    }
+}

--
Gitblit v1.9.3