陈昶聿
7 天以前 2867cf7076a584a34e63174cec0a8b89cdd03e4c
Merge branch 'master' into smartor_ls_261103
已修改23个文件
已添加8个文件
3701 ■■■■■ 文件已修改
doc/api/generalInterface/JZ_MZ_WanChengJZ接口文档.md 252 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/api/generalInterface接口文档.md 1146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/api/妇幼专科档案生成接口文档_20261104.md 342 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/妇幼专科档案生成测试报告_20261104.md 307 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/pom.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceOutPathController.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/pom.xml 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/mapper/PatSpecificArchiveGenerateConfigMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/service/IPatSpecificArchiveGenerateConfigService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/service/impl/PatSpecificArchiveGenerateConfigServiceImpl.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-patarchive/src/main/resources/mapper/patarchive/PatSpecificArchiveGenerateConfigMapper.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/CollectHISController.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ICollectHISService.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/CollectHISServiceImpl.java 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/controller/ServiceSubtaskController.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskCountReq.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskPreachformMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceSubtaskService.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskServiceImpl.java 358 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskMapper.xml 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskPreachformMapper.xml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/pom.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/common/SpecificArchiveConstants.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/IPatSpecificArchiveGenerateService.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/PatMedInhospServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/PatSpecificArchiveGenerateServiceImpl.java 329 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/ServiceExternalServiceImpl.java 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/pat_specific_archive_generate_config_lishui_mch_20261104.sql 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/api/generalInterface/JZ_MZ_WanChengJZ½Ó¿ÚÎĵµ.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,252 @@
# JZ_MZ_WanChengJZ é—¨æ€¥è¯Šå®ŒæˆæŽ¥è¯Š æŽ¥å£æ–‡æ¡£
> æŽ¥å£ï¼š`POST /smartor/serviceExternal/generalInterface`
> æ¶ˆæ¯ç±»åž‹ï¼š`JZ_MZ_WanChengJZ`
> æ–‡æ¡£ç”Ÿæˆæ—¥æœŸï¼š2026-10-20
> è¯´æ˜Žï¼šå­—段名区分大小写,请严格按文档中的拼音字段名传参。
---
## ä¸€ã€æŽ¥å£è¯´æ˜Ž
| é¡¹ç›® | å†…容 |
| --- | --- |
| æŽ¥å£åœ°å€ | `/smartor/serviceExternal/generalInterface` |
| è¯·æ±‚方式 | `POST` |
| Content-Type | `application/json;charset=UTF-8` |
| è®¤è¯æ–¹å¼ | æ— éœ€ç™»å½•(`/smartor/serviceExternal/**` å·²é…ç½® `permitAll`) |
| æ¶ˆæ¯ç±»åž‹ | `JZ_MZ_WanChengJZ` |
| ä¸šåС含义 | é—¨æ€¥è¯Šå®ŒæˆæŽ¥è¯Š |
| å¤„理入口 | `addFinshJZInfo` |
| æ˜¯å¦è½åº“ | æ˜¯ï¼ˆå®žé™…落库) |
| ä¸šåŠ¡è¯´æ˜Ž | æŽ¥æ”¶é—¨æ€¥è¯Šå®ŒæˆæŽ¥è¯Šä¿¡æ¯ï¼Œå†™å…¥é—¨è¯Šå°±è¯Šè®°å½•,并按随访天数生成随访任务。 |
## äºŒã€è¯·æ±‚报文
### 2.1 æŠ¥æ–‡ç»“æž„
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| XiaoXiTou | object | æ˜¯ | æ¶ˆæ¯å¤´ |
| YeWuXX | object | æ˜¯ | ä¸šåŠ¡ä¿¡æ¯ï¼Œå¿…é¡»åŒ…å« `XiaoXiID` |
### 2.2 æ¶ˆæ¯å¤´ `XiaoXiTou`
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| FaSongFCSJC | String | å¦ | å‘送方程序简称,默认 `ZJHES` |
| FaSongJGID | String | æ˜¯ | å‘送机构 ID,对应系统 `orgid`;部分消息类型用它覆盖业务中的机构 ID |
| FaSongJGMC | String | å¦ | å‘送机构名称 |
| FaSongSJ | String | å¦ | å‘送时间,建议 `yyyy-MM-dd HH:mm:ss` |
| FaSongXTJC | String | å¦ | å‘送系统简称,默认 `SUIFANGXT` |
| FaSongXTMC | String | å¦ | å‘送系统名称,默认 `随访系统` |
| XiaoXiID | String | å¦ | æ¶ˆæ¯ ID;本接口响应回显取的是 `YeWuXX.XiaoXiID` |
| XiaoXiLX | String | æ˜¯ | æ¶ˆæ¯ç±»åž‹ï¼Œå†³å®šä¸šåŠ¡åˆ†æ”¯ï¼Œç¼ºå¤±æˆ–ä¸ºç©ºæ—¶æŽ¥å£æŠ¥é”™ |
| XiaoXiMC | String | å¦ | æ¶ˆæ¯åç§° |
| ZuHuID | String | å¦ | åŒ»é™¢ ID |
| ZuHuMC | String | å¦ | åŒ»é™¢åç§° |
### 2.3 ä¸šåŠ¡ä¿¡æ¯ `YeWuXX`
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| XiaoXiID | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | æ¶ˆæ¯ ID,原样放入响应 `Data.XiaoXiID`;缺失时返回 `null` |
| BingRenXX | object | æ˜¯ | æ‚£è€…信息;`ZuZhiJGID` å¯çœç•¥ï¼Œä»£ç ä½¿ç”¨æ¶ˆæ¯å¤´ `FaSongJGID` |
| JiuZhenXX | object | æ˜¯ | å°±è¯Šä¿¡æ¯ï¼Œè‡³å°‘包含科室、就诊日期 |
| ZhenDuanList | array | æ˜¯ | è¯Šæ–­åˆ—表,不能为空 |
| WenZhenList | array | æ˜¯ | é—®è¯Šåˆ—表,不能为空 |
### 2.4 è¯·æ±‚示例
```json
{
  "XiaoXiTou": {
    "FaSongJGID": "47255004333112711A1001",
    "FaSongJGMC": "丽水市中医院",
    "FaSongSJ": "2026-10-20 10:00:00",
    "XiaoXiLX": "JZ_MZ_WanChengJZ",
    "XiaoXiID": "MSG-20261020-0001"
  },
  "YeWuXX": {
    "XiaoXiID": "MSG-20261020-0001",
    "BingRenXX": {
      "BingRenID": "BR0002",
      "XingMing": "李四",
      "JiuZhenYWID": "MZ20261020002",
      "JiuZhenKH": "MZ0002",
      "BingAnHao": "BA20261020002",
      "ZhengJianHM": "330102198505051234",
      "XingBieDM": "2",
      "ChuShengRQ": "1985-05-05 10:00:00",
      "LianXiDH": "13700000000"
    },
    "JiuZhenXX": {
      "JiuZhenKSID": "D002",
      "JiuZhenKSMC": "妇科",
      "JiuZhenRQ": "2026-10-20 10:30:00",
      "sfrq": "2026-11-20 00:00:00"
    },
    "ZhenDuanList": [
      {
        "ZhenDuanYSID": "D1002",
        "ZhenDuanYSXM": "赵医生",
        "ZhenDuanID": "O20.0",
        "ZhenDuanMC": "先兆流产",
        "ZhenDuanLBDM": "1",
        "ZhenDuanLBMC": "主诊断",
        "Icd10": "O20.0"
      }
    ],
    "WenZhenList": [
      {
        "WenZhenLXMC": "主诉",
        "NeiRong": "停经50天,阴道流血1天"
      },
      {
        "WenZhenLXMC": "现病史",
        "NeiRong": "患者停经50天,1天前出现阴道流血……"
      },
      {
        "WenZhenLXMC": "随访天数",
        "NeiRong": "30"
      },
      {
        "WenZhenLXMC": "随访天数单位",
        "NeiRong": "天"
      }
    ]
  }
}
```
## ä¸‰ã€ä¸šåŠ¡å­—æ®µè¯´æ˜Ž
### 3.1 `BingRenXX` æ‚£è€…基本信息
> æœºæž„ ID ä¼šè¢«æ¶ˆæ¯å¤´ `FaSongJGID` è¦†ç›–。
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| ZuZhiJGID | String | æ˜¯ | ç»„织机构 ID(写入 `orgid`) |
| ZuZhiJGMC | String | å¦ | ç»„织机构名称 |
| BingRenID | String | æ˜¯ | ç—…人 ID,写入 `patid_his/patno` |
| XingMing | String | æ˜¯ | æ‚£è€…姓名 |
| JiuZhenYWID | String | æ˜¯ | å°±è¯Šä¸šåŠ¡ ID,与 `BingAnHao` å…±åŒå®šä½ä½é™¢/门诊记录 |
| JiuZhenKH | String | å¦ | å°±è¯Šå¡å·ï¼›è¯ä»¶å·ç ä¸ºç©ºæ—¶ä½œä¸ºæ‚£è€…档案唯一标识 |
| BingAnHao | String | æ˜¯ | ç—…案号(代码按病案号匹配,不使用住院号) |
| ZhengJianLXMC | String | å¦ | è¯ä»¶ç±»åž‹åç§°ï¼Œå†™å…¥æ¡£æ¡ˆ `idcardtype` |
| ZhengJianHM | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | è¯ä»¶å·ç ï¼Œæ‚£è€…档案唯一标识;为空时使用 `JiuZhenKH` |
| XingBieDM | String | å¦ | æ€§åˆ«ä»£ç ï¼Œå¿…须为数字字符串(如 `1`/`2`),代码执行 `Long.valueOf` |
| ChuShengRQ | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | å‡ºç”Ÿæ—¥æœŸï¼Œä»£ç æŒ‰ `yyyy-MM-dd hh:mm:ss` è§£æžï¼Œå»ºè®®ä¼ å®Œæ•´æ—¶é—´ |
| DangQianKSID | String | æ˜¯ | å½“前科室 ID,写入住院/出院科室 |
| DangQianKSMC | String | æ˜¯ | å½“前科室名称 |
| DangQianBQID | String | æ˜¯ | å½“前病区 ID |
| DangQianBQMC | String | æ˜¯ | å½“前病区名称 |
| DangQianCWMC | String | å¦ | åºŠä½åç§°ï¼Œå†™å…¥ä½é™¢ `bedNo` |
| RuYuanSJ | Date | æ˜¯ï¼ˆå…¥é™¢/入科) | å…¥é™¢æ—¶é—´ï¼Œæ ¼å¼ `yyyy-MM-dd HH:mm:ss` |
| LianXiDH | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | è”系电话,写入患者档案手机号 |
| LianXiRDH | String | å¦ | è”系人电话;`LianXiDH` ä¸ºç©ºæ—¶ä½œä¸ºæ‚£è€…手机号 |
| GuoJiMC | String | å¦ | å›½ç±åç§°ï¼Œå†™å…¥æ¡£æ¡ˆ `nativePlace` |
| MinZuMC | String | å¦ | æ°‘族名称,写入档案 `nation` |
| XianZhuZXX | String | å¦ | çŽ°ä½å€ï¼›ä¼˜å…ˆäºŽ `DiZhiXXList` å†™å…¥æ¡£æ¡ˆ |
| CaoZuoRID | String | å¦ | æ“ä½œäºº ID |
| CaoZuoRXM | String | å¦ | æ“ä½œäººå§“名 |
| ZeRenHSID | String | å¦ | è´£ä»»æŠ¤å£« ID |
| ZeRenHSXM | String | å¦ | è´£ä»»æŠ¤å£«å§“名 |
| ZhuZhiYSID | String | å¦ | ä¸»æ²»åŒ»ç”Ÿ ID |
| ZhuZhiYSXM | String | å¦ | ä¸»æ²»åŒ»ç”Ÿå§“名 |
| JingGuanYSID | String | å¦ | ç»ç®¡åŒ»ç”Ÿ ID |
| JingGuanYSXM | String | å¦ | ç»ç®¡åŒ»ç”Ÿå§“名 |
| DuanHaoXX | String | å¦ | çŸ­å·ä¿¡æ¯ |
| BingRenLXDM | String | å¦ | ç—…人类型代码,手术记录写入 `inhospstate` |
### 3.2 `JiuZhenXX` å°±è¯Šä¿¡æ¯
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| JiuZhenKSID | String | æ˜¯ | å°±è¯Šç§‘室 ID |
| JiuZhenKSMC | String | æ˜¯ | å°±è¯Šç§‘室名称 |
| JiuZhenRQ | Date | æ˜¯ | å°±è¯Šæ—¥æœŸï¼Œ`yyyy-MM-dd HH:mm:ss`,写入门诊 `admitdate` |
| sfrq | Date | å¦ | éšè®¿æ—¥æœŸï¼Œå†™å…¥é—¨è¯Š `fudate` |
| CaoZuoRID | String | å¦ | æ“ä½œäºº ID |
| CaoZuoRXM | String | å¦ | æ“ä½œäººå§“名 |
### 3.3 `ZhenDuanList` è¯Šæ–­åˆ—表
> `ZhenDuanLBMC=主诊断` çš„记录用于门诊主诊断。
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| ZhenDuanYSID | String | å¦ | è¯Šæ–­åŒ»ç”Ÿ ID |
| ZhenDuanYSXM | String | å¦ | è¯Šæ–­åŒ»ç”Ÿå§“名 |
| ZhenDuanID | String | å¦ | è¯Šæ–­ ID |
| ZhenDuanMC | String | æ˜¯ | è¯Šæ–­åç§° |
| ZhenDuanLBDM | String | å¦ | è¯Šæ–­ç±»åˆ«ä»£ç  |
| ZhenDuanLBMC | String | æ˜¯ | è¯Šæ–­ç±»åˆ«åç§°ï¼›ç­‰äºŽ `主诊断` æ—¶å†™å…¥é—¨è¯Šä¸»è¯Šæ–­ |
| Icd10 | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | ICD10 ç¼–码 |
### 3.4 `WenZhenList` é—®è¯Šåˆ—表
> ç”¨äºŽçŽ°ç—…å²ã€ä¸»è¯‰ã€éšè®¿å¤©æ•°åŠå•ä½ã€‚
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| WenZhenLXMC | String | æ˜¯ï¼ˆè‹¥ä¼ ï¼‰ | é—®è¯Šç±»åž‹åç§°ï¼š`现病史`、`主诉`、`随访天数`、`随访天数单位` |
| NeiRong | String | æ˜¯ï¼ˆè‹¥ä¼ ï¼‰ | é—®è¯Šå†…容;`随访天数` å¿…须为整数,单位支持 `天`/`周`/`月`/`å¹´` |
## å››ã€å¤„理逻辑
1. è¯»å–患者、就诊、诊断、问诊信息。
2. ä½¿ç”¨ `FaSongJGID` ä½œä¸ºæœºæž„ ID,新增/更新患者档案。
3. æŒ‰ `BingAnHao + JiuZhenYWID` æŸ¥è¯¢é—¨è¯Šè®°å½•,存在则更新,不存在则新增。
4. ä¸»è¯Šæ–­å†™å…¥è¯Šæ–­åç§°ã€åŒ»ç”Ÿã€ICD10,并置 `fuflag=1`。
5. `现病史` å†™å…¥ `hpi`,`主诉` å†™å…¥ `mainsuit`。
6. è‹¥ä¼ äº† `随访天数` ä¸” `fuflag=1`,按天/周/月/年换算后生成随访任务。
7. ä¸½æ°´å¸‚妇幼保健院(`orgid = 47226079133110211G1001`)完成接诊后,按 `pat_specific_archive_generate_config` é…ç½®ç”Ÿæˆå¦‡ç§‘/儿科专科档案;其他机构不生成。详见 `doc/api/妇幼专科档案生成接口文档_20261104.md`。
## äº”、响应说明
成功响应:
```json
{
  "Code": "0",
  "Message": "success",
  "Data": {
    "XiaoXiID": "MSG-20261020-0001",
    "ChuangJianSJ": 1760928000000
  }
}
```
失败响应(业务返回 `false` æˆ–处理异常):
```json
{
  "Code": "500",
  "Message": "error",
  "Data": {
    "XiaoXiID": "MSG-20261020-0001",
    "ChuangJianSJ": 1760928000000
  }
}
```
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
| --- | --- | --- |
| Code | String | `"0"` æˆåŠŸï¼Œ`"500"` å¤±è´¥ |
| Message | String | `success` / `error` |
| Data.XiaoXiID | String | åŽŸæ ·è¿”å›žè¯·æ±‚ `YeWuXX.XiaoXiID` |
| Data.ChuangJianSJ | long | æœåŠ¡å™¨å½“å‰æ—¶é—´ï¼ˆæ¯«ç§’æ—¶é—´æˆ³ï¼‰ |
## å…­ã€æ³¨æ„äº‹é¡¹
- `ZhenDuanList`、`WenZhenList` ä»£ç æœªåšç©ºåˆ¤æ–­ï¼Œç¼ºå¤±ä¼šæŠ¥é”™ã€‚
- `随访天数` å¿…须是整数,`随访天数单位` æ”¯æŒ `天`、`周`、`月`、`å¹´`。
- é—¨è¯Šè®°å½•匹配键为 `BingAnHao + JiuZhenYWID`。
- æŠ¥æ–‡ç¼ºå°‘ `XiaoXiTou`、`XiaoXiLX`、`YeWuXX` æ—¶å¯èƒ½åœ¨å“åº”组装阶段直接抛异常,返回 HTTP 500。
- æ—¥æœŸæ—¶é—´é™¤ç‰¹åˆ«è¯´æ˜Žå¤–统一使用 `yyyy-MM-dd HH:mm:ss`。
---
本文档根据当前代码生成;若后端逻辑调整,请同步更新文档。
doc/api/generalInterface½Ó¿ÚÎĵµ.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1146 @@
# `/smartor/serviceExternal/generalInterface` é€šç”¨æŽ¥å£æ–‡æ¡£
> ç”Ÿæˆæ—¥æœŸï¼š2026-10-20
> å¯¹åº”代码:`ServiceExternalController#generalInterface`、`ServiceExternalServiceImpl#generalInterface/getType` åŠå„业务处理方法
> è¯´æ˜Žï¼šæœ¬æ–‡æ¡£æŒ‰å½“前代码实际解析的字段整理,字段名区分大小写,请严格按文档中的拼音字段名传参。
> æŒ‰æ¶ˆæ¯ç±»åž‹æ‹†åˆ†çš„独立文档见:`doc/api/generalInterface/00_接口文档索引.md`(含同名 PDF)。
---
## ä¸€ã€æŽ¥å£åŸºæœ¬ä¿¡æ¯
| é¡¹ç›® | å†…容 |
| --- | --- |
| æŽ¥å£åœ°å€ | `/smartor/serviceExternal/generalInterface` |
| è¯·æ±‚方式 | `POST` |
| Content-Type | `application/json;charset=UTF-8` |
| è®¤è¯æ–¹å¼ | æ— éœ€ç™»å½•(`SecurityConfig` ä¸­ `/smartor/serviceExternal/**` å·²é…ç½® `permitAll`) |
| è¯·æ±‚体 | å•个 JSON å¯¹è±¡ï¼Œç”± `XiaoXiTou`(消息头)+ `YeWuXX`(业务信息)组成 |
| å“åº”体 | å›ºå®šç»“构:`Code`、`Message`、`Data` |
| ä¸šåŠ¡åˆ†å‘å­—æ®µ | `XiaoXiTou.XiaoXiLX`(消息类型) |
| æœåŠ¡åœ°å€ç¤ºä¾‹ | `http://服务IP:端口/smartor/serviceExternal/generalInterface` |
### 1.1 æŽ¥å£å®šä½
该接口是 HIS/第三方系统向随访系统推送数据的**统一入口**,通过 `XiaoXiTou.XiaoXiLX` åŒºåˆ† 41 ç§ä¸šåŠ¡æ¶ˆæ¯ï¼Œåˆ†åˆ«å¤„ç†ï¼š
- æœºæž„、科室、用户、科室用户、病区(`JG_*`)
- æ‚£è€…档案、住院登记、入科、出院、出院记录、预出院、取消入院/出院、离抢(`BR_DA_*`、`JZ_ZY_*`、`BL_JL_*`)
- é—¨æ€¥è¯Šå®ŒæˆæŽ¥è¯Šï¼ˆ`JZ_MZ_WanChengJZ`)
- æ£€æŸ¥/检验申请、修改、报告(`JC_*`、`JY_*`)
- æ‰‹æœ¯ç”³è¯·ã€æ’¤é”€ï¼ˆ`SM_*`)
- ç–¾ç—…目录(`GY_ZD_JiBingML`)
- éšè®¿ä»»åŠ¡ï¼ˆ`fu_task`)
其中 22 ç§æ¶ˆæ¯ä¼šå®žé™…落库,另外 19 ç§æ¶ˆæ¯å½“前不落库(多数仅记录日志后直接返回成功,见第三章)。
---
## äºŒã€è¯·æ±‚报文结构
### 2.1 é¡¶å±‚结构
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| XiaoXiTou | object | æ˜¯ | æ¶ˆæ¯å¤´ï¼Œç¼ºå¤±æ—¶æŽ¥å£æŠ¥é”™ |
| YeWuXX | object | æ˜¯ | ä¸šåŠ¡ä¿¡æ¯ï¼›ç¼ºå¤±æ—¶å“åº”ç»„è£…é˜¶æ®µæŠ¥é”™ï¼›**必须包含 `XiaoXiID`** |
```json
{
  "XiaoXiTou": {
    "FaSongJGID": "47255004333112711A1001",
    "FaSongJGMC": "丽水市中医院",
    "FaSongSJ": "2026-10-20 10:00:00",
    "XiaoXiLX": "JZ_ZY_RuYuanDJ",
    "XiaoXiID": "5FA92AFB-9833-4608-87C7-F56A654AC171"
  },
  "YeWuXX": {
    "XiaoXiID": "5FA92AFB-9833-4608-87C7-F56A654AC171"
  }
}
```
### 2.2 æ¶ˆæ¯å¤´ `XiaoXiTou`
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| FaSongFCSJC | String | å¦ | å‘送方程序简称,默认 `ZJHES` |
| FaSongJGID | String | æ˜¯ | å‘送机构 ID,对应系统 `orgid`;部分消息类型用它覆盖业务中的机构 ID |
| FaSongJGMC | String | å¦ | å‘送机构名称 |
| FaSongSJ | String | å¦ | å‘送时间,建议 `yyyy-MM-dd HH:mm:ss` |
| FaSongXTJC | String | å¦ | å‘送系统简称,默认 `SUIFANGXT` |
| FaSongXTMC | String | å¦ | å‘送系统名称,默认 `随访系统` |
| XiaoXiID | String | å¦ | æ¶ˆæ¯ ID;**该接口的响应回显取的是 `YeWuXX.XiaoXiID`,不是此字段** |
| XiaoXiLX | String | æ˜¯ | æ¶ˆæ¯ç±»åž‹ï¼Œå†³å®šä¸šåŠ¡åˆ†æ”¯ï¼Œç¼ºå¤±æˆ–ä¸ºç©ºæ—¶æŽ¥å£æŠ¥é”™ |
| XiaoXiMC | String | å¦ | æ¶ˆæ¯åç§° |
| ZuHuID | String | å¦ | åŒ»é™¢ ID |
| ZuHuMC | String | å¦ | åŒ»é™¢åç§° |
### 2.3 ä¸šåŠ¡ä¿¡æ¯ `YeWuXX` å…¬å…±å­—段
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| XiaoXiID | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | æ¶ˆæ¯ ID,原样放入响应 `Data.XiaoXiID`;缺失时返回 `null` |
| BingRenXX | object | è§†æ¶ˆæ¯ç±»åž‹ | æ‚£è€…基本信息,见 4.1 |
| DiZhiXXList | array | è§†æ¶ˆæ¯ç±»åž‹ | æ‚£è€…地址列表,见 4.2 |
| LianXiRList | array | è§†æ¶ˆæ¯ç±»åž‹ | æ‚£è€…联系人列表,见 4.3 |
| ZhenDuanList | array | è§†æ¶ˆæ¯ç±»åž‹ | è¯Šæ–­åˆ—表,见 4.4 |
| JiuZhenXX | object | è§†æ¶ˆæ¯ç±»åž‹ | å°±è¯Š/住院过程信息,见 4.5 |
| RuKeXX | object | ä»…入科 | å…¥ç§‘信息,见 4.6 |
| ChuYuanJL | object | è§†æ¶ˆæ¯ç±»åž‹ | å‡ºé™¢è®°å½•,内部使用 `SuiFangJH`,见 4.7 |
| BingRenKZXXList | array | å¦ | æ‚£è€…扩展信息(短号),见 4.8 |
| WenZhenList | array | ä»…完成接诊 | é—®è¯Šä¿¡æ¯ï¼Œè§ 4.9 |
| å…¶ä»–业务对象 | object/array | è§†æ¶ˆæ¯ç±»åž‹ | `KeShiXX`、`YongHuXX`、`ZuZhiJGXX`、`BingQuXX`、`JiBingMLXX`、`JianChaSQ`、`JianChaBG`、`ShouShuSQ`、`ShouShuCX` ç­‰ï¼Œè§ç¬¬äº”ç«  |
---
## ä¸‰ã€æ¶ˆæ¯ç±»åž‹ï¼ˆ`XiaoXiLX`)总表
| åºå· | XiaoXiLX | ä¸šåС含义 | å¤„理方法 | æ˜¯å¦è½åº“ |
| --- | --- | --- | --- | --- |
| 1 | `BR_DA_DangAnJL` | æ‚£è€…档案建立 | ä»…记录日志 | å¦ï¼ˆç›´æŽ¥è¿”回成功) |
| 2 | `BR_DA_DangAnXG` | æ‚£è€…档案修改 | ä»…记录日志 | å¦ï¼ˆç›´æŽ¥è¿”回成功) |
| 3 | `JZ_MZ_ZhenDuanXXLR` | é—¨è¯Šè¯Šæ–­ä¿¡æ¯å½•å…¥ | ä»…记录日志 | å¦ï¼ˆç›´æŽ¥è¿”回成功) |
| 4 | `JZ_ZY_ZhenDuanXXLR` | ä½é™¢è¯Šæ–­ä¿¡æ¯å½•å…¥ | ä»…记录日志 | å¦ï¼ˆç›´æŽ¥è¿”回成功) |
| 5 | `YZ_MZ_YiZhuSQ` | é—¨è¯ŠåŒ»å˜±ç”³è¯· | ä»…记录日志 | å¦ï¼ˆç›´æŽ¥è¿”回成功) |
| 6 | `YZ_ZY_YiZhuZX` | ä½é™¢åŒ»å˜±æ‰§è¡Œ | ä»…记录日志 | å¦ï¼ˆç›´æŽ¥è¿”回成功) |
| 7 | `YZ_MZ_YiZhuXG` | é—¨è¯ŠåŒ»å˜±ä¿®æ”¹ | ä»…记录日志 | å¦ï¼ˆç›´æŽ¥è¿”回成功) |
| 8 | `YZ_MZ_YiZhuCX` | é—¨è¯ŠåŒ»å˜±æ’¤é”€ | ä»…记录日志 | å¦ï¼ˆç›´æŽ¥è¿”回成功) |
| 9 | `YZ_MZ_YiZhuCS` | é—¨è¯ŠåŒ»å˜±åˆ é™¤ | ä»…记录日志 | å¦ï¼ˆç›´æŽ¥è¿”回成功) |
| 10 | `JC_SQ_ShenQing` | æ£€æŸ¥ç”³è¯· | `addInspectService` | æ˜¯ï¼ˆæ£€æŸ¥ä¸»è¡¨ï¼‰ |
| 11 | `JC_SQ_XiuGai` | æ£€æŸ¥ç”³è¯·ä¿®æ”¹ | `addInspectService` | æ˜¯ï¼ˆæ£€æŸ¥ä¸»è¡¨ï¼‰ |
| 12 | `JC_SQ_CheXiao` | æ£€æŸ¥æ’¤é”€ | ä»…记录日志 | å¦ï¼ˆç›´æŽ¥è¿”回成功) |
| 13 | `JC_BG_BaoGao` | æ£€æŸ¥æŠ¥å‘Š | `addInspectBGService` | æ˜¯ï¼ˆæ£€æŸ¥æŠ¥å‘Šæ˜Žç»†ï¼‰ |
| 14 | `JY_SQ_ShenQing` | æ£€éªŒç”³è¯· | `addInspectService` | æ˜¯ï¼ˆæ£€æŸ¥ä¸»è¡¨ï¼Œprojecttype=1) |
| 15 | `JY_SQ_CheXiao` | æ£€éªŒæ’¤é”€ | ä»…记录日志 | å¦ï¼ˆç›´æŽ¥è¿”回成功) |
| 16 | `JY_SQ_XiuGai` | æ£€éªŒç”³è¯·ä¿®æ”¹ | `addInspectService` | æ˜¯ï¼ˆæ£€æŸ¥ä¸»è¡¨ï¼‰ |
| 17 | `JY_SQ_BiaoBenCJ` | æ£€éªŒæ ‡æœ¬é‡‡é›† | ä»…记录日志 | å¦ï¼ˆç›´æŽ¥è¿”回成功) |
| 18 | `JY_SQ_QuXiaoBBCJ` | å–消检验标本采集 | ä»…记录日志 | å¦ï¼ˆç›´æŽ¥è¿”回成功) |
| 19 | `SM_SQ_ShenQing` | æ‰‹æœ¯ç”³è¯· | `addOperationInfo` | æ˜¯ï¼ˆæ‰‹æœ¯ä¸»è¡¨+明细) |
| 20 | `SM_SQ_ShouShuAP` | æ‰‹æœ¯å®‰æŽ’ | ä»…记录日志 | å¦ï¼ˆç›´æŽ¥è¿”回成功) |
| 21 | `SM_SQ_CheXiao` | æ‰‹æœ¯æ’¤é”€ | `cancelOperationInfo` | æ˜¯ï¼ˆè½¯åˆ é™¤æ‰‹æœ¯åŠä»»åŠ¡ï¼‰ |
| 22 | `TJ_YW_BaoGaoXX` | ä½“检报告 | ä»…记录日志 | å¦ï¼ˆç›´æŽ¥è¿”回成功) |
| 23 | `TJ_YW_QuXiaoBG` | å–消体检报告 | ä»…记录日志 | å¦ï¼ˆç›´æŽ¥è¿”回成功) |
| 24 | `JG_ZZ_JiGou` | ç»„织机构 | `addOrganInfo` | æ˜¯ï¼ˆ`base_organization`) |
| 25 | `JG_ZZ_KeShi` | ç§‘室 | `addDeptInfo` | æ˜¯ï¼ˆ`sys_dept`) |
| 26 | `JG_YH_JiBenXX` | ç”¨æˆ·åŸºæœ¬ä¿¡æ¯ | `addUserInfo` | æ˜¯ï¼ˆ`sys_user`) |
| 27 | `JG_YH_KeShiYH` | ç§‘室用户关系 | `addUserDeptInfo` | æ˜¯ï¼ˆ`sys_user_dept`) |
| 28 | `fu_task` | éšè®¿ä»»åŠ¡ | `addTaskInfo` | å¦ï¼ˆå½“前为空实现) |
| 29 | `GY_ZD_JiBingML` | ç–¾ç—…目录 | `addDiseaseInfo` | æ˜¯ï¼ˆ`icd10`) |
| 30 | `YP_ZD_YaoPinCDJG` | è¯å“äº§åœ°ä»·æ ¼ | ä»…记录日志 | å¦ï¼ˆç›´æŽ¥è¿”回成功) |
| 31 | `JZ_MZ_WanChengJZ` | é—¨æ€¥è¯Šå®ŒæˆæŽ¥è¯Š | `addFinshJZInfo` | æ˜¯ï¼ˆé—¨è¯Šå°±è¯Š+任务) |
| 32 | `JZ_ZY_RuYuanDJ` | å…¥é™¢ç™»è®° | `addInHospInfo` | æ˜¯ï¼ˆæ¡£æ¡ˆ+住院记录) |
| 33 | `JZ_ZY_QuXiaoRY` | å–消入院登记 | `cancelInHospInfo` | æ˜¯ï¼ˆä½é™¢è®°å½•置删除) |
| 34 | `JZ_ZY_ChuYuan` | æ‚£è€…出院 | `addOutHospInfo` | æ˜¯ï¼ˆä½é™¢è®°å½•+出院信息) |
| 35 | `BL_JL_ChuYuanJL` | æ‚£è€…出院记录 | `addOutHospJLInfo` | æ˜¯ï¼ˆä½é™¢è®°å½•+出院信息) |
| 36 | `JZ_ZY_QuXiaoCY` | å–消出院 | `cancelOutHospInfo` | æ˜¯ï¼ˆå›žé€€åœ¨é™¢çŠ¶æ€å¹¶æ¸…ä»»åŠ¡ï¼‰ |
| 37 | `JZ_ZY_YuChuYuan` | æ‚£è€…预出院 | `addPreOutHospInfo` | æ˜¯ï¼ˆä½é™¢è®°å½•预出院状态) |
| 38 | `JZ_ZY_LiQiang` | æ‚£è€…离抢 | `editRescueFinishedOutHospInfo` | æ˜¯ï¼ˆå›žå¡«å‡ºé™¢æ—¶é—´/死亡处理) |
| 39 | `JG_ZZ_BingQu` | ç—…区 | `addWardAreaInfo` | æ˜¯ï¼ˆ`sys_dept`,deptType=2) |
| 40 | `JG_ZZ_YiLiaoZYS` | åŒ»ç–—组医生 | `addMedicalTeam` | å¦ï¼ˆå½“前为空实现) |
| 41 | `JZ_ZY_RuKe` | å…¥ç§‘ | `addInDeptInfo` | æ˜¯ï¼ˆæ¡£æ¡ˆ+住院记录) |
> æœªåœ¨ `switch` ä¸­åŒ¹é…åˆ°çš„ `XiaoXiLX` ä¼šèµ°é»˜è®¤åˆ†æ”¯ï¼Œç›´æŽ¥è¿”回成功(`Code=0`),不产生任何数据变更。请调用方确保类型拼写完全一致。
---
## å››ã€å…¬å…±ä¸šåŠ¡å¯¹è±¡
### 4.1 `BingRenXX` æ‚£è€…基本信息
对应 `ExternalInHospPatientInfo`。住院、接诊、检查、手术类消息均使用该对象。
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| ZuZhiJGID | String | æ˜¯ | ç»„织机构 ID(写入 `orgid`);`JZ_MZ_WanChengJZ` ä¼šæ”¹ç”¨æ¶ˆæ¯å¤´ `FaSongJGID` |
| ZuZhiJGMC | String | å¦ | ç»„织机构名称 |
| MPI | String | å¦ | å±…民主索引 |
| BingRenID | String | æ˜¯ | ç—…人 ID,写入 `patid_his/patno`,是患者匹配依据之一 |
| XingMing | String | æ˜¯ | æ‚£è€…姓名 |
| XingMingQP | String | å¦ | å§“名全拼 |
| JiuZhenYWLX | String | å¦ | å°±è¯Šä¸šåŠ¡ç±»åž‹ |
| JiuZhenYWID | String | æ˜¯ | å°±è¯Šä¸šåŠ¡ ID,与 `BingAnHao` å…±åŒå®šä½ä½é™¢/门诊记录 |
| JiuZhenKH | String | å¦ | å°±è¯Šå¡å·ï¼›è¯ä»¶å·ç ä¸ºç©ºæ—¶ä½œä¸ºæ‚£è€…档案唯一标识 |
| ZhuYuanHao | String | å¦ | ä½é™¢å· |
| BingAnHao | String | æ˜¯ | ç—…案号(代码注释明确使用病案号,不使用住院号) |
| ZhengJianLXDM | String | å¦ | è¯ä»¶ç±»åž‹ä»£ç  |
| ZhengJianLXMC | String | å¦ | è¯ä»¶ç±»åž‹åç§°ï¼Œå†™å…¥æ¡£æ¡ˆ `idcardtype` |
| ZhengJianHM | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | è¯ä»¶å·ç ï¼Œæ‚£è€…档案唯一标识;为空时使用 `JiuZhenKH` |
| XingBieDM | String | å¦ | æ€§åˆ«ä»£ç ï¼›**必须为数字字符串**(如 `1`/`2`),代码执行 `Long.valueOf` |
| XingBieMC | String | å¦ | æ€§åˆ«åç§° |
| ChuShengRQ | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | å‡ºç”Ÿæ—¥æœŸï¼Œæ ¼å¼ `yyyy-MM-dd hh:mm:ss`(代码实际解析格式,建议一并传时间) |
| NianLing | String | å¦ | å¹´é¾„,代码不使用,年龄由出生日期计算 |
| NianLingDW | String | å¦ | å¹´é¾„单位,代码不使用 |
| DangQianKSID | String | æ˜¯ | å½“前科室 ID,写入住院/出院科室 |
| DangQianKSMC | String | æ˜¯ | å½“前科室名称 |
| DangQianBQID | String | æ˜¯ | å½“前病区 ID,写入住院病区 |
| DangQianBQMC | String | æ˜¯ | å½“前病区名称 |
| DangQianCWID | String | å¦ | å½“前床位 ID |
| DangQianCWMC | String | å¦ | å½“前床位名称,写入住院 `bedNo` |
| ZhuYuanCS | Long | å¦ | ä½é™¢æ¬¡æ•° |
| RuYuanSJ | Date | æ˜¯ï¼ˆå…¥é™¢/入科) | å…¥é™¢æ—¶é—´ï¼Œå†™å…¥ä½é™¢ `starttime`;格式 `yyyy-MM-dd HH:mm:ss` |
| FeiYongLBID | String | å¦ | è´¹ç”¨ç±»åˆ« ID |
| FeiYongLBMC | String | å¦ | è´¹ç”¨ç±»åˆ«åç§° |
| FeiYongXZID | String | å¦ | è´¹ç”¨æ€§è´¨ ID |
| FeiYongXZMC | String | å¦ | è´¹ç”¨æ€§è´¨åç§° |
| LianXiDH | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | è”系电话,写入患者档案手机号 |
| LianXiRDH | String | å¦ | è”系人电话;`LianXiDH` ä¸ºç©ºæ—¶ä½œä¸ºæ‚£è€…手机号 |
| DanWeiDH | String | å¦ | å•位电话 |
| DanWeiMC | String | å¦ | å•位名称 |
| HunYinDM | String | å¦ | å©šå§»ä»£ç  |
| HunYinMC | String | å¦ | å©šå§»åç§° |
| ZhiYeDM | String | å¦ | èŒä¸šä»£ç  |
| ZhiYeMC | String | å¦ | èŒä¸šåç§° |
| GuoJiDM | String | å¦ | å›½ç±ä»£ç  |
| GuoJiMC | String | å¦ | å›½ç±åç§°ï¼Œå†™å…¥æ¡£æ¡ˆ `nativePlace` |
| MinZuDM | String | å¦ | æ°‘族代码 |
| MinZuMC | String | å¦ | æ°‘族名称,写入档案 `nation` |
| CaoZuoRID | String | å¦ | æ“ä½œäºº ID(入院登记 `operatorId`) |
| CaoZuoRXM | String | å¦ | æ“ä½œäººå§“名(入院登记 `operator`) |
| YingErBZ | String | å¦ | å©´å„¿æ ‡å¿— |
| MuQinZYJZID | String | å¦ | æ¯äº²å°±è¯Š ID |
| ChanFuBZ | String | å¦ | äº§å¦‡æ ‡å¿— |
| XianZhuZXX | String | å¦ | çŽ°ä½å€ï¼›**优先于 `DiZhiXXList`** å†™å…¥æ¡£æ¡ˆçŽ°ä½å€ |
| ZeRenHSID | String | å¦ | è´£ä»»æŠ¤å£« ID |
| ZeRenHSXM | String | å¦ | è´£ä»»æŠ¤å£«å§“名 |
| ZhuZhiYSID | String | å¦ | ä¸»æ²»åŒ»ç”Ÿ ID |
| ZhuZhiYSXM | String | å¦ | ä¸»æ²»åŒ»ç”Ÿå§“名 |
| JingGuanYSID | String | å¦ | ç»ç®¡åŒ»ç”Ÿ ID |
| JingGuanYSXM | String | å¦ | ç»ç®¡åŒ»ç”Ÿå§“名 |
| DuanHaoXX | String | å¦ | çŸ­å·ä¿¡æ¯ï¼›å‡ºé™¢æ—¶å¯ç”± `BingRenKZXXList` è¦†ç›– |
| BingRenLXDM | String | å¦ | ç—…人类型代码,手术记录写入 `inhospstate` |
| BingRenLXMC | String | å¦ | ç—…人类型名称 |
> `externalInHospPatientLiaisonInfos` å­—段不会从报文读取,联系人请使用 `YeWuXX.LianXiRList`。
### 4.2 `DiZhiXXList` æ‚£è€…地址信息
对应 `ExternalInHospPatientAddrInfo`,取第一条作为患者现住址(`XianZhuZXX` ä¸ºç©ºæ—¶ï¼‰ã€‚
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| DiZhiLX | String | å¦ | åœ°å€ç±»åž‹ |
| DiZhiXX | String | å¦ | åœ°å€ä¿¡æ¯ï¼›æœ‰å€¼æ—¶ç›´æŽ¥ä½œä¸ºçŽ°ä½å€ï¼Œå¦åˆ™ç”±çœ/市/区/乡镇/村/其他拼接 |
| ShengFenDM | String | å¦ | çœä»½ä»£ç  |
| ShengFenMC | String | å¦ | çœä»½åç§° |
| ShiDiQDM | String | å¦ | å¸‚地区代码 |
| ShiDiQMC | String | å¦ | å¸‚地区名称 |
| XianQuDM | String | å¦ | åŽ¿åŒºä»£ç  |
| XianQuMC | String | å¦ | åŽ¿åŒºåç§° |
| XiangZhenDM | String | å¦ | ä¹¡é•‡ä»£ç  |
| XiangZhenMC | String | å¦ | ä¹¡é•‡åç§° |
| CunJiDM | String | å¦ | æ‘级代码 |
| CunJiMC | String | å¦ | æ‘级名称 |
| QiTaXX | String | å¦ | å…¶ä»–信息 |
| YouBian | String | å¦ | é‚®ç¼– |
### 4.3 `LianXiRList` æ‚£è€…联系人信息
对应 `ExternalInHospPatientLiaisonInfo`,写入 `pat_archivecontact`。
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| LianXiRXM | String | å¦ | è”系人姓名 |
| LianXiRDH | String | å¦ | è”系人电话 |
| GuanXiDM | String | å¦ | å…³ç³»ä»£ç  |
| GuanXiMC | String | å¦ | å…³ç³»åç§°ï¼Œå†™å…¥è”系人 `relation` |
### 4.4 `ZhenDuanList` è¯Šæ–­ä¿¡æ¯
不同消息类型字段不同,按下表传递。
**(1)住院类(入院登记、出院、出院记录、预出院)**
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| ZhenDuanID | String | å¦ | è¯Šæ–­ ID |
| ZhenDuanMC | String | æ˜¯ | è¯Šæ–­åç§° |
| Icd10 | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | ICD10 ç¼–码;出院时写入 `leaveicd10code` |
**(2)门急诊完成接诊(`JZ_MZ_WanChengJZ`)**
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| ZhenDuanYSID | String | å¦ | è¯Šæ–­åŒ»ç”Ÿ ID |
| ZhenDuanYSXM | String | å¦ | è¯Šæ–­åŒ»ç”Ÿå§“名 |
| ZhenDuanID | String | å¦ | è¯Šæ–­ ID |
| ZhenDuanMC | String | æ˜¯ | è¯Šæ–­åç§° |
| ZhenDuanLBDM | String | å¦ | è¯Šæ–­ç±»åˆ«ä»£ç  |
| ZhenDuanLBMC | String | æ˜¯ | è¯Šæ–­ç±»åˆ«åç§°ï¼›ç­‰äºŽ `主诊断` æ—¶å†™å…¥é—¨è¯Šä¸»è¯Šæ–­ã€åŒ»ç”Ÿã€ICD10 |
| Icd10 | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | ICD10 ç¼–码 |
**(3)检查/检验申请(`JianChaSQ.ZhenDuanList`)**
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| ZhenDuanID | String | å¦ | è¯Šæ–­ ID |
| ZhenDuanMC | String | å¦ | è¯Šæ–­åç§° |
### 4.5 `JiuZhenXX` å°±è¯Š/住院过程信息
不同消息类型按需取字段,未被使用的字段可忽略。
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| JiuZhenKSID | String | æŽ¥è¯Šå¿…å¡« | å°±è¯Šç§‘室 ID |
| JiuZhenKSMC | String | æŽ¥è¯Šå¿…å¡« | å°±è¯Šç§‘室名称 |
| JiuZhenYSID | String | å¦ | å°±è¯ŠåŒ»ç”Ÿ ID(当前代码未落库) |
| JiuZhenYSMC | String | å¦ | å°±è¯ŠåŒ»ç”Ÿåç§°ï¼ˆå½“前代码未落库) |
| JiuZhenRQ | Date | æŽ¥è¯Šå¿…å¡« | å°±è¯Šæ—¥æœŸï¼Œå†™å…¥é—¨è¯Š `admitdate`,格式 `yyyy-MM-dd HH:mm:ss` |
| sfrq | Date | å¦ | éšè®¿æ—¥æœŸï¼Œå†™å…¥é—¨è¯Š `fudate`(代码备注字段待 HIS ç¡®è®¤ï¼‰ |
| HuanZheQXDM | String | å¦ | æ‚£è€…去向代码 |
| HuanZheQXMC | String | å¦ | æ‚£è€…去向名称 |
| ChuYuanSJ | String | å‡ºé™¢å¿…å¡« | å‡ºé™¢æ—¶é—´ï¼Œæ ¼å¼ `yyyy-MM-dd HH:mm:ss` |
| YuChuYSJ | String | é¢„出院必填 | é¢„出院时间,格式 `yyyy-MM-dd HH:mm:ss` |
| LiQiangSJ | String | ç¦»æŠ¢å»ºè®®å¿…å¡« | ç¦»æŠ¢æ—¶é—´ï¼Œæ ¼å¼ `yyyy-MM-dd HH:mm:ss`;缺失时只做死亡标记处理 |
| LiYuanQXDM | String | é¢„出院必填 | ç¦»é™¢æ–¹å¼ä»£ç ï¼›`5` è¡¨ç¤ºæ­»äº¡ï¼Œè§¦å‘死亡患者处理 |
| CaoZuoRID | String | å¦ | æ“ä½œäºº ID |
| CaoZuoRXM | String | å¦ | æ“ä½œäººå§“名 |
| ZhuZhiYSID | String | å¦ | ä¸»æ²»åŒ»ç”Ÿ ID(写入 `drcode`) |
| ZhuZhiYSXM | String | å¦ | ä¸»æ²»åŒ»ç”Ÿå§“名(写入 `drname`) |
| ZeRenHSID | String | å¦ | è´£ä»»æŠ¤å£« ID |
| ZeRenHSXM | String | å¦ | è´£ä»»æŠ¤å£«å§“名 |
| DangQianCWMC | String | å¦ | åºŠä½åç§°ï¼ˆå†™å…¥ `bedNo`) |
| JingGuanYSID | String | å¦ | ç»ç®¡åŒ»ç”Ÿ ID |
| JingGuanYSXM | String | å¦ | ç»ç®¡åŒ»ç”Ÿå§“名 |
> ç¦»é™¢æ–¹å¼ä»£ç è¯´æ˜Žï¼š`1` åŒ»å˜±ç¦»é™¢ã€`2` åŒ»å˜±è½¬é™¢ã€`3` åŒ»å˜±è½¬ç¤¾åŒºå«ç”ŸæœåŠ¡æœºæž„/乡镇卫生院、`4` éžåŒ»å˜±ç¦»é™¢ã€`5` æ­»äº¡ã€`9` å…¶ä»–、`10` ä»‹å…¥å®¤ã€`20` æ‰‹æœ¯å®¤ã€‚
### 4.6 `RuKeXX` å…¥ç§‘信息
仅 `JZ_ZY_RuKe` ä½¿ç”¨ï¼Œå¯¹åº” `ExternalInDeptPatientInfo`。
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| CaoZuoRID | String | å¦ | æ“ä½œäºº ID |
| CaoZuoRXM | String | å¦ | æ“ä½œäººå§“名 |
| RuKeSJ | String | å¦ | å…¥ç§‘æ—¶é—´ |
| RuYuanSJ | Date | å¦ | å…¥é™¢æ—¶é—´ï¼Œè¦†ç›– `BingRenXX.RuYuanSJ` |
| YiLiaoZID | String | å¦ | åŒ»ç–—组 ID |
| YiLiaoZMC | String | å¦ | åŒ»ç–—组名称 |
| ZhuZhiYSID | String | å¦ | ä¸»æ²»åŒ»ç”Ÿ ID |
| ZhuZhiYSXM | String | å¦ | ä¸»æ²»åŒ»ç”Ÿå§“名 |
| ZeRenHSID | String | å¦ | è´£ä»»æŠ¤å£« ID |
| ZeRenHSXM | String | å¦ | è´£ä»»æŠ¤å£«å§“名 |
| HuShiZID | String | å¦ | æŠ¤å£«ä¸» ID |
| HuShiZXM | String | å¦ | æŠ¤å£«ä¸»å§“名 |
| JingGuanYSID | String | å¦ | ç»ç®¡åŒ»ç”Ÿ ID |
| JingGuanYSXM | String | å¦ | ç»ç®¡åŒ»ç”Ÿå§“名 |
### 4.7 `ChuYuanJL.SuiFangJH` éšè®¿è®¡åˆ’
出院、出院记录、预出院消息使用,用于生成/更新随访计划。
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| SuiFangBS | String | å¦ | éšè®¿æ ‡è¯†ï¼Œ`0` ä¸éœ€è¦éšè®¿ã€`1` éœ€è¦ï¼›é»˜è®¤ `1` |
| SuiFangRQ | String | å¦ | éšè®¿æ—¥æœŸï¼Œæ ¼å¼ `yyyy-MM-dd HH:mm:ss` |
| SuiFangZQJY | String | å¦ | éšè®¿å‘¨æœŸå»ºè®® |
| SuiFangFSDM | String | å¦ | éšè®¿æ–¹å¼ä»£ç  |
| SuiFangFSMC | String | å¦ | éšè®¿æ–¹å¼åç§° |
| FuZhenZD | String | å¦ | å¤è¯ŠæŒ‡å¯¼ |
| QiTaZKJBSF | String | å¦ | å…¶ä»–专科禁补随访说明 |
### 4.8 `BingRenKZXXList` æ‚£è€…扩展信息
出院(`JZ_ZY_ChuYuan`)使用,用于提取短号。
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| XiangMuDM | String | æ˜¯ï¼ˆè‹¥ä¼ ï¼‰ | é¡¹ç›®ä»£ç ï¼›ç­‰äºŽ `DuanHaoXX` æ—¶å– `XiangMuZMC` ä½œä¸ºçŸ­å· |
| XiangMuZMC | String | æ˜¯ï¼ˆè‹¥ä¼ ï¼‰ | é¡¹ç›®å€¼ |
示例:
```json
"BingRenKZXXList": [
  { "XiangMuDM": "DuanHaoXX", "XiangMuZMC": "669999" }
]
```
### 4.9 `WenZhenList` é—®è¯Šä¿¡æ¯
仅 `JZ_MZ_WanChengJZ` ä½¿ç”¨ã€‚代码按 `WenZhenLXMC` åŒ¹é…ä»¥ä¸‹å››ç±»ï¼š
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| WenZhenLXMC | String | æ˜¯ï¼ˆè‹¥ä¼ ï¼‰ | é—®è¯Šç±»åž‹åç§°ï¼š`现病史`、`主诉`、`随访天数`、`随访天数单位` |
| NeiRong | String | æ˜¯ï¼ˆè‹¥ä¼ ï¼‰ | é—®è¯Šå†…容;`随访天数` å¿…须为整数,`随访天数单位` æ”¯æŒ `天`/`周`/`月`/`å¹´` |
> å…¶ä»–字段(`WenZhenLXDM`、`WenZhenSJ`、`WenZhenYSID`、`WenZhenYSXM`、`WenZhenKSID`、`WenZhenKSMC`)当前不参与业务处理。
---
## äº”、各消息类型的业务报文
> ä»¥ä¸‹ä»…列出 `YeWuXX` ä¸­è¯¥æ¶ˆæ¯ç±»åž‹å®žé™…使用的键;未列出的键会被忽略。
### 5.1 æœºæž„、科室、用户、病区
#### 5.1.1 `JG_ZZ_JiGou` ç»„织机构
`YeWuXX` ç»“构:`{ "XiaoXiID": "...", "ZuZhiJGXX": { ... } }`
`ZuZhiJGXX` å­—段:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| ZuZhiJGID | String | æ˜¯ | æœºæž„ ID,同时写入 `orgid` |
| ZuZhiJGMC | String | æ˜¯ | æœºæž„名称 |
| ShiJianDM | String | æ˜¯ | äº‹ä»¶ä»£ç ï¼š`1` æ–°å¢žï¼Œå…¶ä»–值更新(`3` ä½œåºŸå½“前也走更新) |
| ShiJianMC | String | å¦ | äº‹ä»¶åç§° |
| ShuRuMa1 | String | å¦ | è¾“入码 1 |
| ShuRuMa2 | String | å¦ | è¾“入码 2 |
| ShuRuMa3 | String | å¦ | è¾“入码 3 |
| createBy | String | å¦ | åˆ›å»ºäººï¼ˆæ¥è‡ª BaseEntity,实际生效) |
| createTime | Date | å¦ | åˆ›å»ºæ—¶é—´ï¼Œ`yyyy-MM-dd HH:mm:ss` |
| updateBy | String | å¦ | ä¿®æ”¹äººï¼ˆå®žé™…生效) |
| updateTime | Date | å¦ | ä¿®æ”¹æ—¶é—´ï¼Œ`yyyy-MM-dd HH:mm:ss` |
#### 5.1.2 `JG_ZZ_KeShi` ç§‘室
`YeWuXX.KeShiXX`:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| ZuZhiJGID | String | æ˜¯ | æœºæž„ ID |
| KeShiID | String | æ˜¯ | ç§‘室 ID,写入 `his_dept_id` / `dept_code` |
| KeShiMC | String | æ˜¯ | ç§‘室名称 |
| FuKeSID | String | å¦ | çˆ¶ç§‘室 ID,用于查找并填充 `parentId` |
| KeShiLBDM | String | å¦ | ç§‘室类别代码,写入 `type` |
| ShiJianDM | String | æ˜¯ | `1` æ–°å¢žï¼›`3` ä½œåºŸï¼ˆ`status=1`);其他值修改 |
| ShiJianMC | String | å¦ | äº‹ä»¶åç§° |
| KeShiLBMC | String | å¦ | ç§‘室类别名称 |
| FuKeSMC | String | å¦ | çˆ¶ç§‘室名称 |
| MoJiBZ | Integer | å¦ | æœ«çº§æ ‡å¿— |
| ShuRuMa1/2/3 | String | å¦ | è¾“入码 |
| createBy | String | å¦ | åˆ›å»ºäººï¼ˆå®žé™…生效) |
| createTime | Date | å¦ | åˆ›å»ºæ—¶é—´ï¼ˆå®žé™…生效) |
| updateBy | String | å¦ | ä¿®æ”¹äººï¼ˆå®žé™…生效) |
| updateTime | Date | å¦ | ä¿®æ”¹æ—¶é—´ï¼ˆå®žé™…生效) |
#### 5.1.3 `JG_YH_JiBenXX` ç”¨æˆ·åŸºæœ¬ä¿¡æ¯
`YeWuXX.YongHuXX`:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| ZuZhiJGID | String | æ˜¯ | æœºæž„ ID;为空时直接返回失败 |
| YongHuDLM | String | æ˜¯ | ç”¨æˆ·ç™»å½•名;为空时直接返回失败 |
| YongHuID | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | HIS ç”¨æˆ· ID,写入 `his_user_id`,后续科室用户关系按此匹配 |
| YongHuXM | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | ç”¨æˆ·å§“名,写入 `nick_name` |
| LianXiDH | String | å¦ | è”系电话 |
| GongZuoDH | String | å¦ | å·¥ä½œç”µè¯ |
| XingBieDM | String | å¦ | æ€§åˆ«ä»£ç ï¼Œå†™å…¥ `sex` |
| ChuShengRQ | Date | å¦ | å‡ºç”Ÿæ—¥æœŸï¼Œ`yyyy-MM-dd HH:mm:ss` |
| ShiJianDM | String | æ˜¯ | `3` ä½œåºŸï¼ˆ`status=1`);其他值按用户名+机构判断新增或更新 |
| ShiJianMC | String | å¦ | äº‹ä»¶åç§° |
| ShenFenZH | String | å¦ | èº«ä»½è¯å·ï¼ˆå½“前未落库) |
| ChuangJianRXM | String | å¦ | åˆ›å»ºäººå§“名 |
| ChuangJianSJ | Date | å¦ | åˆ›å»ºæ—¶é—´ |
| XiuGaiRXM | String | å¦ | ä¿®æ”¹äººå§“名 |
| XiuGaiSJ | Date | å¦ | ä¿®æ”¹æ—¶é—´ |
> æ–°å¢žç”¨æˆ·æ—¶å¯†ç ä½¿ç”¨ç³»ç»Ÿé»˜è®¤å¯†ç  `defaultPwd` åŠ å¯†åŽå†™å…¥ã€‚
#### 5.1.4 `JG_YH_KeShiYH` ç§‘室用户关系
`YeWuXX.KeShiYH`:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| ZuZhiJGID | String | æ˜¯ | æœºæž„ ID |
| YongHuID | String | æ˜¯ | ç”¨æˆ· ID,用于查找 `sys_user` |
| KeShiID | String | æ˜¯ | ç§‘室 ID,用于查找 `sys_dept` |
| ShiJianDM | String | æ˜¯ | `3` ä½œåºŸï¼ˆ`del_flag=1`);其他值新增或更新关系 |
| ShiJianMC | String | å¦ | äº‹ä»¶åç§° |
| KeShiYHDZID | String | å¦ | ç§‘室用户地址 ID(未落库) |
| YongHuXM | String | å¦ | ç”¨æˆ·å§“名(未落库) |
| KeShiMC | String | å¦ | ç§‘室名称(未落库) |
| ZhiGongID | String | å¦ | èŒå·¥ ID(未落库) |
| YongHuDLM | String | å¦ | ç”¨æˆ·ç™»å½•名(仅日志) |
| ChuangJianRXM | String | å¦ | åˆ›å»ºäººå§“名 |
| ChuangJianSJ | Date | å¦ | åˆ›å»ºæ—¶é—´ |
| XiuGaiRXM | String | å¦ | ä¿®æ”¹äººå§“名 |
| XiuGaiSJ | Date | å¦ | ä¿®æ”¹æ—¶é—´ |
> ç”¨æˆ·æˆ–科室找不到时返回 `Code=500`(`false`)。
#### 5.1.5 `JG_ZZ_BingQu` ç—…区
`YeWuXX.BingQuXX`:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| ZuZhiJGID | String | æ˜¯ | æœºæž„ ID |
| BingQuID | String | æ˜¯ | ç—…区 ID,写入 `his_dept_id` / `dept_code` |
| BingQuMC | String | æ˜¯ | ç—…区名称 |
| BingQuLXDM | String | å¦ | ç—…区类型代码,写入 `type` |
| ShiJianDM | String | æ˜¯ | `1` æ–°å¢žï¼›`3` ä½œåºŸï¼ˆ`status=1`);其他值修改 |
| ShiJianMC | String | å¦ | äº‹ä»¶åç§° |
| BingQuLXMC | String | å¦ | ç—…区类型名称(未落库) |
| ShuRuMa1/2/3 | String | å¦ | è¾“入码 |
| ChuangJianRXM | String | å¦ | åˆ›å»ºäººå§“名(实际生效) |
| createTime | Date | å¦ | åˆ›å»ºæ—¶é—´ï¼Œ`yyyy-MM-dd HH:mm:ss`(实际生效) |
| updateBy | String | å¦ | ä¿®æ”¹äººï¼ˆå®žé™…生效) |
| updateTime | Date | å¦ | ä¿®æ”¹æ—¶é—´ï¼ˆå®žé™…生效) |
> ç—…区记录 `deptType=2`,与科室(`deptType=1`)区分。
### 5.2 `GY_ZD_JiBingML` ç–¾ç—…目录
`YeWuXX.JiBingMLXX`:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| ZuZhiJGID | String | æ˜¯ | æœºæž„ ID |
| JiBingID | String | æ˜¯ | ç–¾ç—… ID,写入 `his_icdid` |
| JiBingMC | String | æ˜¯ | ç–¾ç—…名称,写入 `icdname` |
| IcD10 | String | æ˜¯ | ICD10 ç¼–码,作为新增/更新匹配条件 |
| MOJIBZ | String | å¦ | æœ«çº§æ ‡å¿—,写入 `lastflag` |
| ZhongYiZDBZ | Long | å¦ | ä¸­åŒ»è¯Šæ–­æ ‡å¿—,写入 `chimedflag` |
| ShiJianDM | String | å¦ | äº‹ä»¶ä»£ç ï¼ˆå½“前未参与判断,存在即更新、不存在即新增) |
| å…¶ä»–字段 | - | å¦ | å½“前未使用 |
> ç¼–码不区分新增/修改:按 `IcD10 + æœºæž„` æŸ¥è¯¢ï¼Œå­˜åœ¨åˆ™æ›´æ–°ï¼Œä¸å­˜åœ¨åˆ™æ–°å¢žã€‚
### 5.3 å…¥é™¢ã€å…¥ç§‘、取消入院
#### 5.3.1 `JZ_ZY_RuYuanDJ` å…¥é™¢ç™»è®°
`YeWuXX` ç»“构:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| XiaoXiID | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | å“åº”回显 |
| BingRenXX | object | æ˜¯ | æ‚£è€…信息,见 4.1 |
| DiZhiXXList | array | æ˜¯ | åœ°å€åˆ—表,**代码未做空判断,不能为空** |
| LianXiRList | array | æ˜¯ | è”系人列表,**不能为空** |
| ZhenDuanList | array | æ˜¯ | è¯Šæ–­åˆ—表,**不能为空**,取第一条写入入院诊断 |
处理结果:
- æŒ‰è¯ä»¶å·ï¼ˆæˆ–就诊卡号)新增/更新患者档案 `pat_archive`;
- æŒ‰ `BingAnHao + JiuZhenYWID` æ–°å¢ž/更新住院记录 `pat_med_inhosp`;
- ä½é™¢çŠ¶æ€ `inhospstate=0`(在院)。
请求示例:
```json
{
  "XiaoXiTou": {
    "FaSongJGID": "47255004333112711A1001",
    "FaSongJGMC": "丽水市中医院",
    "FaSongSJ": "2026-10-20 10:00:00",
    "XiaoXiLX": "JZ_ZY_RuYuanDJ",
    "XiaoXiID": "MSG-20261020-0001"
  },
  "YeWuXX": {
    "XiaoXiID": "MSG-20261020-0001",
    "BingRenXX": {
      "ZuZhiJGID": "47255004333112711A1001",
      "ZuZhiJGMC": "丽水市中医院",
      "BingRenID": "BR0001",
      "XingMing": "张三",
      "JiuZhenYWID": "JZ20261020001",
      "JiuZhenKH": "MZ0001",
      "BingAnHao": "BA20261020001",
      "ZhengJianLXMC": "居民身份证",
      "ZhengJianHM": "330102199001011234",
      "XingBieDM": "1",
      "XingBieMC": "男",
      "ChuShengRQ": "1990-01-01 08:00:00",
      "DangQianKSID": "D001",
      "DangQianKSMC": "心血管内科",
      "DangQianBQID": "B001",
      "DangQianBQMC": "心内科病区",
      "DangQianCWMC": "12床",
      "RuYuanSJ": "2026-10-20 09:30:00",
      "LianXiDH": "13800000000",
      "CaoZuoRID": "U001",
      "CaoZuoRXM": "李医生",
      "ZhuZhiYSID": "D1001",
      "ZhuZhiYSXM": "王医生"
    },
    "DiZhiXXList": [
      {
        "DiZhiLX": "1",
        "DiZhiXX": "浙江省丽水市莲都区XXè·¯1号",
        "ShengFenMC": "浙江省",
        "ShiDiQMC": "丽水市",
        "XianQuMC": "莲都区"
      }
    ],
    "LianXiRList": [
      {
        "LianXiRXM": "李四",
        "LianXiRDH": "13900000000",
        "GuanXiMC": "配偶"
      }
    ],
    "ZhenDuanList": [
      {
        "ZhenDuanID": "I10",
        "ZhenDuanMC": "原发性高血压",
        "Icd10": "I10"
      }
    ]
  }
}
```
#### 5.3.2 `JZ_ZY_RuKe` å…¥ç§‘
`YeWuXX` ç»“构:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| XiaoXiID | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | å“åº”回显 |
| BingRenXX | object | æ˜¯ | æ‚£è€…信息;其中 `DiZhiXXList` **不能为空** |
| RuKeXX | object | æ˜¯ | å…¥ç§‘信息,见 4.6 |
| JiuZhenXX | object | å¦ | ä»£ç è¯»å–但未使用,可忽略 |
处理结果:用 `RuKeXX` ä¸­çš„主治医生、责任护士、入院时间覆盖患者信息,然后新增/更新档案与住院记录。
#### 5.3.3 `JZ_ZY_QuXiaoRY` å–消入院登记
`YeWuXX` ç»“构:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| XiaoXiID | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | å“åº”回显 |
| BingRenXX | object | æ˜¯ | è‡³å°‘包含 `BingAnHao`、`JiuZhenYWID` |
| JiuZhenXX | object | å¦ | å– `CaoZuoRID`、`CaoZuoRXM` è®°å½•操作人 |
处理结果:按 `BingAnHao + JiuZhenYWID` æ‰¾åˆ°ä½é™¢è®°å½•,置 `schemestatus=3`、`del_flag=1`。
### 5.4 å‡ºé™¢ã€å‡ºé™¢è®°å½•、预出院、取消出院、离抢
#### 5.4.1 `JZ_ZY_ChuYuan` æ‚£è€…出院
`YeWuXX` ç»“构:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| XiaoXiID | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | å“åº”回显 |
| BingRenXX | object | æ˜¯ | æ‚£è€…信息,见 4.1 |
| JiuZhenXX | object | æ˜¯ | **必须包含 `ChuYuanSJ`**(出院时间);另取操作人、主治医生、责任护士、床位、经管医生、`LiYuanQXDM` |
| DiZhiXXList | array | å¦ | åœ°å€åˆ—表,为空不报错 |
| LianXiRList | array | å¦ | è”系人列表,为空不报错 |
| ChuYuanJL | object | å¦ | å‡ºé™¢è®°å½•,取 `SuiFangJH` éšè®¿è®¡åˆ’ |
| BingRenKZXXList | array | å¦ | æ‚£è€…扩展信息,`XiangMuDM=DuanHaoXX` æ—¶è¦†ç›–短号 |
| ZhenDuanList | array | å¦ | è¯Šæ–­åˆ—表,取第一条写入出院诊断、ICD10 |
处理结果:
- æŒ‰ `BingAnHao + JiuZhenYWID` æŸ¥æ‰¾ä½é™¢è®°å½•,存在则更新,不存在则新增;
- `schemestatus=2`、`inhospstate=1`(出院);
- å†™å…¥å‡ºé™¢æ—¶é—´ã€å‡ºé™¢ç§‘室/病区、出院诊断;
- `LiYuanQXDM=5`(死亡)时:`fuflag=0`,患者档案标记 `notrequiredFlag=1`、原因“患者已离世”,并终止已生成的随访任务(`sendstate=4`);
- æœºæž„在 `noLongTask` é…ç½®ä¸­æ—¶ï¼Œ`fuflag=0`(不做长期随访)。
#### 5.4.2 `BL_JL_ChuYuanJL` æ‚£è€…出院记录
`YeWuXX` ç»“构:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| XiaoXiID | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | å“åº”回显 |
| BingRenXX | object | æ˜¯ | æ‚£è€…信息,见 4.1 |
| ChuYuanJL | object | æ˜¯ï¼ˆå»ºè®®ï¼‰ | å– `SuiFangJH` éšè®¿è®¡åˆ’ |
| DiZhiXXList | array | å¦ | åœ°å€åˆ—表 |
| LianXiRList | array | å¦ | è”系人列表 |
| ZhenDuanList | array | å¦ | è¯Šæ–­åˆ—表,取第一条写入出院诊断、ICD10 |
处理结果与 `JZ_ZY_ChuYuan` ç±»ä¼¼ï¼Œä½†**不读取 `JiuZhenXX`**、不修改在院状态、也不做死亡标记处理;仅更新住院记录的出院信息(`schemestatus=2`)与随访计划。
#### 5.4.3 `JZ_ZY_YuChuYuan` æ‚£è€…预出院
`YeWuXX` ç»“构:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| XiaoXiID | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | å“åº”回显 |
| BingRenXX | object | æ˜¯ | æ‚£è€…信息,见 4.1 |
| JiuZhenXX | object | æ˜¯ | **必须包含 `YuChuYSJ`、`LiYuanQXDM`**(代码未做空判断) |
| ChuYuanJL | object | å¦ | å– `SuiFangJH` éšè®¿è®¡åˆ’ |
| DiZhiXXList | array | å¦ | åœ°å€åˆ—表 |
| LianXiRList | array | å¦ | è”系人列表 |
| ZhenDuanList | array | å¦ | è¯Šæ–­åˆ—表 |
处理结果:`schemestatus=2`、`inhospstate=3`(预出院),写入预出院时间、诊断、随访计划;`LiYuanQXDM=5` æ—¶æ‰§è¡Œæ­»äº¡æ‚£è€…处理。
#### 5.4.4 `JZ_ZY_QuXiaoCY` å–消出院
`YeWuXX` ç»“构:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| XiaoXiID | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | å“åº”回显 |
| BingRenXX | object | æ˜¯ | è‡³å°‘包含 `BingAnHao`、`JiuZhenYWID` |
| JiuZhenXX | object | å¦ | å– `CaoZuoRID`、`CaoZuoRXM` |
处理结果:
- ä½é™¢è®°å½• `schemestatus=4`、`inhospstate=0`(恢复在院);
- åˆ é™¤è¯¥æ‚£è€… `sendstate=1/2` çš„随访子任务,并清理 Redis ç¼“存中的任务 ID。
#### 5.4.5 `JZ_ZY_LiQiang` æ‚£è€…离抢
`YeWuXX` ç»“构:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| XiaoXiID | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | å“åº”回显 |
| BingRenXX | object | æ˜¯ | è‡³å°‘包含 `BingAnHao`、`JiuZhenYWID` |
| JiuZhenXX | object | æ˜¯ | å– `LiQiangSJ`(离抢时间)、`LiYuanQXDM` |
处理结果:把 `LiQiangSJ` å›žå¡«åˆ°ä½é™¢è®°å½•出院时间,`inhospstate=1`;`LiYuanQXDM=5` æ—¶æ‰§è¡Œæ­»äº¡æ‚£è€…处理。找不到住院记录时返回失败。
### 5.5 `JZ_MZ_WanChengJZ` é—¨æ€¥è¯Šå®ŒæˆæŽ¥è¯Š
`YeWuXX` ç»“构:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| XiaoXiID | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | å“åº”回显 |
| BingRenXX | object | æ˜¯ | æ‚£è€…信息;`ZuZhiJGID` å¯çœç•¥ï¼Œä»£ç ä½¿ç”¨æ¶ˆæ¯å¤´ `FaSongJGID` |
| JiuZhenXX | object | æ˜¯ | è‡³å°‘包含 `JiuZhenKSID`、`JiuZhenKSMC`、`JiuZhenRQ` |
| ZhenDuanList | array | æ˜¯ | **不能为空**;`ZhenDuanLBMC=主诊断` çš„记录用于主诊断 |
| WenZhenList | array | æ˜¯ | **不能为空**;用于现病史、主诉、随访天数、随访天数单位 |
处理结果:
- æ–°å¢ž/更新患者档案;
- æŒ‰ `BingAnHao + JiuZhenYWID` æ–°å¢ž/更新门诊就诊记录 `pat_med_outhosp`,`admitdate` å– `JiuZhenRQ`;
- ä¸»è¯Šæ–­è®°å½•写入 `diagname`、`drcode`、`drname`、`icd10code`,并置 `fuflag=1`;
- `WenZhenList` ä¸­ `现病史`→`hpi`、`主诉`→`mainsuit`;
- è‹¥ä¼ äº† `随访天数`(且单位为天/周/月/年)并且 `fuflag=1`,自动生成/处理随访任务;
- ä¸½æ°´å¸‚妇幼保健院(`orgid = 47226079133110211G1001`)完成接诊后,按 `pat_specific_archive_generate_config` é…ç½®ç”Ÿæˆå¦‡ç§‘/儿科专科档案;其他机构不生成。详见 `doc/api/妇幼专科档案生成接口文档_20261104.md`。
请求示例:
```json
{
  "XiaoXiTou": {
    "FaSongJGID": "47255004333112711A1001",
    "FaSongJGMC": "丽水市中医院",
    "FaSongSJ": "2026-10-20 11:00:00",
    "XiaoXiLX": "JZ_MZ_WanChengJZ",
    "XiaoXiID": "MSG-20261020-0002"
  },
  "YeWuXX": {
    "XiaoXiID": "MSG-20261020-0002",
    "BingRenXX": {
      "BingRenID": "BR0002",
      "XingMing": "李四",
      "JiuZhenYWID": "MZ20261020002",
      "JiuZhenKH": "MZ0002",
      "BingAnHao": "BA20261020002",
      "ZhengJianHM": "330102198505051234",
      "XingBieDM": "2",
      "ChuShengRQ": "1985-05-05 10:00:00",
      "LianXiDH": "13700000000"
    },
    "JiuZhenXX": {
      "JiuZhenKSID": "D002",
      "JiuZhenKSMC": "妇科",
      "JiuZhenRQ": "2026-10-20 10:30:00",
      "sfrq": "2026-11-20 00:00:00"
    },
    "ZhenDuanList": [
      {
        "ZhenDuanYSID": "D1002",
        "ZhenDuanYSXM": "赵医生",
        "ZhenDuanID": "O20.0",
        "ZhenDuanMC": "先兆流产",
        "ZhenDuanLBDM": "1",
        "ZhenDuanLBMC": "主诊断",
        "Icd10": "O20.0"
      }
    ],
    "WenZhenList": [
      { "WenZhenLXMC": "主诉", "NeiRong": "停经50天,阴道流血1天" },
      { "WenZhenLXMC": "现病史", "NeiRong": "患者停经50天……" },
      { "WenZhenLXMC": "随访天数", "NeiRong": "30" },
      { "WenZhenLXMC": "随访天数单位", "NeiRong": "天" }
    ]
  }
}
```
### 5.6 æ£€æŸ¥/检验申请与修改
适用类型:`JC_SQ_ShenQing`、`JC_SQ_XiuGai`、`JY_SQ_ShenQing`、`JY_SQ_XiuGai`。
`YeWuXX` ç»“构:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| XiaoXiID | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | å“åº”回显 |
| BingRenXX | object | æ˜¯ | æ‚£è€…信息;`ZuZhiJGID` ä¼šè¢«æ¶ˆæ¯å¤´ `FaSongJGID` è¦†ç›– |
| JianChaSQ | object | æ˜¯ | æ£€æŸ¥/检验申请信息,见下表 |
| JianChaBG | object | å¦ | æŠ¥å‘Šä¿¡æ¯ï¼Œæ­¤å¤„仅使用 `ShenQingDID` å›žå¡«ç”³è¯·å•号 |
`JianChaSQ` å­—段:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| YiZhuID | String | æ˜¯ | åŒ»å˜± ID,代码执行 `Long.valueOf`,必须为数字 |
| ShenQingDID | String | æ˜¯ | ç”³è¯·å• ID,作为检查记录匹配键 |
| YouXianJi | String | å¦ | ä¼˜å…ˆçº§ |
| KaiDanRID | String | å¦ | å¼€å•人 ID |
| KaiDanRXM | String | å¦ | å¼€å•人姓名 |
| KaiDanKSID | String | å¦ | å¼€å•科室 ID |
| KaiDanKSMC | String | å¦ | å¼€å•科室名称 |
| KaiDanSJ | String | å¦ | å¼€å•时间,`yyyy-MM-dd HH:mm:ss` |
| ShenHeRID | String | å¦ | å®¡æ ¸äºº ID |
| ShenHeRXM | String | å¦ | å®¡æ ¸äººå§“名 |
| ShenHeSJ | String | å¦ | å®¡æ ¸æ—¶é—´ï¼Œ`yyyy-MM-dd HH:mm:ss` |
| ZhiXingSJ | String | å¦ | æ‰§è¡Œæ—¶é—´ï¼Œ`yyyy-MM-dd HH:mm:ss` |
| JianChaLXID | String | å¦ | æ£€æŸ¥ç±»åž‹ ID |
| JianChaLXMC | String | å¦ | æ£€æŸ¥ç±»åž‹åç§° |
| JinE | String | æ˜¯ | é‡‘额,代码执行 `Float.valueOf`,必须为数字 |
| JiaoPianFBZ | String | æ˜¯ | äº¤ç‰‡è´¹æ ‡å¿—,`0/1`,代码执行 `Integer.valueOf` |
| ChuangBianBZ | String | æ˜¯ | åºŠè¾¹æ ‡å¿—,`0/1` |
| TuiChuangBZ | String | æ˜¯ | æŽ¨åºŠæ ‡å¿—,`0/1` |
| JiaJiBZ | String | æ˜¯ | åŠ æ€¥æ ‡å¿—ï¼Œ`0/1` |
| WeiShouXZXBZ | String | æ˜¯ | æœªæ”¶å…ˆæ‰§è¡Œæ ‡å¿—,`0/1` |
| JianChaBWList | array | å¦ | æ£€æŸ¥éƒ¨ä½åˆ—表;为空则不写项目/部位 |
| ZhenDuanList | array | å¦ | è¯Šæ–­åˆ—表,取第一条写入诊断 ID/名称 |
`JianChaBWList` å…ƒç´ ï¼š
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| JianChaXMID | String | å¦ | æ£€æŸ¥é¡¹ç›® ID |
| JianChaXMMC | String | å¦ | æ£€æŸ¥é¡¹ç›®åç§° |
| JianChaBWID | String | å¦ | æ£€æŸ¥éƒ¨ä½ ID |
| JianChaBWMC | String | å¦ | æ£€æŸ¥éƒ¨ä½åç§° |
`ZhenDuanList` å…ƒç´ ï¼š
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| ZhenDuanID | String | å¦ | è¯Šæ–­ ID |
| ZhenDuanMC | String | å¦ | è¯Šæ–­åç§° |
处理结果:
- æŒ‰ `ShenQingDID`(`appliyid`)查询检查记录,存在则更新,不存在则新增;
- æ£€æŸ¥é¡¹ç›®ã€éƒ¨ä½å– `JianChaBWList` ç¬¬ä¸€æ¡ï¼›
- `JY_SQ_*` ä¸Ž `JC_SQ_*` å…±ç”¨åŒä¸€å¼ è¡¨ï¼Œä»…类型不同。
请求示例:
```json
{
  "XiaoXiTou": {
    "FaSongJGID": "47255004333112711A1001",
    "FaSongJGMC": "丽水市中医院",
    "FaSongSJ": "2026-10-20 12:00:00",
    "XiaoXiLX": "JC_SQ_ShenQing",
    "XiaoXiID": "MSG-20261020-0003"
  },
  "YeWuXX": {
    "XiaoXiID": "MSG-20261020-0003",
    "BingRenXX": {
      "ZuZhiJGID": "47255004333112711A1001",
      "BingRenID": "BR0001",
      "XingMing": "张三",
      "JiuZhenYWID": "JZ20261020001",
      "BingAnHao": "BA20261020001",
      "ZhengJianHM": "330102199001011234",
      "LianXiDH": "13800000000"
    },
    "JianChaSQ": {
      "YiZhuID": "100001",
      "ShenQingDID": "SQ20261020001",
      "YouXianJi": "1",
      "KaiDanRID": "D1001",
      "KaiDanRXM": "王医生",
      "KaiDanKSID": "D001",
      "KaiDanKSMC": "心血管内科",
      "KaiDanSJ": "2026-10-20 11:30:00",
      "ShenHeRID": "D1002",
      "ShenHeRXM": "赵医生",
      "ShenHeSJ": "2026-10-20 11:40:00",
      "ZhiXingSJ": "2026-10-20 13:00:00",
      "JianChaLXID": "CT",
      "JianChaLXMC": "CT检查",
      "JinE": "280.00",
      "JiaoPianFBZ": "0",
      "ChuangBianBZ": "0",
      "TuiChuangBZ": "0",
      "JiaJiBZ": "0",
      "WeiShouXZXBZ": "0",
      "JianChaBWList": [
        {
          "JianChaXMID": "CT001",
          "JianChaXMMC": "胸部CT",
          "JianChaBWID": "BW001",
          "JianChaBWMC": "胸部"
        }
      ],
      "ZhenDuanList": [
        { "ZhenDuanID": "I10", "ZhenDuanMC": "原发性高血压" }
      ]
    }
  }
}
```
### 5.7 `JC_BG_BaoGao` æ£€æŸ¥æŠ¥å‘Š
`YeWuXX` ç»“构:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| XiaoXiID | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | å“åº”回显 |
| BingRenXX | object | æ˜¯ | è‡³å°‘包含 `ZuZhiJGID`、`BingAnHao`、`BingRenID`、`XingMing` |
| JianChaBG | object | æ˜¯ | æ£€æŸ¥æŠ¥å‘Šä¿¡æ¯ï¼Œè§ä¸‹è¡¨ |
`JianChaBG` å­—段:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| ShenQingDID | String | æ˜¯ | ç”³è¯·å• ID,用于查找检查记录;找不到时会先调用申请逻辑补建 |
| JianChaHao | String | æ˜¯ | æ£€æŸ¥å·ï¼Œå†™å…¥ `inspection_name` |
| DangQianZTDM | String | æ˜¯ | å½“前状态代码 |
| DangQianZTMC | String | æ˜¯ | å½“前状态名称 |
| BaoGaoRID | String | æ˜¯ | æŠ¥å‘Šäºº ID;注意代码最终把它写入 `reportid` |
| BaoGaoRXM | String | æ˜¯ | æŠ¥å‘Šäººå§“名 |
| BaoGaoSJ | String | å¦ | æŠ¥å‘Šæ—¶é—´ï¼Œ`yyyy-MM-dd HH:mm:ss` |
| ZhenDuanYX | String | æ˜¯ | è¯Šæ–­å°è±¡ |
| ZhenDuanSJ | String | æ˜¯ | è¯Šæ–­æ‰€è§ |
| ZhenDuanJY | String | æ˜¯ | è¯Šæ–­å»ºè®® |
| BaoGaoYXDZ | String | æ˜¯ | æŠ¥å‘Šå½±åƒåœ°å€ |
| WeiJiZBZ | String | æ˜¯ | å±æ€¥å€¼æ ‡å¿—,必须为数字 |
| WeiJiZNR | String | æ˜¯ | å±æ€¥å€¼å†…容 |
| BaoGaoKSID | String | æ˜¯ | æŠ¥å‘Šç§‘室 ID |
| BaoGaoKSMC | String | æ˜¯ | æŠ¥å‘Šç§‘室名称 |
| JianChaJGDM | String | æ˜¯ | æ£€æŸ¥ç»“果代码 |
| JianChaJGMC | String | æ˜¯ | æ£€æŸ¥ç»“果名称 |
> ä»£ç å¯¹ä¸Šè¿°å­—符串字段大量直接调用 `.toString()`,缺失会抛空指针导致返回失败,请务必传齐。
处理结果:按 `ShenQingDID` æ‰¾åˆ°æ£€æŸ¥ä¸»è®°å½•,新增或更新 `pat_med_inspection_item` æŠ¥å‘Šæ˜Žç»†ã€‚
### 5.8 æ‰‹æœ¯ç”³è¯·ã€æ’¤é”€
#### 5.8.1 `SM_SQ_ShenQing` æ‰‹æœ¯ç”³è¯·
`YeWuXX` ç»“构:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| XiaoXiID | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | å“åº”回显 |
| BingRenXX | object | æ˜¯ | æ‚£è€…信息,见 4.1 |
| ShouShuSQ | object | æ˜¯ | æ‰‹æœ¯ç”³è¯·ä¿¡æ¯ï¼Œè§ä¸‹è¡¨ |
`ShouShuSQ` å­—段:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| ShenQingDID | String | æ˜¯ | ç”³è¯·å• ID,写入 `opreqcode`,作为手术记录匹配键 |
| ShouShuLBDM | String | å¦ | æ‰‹æœ¯ç±»åˆ«ä»£ç ï¼Œå†™å…¥ `optypecode` |
| ShouShuLBMC | String | å¦ | æ‰‹æœ¯ç±»åˆ«åç§°ï¼Œå†™å…¥ `optypename` |
| YaoQiuSJ | Date | å¦ | è¦æ±‚时间,写入 `operatortime` |
| ShenQingSJ | Date | å¦ | ç”³è¯·æ—¶é—´ï¼Œå†™å…¥ `opreqtime` |
| ShenQingYSID | String | å¦ | ç”³è¯·åŒ»ç”Ÿ ID |
| ShenQingYSXM | String | å¦ | ç”³è¯·åŒ»ç”Ÿå§“名 |
| ShenQingKSID | String | å¦ | ç”³è¯·ç§‘室 ID |
| ShenQingKSMC | String | å¦ | ç”³è¯·ç§‘室名称 |
| ShouShuKSID | String | å¦ | æ‰‹æœ¯ç§‘室 ID |
| ShouShuKSMC | String | å¦ | æ‰‹æœ¯ç§‘室名称 |
| ZhuDaoYSID | String | å¦ | ä¸»åˆ€åŒ»ç”Ÿ ID |
| ZhuDaoYSXM | String | å¦ | ä¸»åˆ€åŒ»ç”Ÿå§“名 |
| YiZhuID | String | å¦ | åŒ»å˜± ID(代码映射到 `firasscode`) |
| YiZhuMC | String | å¦ | åŒ»å˜±åç§°ï¼ˆä»£ç æ˜ å°„到 `firassname`) |
| ErZhuYSID | String | å¦ | äºŒåŠ©åŒ»ç”Ÿ ID |
| ErZhuYSXM | String | å¦ | äºŒåŠ©åŒ»ç”Ÿå§“å |
| SanZhuYSID | String | å¦ | ä¸‰åŠ©åŒ»ç”Ÿ ID |
| SanZhuYSXM | String | å¦ | ä¸‰åŠ©åŒ»ç”Ÿå§“å |
| ShouShuMXList | array | æ˜¯ | æ‰‹æœ¯æ˜Žç»†ï¼Œ**代码未做空判断**,见下表 |
`ShouShuMXList` å…ƒç´ ï¼š
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| ShouShuMCID | String | å¦ | æ‰‹æœ¯åç§° ID |
| ShouShuMC | String | å¦ | æ‰‹æœ¯åç§° |
| ZhuShouSBZ | String | æ˜¯ | ä¸»æ‰‹æœ¯æ ‡å¿—,`1` è¡¨ç¤ºä¸»æ‰‹æœ¯ï¼›ä»£ç ç›´æŽ¥è°ƒç”¨ `.equals`,**不能为空** |
| QieKouLBDM | String | å¦ | åˆ‡å£ç±»åˆ«ä»£ç  |
| QieKouLBMC | String | å¦ | åˆ‡å£ç±»åˆ«åç§° |
| ShouShuJBDM | String | å¦ | æ‰‹æœ¯çº§åˆ«ä»£ç  |
| ShouShuJBMC | String | å¦ | æ‰‹æœ¯çº§åˆ«åç§° |
| ShouShuBW | String | å¦ | æ‰‹æœ¯éƒ¨ä½ |
| BiaoZhunBM | String | å¦ | æ ‡å‡†ç¼–码 |
| ShouShuMCQZ | String | å¦ | æ‰‹æœ¯åç§°å‰ç¼€ |
| ShouShuMCHZ | String | å¦ | æ‰‹æœ¯åç§°åŽç¼€ |
处理结果:
- æŒ‰ `patid + ShenQingDID` æŸ¥è¯¢æ‰‹æœ¯è®°å½•,存在则更新,不存在则新增;
- ä¸»æ‰‹æœ¯ï¼ˆ`ZhuShouSBZ=1`)信息回写手术主表,其余写入 `pat_med_operation_item`;
- å·²å­˜åœ¨çš„æ‰‹æœ¯ä¼šå…ˆç‰©ç†åˆ é™¤æ—§æ˜Žç»†å†é‡å»ºã€‚
请求示例:
```json
{
  "XiaoXiTou": {
    "FaSongJGID": "47255004333112711A1001",
    "FaSongJGMC": "丽水市中医院",
    "FaSongSJ": "2026-10-20 14:00:00",
    "XiaoXiLX": "SM_SQ_ShenQing",
    "XiaoXiID": "MSG-20261020-0004"
  },
  "YeWuXX": {
    "XiaoXiID": "MSG-20261020-0004",
    "BingRenXX": {
      "ZuZhiJGID": "47255004333112711A1001",
      "BingRenID": "BR0001",
      "XingMing": "张三",
      "JiuZhenYWID": "JZ20261020001",
      "BingAnHao": "BA20261020001",
      "DangQianKSID": "D001",
      "DangQianKSMC": "心血管内科",
      "DangQianBQID": "B001",
      "DangQianBQMC": "心内科病区"
    },
    "ShouShuSQ": {
      "ShenQingDID": "SS20261020001",
      "ShouShuLBDM": "1",
      "ShouShuLBMC": "择期手术",
      "YaoQiuSJ": "2026-10-21 09:00:00",
      "ShenQingSJ": "2026-10-20 13:30:00",
      "ShenQingYSID": "D1001",
      "ShenQingYSXM": "王医生",
      "ShenQingKSID": "D001",
      "ShenQingKSMC": "心血管内科",
      "ShouShuKSID": "D003",
      "ShouShuKSMC": "手术室",
      "ZhuDaoYSID": "D1001",
      "ZhuDaoYSXM": "王医生",
      "ShouShuMXList": [
        {
          "ShouShuMCID": "OP001",
          "ShouShuMC": "冠状动脉造影术",
          "ZhuShouSBZ": "1",
          "QieKouLBDM": "1",
          "QieKouLBMC": "Ⅰ类切口",
          "ShouShuJBDM": "3",
          "ShouShuJBMC": "三级手术",
          "ShouShuBW": "心脏"
        }
      ]
    }
  }
}
```
#### 5.8.2 `SM_SQ_CheXiao` æ‰‹æœ¯æ’¤é”€
`YeWuXX` ç»“构:
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| XiaoXiID | String | æ˜¯ï¼ˆå»ºè®®ï¼‰ | å“åº”回显 |
| BingRenXX | object | æ˜¯ | è‡³å°‘包含 `BingAnHao`、`JiuZhenYWID` |
| ShouShuCX | object | æ˜¯ | æ’¤é”€ä¿¡æ¯ï¼Œ**至少包含 `ShenQingDID`**,其余字段当前未使用 |
处理结果:
- æŒ‰ `patid + ShenQingDID` æ‰¾åˆ°æ‰‹æœ¯è®°å½•,置 `checkFlag=1`、`delFlag=1`;
- è½¯åˆ é™¤æ‰‹æœ¯æ˜Žç»†ï¼›
- ç»ˆæ­¢è¯¥æ‰‹æœ¯å…³è”的随访子任务(`sendstate=4`,备注“手术撤销”)并清理 Redis ç¼“存。
> `ShouShuGX`(手术更新)虽在 `addOperationInfo` ä¸­é¢„留,但当前没有 `XiaoXiLX` è·¯ç”±åˆ°è¯¥åˆ†æ”¯ï¼Œå®žé™…不会生效。
---
## å…­ã€å“åº”说明
### 6.1 æˆåŠŸå“åº”
```json
{
  "Code": "0",
  "Message": "success",
  "Data": {
    "XiaoXiID": "MSG-20261020-0001",
    "ChuangJianSJ": 1760928000000
  }
}
```
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
| --- | --- | --- |
| Code | String | `"0"` æˆåŠŸï¼Œ`"500"` å¤±è´¥ |
| Message | String | `success` / `error` |
| Data.XiaoXiID | String | åŽŸæ ·è¿”å›žè¯·æ±‚ `YeWuXX.XiaoXiID` |
| Data.ChuangJianSJ | long | æœåŠ¡å™¨å½“å‰æ—¶é—´ï¼ˆJSON åºåˆ—化为毫秒时间戳) |
### 6.2 å¤±è´¥å“åº”
```json
{
  "Code": "500",
  "Message": "error",
  "Data": {
    "XiaoXiID": "MSG-20261020-0001",
    "ChuangJianSJ": 1760928000000
  }
}
```
### 6.3 å¤±è´¥åˆ¤å®šè§„则
- ä¸šåŠ¡æ–¹æ³•è¿”å›ž `false`(如找不到对应住院记录、用户/科室不存在、数据库操作影响行数不为 1)→ `Code="500"`;
- ä¸šåŠ¡æ–¹æ³•æŠ›å‡ºå¼‚å¸¸ï¼ˆå­—æ®µç¼ºå¤±ã€ç±»åž‹è½¬æ¢å¤±è´¥ã€ç©ºæŒ‡é’ˆç­‰ï¼‰â†’ æŽ§åˆ¶å™¨æ•获并记录日志,返回 `Code="500"`;
- `XiaoXiTou` ç¼ºå¤±ã€`XiaoXiLX` ä¸ºç©ºã€`YeWuXX` ç¼ºå¤±ç­‰æŠ¥æ–‡çº§é”™è¯¯ï¼Œå¯èƒ½åœ¨å“åº”组装阶段直接抛异常,返回 HTTP 500。建议调用方严格按本文档组装报文。
---
## ä¸ƒã€è°ƒç”¨æ³¨æ„äº‹é¡¹
1. **字段名大小写敏感**:所有字段均为拼音首字母大写格式,如 `BingRenXX`、`JiuZhenYWID`,不能写成下划线或全小写。
2. **唯一业务键**:
   - ä½é™¢ç±»æ¶ˆæ¯ä½¿ç”¨ `BingAnHao + JiuZhenYWID` å®šä½ä½é™¢è®°å½•ï¼›
   - é—¨è¯ŠæŽ¥è¯Šä½¿ç”¨ `BingAnHao + JiuZhenYWID` å®šä½é—¨è¯Šè®°å½•ï¼›
   - æ‚£è€…档案使用 `ZhengJianHM`(为空时用 `JiuZhenKH`)+ æœºæž„定位;
   - æ£€æŸ¥ä½¿ç”¨ `ShenQingDID`;
   - æ‰‹æœ¯ä½¿ç”¨ `ShenQingDID`;
   - æœºæž„/科室/用户/病区使用各自 ID + æœºæž„。
3. **重复推送**:代码按上述业务键查询后执行“存在则更新、不存在则新增”,重复推送一般不会重复插入;但若业务键变化,会产生新记录。
4. **必传列表**:以下消息对列表字段没有空判断,缺失会直接报错:
   - `JZ_ZY_RuYuanDJ`:`DiZhiXXList`、`LianXiRList`、`ZhenDuanList`;
   - `JZ_ZY_RuKe`:`BingRenXX.DiZhiXXList`;
   - `JZ_MZ_WanChengJZ`:`ZhenDuanList`、`WenZhenList`;
   - `SM_SQ_ShenQing`:`ShouShuSQ.ShouShuMXList`,且每条明细 `ZhuShouSBZ` ä¸èƒ½ä¸ºç©ºã€‚
5. **日期格式**:除特别说明外,日期时间统一使用 `yyyy-MM-dd HH:mm:ss`;`BingRenXX.ChuShengRQ` ä»£ç å†…部使用 `yyyy-MM-dd hh:mm:ss` è§£æžï¼Œå»ºè®®åŒæ ·ä¼ å®Œæ•´æ—¶é—´ï¼Œä¸”尽量使用 `08:00:00` è¿™ç±» 12 å°æ—¶åˆ¶å¯è§£æžçš„æ—¶é—´ã€‚
6. **数字字段**:`BingRenXX.XingBieDM`、`JianChaSQ.YiZhuID`、`JinE`、各类 `*BZ` æ ‡å¿—、`JC_BG_BaoGao.WeiJiZBZ` ç­‰ä¼šè¢«ç›´æŽ¥è½¬æ¢ä¸º Long/Float/Integer,必须传数字字符串。
7. **机构与院区**:
   - `FaSongJGID` æ˜¯æœºæž„过滤与落库 `orgid` çš„主要来源;
   - é™¢åŒºå­—段当前统一写死为 `"1"`(丽水无院区概念);
   - `JZ_MZ_WanChengJZ` ä½¿ç”¨ `FaSongJGID` è¦†ç›–患者信息中的机构 ID。
8. **死亡患者处理**:`LiYuanQXDM=5` æ—¶ï¼Œç³»ç»Ÿä¼šå–消患者后续随访(`fuflag=0`)、标记档案无需随访、终止已生成任务;请确保离院方式代码准确。
9. **不随访机构**:配置项 `noLongTask` ä¸­çš„æœºæž„,出院/接诊时 `fuflag` ä¼šè¢«ç½®ä¸º `0`。
10. **消息类型白名单**:未在第三章列出的 `XiaoXiLX` ä¼šç›´æŽ¥è¿”回成功但不做任何处理,联调时请先核对类型拼写。
11. **接口无鉴权**:该地址对外网开放时存在数据写入风险,建议通过网关/防火墙限制来源 IP,或在网关层增加签名校验。
---
## å…«ã€å¿«é€ŸæŽ’查
| çŽ°è±¡ | å¯èƒ½åŽŸå›  |
| --- | --- |
| è¿”回 `Code=500` ä¸”日志提示空指针 | ç¼ºå°‘ `XiaoXiTou`、`XiaoXiLX`、`BingRenXX`、`JiuZhenXX` æˆ–必传列表 |
| è¿”回 `Code=500` ä¸”日志提示 `NumberFormatException` | `XingBieDM`、`YiZhuID`、`JinE`、`*BZ` ç­‰å­—段传了非数字 |
| è¿”回 `Code=500` ä¸”日志提示 `ParseException` | æ—¥æœŸä¸æ˜¯ `yyyy-MM-dd HH:mm:ss` |
| è¿”回成功但数据未更新 | `XiaoXiLX` æ‹¼å†™é”™è¯¯ï¼Œèµ°äº†é»˜è®¤åˆ†æ”¯ï¼›æˆ–业务键(病案号/就诊业务 ID/申请单 ID)不匹配 |
| æ‚£è€…档案重复 | `ZhengJianHM`、`JiuZhenKH` ä¸ºç©ºï¼Œæˆ–不同机构下重复建档 |
| ä½é™¢è®°å½•重复 | `BingAnHao` æˆ– `JiuZhenYWID` å‘生变化 |
| å‡ºé™¢åŽä»åœ¨é™¢ | æœªä¼  `JiuZhenXX.ChuYuanSJ`,或 `LiYuanQXDM` éžæ³• |
| éšè®¿ä»»åŠ¡æœªç”Ÿæˆ | `SuiFangJH.SuiFangBS=0`、机构在 `noLongTask` ä¸­ã€`fuflag=0`,或 `JZ_MZ_WanChengJZ` æœªä¼ éšè®¿å¤©æ•° |
doc/api/¸¾Ó×ר¿Æµµ°¸Éú³É½Ó¿ÚÎĵµ_20261104.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,342 @@
# å¦‡å¹¼ä¸“科档案生成接口文档
| é¡¹ç›® | å†…容 |
| --- | --- |
| æ¨¡å— | æ™ºèƒ½éšè®¿ï¼ˆsmartor) |
| åŠŸèƒ½ | é—¨è¯ŠæŽ¥è¯Šå®Œæˆè®°å½•生成妇科/儿科专科档案 |
| é€‚用机构 | `orgid = 47226079133110211G1001`(丽水市妇幼保健院) |
| å…³è”表 | `pat_med_outhosp`、`pat_specific_archive_generate_config`、`pat_maternity_archive(_detail)`、`pat_child_archive(_detail)` |
| ç¼–写日期 | 2026-11-04 |
---
## ä¸€ã€åŠŸèƒ½è¯´æ˜Ž
1. HIS è°ƒç”¨å®ŒæˆæŽ¥è¯ŠæŽ¥å£ `JZ_MZ_WanChengJZ` åŽï¼Œç³»ç»Ÿå†™å…¥é—¨è¯Šè®°å½• `pat_med_outhosp`。
2. ç³»ç»Ÿè¯»å– `pat_specific_archive_generate_config` ä¸­ä¸½æ°´å¸‚妇幼保健院的配置。
3. æŒ‰é…ç½®åŒ¹é…é—¨è¯Šè®°å½•的科室 Code æˆ–主诊断 ICD10 Code:
   - å‘½ä¸­å¦‡ç§‘配置:写入 `pat_maternity_archive` / `pat_maternity_archive_detail`;
   - å‘½ä¸­å„¿ç§‘配置:写入 `pat_child_archive` / `pat_child_archive_detail`。
4. é™¤å®žæ—¶å›žè°ƒå¤–,还提供手动补采接口和定时任务入口。
> ä»… `orgid = 47226079133110211G1001` ç”Ÿæ•ˆï¼Œå…¶ä»–机构调用完成接诊接口不会生成妇幼专科档案。
---
## äºŒã€å®ŒæˆæŽ¥è¯Šå›žè°ƒæŽ¥å£
### 2.1 åŸºæœ¬ä¿¡æ¯
| é¡¹ç›® | å†…容 |
| --- | --- |
| æŽ¥å£åœ°å€ | `/smartor/serviceExternal/generalInterface` |
| è¯·æ±‚方式 | `POST` |
| Content-Type | `application/json;charset=UTF-8` |
| è®¤è¯æ–¹å¼ | æ— éœ€ç™»å½•(`/smartor/serviceExternal/**` å·²æ”¾è¡Œï¼‰ |
| æ¶ˆæ¯ç±»åž‹ | `JZ_MZ_WanChengJZ` |
| ä¸šåС含义 | é—¨æ€¥è¯Šå®ŒæˆæŽ¥è¯Š |
| å¤„理入口 | `ServiceExternalServiceImpl.addFinshJZInfo` |
### 2.2 æ¶ˆæ¯å¤´ `XiaoXiTou`
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| `XiaoXiLX` | String | æ˜¯ | å›ºå®šä¼  `JZ_MZ_WanChengJZ` |
| `XiaoXiID` | String | å»ºè®® | æ¶ˆæ¯ ID,响应原样返回 |
| `FaSongJGID` | String | æ˜¯ | æœºæž„ ID,写入 `orgid`;生成档案时必须是 `47226079133110211G1001` |
| `FaSongJGMC` | String | å¦ | æœºæž„名称 |
| `FaSongSJ` | String | å¦ | å‘送时间,`yyyy-MM-dd HH:mm:ss` |
### 2.3 ä¸šåŠ¡ä¿¡æ¯ `YeWuXX`
#### 2.3.1 æ‚£è€…信息 `BingRenXX`
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| `XingMing` | String | æ˜¯ | æ‚£è€…姓名,用于患者档案与专科档案 |
| `BingAnHao` | String | æ˜¯ | ç—…案号,写入门诊记录 `outhospno`,档案幂等键之一 |
| `JiuZhenYWID` | String | æ˜¯ | å°±è¯Šä¸šåŠ¡ ID,写入门诊记录 `serialnum` |
| `JiuZhenKH` | String | å¦ | å°±è¯Šå¡å· |
| `ZhengJianHM` | String | å»ºè®® | è¯ä»¶å·ç ï¼Œç”¨äºŽæ‚£è€…档案匹配 |
| `XingBieDM` | String | å¦ | æ€§åˆ«ä»£ç ï¼Œæ•°å­—字符串 |
| `ChuShengRQ` | String | å»ºè®® | å‡ºç”Ÿæ—¥æœŸï¼Œ`yyyy-MM-dd HH:mm:ss` |
| `LianXiDH` | String | å»ºè®® | è”系电话 |
| `XianZhuZXX` | String | å¦ | çŽ°ä½å€ |
> ä»£ç ä½¿ç”¨ `XingMing` å–姓名;入参中的 `BingRenXM` ä¸å‚与该字段赋值,但样例同时传了 `XingMing`。
#### 2.3.2 å°±è¯Šä¿¡æ¯ `JiuZhenXX`
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| `JiuZhenKSID` | String | æ˜¯ | å°±è¯Šç§‘室 ID,写入 `deptcode`,用于科室匹配 |
| `JiuZhenKSMC` | String | æ˜¯ | å°±è¯Šç§‘室名称,写入 `deptname` |
| `JiuZhenRQ` | String | æ˜¯ | å°±è¯Šæ—¥æœŸï¼Œ`yyyy-MM-dd HH:mm:ss`,写入 `admitdate`、档案 `diagTime` |
| `ChuZhenBZ` | Integer | å¦ | åˆè¯Šæ ‡å¿—,不参与档案生成 |
#### 2.3.3 è¯Šæ–­åˆ—表 `ZhenDuanList`
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| `ZhenDuanLBMC` | String | æ˜¯ | è¯Šæ–­ç±»åˆ«åç§°ï¼›ç­‰äºŽ `主诊断` çš„记录写入门诊主诊断和档案诊断 |
| `ZhenDuanMC` | String | æ˜¯ | è¯Šæ–­åç§° |
| `Icd10` | String | æ˜¯ | ICD10 ç¼–码,用于诊断匹配,写入 `icd10code` |
| `ZhenDuanYSID` | String | å¦ | è¯Šæ–­åŒ»ç”Ÿ ID |
| `ZhenDuanYSXM` | String | å¦ | è¯Šæ–­åŒ»ç”Ÿå§“名 |
> æ¡£æ¡ˆç”Ÿæˆåªä½¿ç”¨ `ZhenDuanLBMC = ä¸»è¯Šæ–­` çš„那一条诊断。子诊断不参与匹配。
#### 2.3.4 é—®è¯Šåˆ—表 `WenZhenList`
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| `WenZhenLXMC` | String | å¦ | `主诉`、`现病史`、`随访天数`、`随访天数单位` ç­‰ |
| `NeiRong` | String | å¦ | é—®è¯Šå†…容 |
> é—®è¯Šä¿¡æ¯ç”¨äºŽé—¨è¯Šè®°å½•的主诉、现病史和随访任务,不影响专科档案生成。
### 2.4 è¯·æ±‚示例
以下为“妇科-早孕关爱”样例(完整报文见 `D:\OneDrive\Desktop\移库\妇幼\妇科-早孕关爱.txt`):
```json
{
  "XiaoXiTou": {
    "XiaoXiLX": "JZ_MZ_WanChengJZ",
    "XiaoXiMC": "完成接诊",
    "XiaoXiID": "109b22c7-d6c6-49c3-adb1-cf0e6af9e9ef",
    "FaSongSJ": "2026-11-04 09:44:37",
    "FaSongJGID": "47226079133110211G1001",
    "FaSongJGMC": "丽水市妇幼保健院"
  },
  "YeWuXX": {
    "BingRenXX": {
      "BingRenID": "1000000002032043552",
      "XingMing": "张悦",
      "JiuZhenYWID": "1659700037955006464",
      "BingAnHao": "202637799",
      "ZhengJianHM": "332501200108221226",
      "XingBieDM": "2",
      "ChuShengRQ": "2001-08-22 00:00:00",
      "LianXiDH": "15857816735",
      "ZuZhiJGID": "47226079133110211G1001"
    },
    "JiuZhenXX": {
      "JiuZhenKSID": "010104010601",
      "JiuZhenKSMC": "早孕关爱门诊",
      "JiuZhenRQ": "2026-11-04 09:44:36"
    },
    "ZhenDuanList": [
      {
        "ZhenDuanLBDM": "0",
        "ZhenDuanLBMC": "主诊断",
        "ZhenDuanID": "22965",
        "ZhenDuanMC": "孕19周",
        "ZhenDuanYSID": "1551507108942479360",
        "ZhenDuanYSXM": "谢妙妙",
        "Icd10": "O26.900x206"
      }
    ],
    "WenZhenList": [
      {
        "WenZhenLXDM": "1",
        "WenZhenLXMC": "主诉",
        "NeiRong": "停经19+3 å‘¨ï¼Œäº§æ£€ã€‚"
      },
      {
        "WenZhenLXDM": "2",
        "WenZhenLXMC": "现病史",
        "NeiRong": "孕 19+3周,已建册,定期产检,现无腹痛,无阴道流血流液等异常"
      }
    ]
  }
}
```
### 2.5 å“åº”说明
成功响应:
```json
{
  "Code": "0",
  "Message": "success",
  "Data": {
    "XiaoXiID": "109b22c7-d6c6-49c3-adb1-cf0e6af9e9ef",
    "ChuangJianSJ": 1793756677000
  }
}
```
失败响应:
```json
{
  "Code": "500",
  "Message": "error",
  "Data": {
    "XiaoXiID": "109b22c7-d6c6-49c3-adb1-cf0e6af9e9ef",
    "ChuangJianSJ": 1793756677000
  }
}
```
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
| --- | --- | --- |
| `Code` | String | `"0"` æˆåŠŸï¼Œ`"500"` å¤±è´¥ |
| `Message` | String | `success` / `error` |
| `Data.XiaoXiID` | String | åŽŸæ ·è¿”å›ž `YeWuXX.XiaoXiID`,不存在时取 `XiaoXiTou.XiaoXiID` |
| `Data.ChuangJianSJ` | long | æœåŠ¡å™¨å½“å‰æ—¶é—´æˆ³ï¼ˆæ¯«ç§’ï¼‰ |
> æ¡£æ¡ˆç”Ÿæˆå¤±è´¥å·²åœ¨ `genePatSpecificArchive` å†…捕获并记录日志,不会导致完成接诊接口返回失败。
---
## ä¸‰ã€äº”个样例的档案生成映射
| æ ·ä¾‹æ–‡ä»¶ | æ‚£è€… | é—¨è¯Šç¼–号 `outhospno` | æµæ°´å· `serialnum` | ç§‘室 `deptcode` | ç§‘室名称 | ä¸»è¯Šæ–­ ICD10 | ç›®æ ‡æ¡£æ¡ˆè¡¨ | `archiveType` |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| å¦‡ç§‘-早孕关爱.txt | å¼ æ‚¦ | 202637799 | 1659700037955006464 | 010104010601 | æ—©å­•关爱门诊 | O26.900x206 | `pat_maternity_archive` | 1 |
| å¦‡ç§‘-围绝经期MHI.txt | å¼ ç¥Ž | 202620972 | 1656887374212886528 | 010102010207 | å¦‡ç§‘内分泌(青春期、更年期)门诊 | N95.900 | `pat_maternity_archive` | 2 |
| å„¿ç§‘-营养性疾病.txt | æŽæ²…初 | 202651402 | 1659715824983924736 | 010104010403 | å„¿ç«¥å¿ƒç†/语言/行为发育门诊 | E46.x00x003 | `pat_child_archive` | 1 |
| å„¿ç§‘-学习困难.txt | å½­é›…琪 | 202647368 | 1658992475891146752 | 010104010401 | å„¿ç«¥ä¿å¥é—¨è¯Š | F81.900 | `pat_child_archive` | 2 |
| å„¿ç§‘-高危儿专案.txt | çŽ‹å—æ˜• | 202607546 | 1659487324381261824 | 010104010405 | æ—©äº§å„¿/高危儿门诊 | P96.800x904 | `pat_child_archive` | 3 |
### 3.1 é…ç½®è¯´æ˜Ž
配置表:`pat_specific_archive_generate_config`。
| å­—段 | è¯´æ˜Ž |
| --- | --- |
| `dept_type` | `1` ç§‘室,`2` ç—…区;门诊记录按 `1` åŒ¹é… |
| `dept_category` | `1` å¦‡ç§‘,`2` å„¿ç§‘ |
| `archive_type` | å¦‡ç§‘:`1` æ—©å­•关爱、`2` å›´ç»ç»æœŸMHI;儿科:`1` è¥å…»æ€§ç–¾ç—…、`2` å­¦ä¹ å›°éš¾ã€`3` é«˜å±å„¿ä¸“案 |
| `dept_codes` | ç§‘室 Code JSON æ•°ç»„字符串 |
| `icd10_codes` | è¯Šæ–­ Code JSON æ•°ç»„字符串 |
| `orgid` | æœºæž„ ID |
| `campusid` | é™¢åŒº ID |
| `del_flag` | `0` æœ‰æ•ˆï¼Œ`1` åˆ é™¤ |
匹配规则:
1. ä»…查询 `del_flag = '0'` çš„配置。
2. æœºæž„配置优先;该机构没有配置时才使用全局配置。
3. `campusid` ä¸ºç©ºä¸é™åˆ¶ï¼›ä¸ä¸ºç©ºæ—¶å¿…须与记录的院区一致。
4. éƒ¨é—¨æ¡ä»¶ä¸Žè¯Šæ–­æ¡ä»¶ä¸º **OR**:任一命中即生成。
5. åŒä¸€é—¨è¯Šè®°å½•、同一部门分类、同一档案类型只生成一条档案。
6. æœªé…ç½®ã€æœªå‘½ä¸­æˆ–生成异常均不阻断完成接诊主流程。
初始化脚本:
```text
sql/pat_specific_archive_generate_config_lishui_mch_20261104.sql
```
---
## å››ã€æ‰‹åŠ¨è¡¥é‡‡æŽ¥å£
### 4.1 åŸºæœ¬ä¿¡æ¯
| é¡¹ç›® | å†…容 |
| --- | --- |
| æŽ¥å£åœ°å€ | `/monitor/his/collectMchArchive` |
| è¯·æ±‚方式 | `POST` |
| Content-Type | `application/json` |
| è®¤è¯æ–¹å¼ | éœ€ç™»å½•,请求头携带 `Authorization: Bearer {token}` |
| åŠŸèƒ½ | ä»Ž HIS é—¨è¯Šè§†å›¾æŒ‰å°±è¯Šæ—¥æœŸè¡¥é‡‡ä¸½æ°´å¦‡å¹¼é—¨è¯ŠæŽ¥è¯Šå®Œæˆè®°å½•,并生成专科档案 |
### 4.2 è¯·æ±‚参数
| å­—段 | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
| --- | --- | --- | --- |
| `startTime` | Date | å¦ | å°±è¯Šå¼€å§‹æ—¥æœŸï¼Œæ ¼å¼ `yyyy-MM-dd`;不传默认昨天 |
| `endTime` | Date | å¦ | å°±è¯Šç»“束日期,格式 `yyyy-MM-dd`;不传默认今天;传日期时自动补齐到当天 `23:59:59` |
请求示例:
```json
{
  "startTime": "2026-11-03",
  "endTime": "2026-11-03"
}
```
### 4.3 å“åº”说明
```json
{
  "code": 200,
  "msg": "操作成功",
  "data": 25
}
```
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
| --- | --- | --- |
| `code` | int | `200` æˆåŠŸï¼Œ`500` å¤±è´¥ |
| `msg` | String | æç¤ºä¿¡æ¯ |
| `data` | Integer | æœ¬æ¬¡å¤„理影响行数(患者档案、门诊记录写入/更新行数,不等同于档案条数) |
> è¯¥æŽ¥å£å†…部校验 `orgid = 47226079133110211G1001`,其他机构直接返回 0。
---
## äº”、定时任务
| é¡¹ç›® | å†…容 |
| --- | --- |
| ä»»åŠ¡æ–¹æ³• | `ryTask.collectMchArchive()` |
| å‚æ•° | æ—  |
| é»˜è®¤æ—¶é—´èŒƒå›´ | æ˜¨å¤© `00:00:00` ~ ä»Šå¤© `00:00:00` |
| åŠŸèƒ½ | é‡‡é›†ä¸½æ°´å¦‡å¹¼é—¨è¯ŠæŽ¥è¯Šå®Œæˆè®°å½•并生成专科档案 |
`sys_job` é…ç½®ç¤ºä¾‹ï¼š
| å­—段 | ç¤ºä¾‹å€¼ |
| --- | --- |
| ä»»åŠ¡åç§° | ä¸½æ°´å¦‡å¹¼é—¨è¯Šä¸“科档案生成 |
| è°ƒç”¨ç›®æ ‡å­—符串 | `ryTask.collectMchArchive()` |
| cron è¡¨è¾¾å¼ | `0 30 1 * * ?` |
| çŠ¶æ€ | æ­£å¸¸ |
---
## å…­ã€æ¡£æ¡ˆæŸ¥è¯¢æŽ¥å£
档案查询、详情、删除接口沿用现有妇幼档案接口,详见 `doc/api/妇保接口文档.md`。
| åŠŸèƒ½ | æŽ¥å£ |
| --- | --- |
| æŸ¥è¯¢å¦‡ç§‘档案列表 | `POST /smartor/maternityArchive/selectPatMaternityArchiveList` |
| èŽ·å–å¦‡ç§‘æ¡£æ¡ˆè¯¦æƒ… | `GET /smartor/maternityArchiveDetail/getInfo/{archiveid}` |
| æŸ¥è¯¢å„¿ç§‘档案列表 | `POST /smartor/childArchive/selectPatChildArchiveList` |
| èŽ·å–å„¿ç§‘æ¡£æ¡ˆè¯¦æƒ… | `GET /smartor/childArchiveDetail/getInfo/{archiveid}` |
列表查询可按 `outhospno`、`archiveType`、`patname`、`deptcode`、`icd10code` ç­‰æ¡ä»¶æ ¸å¯¹ç”Ÿæˆç»“果。
---
## ä¸ƒã€å¼‚常与边界场景
| åœºæ™¯ | å¤„理方式 |
| --- | --- |
| æœºæž„不是丽水妇幼 | ä¸ç”Ÿæˆæ¡£æ¡ˆï¼Œå®ŒæˆæŽ¥è¯ŠæŽ¥å£æ­£å¸¸è¿”回 |
| æœªé…ç½®è§„则或配置已删除 | ä¸ç”Ÿæˆæ¡£æ¡ˆï¼Œä¸æŠ¥é”™ |
| é…ç½® `dept_codes`、`icd10_codes` éƒ½ä¸ºç©º | é…ç½®è§†ä¸ºæ— æ•ˆï¼Œè·³è¿‡ |
| `dept_type = 2`(病区) | é—¨è¯Šè®°å½•没有病区字段,不参与门诊匹配 |
| è¯Šæ–­ Code ä¸ºç©º | ä»…按科室条件匹配;若配置只有诊断条件则不命中 |
| é—¨è¯Šç¼–号 `outhospno` ä¸ºç©º | è·³è¿‡è¯¥è®°å½•,记录告警日志 |
| æ‚£è€…档案不存在 | æŒ‰æ‚£è€…编号/证件号回查仍找不到时跳过 |
| é‡å¤å›žè°ƒ | æŒ‰ä¸šåŠ¡é”®æ›´æ–°å·²æœ‰æ¡£æ¡ˆï¼Œä¸é‡å¤æ’å…¥ |
| ä¸€æ¡è®°å½•命中多条配置 | æŒ‰ä¸åŒ `dept_category + archive_type` åˆ†åˆ«ç”Ÿæˆ |
| é…ç½® JSON æ ¼å¼å¼‚常 | è®°å½•告警日志,按逗号分隔兜底解析 |
| æ¡£æ¡ˆç”Ÿæˆå¼‚常 | æ•获异常并记录日志,不影响完成接诊响应 |
---
## å…«ã€å˜æ›´è®°å½•
| æ—¥æœŸ | å†…容 |
| --- | --- |
| 2026-11-04 | æ–°å¢žä¸½æ°´å¸‚妇幼保健院门诊接诊完成记录生成妇科/儿科档案说明 |
docs/¸¾Ó×ר¿Æµµ°¸Éú³É²âÊÔ±¨¸æ_20261104.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,307 @@
# å¦‡å¹¼ä¸“科档案生成测试报告
| é¡¹ç›® | å†…容 |
| --- | --- |
| æ¨¡å— | æ™ºèƒ½éšè®¿ï¼ˆsmartor) |
| åŠŸèƒ½ | é—¨è¯ŠæŽ¥è¯Šå®Œæˆè®°å½•生成妇科/儿科专科档案 |
| é€‚用机构 | `orgid = 47226079133110211G1001`(丽水市妇幼保健院) |
| æµ‹è¯•类型 | å¼€å‘自测(静态验证 + è”调用例设计) |
| æµ‹è¯•日期 | 2026-11-04 |
| å…³è”文档 | `doc/api/妇幼专科档案生成接口文档_20261104.md` |
---
## 1. æµ‹è¯•概述
### 1.1 æµ‹è¯•目标
验证以下能力:
1. `JZ_MZ_WanChengJZ` å®ŒæˆæŽ¥è¯ŠæŠ¥æ–‡å†™å…¥é—¨è¯Šè®°å½•后,能按配置生成妇科/儿科档案。
2. äº”个参考样例分别生成正确档案类型。
3. ä»… `orgid = 47226079133110211G1001` ç”Ÿæ•ˆï¼Œå…¶ä»–机构不生成。
4. é‡å¤å›žè°ƒã€æ— é…ç½®ã€é…ç½®åœç”¨ç­‰åœºæ™¯ä¸å½±å“å®ŒæˆæŽ¥è¯Šä¸»æµç¨‹ã€‚
5. æ‰‹åŠ¨è¡¥é‡‡æŽ¥å£å’Œå®šæ—¶ä»»åŠ¡å…¥å£å¯ç”¨ã€‚
### 1.2 æµ‹è¯•范围
| èŒƒå›´ | è¯´æ˜Ž |
| --- | --- |
| å®ŒæˆæŽ¥è¯Šå›žè°ƒ | `/smartor/serviceExternal/generalInterface`,消息类型 `JZ_MZ_WanChengJZ` |
| æ‰‹åŠ¨è¡¥é‡‡æŽ¥å£ | `/monitor/his/collectMchArchive` |
| å®šæ—¶ä»»åŠ¡ | `ryTask.collectMchArchive()` |
| æ¡£æ¡ˆç”ŸæˆæœåŠ¡ | `PatSpecificArchiveGenerateServiceImpl` |
| é…ç½®æŸ¥è¯¢ | `pat_specific_archive_generate_config` |
| æ¡£æ¡ˆè½åº“ | `pat_maternity_archive(_detail)`、`pat_child_archive(_detail)` |
### 1.3 ä¸åœ¨æœ¬æ¬¡èŒƒå›´
- å¦‡ç§‘/儿科档案页面 UI äº¤äº’测试。
- éšè®¿ä»»åŠ¡ç”Ÿæˆã€çŸ­ä¿¡/微信发送测试。
- HIS ä¾§ç½‘络、防火墙、接口联调环境可用性。
---
## 2. æµ‹è¯•环境
| é¡¹ç›® | å†…容 |
| --- | --- |
| ä»£ç åˆ†æ”¯ | æœ¬åœ° `master` å·¥ä½œåŒº |
| JDK | 21.0.12 |
| Maven | 3.9.9 |
| åº”用端口 | 8095(配置值) |
| ä¸»åº“ | MySQL,库名 `smartor-lisui` å¯¹åº”环境 |
| HIS æ¥æº | `healthy_outhosp` é—¨è¯Šè§†å›¾ / `JZ_MZ_WanChengJZ` å®žæ—¶æŠ¥æ–‡ |
| æŽ¥å£å·¥å…· | curl / Postman(联调时使用) |
> æœ¬æ¬¡æœªè¿žæŽ¥å®žé™…业务数据库执行落库用例,数据库相关用例标记为“待联调执行”。
---
## 3. æµ‹è¯•数据
测试数据目录:
```text
D:\OneDrive\Desktop\移库\妇幼
```
| åºå· | æ–‡ä»¶ | æ‚£è€… | é—¨è¯Šç¼–号 | æµæ°´å· | ç§‘室 Code | ä¸»è¯Šæ–­ ICD10 | æœŸæœ›æ¡£æ¡ˆ |
| --- | --- | --- | --- | --- | --- | --- | --- |
| 1 | å¦‡ç§‘-早孕关爱.txt | å¼ æ‚¦ | 202637799 | 1659700037955006464 | 010104010601 | O26.900x206 | å¦‡ç§‘档案,`archiveType=1` |
| 2 | å¦‡ç§‘-围绝经期MHI.txt | å¼ ç¥Ž | 202620972 | 1656887374212886528 | 010102010207 | N95.900 | å¦‡ç§‘档案,`archiveType=2` |
| 3 | å„¿ç§‘-营养性疾病.txt | æŽæ²…初 | 202651402 | 1659715824983924736 | 010104010403 | E46.x00x003 | å„¿ç§‘档案,`archiveType=1` |
| 4 | å„¿ç§‘-学习困难.txt | å½­é›…琪 | 202647368 | 1658992475891146752 | 010104010401 | F81.900 | å„¿ç§‘档案,`archiveType=2` |
| 5 | å„¿ç§‘-高危儿专案.txt | çŽ‹å—æ˜• | 202607546 | 1659487324381261824 | 010104010405 | P96.800x904 | å„¿ç§‘档案,`archiveType=3` |
配置文件:
```text
sql/pat_specific_archive_generate_config_lishui_mch_20261104.sql
```
---
## 4. æµ‹è¯•执行汇总
| ç±»åˆ« | ç”¨ä¾‹æ•° | é€šè¿‡ | å¤±è´¥ | å¾…执行 |
| --- | ---: | ---: | ---: | ---: |
| é™æ€éªŒè¯ | 3 | 3 | 0 | 0 |
| è§„则匹配模拟 | 5 | 5 | 0 | 0 |
| æ•°æ®åº“落库联调 | 16 | 0 | 0 | 16 |
| åˆè®¡ | 24 | 8 | 0 | 16 |
> é™æ€éªŒè¯å’Œè§„则匹配模拟已实际执行;数据库落库类用例需在联调环境执行。
---
## 5. å·²æ‰§è¡Œç”¨ä¾‹åŠç»“æžœ
### 5.1 ST-01 ä»£ç ç¼–译
执行命令:
```bash
mvn -pl smartor,ruoyi-quartz -am -DskipTests -Dmaven.test.skip=true compile
mvn -pl ruoyi-admin -am -DskipTests -Dmaven.test.skip=true compile
```
执行结果:
```text
BUILD SUCCESS
```
结论:通过。
### 5.2 ST-02 äº”个入参 JSON æ ¼å¼æ ¡éªŒ
使用 Node.js å¯¹äº”个 txt æ–‡ä»¶æ‰§è¡Œ `JSON.parse`。
执行结果:
```text
OK å„¿ç§‘-学习困难.txt
OK å„¿ç§‘-营养性疾病.txt
OK å„¿ç§‘-高危儿专案.txt
OK å¦‡ç§‘-围绝经期MHI.txt
OK å¦‡ç§‘-早孕关爱.txt
```
结论:5/5 é€šè¿‡ã€‚
### 5.3 ST-03 é…ç½®è§„则匹配模拟
按 `sql/pat_specific_archive_generate_config_lishui_mch_20261104.sql` ä¸­çš„ 5 æ¡è§„则,对五个入参的 `JiuZhenKSID` å’Œä¸»è¯Šæ–­ `Icd10` åš OR åŒ¹é…æ¨¡æ‹Ÿã€‚
执行结果:
| æ–‡ä»¶ | ç§‘室 Code | ä¸»è¯Šæ–­ ICD10 | å‘½ä¸­è§„则 | ç»“æžœ |
| --- | --- | --- | --- | --- |
| å¦‡ç§‘-早孕关爱.txt | 010104010601 | O26.900x206 | æ—©å­•关爱 / å¦‡ç§‘ / `archiveType=1` | PASS |
| å¦‡ç§‘-围绝经期MHI.txt | 010102010207 | N95.900 | å›´ç»ç»æœŸMHI / å¦‡ç§‘ / `archiveType=2` | PASS |
| å„¿ç§‘-营养性疾病.txt | 010104010403 | E46.x00x003 | è¥å…»æ€§ç–¾ç—… / å„¿ç§‘ / `archiveType=1` | PASS |
| å„¿ç§‘-学习困难.txt | 010104010401 | F81.900 | å­¦ä¹ å›°éš¾ / å„¿ç§‘ / `archiveType=2` | PASS |
| å„¿ç§‘-高危儿专案.txt | 010104010405 | P96.800x904 | é«˜å±å„¿ä¸“案 / å„¿ç§‘ / `archiveType=3` | PASS |
汇总:
```text
SUMMARY 5/5
```
结论:五个样例均唯一命中预期规则,未出现交叉命中。
---
## 6. è”调用例(待执行)
> ä»¥ä¸‹ç”¨ä¾‹éœ€åœ¨å…·å¤‡ MySQL、HIS è§†å›¾å’Œå®Œæ•´åº”用环境的联调环境执行。
### 6.1 é…ç½®åˆå§‹åŒ–
| ç”¨ä¾‹ç¼–号 | æ­¥éª¤ | é¢„期结果 |
| --- | --- | --- |
| TC-00 | æ‰§è¡Œ `sql/pat_specific_archive_generate_config_lishui_mch_20261104.sql` | è„šæœ¬æœ«å°¾æ ¸éªŒ SQL è¿”回 5 æ¡é…ç½®ï¼Œ`orgid` å‡ä¸º `47226079133110211G1001` |
### 6.2 äº”个样例生成
| ç”¨ä¾‹ç¼–号 | å…¥å‚ | æ­¥éª¤ | é¢„期结果 |
| --- | --- | --- | --- |
| TC-01 | å¦‡ç§‘-早孕关爱.txt | POST `/smartor/serviceExternal/generalInterface`,然后查询 `pat_maternity_archive` | æ–°å¢ž 1 æ¡ `archive_type='1'`、`outhospno='202637799'`、`patname='张悦'` çš„妇科档案,详情表同步 1 æ¡ |
| TC-02 | å¦‡ç§‘-围绝经期MHI.txt | åŒä¸Š | æ–°å¢ž 1 æ¡ `archive_type='2'`、`outhospno='202620972'`、`patname='张祎'` çš„妇科档案 |
| TC-03 | å„¿ç§‘-营养性疾病.txt | åŒä¸Šï¼ŒæŸ¥è¯¢ `pat_child_archive` | æ–°å¢ž 1 æ¡ `archive_type='1'`、`outhospno='202651402'`、`patname='李沅初'` çš„儿科档案 |
| TC-04 | å„¿ç§‘-学习困难.txt | åŒä¸Š | æ–°å¢ž 1 æ¡ `archive_type='2'`、`outhospno='202647368'`、`patname='彭雅琪'` çš„儿科档案 |
| TC-05 | å„¿ç§‘-高危儿专案.txt | åŒä¸Š | æ–°å¢ž 1 æ¡ `archive_type='3'`、`outhospno='202607546'`、`patname='王南昕'` çš„儿科档案 |
### 6.3 å­—段完整性
| ç”¨ä¾‹ç¼–号 | æ£€æŸ¥é¡¹ | é¢„期结果 |
| --- | --- | --- |
| TC-06 | å¦‡ç§‘档案字段 | `patid`、`patname`、`birthdate`、`phone`、`deptcode`、`deptname`、`icd10code`、`diagname`、`diag_time`、`archive_time`、`orgid`、`campusid` æœ‰å€¼ï¼›`source_type='1'` |
| TC-07 | å„¿ç§‘档案字段 | é™¤ä¸Šè¿°å…¬å…±å­—段外,`parent_phone`、`parent_address` å°½é‡æœ‰å€¼ï¼›è¥å…»æ€§ç–¾ç—…档案 `nutrition_icd10_code`、`nutrition_diag_name` ä¸Žä¸»è¯Šæ–­ä¸€è‡´ |
### 6.4 å¹‚等与隔离
| ç”¨ä¾‹ç¼–号 | åœºæ™¯ | é¢„期结果 |
| --- | --- | --- |
| TC-08 | åŒä¸€å…¥å‚连续 POST 2 æ¬¡ | ä¸»è¡¨ä»åªæœ‰ 1 æ¡ï¼Œè¯¦æƒ…表仍只有 1 æ¡ï¼Œå†…容更新为最后一次 |
| TC-09 | å°† `FaSongJGID` æ”¹ä¸ºå…¶ä»–机构后 POST | ä¸ç”Ÿæˆå¦‡å¹¼ä¸“科档案,完成接诊接口正常返回 `Code=0` |
| TC-10 | å°†å¯¹åº”配置 `del_flag` æ”¹ä¸º `1` åŽ POST | ä¸ç”Ÿæˆæ¡£æ¡ˆï¼ŒæŽ¥å£æ­£å¸¸è¿”回 |
| TC-11 | é…ç½® `dept_type='2'`(病区),门诊记录无病区字段 | ç§‘室条件不命中;仅诊断条件可命中 |
| TC-12 | åŒä¸€è®°å½•命中两条不同 `archiveType` é…ç½® | åˆ†åˆ«ç”Ÿæˆä¸¤æ¡ä¸åŒæ¡£æ¡ˆç±»åž‹çš„æ¡£æ¡ˆ |
### 6.5 æ‰‹åŠ¨è¡¥é‡‡ä¸Žå®šæ—¶ä»»åŠ¡
| ç”¨ä¾‹ç¼–号 | åœºæ™¯ | é¢„期结果 |
| --- | --- | --- |
| TC-13 | POST `/monitor/his/collectMchArchive`,传 `startTime`、`endTime` | æŒ‰å°±è¯Šæ—¥æœŸè¡¥é‡‡æŒ‡å®šæœºæž„记录,并触发档案生成;返回 `code=200`、`data` ä¸ºå¤„理行数 |
| TC-14 | ä¸ä¼ è¯·æ±‚体调用手动补采接口 | é»˜è®¤é‡‡é›†æ˜¨å¤©æ•°æ® |
| TC-15 | é…ç½®å¹¶æ‰§è¡Œ `ryTask.collectMchArchive()` | é‡‡é›†æ˜¨å¤©ä¸½æ°´å¦‡å¹¼é—¨è¯Šè®°å½•并生成档案,日志无异常 |
---
## 7. æ•°æ®åº“核验 SQL
### 7.1 é…ç½®æ ¸éªŒ
```sql
SELECT id, dept_type, dept_category, archive_type, dept_codes, icd10_codes, orgid, campusid, del_flag
FROM pat_specific_archive_generate_config
WHERE orgid = '47226079133110211G1001'
  AND del_flag = '0'
ORDER BY dept_category, archive_type;
```
预期:5 æ¡æœ‰æ•ˆé…ç½®ã€‚
### 7.2 å¦‡ç§‘档案核验
```sql
SELECT id, archive_type, outhospno, serialnum, patid, patname, deptcode, deptname,
       icd10code, diagname, orgid, campusid, create_time
FROM pat_maternity_archive
WHERE outhospno IN ('202637799', '202620972')
  AND del_flag = '0'
ORDER BY outhospno;
```
预期:
| outhospno | archive_type | patname |
| --- | --- | --- |
| 202620972 | 2 | å¼ ç¥Ž |
| 202637799 | 1 | å¼ æ‚¦ |
### 7.3 å„¿ç§‘档案核验
```sql
SELECT id, archive_type, outhospno, serialnum, patid, patname, deptcode, deptname,
       icd10code, diagname, orgid, campusid, create_time
FROM pat_child_archive
WHERE outhospno IN ('202651402', '202647368', '202607546')
  AND del_flag = '0'
ORDER BY outhospno;
```
预期:
| outhospno | archive_type | patname |
| --- | --- | --- |
| 202607546 | 3 | çŽ‹å—æ˜• |
| 202647368 | 2 | å½­é›…琪 |
| 202651402 | 1 | æŽæ²…初 |
### 7.4 è¯¦æƒ…表核验
```sql
SELECT d.id, d.archiveid, d.archive_type, d.source_type, d.outhospno, d.patid,
       d.patname, d.deptcode, d.icd10code, d.diagname, d.orgid, d.campusid
FROM pat_maternity_archive_detail d
WHERE d.outhospno IN ('202637799', '202620972')
  AND d.del_flag = '0';
```
```sql
SELECT d.id, d.archiveid, d.archive_type, d.source_type, d.outhospno, d.patid,
       d.patname, d.deptcode, d.icd10code, d.diagname, d.orgid, d.campusid
FROM pat_child_archive_detail d
WHERE d.outhospno IN ('202651402', '202647368', '202607546')
  AND d.del_flag = '0';
```
预期:每个门诊编号对应 1 æ¡è¯¦æƒ…,`source_type='1'`,`archiveid` ä¸Žä¸»è¡¨ä¸€è‡´ã€‚
---
## 8. é£Žé™©ä¸Žé—留问题
| åºå· | é£Žé™©/问题 | è¯´æ˜Ž | å»ºè®® |
| --- | --- | --- | --- |
| 1 | é…ç½®è¡¨ä¾èµ– | æœªæ‰§è¡Œé…ç½®è„šæœ¬æˆ–é…ç½®è¢«åˆ é™¤æ—¶ä¸ç”Ÿæˆæ¡£æ¡ˆ | ä¸Šçº¿å‰ç¡®è®¤ 5 æ¡é…ç½®å·²è½åº“ |
| 2 | è¯Šæ–­ç¼–码精确匹配 | å½“前按入参中的主诊断 `Icd10` ç²¾ç¡®åŒ¹é…ï¼›ç¼–码升级/变体可能不命中 | è”调时确认是否需要扩展为前缀或编码集合匹配 |
| 3 | ç§‘室与诊断 OR åŒ¹é… | é…ç½®åŒæ—¶å¡«å†™ `dept_codes` å’Œ `icd10_codes` æ—¶ä»»ä¸€å‘½ä¸­å³ç”Ÿæˆ | å¯¹ç§‘室区分度低的规则,建议只配置诊断 Code |
| 4 | å„¿ç§‘营养/学习样例科室区分度低 | ä¸¤ä¸ªæ ·ä¾‹ç§‘室名称与档案类型并非一一对应 | å·²æŒ‰è¯Šæ–­ Code é…ç½®ï¼Œé¿å…æŒ‰ç§‘室误生成 |
| 5 | é—¨è¯Šè®°å½•分表 | `pat_med_outhosp` å¯èƒ½å·²åˆ†è¡¨ï¼Œç›´æŽ¥ SQL æŸ¥è¯¢å¯èƒ½æŸ¥ä¸åˆ° | ä½¿ç”¨ `sp_query_outhosp` æˆ–查询对应分表 |
| 6 | è¿è¡ŒçŽ¯å¢ƒ | æœ¬æ¬¡æœªè¿žæŽ¥å®žé™…数据库执行落库验证 | è”调环境执行第 6 èŠ‚ç”¨ä¾‹åŽå†åˆ¤å®šä¸Šçº¿ |
---
## 9. æµ‹è¯•结论
1. ä»£ç ç¼–译通过,五个参考入参 JSON æ ¼å¼åˆæ³•,规则匹配模拟 5/5 é€šè¿‡ã€‚
2. äº”个样例均唯一命中预期档案类型,未发现规则交叉。
3. æ•°æ®åº“落库、幂等、机构隔离等 16 ä¸ªè”调用例已设计,待联调环境执行。
4. å½“前结论:**具备联调条件,联调通过后可上线**。
---
## 10. é™„录:联调执行顺序
1. æ‰§è¡Œé…ç½®è„šæœ¬ `sql/pat_specific_archive_generate_config_lishui_mch_20261104.sql`。
2. ç¡®è®¤é…ç½®æŸ¥è¯¢è¿”回 5 æ¡ã€‚
3. ä¾æ¬¡ POST äº”个入参文件到 `/smartor/serviceExternal/generalInterface`。
4. æŒ‰ç¬¬ 7 èŠ‚ SQL æ ¸å¯¹ `pat_maternity_archive`、`pat_child_archive` åŠè¯¦æƒ…表。
5. æ‰§è¡Œé‡å¤ POST、其他机构、停用配置等隔离用例。
6. è°ƒç”¨ `/monitor/his/collectMchArchive` éªŒè¯æ‰‹åŠ¨è¡¥é‡‡ã€‚
7. é…ç½® `ryTask.collectMchArchive()` å®šæ—¶ä»»åŠ¡å¹¶è§‚å¯Ÿæ—¥å¿—ã€‚
pom.xml
@@ -24,6 +24,8 @@
        <kaptcha.version>2.3.3</kaptcha.version>
        <pagehelper.boot.version>1.4.6</pagehelper.boot.version>
        <fastjson.version>2.0.23</fastjson.version>
        <!-- Lombok 1.18.22 is incompatible with JDK 21+ (JCTree$JCImport.qualid removed) -->
        <lombok.version>1.18.36</lombok.version>
        <oshi.version>6.4.0</oshi.version>
        <commons.io.version>2.11.0</commons.io.version>
        <commons.fileupload.version>1.4</commons.fileupload.version>
@@ -31,6 +33,10 @@
        <poi.version>4.1.2</poi.version>
        <velocity.version>2.3</velocity.version>
        <jwt.version>0.9.1</jwt.version>
        <jws.version>1.1</jws.version>
        <jaxws.version>2.3.1</jaxws.version>
        <jaxb.impl.version>2.3.0</jaxb.impl.version>
        <javassist.version>3.28.0-GA</javassist.version>
    </properties>
    <!-- ä¾èµ–声明 -->
@@ -46,6 +52,44 @@
                <scope>import</scope>
            </dependency>
            <!-- è¦†ç›– Spring Boot BOM ä¸­çš„ Lombok ç‰ˆæœ¬ï¼Œå…¼å®¹ JDK 21+ -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
            <!-- JDK 11+ å·²ç§»é™¤ Java EE æ¨¡å—,显式管理 JAX-WS/JWS API ç‰ˆæœ¬ -->
            <dependency>
                <groupId>javax.jws</groupId>
                <artifactId>javax.jws-api</artifactId>
                <version>${jws.version}</version>
            </dependency>
            <dependency>
                <groupId>javax.xml.ws</groupId>
                <artifactId>jaxws-api</artifactId>
                <version>${jaxws.version}</version>
            </dependency>
            <!-- ç»Ÿä¸€ JAXB ç‰ˆæœ¬ï¼Œé¿å… CXF 3.1.6 å¼•入在 JDK 9+ ä¸‹ POM éžæ³•çš„ 2.2.11 -->
            <dependency>
                <groupId>com.sun.xml.bind</groupId>
                <artifactId>jaxb-impl</artifactId>
                <version>${jaxb.impl.version}</version>
            </dependency>
            <dependency>
                <groupId>com.sun.xml.bind</groupId>
                <artifactId>jaxb-core</artifactId>
                <version>${jaxb.impl.version}</version>
            </dependency>
            <!-- ç»Ÿä¸€ Javassist ç‰ˆæœ¬ï¼Œ3.20.0-GA çš„ tools.jar systemPath åœ¨ JDK 9+ ä¸‹ä¼šå¯¼è‡´ POM æ— æ•ˆ -->
            <dependency>
                <groupId>org.javassist</groupId>
                <artifactId>javassist</artifactId>
                <version>${javassist.version}</version>
            </dependency>
            <!-- é˜¿é‡Œæ•°æ®åº“连接池 -->
            <dependency>
                <groupId>com.alibaba</groupId>
ruoyi-admin/pom.xml
@@ -173,6 +173,16 @@
            <artifactId>spring-boot-starter-web-services</artifactId>
        </dependency>
        <!-- JDK 11+ å·²ç§»é™¤ Java EE æ¨¡å—,本模块的 WebService ç›´æŽ¥ä½¿ç”¨ JAX-WS/JWS API -->
        <dependency>
            <groupId>javax.jws</groupId>
            <artifactId>javax.jws-api</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.xml.ws</groupId>
            <artifactId>jaxws-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
@@ -249,17 +259,17 @@
            <groupId>com.oracle.database.jdbc</groupId>
            <artifactId>ojdbc8</artifactId>
            <version>19.3.0.0</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.6.0</version> <!-- è¯·æ ¹æ®éœ€è¦é€‰æ‹©æœ€æ–°ç‰ˆæœ¬ -->
        </dependency>
        <!--oracle-->
        <!-- é€šç”¨å·¥å…·ï¼ˆæœ¬æ¨¡å—直接使用 ruoyi-common ä¸­çš„类) -->
        <dependency>
            <groupId>com.oracle.database.jdbc</groupId>
            <artifactId>ojdbc8</artifactId>
            <scope>runtime</scope>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common</artifactId>
        </dependency>
    </dependencies>
    <build>
@@ -300,10 +310,6 @@
        <repository>
            <id>aliyun</id>
            <url>https://maven.aliyun.com/repository/public</url>
        </repository>
        <repository>
            <id>jcenter</id>
            <url>https://jcenter.bintray.com/</url>
        </repository>
    </repositories>
</project>
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceOutPathController.java
@@ -94,15 +94,14 @@
                    serviceOutPath1.setOrgname(orgname);
                }
            }
            if(StringUtils.isNotEmpty(serviceOutPath1.getParam2())){
                RSAPublicKeyExample rsaPublicKeyExample = new RSAPublicKeyExample();
                Long patId = Long.valueOf(rsaPublicKeyExample.decryptedData(serviceOutPath1.getParam2(), pri_key));
                if(patId != null){
                    PatArchive patArchive = patArchiveService.selectPatArchiveByPatid(patId);
                    serviceOutPath1.setPatArchive(patArchive);
                }
            }
//            if(StringUtils.isNotEmpty(serviceOutPath1.getParam2())){
//                RSAPublicKeyExample rsaPublicKeyExample = new RSAPublicKeyExample();
//                Long patId = Long.valueOf(rsaPublicKeyExample.decryptedData(serviceOutPath1.getParam2(), pri_key));
//                if(patId != null){
//                    PatArchive patArchive = patArchiveService.selectPatArchiveByPatid(patId);
//                    serviceOutPath1.setPatArchive(patArchive);
//                }
//            }
            return success(serviceOutPath1);
        }
        return success(null);
ruoyi-common/pom.xml
@@ -168,6 +168,16 @@
            <artifactId>javax.servlet-api</artifactId>
        </dependency>
        <!-- JDK 11+ å·²ç§»é™¤ Java EE æ¨¡å—,这里显式引入 JAX-WS/JWS API -->
        <dependency>
            <groupId>javax.jws</groupId>
            <artifactId>javax.jws-api</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.xml.ws</groupId>
            <artifactId>jaxws-api</artifactId>
        </dependency>
        <!-- AOP支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
@@ -176,7 +186,6 @@
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.34</version>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/mapper/PatSpecificArchiveGenerateConfigMapper.java
@@ -2,6 +2,7 @@
import com.ruoyi.patarchive.domain.PatSpecificArchiveGenerateConfig;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@@ -18,6 +19,17 @@
    public List<PatSpecificArchiveGenerateConfig> selectPatSpecificArchiveGenerateConfigList(PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig);
    /**
     * æŸ¥è¯¢æœºæž„/院区可用的专科档案生成配置。
     * æœºæž„配置优先;没有机构配置时才返回全局配置。
     *
     * @param orgid    æœºæž„ID
     * @param campusid é™¢åŒºID
     * @return é…ç½®é›†åˆ
     */
    public List<PatSpecificArchiveGenerateConfig> selectEffectivePatSpecificArchiveGenerateConfigList(@Param("orgid") String orgid,
                                                                                                       @Param("campusid") String campusid);
    public int insertPatSpecificArchiveGenerateConfig(PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig);
    public int updatePatSpecificArchiveGenerateConfig(PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig);
ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/service/IPatSpecificArchiveGenerateConfigService.java
@@ -16,6 +16,15 @@
    public List<PatSpecificArchiveGenerateConfig> selectPatSpecificArchiveGenerateConfigList(PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig);
    /**
     * æŸ¥è¯¢æœºæž„/院区可用的配置。机构配置优先,机构没有配置时返回全局配置。
     *
     * @param orgid    æœºæž„ID
     * @param campusid é™¢åŒºID
     * @return å¯ç”¨é…ç½®é›†åˆ
     */
    public List<PatSpecificArchiveGenerateConfig> selectEffectiveConfigList(String orgid, String campusid);
    public int insertPatSpecificArchiveGenerateConfig(PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig);
    public int updatePatSpecificArchiveGenerateConfig(PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig);
ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/service/impl/PatSpecificArchiveGenerateConfigServiceImpl.java
@@ -5,10 +5,13 @@
import com.ruoyi.patarchive.domain.PatSpecificArchiveGenerateConfig;
import com.ruoyi.patarchive.mapper.PatSpecificArchiveGenerateConfigMapper;
import com.ruoyi.patarchive.service.IPatSpecificArchiveGenerateConfigService;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
 * ä¸“科档案生成配置Service业务层处理
@@ -35,6 +38,33 @@
    }
    @Override
    public List<PatSpecificArchiveGenerateConfig> selectEffectiveConfigList(String orgid, String campusid)
    {
        List<PatSpecificArchiveGenerateConfig> configs =
                patSpecificArchiveGenerateConfigMapper.selectEffectivePatSpecificArchiveGenerateConfigList(orgid, campusid);
        if (CollectionUtils.isEmpty(configs))
        {
            return Collections.emptyList();
        }
        // æœºæž„配置优先:存在机构配置时,不再使用全局配置,避免规则重复命中
        if (StringUtils.isNotEmpty(orgid))
        {
            List<PatSpecificArchiveGenerateConfig> orgConfigs = configs.stream()
                    .filter(config -> orgid.equals(config.getOrgid()))
                    .collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(orgConfigs))
            {
                return orgConfigs;
            }
        }
        return configs.stream()
                .filter(config -> StringUtils.isEmpty(config.getOrgid()))
                .collect(Collectors.toList());
    }
    @Override
    public int insertPatSpecificArchiveGenerateConfig(PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig)
    {
        patSpecificArchiveGenerateConfig.setCreateTime(DateUtils.getNowDate());
ruoyi-patarchive/src/main/resources/mapper/patarchive/PatSpecificArchiveGenerateConfigMapper.xml
@@ -57,6 +57,14 @@
            </if>
    </select>
    <select id="selectEffectivePatSpecificArchiveGenerateConfigList" resultMap="PatSpecificArchiveGenerateConfigResult">
        <include refid="selectPatSpecificArchiveGenerateConfigVo"/>
        where del_flag = '0'
          and (orgid = #{orgid} or orgid is null or orgid = '')
          and (campusid is null or campusid = '' or campusid = #{campusid})
        order by case when orgid = #{orgid} then 0 else 1 end, id asc
    </select>
    <select id="selectPatSpecificArchiveGenerateConfigById" parameterType="Long" resultMap="PatSpecificArchiveGenerateConfigResult">
        <include refid="selectPatSpecificArchiveGenerateConfigVo"/>
        where id = #{id}
ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/CollectHISController.java
@@ -8,6 +8,7 @@
import com.ruoyi.datacollection.domain.HnDataGatherVO;
import com.ruoyi.common.core.domain.entity.Icd10;
import com.ruoyi.datacollection.service.IHNGatherPatArchiveService;
import com.smartor.common.SpecificArchiveConstants;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
@@ -41,6 +42,24 @@
    }
    /**
     * é‡‡é›†ä¸½æ°´å¸‚妇幼保健院门诊接诊完成记录,并生成妇科/儿科档案。
     * è¯·æ±‚体可传 startTime、endTime(yyyy-MM-dd æˆ– yyyy-MM-dd HH:mm:ss),不传默认采集昨天。
     */
    @PostMapping("/collectMchArchive")
    @ApiOperation("采集丽水妇幼门诊接诊完成记录并生成专科档案")
    public AjaxResult collectMchArchive(@RequestBody(required = false) HnDataGatherVO hnDataGatherVO) {
        try {
            HnDataGatherVO request = hnDataGatherVO != null ? hnDataGatherVO : new HnDataGatherVO();
            Integer count = ichService.collectMchOuthospAndGenerateArchive(
                    SpecificArchiveConstants.LISHUI_MCH_ORG_ID, request.getStartTime(), request.getEndTime());
            return AjaxResult.success(count);
        } catch (Exception ex) {
            log.error("采集丽水妇幼门诊接诊完成记录失败", ex);
            return AjaxResult.error(ex.getMessage());
        }
    }
    /**
     * æŸ¥è¯¢ç–¾ç—…列表数据采集
     */
    @PostMapping("/collectIcd10")
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ICollectHISService.java
@@ -6,6 +6,7 @@
import com.ruoyi.outpatientvisit.domain.PatMedOuthosp;
import com.ruoyi.inhospvisit.domain.PatMedInhosp;
import java.util.Date;
import java.util.List;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.common.core.domain.entity.Icd10;
@@ -96,4 +97,15 @@
     * å¸‚一同步
     */
    public Integer syncOper(String startTime, String endTime);
    /**
     * é‡‡é›†æŒ‡å®šæœºæž„的门诊接诊完成记录,并按配置生成妇科/儿科档案。
     * å½“前仅支持丽水市妇幼保健院(orgid = 47226079133110211G1001)。
     *
     * @param orgid     æœºæž„ID
     * @param startTime å°±è¯Šå¼€å§‹æ—¶é—´ï¼Œä¸ºç©ºæ—¶é»˜è®¤æ˜¨å¤© 00:00:00
     * @param endTime   å°±è¯Šç»“束时间,为空时默认今天 00:00:00
     * @return é‡‡é›†å¹¶å¤„理的记录数
     */
    public Integer collectMchOuthospAndGenerateArchive(String orgid, Date startTime, Date endTime);
}
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/CollectHISServiceImpl.java
@@ -11,9 +11,11 @@
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.quartz.mapper.CollectHISMapper;
import com.ruoyi.quartz.service.ICollectHISService;
import com.smartor.common.SpecificArchiveConstants;
import com.smartor.domain.*;
import com.smartor.mapper.*;
import com.smartor.service.IPatMedOuthospService;
import com.smartor.service.IPatSpecificArchiveGenerateService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
@@ -23,6 +25,7 @@
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
@@ -35,6 +38,7 @@
import com.ruoyi.outpatientvisit.mapper.PatMedOuthospMapper;
import com.ruoyi.othervisit.domain.PatMedOperation;
import com.ruoyi.outpatientvisit.domain.PatMedOuthosp;
import com.ruoyi.outpatientvisit.domain.PatMedOuthospQueryReq;
import com.ruoyi.inhospvisit.domain.PatMedInhosp;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
@@ -65,6 +69,9 @@
    @Autowired
    private IPatMedOuthospService patMedOuthospService;
    @Autowired
    private IPatSpecificArchiveGenerateService patSpecificArchiveGenerateService;
    @Autowired
    private SysDept2Mapper sysDept2Mapper;
@@ -297,13 +304,36 @@
     * @return
     */
    private Integer syncOuthosp(String startTime, String endTime) {
        return syncOuthosp(startTime, endTime, null, false);
    }
    /**
     * åŒæ­¥é—¨è¯Šä¿¡æ¯ã€‚
     *
     * @param startTime   å¼€å§‹æ—¶é—´
     * @param endTime     ç»“束时间
     * @param orgid       æœºæž„ID,为空时同步全部机构
     * @param byAdmitDate true=按就诊日期过滤(补采);false=按最后更新时间增量同步
     */
    private Integer syncOuthosp(String startTime, String endTime, String orgid, boolean byAdmitDate) {
        Integer count = 0;
        PatMedOuthosp patMedOuthosp = new PatMedOuthosp();
        patMedOuthosp.setLastStartTime(startTime);
        patMedOuthosp.setLastEndTime(endTime);
        if (byAdmitDate) {
            patMedOuthosp.setBeginTime(parseDateTime(startTime));
            patMedOuthosp.setEndTime(parseDateTime(endTime));
        } else {
            patMedOuthosp.setLastStartTime(startTime);
            patMedOuthosp.setLastEndTime(endTime);
        }
        patMedOuthosp.setOrgid(orgid);
        List<PatMedOuthosp> patMedOuthosps = chMapper.selectPatMedOuthospList(patMedOuthosp);
        log.info("同步门诊信息数量" + patMedOuthosps.size());
        log.info("同步门诊信息数量:{},orgid={},byAdmitDate={}", patMedOuthosps.size(), orgid, byAdmitDate);
        for (PatMedOuthosp patMedOuthosp1 : patMedOuthosps) {
            // æŒ‰æœºæž„补采时,HIS è§†å›¾æœªè¿”回 orgid çš„记录补上查询条件中的机构
            if (StringUtils.isEmpty(patMedOuthosp1.getOrgid()) && StringUtils.isNotEmpty(orgid)) {
                patMedOuthosp1.setOrgid(orgid);
            }
            PatArchive localArchive = null;
            try {
                //患者基本信息
                PatArchive patArchive = new PatArchive();
@@ -314,26 +344,47 @@
                    pa1.setPatientno(patMedOuthosp1.getPatno());
                    List<PatArchive> patArchives1 = patArchiveMapper.selectPatArchiveList(pa1);
                    if (CollectionUtils.isNotEmpty(patArchives1)) {
                        patArchives.get(0).setId(patArchives1.get(0).getId());
                        localArchive = patArchives1.get(0);
                        patArchives.get(0).setId(localArchive.getId());
                        patArchives.get(0).setUpdateTime(new Date());
                        count += patArchiveMapper.updatePatArchive(patArchives.get(0));
                    } else {
                        patArchives.get(0).setCreateTime(new Date());
                        count += patArchiveMapper.insertPatArchiveSingle(patArchives.get(0));
                        localArchive = patArchives.get(0);
                    }
                }
            } catch (Exception ex) {
                log.info("同患者基本信息:" + ex.getMessage());
            }
            // HIS æ‚£è€…信息缺失时,仍按患者编号回查本地档案
            if (localArchive == null && StringUtils.isNotEmpty(patMedOuthosp1.getPatno())) {
                try {
                    PatArchive localQuery = new PatArchive();
                    localQuery.setPatientno(patMedOuthosp1.getPatno());
                    List<PatArchive> localArchives = patArchiveMapper.selectPatArchiveList(localQuery);
                    if (CollectionUtils.isNotEmpty(localArchives)) {
                        localArchive = localArchives.get(0);
                    }
                } catch (Exception ex) {
                    log.info("回查本地患者档案失败:" + ex.getMessage());
                }
            }
            try {
                PatMedOuthosp patMedOuthospTemp = new PatMedOuthosp();
                patMedOuthospTemp.setSerialnum(patMedOuthosp1.getSerialnum());
                List<PatMedOuthosp> patMedOuthospsTemp = chMapper.selectPatMedOuthospList(patMedOuthosp);
                if (patMedOuthospsTemp.size() > 0) {
                if (localArchive != null && localArchive.getId() != null) {
                    patMedOuthosp1.setPatid(localArchive.getId());
                }
                PatMedOuthosp localOuthosp = null;
                try {
                    localOuthosp = findLocalOuthosp(patMedOuthosp1);
                } catch (Exception e) {
                    log.warn("回查本地门诊记录失败,按新增处理,serialnum={}", patMedOuthosp1.getSerialnum(), e);
                }
                if (localOuthosp != null) {
                    patMedOuthosp1.setUpdateTime(new Date());
                    patMedOuthosp1.setId(patMedOuthospsTemp.get(0).getId());
                    patMedOuthosp1.setId(localOuthosp.getId());
                    //市一 é™¢åŒºid对应orgid,机构id为默认的1
                    if(active.equals("hzszlyy")) {
                        patMedOuthosp1.setCampusid(patMedOuthosp1.getOrgid());
@@ -349,14 +400,95 @@
                    }
                    count += patMedOuthospService.insertPatMedOuthosp(patMedOuthosp1);
                }
                generateSpecificArchive(patMedOuthosp1, localArchive);
            } catch (Exception ex) {
                log.info("同步出入院:" + ex.getMessage());
                log.info("同步门诊信息:" + ex.getMessage());
            }
        }
        return count;
    }
    /**
     * å›žæŸ¥æœ¬åœ°é—¨è¯Šè®°å½•,避免重复采集时插入重复数据。
     */
    private PatMedOuthosp findLocalOuthosp(PatMedOuthosp record) {
        if (StringUtils.isEmpty(record.getSerialnum()) && StringUtils.isEmpty(record.getOuthospno())) {
            return null;
        }
        PatMedOuthospQueryReq req = new PatMedOuthospQueryReq();
        req.setSerialnum(record.getSerialnum());
        req.setOuthospno(record.getOuthospno());
        req.setOrgid(record.getOrgid());
        List<PatMedOuthosp> localList = patMedOuthospMapper.callSpQueryOuthosp(req);
        return CollectionUtils.isNotEmpty(localList) ? localList.get(0) : null;
    }
    /**
     * é—¨è¯ŠæŽ¥è¯Šå®Œæˆè®°å½•采集后生成专科档案(仅丽水市妇幼保健院生效)。
     */
    private void generateSpecificArchive(PatMedOuthosp patMedOuthosp, PatArchive patArchive) {
        if (!SpecificArchiveConstants.LISHUI_MCH_ORG_ID.equals(patMedOuthosp.getOrgid())) {
            return;
        }
        try {
            int generated = patSpecificArchiveGenerateService.generateByOuthosp(patMedOuthosp, patArchive);
            if (generated > 0) {
                log.info("门诊接诊完成记录生成专科档案成功,outhospno={},生成数量={}",
                        patMedOuthosp.getOuthospno(), generated);
            }
        } catch (Exception e) {
            log.error("门诊接诊完成记录生成专科档案失败,outhospno={}", patMedOuthosp.getOuthospno(), e);
        }
    }
    private Date parseDateTime(String value) {
        if (StringUtils.isEmpty(value)) {
            return null;
        }
        try {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(value);
        } catch (Exception e) {
            try {
                return new SimpleDateFormat("yyyy-MM-dd").parse(value);
            } catch (Exception ex) {
                log.warn("解析门诊采集时间失败:{}", value);
                return null;
            }
        }
    }
    /**
     * å‰ç«¯/定时任务传入 yyyy-MM-dd æ—¶ï¼Œç»“束时间补齐到当天 23:59:59,避免漏掉当天数据。
     */
    private Date normalizeEndTime(Date endTime) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(endTime);
        if (calendar.get(Calendar.HOUR_OF_DAY) == 0 && calendar.get(Calendar.MINUTE) == 0
                && calendar.get(Calendar.SECOND) == 0 && calendar.get(Calendar.MILLISECOND) == 0) {
            calendar.add(Calendar.DAY_OF_MONTH, 1);
            calendar.add(Calendar.SECOND, -1);
            return calendar.getTime();
        }
        return endTime;
    }
    @Override
    public Integer collectMchOuthospAndGenerateArchive(String orgid, Date startTime, Date endTime) {
        if (!SpecificArchiveConstants.LISHUI_MCH_ORG_ID.equals(orgid)) {
            log.warn("采集门诊接诊完成记录仅支持丽水市妇幼保健院,orgid={}", orgid);
            return 0;
        }
        Date end = endTime != null ? normalizeEndTime(endTime) : new Date();
        Date start = startTime != null ? startTime : new Date(end.getTime() - 24 * 60 * 60 * 1000L);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        log.info("开始采集丽水妇幼门诊接诊完成记录,时间范围:{} ~ {}", sdf.format(start), sdf.format(end));
        int count = syncOuthosp(sdf.format(start), sdf.format(end), orgid, true);
        log.info("丽水妇幼门诊接诊完成记录采集完成,时间范围:{} ~ {},处理数量:{}",
                sdf.format(start), sdf.format(end), count);
        return count;
    }
    /**
     * åŒæ­¥æ‰‹æœ¯ä¿¡æ¯
     *
     * @param
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java
@@ -33,6 +33,7 @@
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.message.sms.provider.sltd.ws.MessageSend;
import com.smartor.common.QwenLLMUtil;
import com.smartor.common.SpecificArchiveConstants;
import com.smartor.domain.*;
import com.ruoyi.task.domain.VO.ServiceSubtaskDetailTraceVO;
import com.smartor.mapper.*;
@@ -1168,6 +1169,25 @@
            Optional<Long> currentSort = serviceSubtaskPreachforms.stream().filter(item -> serviceSubtask.getCurrentPreachform().equals(item.getPreachform())).map(ServiceSubtaskPreachform::getSort).findFirst();
            Optional<Long> id = serviceSubtaskPreachforms.stream().filter(item -> serviceSubtask.getCurrentPreachform().equals(item.getPreachform())).map(ServiceSubtaskPreachform::getId).findFirst();
            if (currentSort.isPresent()) {
                // æ–¹æ¡ˆB:只处理当前子任务——公众号已领取(service_subtask.sendstate=1 + current_preachform=5)
                // ä¸”未填写时,按默认选项完成,避免进入下一种发送方式或最后一步超时。
                // å…ˆç”¨å†…存字段快速过滤,其他医院/任务/发送方式不会进入下面的默认完成逻辑。
                if (IServiceSubtaskService.WECHAT_DEFAULT_COMPLETE_ORGID.equals(serviceSubtask.getOrgid())
                        && "2".equals(serviceSubtask.getType())
                        && "5".equals(serviceSubtask.getCurrentPreachform())
                        && Long.valueOf(1L).equals(serviceSubtask.getSendstate())
                        && !Long.valueOf(1L).equals(serviceSubtask.getSubmit())) {
                    try {
                        if (iServiceSubtaskService.completeWechatClaimedByDefault(serviceSubtask)) {
                            log.info("【compensateTask】subid:{},微信公众号已领取未填写,按默认选项完成", serviceSubtask.getId());
                            continue;
                        }
                    } catch (Exception e) {
                        // å•个子任务失败不影响其他任务,回滚后继续原补偿流程
                        log.error("【compensateTask】微信公众号默认完成失败,subid:{},继续原补偿流程", serviceSubtask.getId(), e);
                    }
                }
                //1先检查一下,是不是有执行状态是完成的(怕之前已经有完的了,没有将servuce_subtask的状态改成功,这里再检查一下)
                boolean finishState = serviceSubtaskPreachforms.stream().allMatch(item -> item.getSendstate().equals("9"));
                if (finishState) {
@@ -1780,6 +1800,25 @@
        }
    }
    /**
     * é‡‡é›†ä¸½æ°´å¸‚妇幼保健院门诊接诊完成记录,并按配置生成妇科/儿科档案。
     * å®šæ—¶ä»»åŠ¡é»˜è®¤é‡‡é›†æ˜¨å¤© 00:00:00 ~ ä»Šå¤© 00:00:00。
     */
    public void collectMchArchive() {
        try {
            LocalDateTime todayZero = LocalDateTime.now().with(LocalTime.MIN);
            LocalDateTime yesterdayZero = todayZero.minusDays(1);
            Date startTime = Date.from(yesterdayZero.atZone(ZoneId.systemDefault()).toInstant());
            Date endTime = Date.from(todayZero.atZone(ZoneId.systemDefault()).toInstant());
            log.info("【collectMchArchive】开始采集丽水妇幼门诊接诊完成记录");
            ichService.collectMchOuthospAndGenerateArchive(
                    SpecificArchiveConstants.LISHUI_MCH_ORG_ID, startTime, endTime);
            log.info("【collectMchArchive】丽水妇幼门诊接诊完成记录采集结束");
        } catch (Exception e) {
            log.error("【collectMchArchive】丽水妇幼门诊接诊完成记录采集异常", e);
        }
    }
    public void collectHISYesterday() {
        try {
            // èŽ·å–æ˜¨å¤©0点到今天0点的时间范围
ruoyi-task/src/main/java/com/ruoyi/task/controller/ServiceSubtaskController.java
@@ -854,26 +854,35 @@
    @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
    @PostMapping("/getSfStatisticsCountDetailsHyperLink")
    public AjaxResult getSfStatisticsCountDetailsHyperLink(@RequestBody ServiceSubtaskCountReq serviceSubtaskCountReq) {
        Integer statisticaltype = serviceSubtaskCountReq.getStatisticaltype();
        if (statisticaltype != null && statisticaltype != 1 && statisticaltype != 2 && statisticaltype != 3) {
            return error("统计维度statisticaltype仅支持:1-按病区 2-按科室 3-按taskid");
        }
        if (serviceSubtaskCountReq.getScriptid() == null
                && StringUtils.isEmpty(serviceSubtaskCountReq.getScriptContent())) {
            return error("问题ID(scriptid)不能为空");
        }
        if (serviceSubtaskCountReq.getOptionText() == null) {
            return error("问题选项(optionText)不能为空");
        }
        LoginUser loginUser = getLoginUser();
        String orgid = loginUser.getUser().getOrgid();
        serviceSubtaskCountReq.setOrgid(orgid);
        // ä¸Ž getSfStatisticsCountDetails ä¿æŒç›¸åŒçš„配置筛选口径
        String configKey = serviceSubtaskCountReq.getConfigKey();
        if (StringUtils.isNotEmpty(configKey)) {
            String configValue = sysConfigService.selectConfigByKey(configKey, orgid);
            serviceSubtaskCountReq.setConfigValue(configValue);
            if (StringUtils.isEmpty(configValue)) {
                String logInfo = "getSfStatisticsCount-参数 " + configKey + " æœªé…ç½®, è¯·é…ç½®å¥½åŽé‡è¯•";
                String logInfo = "getSfStatisticsCountDetailsHyperLink-参数 " + configKey + " æœªé…ç½®, è¯·é…ç½®å¥½åŽé‡è¯•";
                log.error(logInfo);
                return error(logInfo);
            }
        }
        String followUpCountStyle = sysConfigService.selectConfigByKey("followUpCountStyle", orgid);
        if (ObjectUtils.isNotEmpty(followUpCountStyle)) {
            serviceSubtaskCountReq.setFollowUpCountStyle(followUpCountStyle);
        } else {
            serviceSubtaskCountReq.setFollowUpCountStyle("1");
        }
        return success(serviceSubtaskService.getSfStatisticsHyperlink(serviceSubtaskCountReq));
        return success(serviceSubtaskService.getSfStatisticsScriptDetailsHyperlink(serviceSubtaskCountReq));
    }
    /**
     * å‘送短信模板
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskCountReq.java
@@ -1,6 +1,7 @@
package com.ruoyi.task.domain;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
@@ -79,7 +80,8 @@
    @ApiModelProperty(value = "子任务Id集合")
    private List<Long> subTaskIds;
    @ApiModelProperty(value = "科室病区:1,病区统计  2,科室统计  3. ç—…种统计")
    @ApiModelProperty(value = "统计维度:1-按病区(leavehospitaldistrictcode) 2-按科室(deptcode) 3-按taskid")
    @JsonAlias("statisticalType")
    private Integer statisticaltype;
    private String visitDeptCode;
@@ -139,6 +141,7 @@
    private String scriptids;
    /**
    /**
     * æŠŠ scriptids(逗号分隔的问题ID字符串)严格解析为 Long é›†åˆï¼Œä»…ä¾› SQL çš„ foreach é¢„编译使用。
     * <p>
     * åªä¿ç•™çº¯æ•°å­—项,其余内容一律丢弃,返回 null è¡¨ç¤ºâ€œä¸è¿‡æ»¤â€ã€‚
@@ -158,6 +161,33 @@
        }
        return scriptidList.isEmpty() ? null : scriptidList;
    }
    /**
     * é—®é¢˜ID(题目明细行返回的 scriptid),用于选项数字下钻
     */
    @ApiModelProperty(value = "问题ID")
    @JsonAlias("scriptId")
    private Long scriptid;
    /**
     * é—®é¢˜å†…容。传入后按题目内容匹配,兼容同一道题被复制到多个任务模板的场景
     */
    @ApiModelProperty(value = "问题内容")
    private String scriptContent;
    /**
     * é€‰é¡¹æ–‡æœ¬ï¼ˆé¢˜ç›®æ˜Žç»†è¡Œè¿”回的 optionText)
     */
    @ApiModelProperty(value = "选项文本")
    @JsonAlias({"optiontext", "option", "optionContent"})
    private String optionText;
    /**
     * é¢˜ç›®æ˜Žç»†ä¸­çš„任务类型(subtaskType),用于下钻时区分语音/问卷
     */
    @ApiModelProperty(value = "任务类型(题目明细中的 subtaskType)")
    @JsonAlias("subTaskType")
    private String subtaskType;
    /**
     * åˆ†ç»„类型 drCode, deptCode, leaveHospitalDistrictCode
@@ -280,6 +310,7 @@
    private String hyperLinkInfoType;
    @ApiModelProperty(value = "任务ID", required = true)
    @JsonAlias("taskid")
    private Long taskId;
    @ApiModelProperty(value = "taskIds集合")
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskMapper.java
@@ -158,6 +158,14 @@
    public Boolean updateServiceSubtaskByCondition(ServiceSubtask ServiceSubtask);
    /**
     * å¾®ä¿¡å…¬ä¼—号已领取未填写按默认选项完成:只更新当前子任务,且只允许 sendstate=1、未提交的数据。
     *
     * @param serviceSubtask å¿…å¡« id、finishtime、result、remark
     * @return å½±å“è¡Œæ•°
     */
    public int updateWechatDefaultComplete(ServiceSubtask serviceSubtask);
    public Boolean updateServiceSubtaskByTaskGuid(ServiceSubtask ServiceSubtask);
    /**
@@ -255,6 +263,14 @@
     */
    public List<ServiceSubtaskScriptCount> getSfStatisticsScriptItemDetails(ServiceSubtaskCountReq serviceSubtaskCountReq);
    /**
     * é—®é¢˜é€‰é¡¹æ•°å­—下钻:查询选择了指定选项的 service_subtask åˆ—表
     *
     * @param serviceSubtaskCountReq ç»Ÿè®¡ç»´åº¦ã€ç§‘室/病区、任务、题目及选项条件
     * @return service_subtask åˆ—表
     */
    public List<ServiceSubtask> getSfStatisticsScriptDetailsHyperlink(ServiceSubtaskCountReq serviceSubtaskCountReq);
    public List<PatMedRes> getDeptRanking(PatMedReq patMedReq);
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskPreachformMapper.java
@@ -57,6 +57,15 @@
    int updateSSPByCondition(ServiceSubtaskPreachform row);
    /**
     * å¾®ä¿¡å…¬ä¼—号已领取未填写按默认选项完成时,将当前发送方式置为完成(9)。
     * ä»…更新当前子任务、当前公众号行,且只允许从已发送(2)/已领取(3)迁移,避免影响其他任务。
     *
     * @param row å¿…å¡« id、subid、preachform、remark
     * @return å½±å“è¡Œæ•°
     */
    int updateWechatPreachformComplete(ServiceSubtaskPreachform row);
    /**
     * æŒ‰ä¸»é”®è½¯åˆ ã€‚
     */
    int deleteServiceSubtaskPreachformById(Long id);
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceSubtaskService.java
@@ -34,6 +34,12 @@
 */
public interface IServiceSubtaskService {
    /**
     * ä¸½æ°´ä¸­åŒ»é™¢ orgid。
     * å¾®ä¿¡å…¬ä¼—号已领取未填写按默认选项完成,暂时只对该机构生效,其他医院仍走原补偿逻辑。
     */
    String WECHAT_DEFAULT_COMPLETE_ORGID = "47231022633110211A2101";
    /**
     * æŸ¥è¯¢å•一任务(随访)
     *
     * @param id å•一任务(随访)主键
@@ -183,6 +189,14 @@
    public Map<String, Object> getSfStatisticsScriptDetails(ServiceSubtaskCountReq serviceSubtaskCountReq);
    /**
     * é—®é¢˜é€‰é¡¹æ•°å­—下钻:查询选择了指定选项的 service_subtask åˆ—表
     *
     * @param serviceSubtaskCountReq ç»Ÿè®¡ç»´åº¦ã€ç§‘室/病区、任务、题目及选项条件
     * @return service_subtask åˆ—表
     */
    public List<ServiceSubtask> getSfStatisticsScriptDetailsHyperlink(ServiceSubtaskCountReq serviceSubtaskCountReq);
    public List<PatArchiveSrmVO> sendMsgTemplate(List<PatArchiveSrmVO> patArchiveSrmVOS);
    public List<ServiceSubtaskDiagname> convertToDiagnameList(List<ServiceSubtask> subtaskList);
@@ -227,6 +241,19 @@
     */
    public void setFailSubTaskDefalutVal(Long failDay);
    /**
     * å¾®ä¿¡å…¬ä¼—号已领取未填写:按题目默认选项补齐当前子任务,并将当前子任务完成。
     * <p>
     * ä»…处理传入的当前子任务(subid),只允许:
     * é—®å·ç±»åž‹(type=2)、当前发送方式为公众号(5)、子任务已领取(1)、未提交(submit!=1)、
     * å½“前公众号发送方式为已发送(2)/已领取(3) çš„æ•°æ®ã€‚
     * å…¶ä»–任务、其他发送方式、其他子任务均不受影响。
     *
     * @param serviceSubtask å½“前子任务
     * @return true=已按默认选项完成;false=不满足条件或没有可填充的默认选项
     */
    public Boolean completeWechatClaimedByDefault(ServiceSubtask serviceSubtask);
    public Map<String, Object> getHeLibraryCount(@RequestBody HeLibraryCountVO heLibraryCountVO);
    public List<ServiceSubtask> gethelibraryCountHyperlink(@RequestBody HeLibraryCountVO heLibraryCountVO);
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskServiceImpl.java
@@ -1060,10 +1060,10 @@
                return "再次随访任务创建失败,找不到对应任务 taskid = " + serviceSubtask.getTaskid() + ", å†è¯·æ¬¡é‡è¯•";
            }
            // nexttaskflag=0 è¡¨ç¤ºä»»åŠ¡é“¾ç»ˆæ­¢ï¼Œä¸å…è®¸å†ç”Ÿæˆ
            if ("0".equals(serviceTask.getNexttaskflag())) {
                log.info("无需再次随访,任务已标记无后续随访, taskid = {}", serviceSubtask.getTaskid());
                return "无需再次随访,该任务无后续随访安排";
            }
//            if ("0".equals(serviceTask.getNexttaskflag())) {
//                log.info("无需再次随访,任务已标记无后续随访, taskid = {}", serviceSubtask.getTaskid());
//                return "无需再次随访,该任务无后续随访安排";
//            }
            // å¹‚等校验:同一原任务只允许生成一条二次随访
            ServiceSubtaskEntity againQuery = new ServiceSubtaskEntity();
            againQuery.setUpid(serviceSubtask.getId().intValue());
@@ -1165,6 +1165,18 @@
                return "再次随访任务创建失败,患者出院记录不完整或下一任务配置异常, å†è¯·æ¬¡é‡è¯•";
            }
            Date now = new Date();
            applyAgainVisitTime(serviceSubtaskAgain, serviceSubtask, nextServiceTask, config, now);
            // åŒ»ç”Ÿå¤„理意见取自本次请求,按下一任务重建子任务时需显式带入,否则不会落库
            if (request != null) {
                serviceSubtaskAgain.setDoctorOpinion(request.getDoctorOpinion());
                serviceSubtaskAgain.setRemark(request.getRemark());
                serviceSubtaskAgain.setDrcode(request.getDrcode());
                serviceSubtaskAgain.setDrname(request.getDrname());
                serviceSubtaskAgain.setNurseId(request.getNurseId());
                serviceSubtaskAgain.setNurseName(request.getNurseName());
                serviceSubtaskAgain.setManagementDoctor(request.getManagementDoctor());
                serviceSubtaskAgain.setManagementDoctorCode(request.getManagementDoctorCode());
            }
            serviceSubtaskAgain.setId(null);
            serviceSubtaskAgain.setUpid(serviceSubtask.getId().intValue());
            serviceSubtaskAgain.setVisitCount(ObjectUtils.isNotEmpty(serviceSubtask.getVisitCount())
@@ -1173,9 +1185,6 @@
            serviceSubtaskAgain.setIsVisitAgain(1);
            serviceSubtaskAgain.setCreateTime(now);
            serviceSubtaskAgain.setUpdateTime(now);
            // äºŒæ¬¡éšè®¿æ—¶é—´ï¼šä¸Šæ¬¡éšè®¿å®Œæˆæ—¶é—´ï¼ˆæœªå®Œæˆåˆ™å½“前时间)+ ä¸‹ä¸€ä»»åŠ¡å‘é€å¤©æ•°ï¼›è¿‡åŽ»æ—¶é—´å…œåº•ä¸ºå½“å‰æ—¶é—´
            applyAgainVisitTime(serviceSubtaskAgain, serviceSubtask, nextServiceTask, config, now);
            // æ–°ä»»åŠ¡çš„éšè®¿æ–¹å¼è®°å½•æœ¬æ¬¡é€‰æ‹©çš„å†æ¬¡éšè®¿æ–¹å¼ï¼›resource æœ¬èº«ä¸è½åº“
            serviceSubtaskAgain.setVisitType(resource);
            serviceSubtaskAgain.setVisitDeptCode(visitDeptCode);
@@ -1253,6 +1262,10 @@
        if (dueTime.before(now)) {
            dueTime = now;
        }
        if(ObjectUtils.isNotEmpty(origin.getVisitTime())){
            dueTime = ObjectUtils.isNotEmpty(origin.getVisitTime()) ? origin.getVisitTime() : now;
        }
        target.setLongSendTime(dueTime);
        Date visitTime = dueTime;
        if ("3".equals(nextTask.getType())) {
@@ -2448,6 +2461,9 @@
            } else {
                groupKey = "deptcode";
            }
        } else if (serviceSubtaskCountReq.getStatisticaltype() != null && serviceSubtaskCountReq.getStatisticaltype() == 3) {
            // ç—…种统计:按任务ID分组,每个任务即一个病种随访任务
            groupKey = "taskid";
        }
        List<ServiceSubtaskStatistic> serviceSubtaskStatistics = new ArrayList<>();
        // æ ¹æ®æ¡ä»¶è¿›è¡Œåˆ†ç»„
@@ -2473,6 +2489,9 @@
            case "leavehospitaldistrictcode":
                collect = rawData.stream().collect(Collectors.groupingBy(subtask -> Optional.ofNullable(subtask.getLeavehospitaldistrictcode()).orElse("Unknown")));
                break;
            case "taskid":
                collect = rawData.stream().collect(Collectors.groupingBy(subtask -> Optional.ofNullable(subtask.getTaskid()).map(String::valueOf).orElse("Unknown")));
                break;
            default:
                collect = rawData.stream().collect(Collectors.groupingBy(subtask -> Optional.ofNullable(subtask.getDrcode()).orElse("Unknown")));
                break;
@@ -2485,6 +2504,9 @@
                continue;
            }
            ServiceSubtaskStatistic statistic = getStatistic(serviceSubtaskList, serviceSubtaskCountReq.getFollowUpCountStyle(), serviceSubtaskCountReq.getVisitCount());
            if ("taskid".equals(groupKey)) {
                applyTaskGroupInfo(statistic, serviceSubtaskList);
            }
            if (ObjectUtils.isNotEmpty(jsRates)) {
                Map<String, Object> jsRateMap = jsRates.get(collectKey);
                BigDecimal jsRate = new BigDecimal("0");
@@ -2573,7 +2595,16 @@
        ssRate.setGroupKey(groupKey);
        ssRate.setGroupKeyList(groupKeyList);
        ssRate.setRateDay(serviceSubtaskCountReq.getRateDay());
        return serviceSubtaskMapper.selectTimelyRateBatch(ssRate);
        Map<String, Map<String, Object>> rateResult = serviceSubtaskMapper.selectTimelyRateBatch(ssRate);
        if (rateResult == null || rateResult.isEmpty()) {
            return rateResult;
        }
        // taskid ä¸ºæ•°å€¼ç±»åž‹ï¼ŒMyBatis è¿”回的 Map key å¯èƒ½æ˜¯ Long,这里统一转成 String ä¸Žåˆ†ç»„ key å¯¹é½
        Map<String, Map<String, Object>> jsRates = new HashMap<>();
        for (Map.Entry<String, Map<String, Object>> entry : rateResult.entrySet()) {
            jsRates.put(String.valueOf(entry.getKey()), entry.getValue());
        }
        return jsRates;
    }
    /**
@@ -2597,6 +2628,9 @@
            ServiceSubtaskStatistic totalRow = calculateTotalRow(sfStatistics);
            totalRow.setLeavehospitaldistrictname("总计");
            totalRow.setDeptname("总计");
            if (serviceSubtaskCountReq.getStatisticaltype() != null && serviceSubtaskCountReq.getStatisticaltype() == 3) {
                totalRow.setTaskName("总计");
            }
            sfStatistics.add(totalRow);
        }
@@ -2693,6 +2727,22 @@
        }
        return serviceSubtaskStatistics;
    }
    /**
     * ç—…种统计(按 taskid åˆ†ç»„)时补齐任务ID、任务名称和疾病病种。
     *
     * @param statistic          ç»Ÿè®¡ç»“æžœ
     * @param serviceSubtaskList å½“前任务分组下的子任务
     */
    private void applyTaskGroupInfo(ServiceSubtaskStatistic statistic, List<ServiceSubtask> serviceSubtaskList) {
        if (statistic == null || CollectionUtils.isEmpty(serviceSubtaskList)) {
            return;
        }
        ServiceSubtask first = serviceSubtaskList.get(0);
        statistic.setTaskId(first.getTaskid());
        statistic.setTaskName(first.getTaskName());
        statistic.setDiagType(first.getDiagType());
    }
    private ServiceSubtaskStatistic getStatistic(List<ServiceSubtask> serviceSubtaskList, String followUpCountStyle, Integer visitCount) {
@@ -4006,6 +4056,9 @@
        } else if (serviceSubtaskCountReq.getStatisticaltype() != null && serviceSubtaskCountReq.getStatisticaltype() == 2) {
            // åŽ†å²å…¥å‚ï¼š2 = ç§‘室统计(原 deptcode å³çŽ°åœ¨çš„ç¦»é™¢ç§‘å®¤ leaveldeptcode)
            groupKey = hasDrcode ? "drcode" : "leaveldeptcode";
        } else if (serviceSubtaskCountReq.getStatisticaltype() != null && serviceSubtaskCountReq.getStatisticaltype() == 3) {
            // ç—…种统计:按任务ID分组,每个任务即一个病种随访任务
            groupKey = "taskid";
        }
        serviceSubtaskCountReq.setGroupKey(groupKey);
        // åˆ†ç»„列与过滤列保持一致:离院科室 leaveldeptcode、在院科室 deptcode
@@ -4049,6 +4102,35 @@
        List<ServiceSubtaskStatistic> sumStatistics = serviceSubtaskMapper.getSfStatisticsScriptSum(serviceSubtaskCountReq);
        if (CollectionUtils.isEmpty(sumStatistics)) {
            return serviceSubtaskStatistics;
        }
        /*
         * ç—…种统计(按 taskid åˆ†ç»„)时补齐任务ID、任务名称和疾病病种。
         * èšåˆ SQL å·²è¿”回 taskid/task_name,这里再补 diag_type。
         */
        if ("taskid".equals(groupKey)) {
            Map<Long, String> diagTypeCache = new HashMap<>();
            for (ServiceSubtaskStatistic statistic : sumStatistics) {
                Long taskId = statistic.getTaskId();
                if (taskId == null && StringUtils.isNotEmpty(statistic.getGroupCode())) {
                    try {
                        taskId = Long.valueOf(statistic.getGroupCode());
                    } catch (NumberFormatException ignore) {
                        // åŽ†å²æ•°æ®å¯èƒ½ä¸æ˜¯æ•°å€¼ taskid,保持原分组键即可
                    }
                }
                statistic.setTaskId(taskId);
                if (StringUtils.isEmpty(statistic.getTaskName())) {
                    statistic.setTaskName(statistic.getGroupName());
                }
                if (taskId != null) {
                    if (!diagTypeCache.containsKey(taskId)) {
                        ServiceTask task = serviceTaskService.selectServiceTaskByTaskid(taskId);
                        diagTypeCache.put(taskId, task == null ? null : task.getDiagType());
                    }
                    statistic.setDiagType(diagTypeCache.get(taskId));
                }
            }
        }
        /*
@@ -4300,6 +4382,88 @@
        result.put("rows", new ArrayList<>(pageCounts));
        result.put("data", pageData);
        return result;
    }
    /**
     * é—®é¢˜é€‰é¡¹æ•°å­—下钻:返回选择了指定选项的 service_subtask åˆ—表。
     * <p>
     * ç»Ÿè®¡æ˜Žç»†ä¸­çš„选项数量来自 service_subtask_detail çš„
     * IFNULL(matchedtext, asrtext),因此下钻必须使用同一题目的相同选项进行匹配。
     *
     * @param serviceSubtaskCountReq ä¸‹é’»æ¡ä»¶
     * @return service_subtask åˆ—表
     */
    @Override
    public List<ServiceSubtask> getSfStatisticsScriptDetailsHyperlink(ServiceSubtaskCountReq serviceSubtaskCountReq) {
        log.info("getSfStatisticsScriptDetailsHyperlink的入参为:{}", serviceSubtaskCountReq);
        if (serviceSubtaskCountReq == null) {
            return new ArrayList<>();
        }
        // å…¼å®¹å‰ç«¯ç›´æŽ¥å›žä¼ ç»Ÿè®¡æ˜Žç»†è¡Œä¸­çš„ subtaskType
        if (StringUtils.isBlank(serviceSubtaskCountReq.getType())
                && StringUtils.isNotBlank(serviceSubtaskCountReq.getSubtaskType())) {
            serviceSubtaskCountReq.setType(serviceSubtaskCountReq.getSubtaskType());
        }
        resolveScriptContentAndType(serviceSubtaskCountReq);
        if (StringUtils.isBlank(serviceSubtaskCountReq.getScriptContent())
                && serviceSubtaskCountReq.getScriptid() == null) {
            log.warn("getSfStatisticsScriptDetailsHyperlink-题目scriptid和scriptContent均为空,返回空列表");
            return new ArrayList<>();
        }
        if (serviceSubtaskCountReq.getOptionText() == null) {
            log.warn("getSfStatisticsScriptDetailsHyperlink-选项optionText为空,返回空列表");
            return new ArrayList<>();
        }
        return serviceSubtaskMapper.getSfStatisticsScriptDetailsHyperlink(serviceSubtaskCountReq);
    }
    /**
     * æ ¹æ® scriptid è¡¥é½é¢˜ç›®å†…容和任务类型。
     * <p>
     * ç»Ÿè®¡æ˜Žç»†æŒ‰â€œé¢˜ç›®å†…容 + ä»»åŠ¡ç±»åž‹â€åˆå¹¶ï¼ŒåŒä¸€é“é¢˜å¤åˆ¶åˆ°å¤šä¸ªä»»åŠ¡æ¨¡æ¿æ—¶ scriptid å¯èƒ½ä¸åŒï¼Œ
     * å› æ­¤ä¼˜å…ˆæŒ‰é¢˜ç›®å†…容下钻;只有查不到题目内容时才退化为 scriptid ç²¾ç¡®åŒ¹é…ã€‚
     */
    private void resolveScriptContentAndType(ServiceSubtaskCountReq serviceSubtaskCountReq) {
        if (serviceSubtaskCountReq.getScriptid() == null) {
            return;
        }
        // å·²æœ‰é¢˜ç›®å†…容和任务类型时无需再查模板问题表
        if (StringUtils.isNotBlank(serviceSubtaskCountReq.getScriptContent())
                && StringUtils.isNotBlank(serviceSubtaskCountReq.getType())) {
            return;
        }
        String type = serviceSubtaskCountReq.getType();
        if (StringUtils.isBlank(type) || "1".equals(type)) {
            IvrTaskTemplateScript ivrTaskTemplateScript =
                    iIvrTaskTemplateScriptService.selectIvrTaskTemplateScriptByID(serviceSubtaskCountReq.getScriptid());
            if (ObjectUtils.isNotEmpty(ivrTaskTemplateScript)) {
                if (StringUtils.isBlank(serviceSubtaskCountReq.getType())) {
                    serviceSubtaskCountReq.setType("1");
                }
                if (StringUtils.isBlank(serviceSubtaskCountReq.getScriptContent())) {
                    serviceSubtaskCountReq.setScriptContent(ivrTaskTemplateScript.getScriptContent());
                }
                return;
            }
            // æ˜Žç¡®æŒ‡å®šäº†è¯­éŸ³ç±»åž‹æ—¶ä¸å†å°è¯•问卷表
            if (StringUtils.isNotBlank(type)) {
                return;
            }
        }
        if (StringUtils.isBlank(serviceSubtaskCountReq.getType()) || "2".equals(type)) {
            SvyTaskTemplateScript svyTaskTemplateScript =
                    svyTaskTemplateScriptService.selectSvyTaskTemplateScriptByID(serviceSubtaskCountReq.getScriptid());
            if (ObjectUtils.isNotEmpty(svyTaskTemplateScript)) {
                if (StringUtils.isBlank(serviceSubtaskCountReq.getType())) {
                    serviceSubtaskCountReq.setType("2");
                }
                if (StringUtils.isBlank(serviceSubtaskCountReq.getScriptContent())) {
                    serviceSubtaskCountReq.setScriptContent(svyTaskTemplateScript.getScriptContent());
                }
            }
        }
    }
    /**
@@ -4709,6 +4873,184 @@
        }
    }
    /**
     * å¾®ä¿¡å…¬ä¼—号已领取未填写:按题目默认选项补齐当前子任务,并将当前子任务完成。
     * <p>
     * æ‰€æœ‰æŸ¥è¯¢ã€æ˜Žç»†å†™å…¥ã€çŠ¶æ€æ›´æ–°éƒ½åªä½¿ç”¨å½“å‰ subid,避免影响其他任务/子任务。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean completeWechatClaimedByDefault(ServiceSubtask serviceSubtask) {
        if (serviceSubtask == null || serviceSubtask.getId() == null) {
            return false;
        }
        // é‡æ–°è¯»å–当前子任务,所有判断只针对这一个 subid
        ServiceSubtask current = serviceSubtaskMapper.selectServiceSubtaskById(serviceSubtask.getId());
        if (current == null) {
            return false;
        }
        // åªå¤„理:丽水中医院、问卷、当前方式公众号、已领取、未提交
        // å…¶ä»–医院 orgid ä¸åŒ¹é…æ—¶ç›´æŽ¥è¿”回 false,继续走原补偿逻辑。
        if (!IServiceSubtaskService.WECHAT_DEFAULT_COMPLETE_ORGID.equals(current.getOrgid())
                || !"2".equals(current.getType())
                || !"5".equals(current.getCurrentPreachform())
                || !Long.valueOf(1L).equals(current.getSendstate())
                || Long.valueOf(1L).equals(current.getSubmit())) {
            return false;
        }
        // æŸ¥è¯¢å½“前子任务自己的公众号发送方式,必须是已发送(2)或已领取(3)
        ServiceSubtaskPreachform query = new ServiceSubtaskPreachform();
        query.setSubid(current.getId());
        query.setTaskid(current.getTaskid());
        query.setPreachform("5");
        List<ServiceSubtaskPreachform> preachforms = taskPreachformPort.selectServiceSubtaskPreachformList(query);
        if (CollectionUtils.isEmpty(preachforms)) {
            return false;
        }
        ServiceSubtaskPreachform currentPreachform = preachforms.stream()
                .filter(item -> "2".equals(item.getSendstate()) || "3".equals(item.getSendstate()))
                .findFirst().orElse(null);
        if (currentPreachform == null) {
            return false;
        }
        // åªç»™å½“前 subid è¡¥é½é»˜è®¤é€‰é¡¹ï¼›è‡³å°‘有一题默认选项才继续
        int filled = fillWechatDefaultDetailForCurrentSubtask(current);
        if (filled <= 0) {
            log.info("【微信公众号默认完成】subid:{} æ²¡æœ‰å¯å¡«å……的默认选项,跳过", current.getId());
            return false;
        }
        // å½“前公众号发送方式:已发送/已领取 -> å®Œæˆ(9)
        ServiceSubtaskPreachform updatePreachform = new ServiceSubtaskPreachform();
        updatePreachform.setId(currentPreachform.getId());
        updatePreachform.setSubid(current.getId());
        updatePreachform.setPreachform("5");
        updatePreachform.setOrgid(current.getOrgid());
        updatePreachform.setRemark("已领取未填写,按默认选项完成");
        int preachformUpdated = taskPreachformPort.updateWechatPreachformComplete(updatePreachform);
        if (preachformUpdated == 0) {
            throw new BaseException("微信公众号默认完成失败:发送方式状态已变化,subid=" + current.getId());
        }
        // å½“前子任务:已领取(1) -> å®Œæˆ(6),只更新这一个 subid
        ServiceSubtask complete = new ServiceSubtask();
        complete.setId(current.getId());
        complete.setOrgid(current.getOrgid());
        complete.setFinishtime(new Date());
        complete.setResult("success");
        complete.setRemark("已领取未填写,按默认选项完成");
        int subtaskUpdated = serviceSubtaskMapper.updateWechatDefaultComplete(complete);
        if (subtaskUpdated == 0) {
            throw new BaseException("微信公众号默认完成失败:子任务状态已变化,subid=" + current.getId());
        }
        log.info("【微信公众号默认完成】subid:{},已按默认选项完成,填充题目数:{}", current.getId(), filled);
        return true;
    }
    /**
     * ä»…给当前子任务补齐问卷单选/多选题中 default_value=2 çš„选项。
     * å·²æœ‰éžç©ºç­”案的题目跳过,不覆盖患者已填写内容。
     *
     * @return å®žé™…填充(或此前已由系统默认填充)的题目数
     */
    private int fillWechatDefaultDetailForCurrentSubtask(ServiceSubtask serviceSubtask) {
        Long templateId = serviceSubtask.getTemplateid();
        ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
        if (templateId == null && serviceTask != null) {
            templateId = serviceTask.getTemplateid();
        }
        if (templateId == null) {
            return 0;
        }
        SvyTaskTemplateScript scriptQuery = new SvyTaskTemplateScript();
        scriptQuery.setTemplateID(templateId);
        List<SvyTaskTemplateScript> scripts = svyTaskTemplateScriptService.selectSvyTaskTemplateScriptList(scriptQuery);
        if (CollectionUtils.isEmpty(scripts)) {
            return 0;
        }
        int filled = 0;
        for (SvyTaskTemplateScript script : scripts) {
            SvyTaskTemplateScriptVO scriptVO = DtoConversionUtils.sourceToTarget(script, SvyTaskTemplateScriptVO.class);
            SvyTaskTemplateScriptVO scriptWithOptions = svyTaskTemplateScriptService.selectInfoByCondition(scriptVO);
            if (scriptWithOptions == null
                    || scriptWithOptions.getId() == null
                    || CollectionUtils.isEmpty(scriptWithOptions.getSvyTaskTemplateTargetoptions())
                    || (!"1".equals(scriptWithOptions.getScriptType()) && !"2".equals(scriptWithOptions.getScriptType()))) {
                continue;
            }
            List<SvyTaskTemplateTargetoption> options = scriptWithOptions.getSvyTaskTemplateTargetoptions();
            List<SvyTaskTemplateTargetoption> defaultOptions = options.stream()
                    .filter(option -> option.getDefaultValue() != null && option.getDefaultValue() == 2L)
                    .filter(option -> StringUtils.isNotBlank(option.getOptioncontent()))
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(defaultOptions)) {
                continue;
            }
            // å·²æœ‰éžç©ºç­”案则不覆盖,避免影响患者已填写内容
            ServiceSubtaskDetail detailQuery = new ServiceSubtaskDetail();
            detailQuery.setSubId(serviceSubtask.getId());
            detailQuery.setScriptid(scriptWithOptions.getId());
            List<ServiceSubtaskDetail> existingDetails = serviceSubtaskDetailMapper.selectServiceSubtaskDetailList(detailQuery);
            ServiceSubtaskDetail existing = CollectionUtils.isNotEmpty(existingDetails) ? existingDetails.get(0) : null;
            if (existing != null
                    && (StringUtils.isNotBlank(existing.getAsrtext()) || StringUtils.isNotBlank(existing.getMatchedtext()))) {
                // ä¹‹å‰å·²ç”±ç³»ç»Ÿé»˜è®¤å¡«å……过的,计入已填充,保证重试幂等
                if ("DEFAULT".equals(existing.getOperate())) {
                    filled++;
                }
                continue;
            }
            String targetValue = options.stream()
                    .map(SvyTaskTemplateTargetoption::getOptioncontent)
                    .filter(StringUtils::isNotBlank)
                    .collect(Collectors.joining("&"));
            String defaultValue = defaultOptions.stream()
                    .map(SvyTaskTemplateTargetoption::getOptioncontent)
                    .collect(Collectors.joining("&"));
            ServiceSubtaskDetail detail = new ServiceSubtaskDetail();
            detail.setSubId(serviceSubtask.getId());
            detail.setTaskid(serviceSubtask.getTaskid());
            detail.setPatid(serviceSubtask.getPatid());
            detail.setScriptid(scriptWithOptions.getId());
            detail.setTemplatequestionnum(scriptWithOptions.getId());
            detail.setQuestiontext(scriptWithOptions.getScriptContent());
            detail.setCategoryid(scriptWithOptions.getCategoryid());
            detail.setTargetid(scriptWithOptions.getTargetid() != null ? scriptWithOptions.getTargetid().intValue() : null);
            detail.setValueType(scriptWithOptions.getScriptType());
            detail.setTargetvalue(targetValue);
            detail.setAsrtext(defaultValue);
            detail.setMatchedtext(defaultValue);
            detail.setOperate("DEFAULT");
            detail.setTemplateType(2);
            detail.setLibTemplateid(serviceSubtask.getLibtemplateid() != null ? serviceSubtask.getLibtemplateid().intValue() : null);
            detail.setTemplateid(templateId.toString());
            detail.setGuid(scriptWithOptions.getGuid());
            detail.setOrgid(StringUtils.isNotEmpty(scriptWithOptions.getOrgid()) ? scriptWithOptions.getOrgid()
                    : (serviceTask != null ? serviceTask.getOrgid() : null));
            detail.setCreateTime(new Date());
            detail.setUpdateTime(new Date());
            if (existing != null) {
                detail.setId(existing.getId());
                serviceSubtaskDetailMapper.updateServiceSubtaskDetail(detail);
            } else {
                serviceSubtaskDetailMapper.insertServiceSubtaskDetail(detail);
            }
            filled++;
        }
        return filled;
    }
    @Override
    public Map<String, Object> getHeLibraryCount(@RequestBody HeLibraryCountVO heLibraryCountVO) {
        if (ObjectUtils.isEmpty(heLibraryCountVO)) {
ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskMapper.xml
@@ -108,6 +108,7 @@
        <result property="loopCount" column="loop_count"/>
        <result property="isManuallyCalled" column="is_manually_called"/>
        <result property="campusid" column="campusid"/>
        <result property="diagType" column="diag_type"/>
    </resultMap>
    <resultMap type="com.ruoyi.task.domain.ServiceSubtaskCount" id="ServiceSubtaskResult2">
@@ -1418,6 +1419,25 @@
        where id = #{id}
    </update>
    <!-- å¾®ä¿¡å…¬ä¼—号已领取未填写按默认选项完成:只更新当前子任务。
         æ¡ä»¶ sendstate=1 + æœªæäº¤ï¼Œä¿è¯åªå¤„理当前被领取且未填写的子任务,不波及其他任务。 -->
    <update id="updateWechatDefaultComplete" parameterType="com.ruoyi.task.domain.ServiceSubtask">
        update service_subtask
        set sendstate = 6,
            finishtime = #{finishtime},
            result = #{result},
            remark = case
                         when remark is null or remark = '' then #{remark}
                         else concat(remark, ',', #{remark})
                     end,
            update_time = now(),
            finish_state = <include refid="finishStateExpr"/>
        where id = #{id}
          and orgid = #{orgid}
          and sendstate = 1
          and (submit is null or submit != 1)
    </update>
    <update id="updateServiceSubtaskByPatId" parameterType="com.ruoyi.task.domain.ServiceSubtask">
        update service_subtask
        <trim prefix="SET" suffixOverrides=",">
@@ -1806,6 +1826,9 @@
        nurse_name,
        taskid,
        task_name,
        <if test="groupKey != null and groupKey == 'taskid'">
            (SELECT st.diag_type FROM service_task st WHERE st.taskid = service_subtask.taskid LIMIT 1) AS diag_type,
        </if>
        visit_time,
        visit_dept_code,
        finishtime,
@@ -1854,6 +1877,13 @@
            <foreach collection="deptcodes" item="deptcode" open="(" separator=","
                     close=")">
                #{deptcode}
            </foreach>
        </if>
        <if test="taskIds != null and taskIds.size() > 0">
            AND taskid IN
            <foreach collection="taskIds" item="taskId" open="(" separator=","
                     close=")">
                #{taskId}
            </foreach>
        </if>
        <if test="serviceType != null and serviceType.size() > 0">
@@ -2100,6 +2130,13 @@
                #{hospitaldeptcode}
            </foreach>
        </if>
        <if test="taskIds != null and taskIds.size() > 0">
            AND taskid IN
            <foreach collection="taskIds" item="taskId" open="(" separator=","
                     close=")">
                #{taskId}
            </foreach>
        </if>
        <if test="serviceType != null and serviceType.size() > 0">
            AND service_type IN
            <foreach collection="serviceType" item="serviceType" open="(" separator=","
@@ -2173,6 +2210,8 @@
    <resultMap type="com.ruoyi.task.domain.ServiceSubtaskStatistic" id="ServiceSubtaskScriptStatisticResult">
        <result property="groupCode" column="group_code"/>
        <result property="groupName" column="group_name"/>
        <result property="taskId" column="taskid"/>
        <result property="taskName" column="task_name"/>
        <result property="dischargeCount" column="discharge_count"/>
        <result property="firstVisitCount" column="first_visit_count"/>
        <result property="againVisitCount" column="again_visit_count"/>
@@ -2225,7 +2264,16 @@
        <!-- åˆ†ç»„键对应的显示名:病区统计时就是这一组的病区名。
             ä»¥å‰è¿™ä¸€åˆ—是 MAX(hospitaldistrictname),一个组混了多个病区时会随机显示其中一个,
             äºŽæ˜¯å‡ºçŽ°â€œé€‰è¡€æ¶²å†…ç§‘ç—…åŒºå´æ˜¾ç¤ºéª¨ç§‘ä¸­å¿ƒä¸‰ç—…åŒºâ€ã€‚ -->
        MAX(IFNULL(leavehospitaldistrictname, hospitaldistrictname)) as group_name,
        <choose>
            <when test="groupKey != null and groupKey == 'taskid'">
                MAX(task_name) as group_name,
            </when>
            <otherwise>
                MAX(IFNULL(leavehospitaldistrictname, hospitaldistrictname)) as group_name,
            </otherwise>
        </choose>
        MAX(taskid) as taskid,
        MAX(task_name) as task_name,
        MAX(drcode) as drcode,
        MAX(drname) as drname,
        MAX(leaveldeptcode) as deptcode,
@@ -3103,6 +3151,136 @@
        group by a.script_content, ss.type, IFNULL(ssd.matchedtext,ssd.asrtext)
    </select>
    <!--
      é—®é¢˜é€‰é¡¹æ•°å­—下钻:返回选择了指定选项的 service_subtask åˆ—表。
      è¿‡æ»¤å£å¾„与 getSfStatisticsScriptDetails / getSfStatisticsScriptItemDetails ä¿æŒä¸€è‡´ï¼Œ
      é¢å¤–支持 statisticaltype:1-病区、2-科室、3-病种。
    -->
    <select id="getSfStatisticsScriptDetailsHyperlink" parameterType="com.ruoyi.task.domain.ServiceSubtaskCountReq"
            resultMap="ServiceSubtaskResult">
        SELECT ss.*
        FROM service_subtask ss
        WHERE ss.del_flag = 0
        <if test="orgid != null and orgid != ''">
            AND ss.orgid = #{orgid}
        </if>
        <if test="campusid != null and campusid != ''">
            AND ss.campusid = #{campusid}
        </if>
        <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0">
            AND ss.leavehospitaldistrictcode IN
            <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="(" separator=","
                     close=")">
                #{leavehospitaldistrictcode}
            </foreach>
        </if>
        <if test="deptcodes != null and deptcodes.size() > 0">
            AND ss.deptcode IN
            <foreach collection="deptcodes" item="deptcode" open="(" separator="," close=")">
                #{deptcode}
            </foreach>
        </if>
        <if test="serviceType != null and serviceType.size() > 0">
            AND ss.service_type IN
            <foreach collection="serviceType" item="serviceType" open="(" separator="," close=")">
                #{serviceType}
            </foreach>
        </if>
        <if test="startTime != null and endTime != null">
            AND ss.visit_time >= DATE(#{startTime})
            AND ss.visit_time &lt; DATE_ADD(DATE(#{endTime}), INTERVAL 1 DAY)
        </if>
        <if test="type != null and type != ''">
            AND ss.type = #{type}
        </if>
        <!-- ç»Ÿè®¡ç»´åº¦ï¼š1-按病区 2-按科室 3-按taskid -->
        <if test="statisticaltype != null and statisticaltype == 1">
            <choose>
                <when test="deptcode != null and deptcode != ''">
                    AND ss.leavehospitaldistrictcode = #{deptcode}
                </when>
                <when test="leavehospitaldistrictcode != null and leavehospitaldistrictcode != ''">
                    AND ss.leavehospitaldistrictcode = #{leavehospitaldistrictcode}
                </when>
            </choose>
        </if>
        <if test="statisticaltype != null and statisticaltype == 2 and deptcode != null and deptcode != ''">
            AND ss.deptcode = #{deptcode}
        </if>
        <if test="statisticaltype != null and statisticaltype == 3 and taskId != null">
            AND ss.taskid = #{taskId}
        </if>
        <!-- å…¼å®¹ä¸ä¼  statisticaltype æ—¶æŒ‰å•值条件过滤 -->
        <if test="statisticaltype == null">
            <if test="leavehospitaldistrictcode != null and leavehospitaldistrictcode != ''">
                AND ss.leavehospitaldistrictcode = #{leavehospitaldistrictcode}
            </if>
            <if test="deptcode != null and deptcode != ''">
                AND ss.deptcode = #{deptcode}
            </if>
            <if test="taskId != null">
                AND ss.taskid = #{taskId}
            </if>
        </if>
        <!-- é¢˜ç›® + é€‰é¡¹åŒ¹é…ï¼šåªä¿ç•™å›žç­”中选择了该选项的子任务 -->
        AND EXISTS (
            SELECT 1
            FROM service_subtask_detail ssd
            WHERE ssd.sub_id = ss.id
              AND ssd.del_flag = 0
            <if test="configValue != null and configValue != ''">
                AND ssd.categoryid IN (${configValue})
            </if>
            <choose>
                <when test="optionText != null">
                    AND IFNULL(ssd.matchedtext, ssd.asrtext) = #{optionText}
                </when>
                <otherwise>
                    AND 1 = 0
                </otherwise>
            </choose>
            <choose>
                <!-- ä¼˜å…ˆæŒ‰é¢˜ç›®å†…容匹配:同一道题复制到多个任务模板时 scriptid ä¸åŒï¼Œä½† script_content ç›¸åŒ -->
                <when test="scriptContent != null and scriptContent != ''">
                    AND ssd.scriptid IN (
                        <choose>
                            <when test="type == '1'">
                                SELECT its.id FROM ivr_task_template_script its
                                WHERE its.script_content = #{scriptContent}
                            </when>
                            <when test="type == '2'">
                                SELECT sts.id FROM svy_task_template_script sts
                                WHERE sts.script_content = #{scriptContent}
                            </when>
                            <otherwise>
                                SELECT its.id FROM ivr_task_template_script its
                                WHERE its.script_content = #{scriptContent}
                                UNION
                                SELECT sts.id FROM svy_task_template_script sts
                                WHERE sts.script_content = #{scriptContent}
                            </otherwise>
                        </choose>
                    )
                </when>
                <when test="scriptid != null">
                    AND ssd.scriptid = #{scriptid}
                </when>
                <otherwise>
                    AND 1 = 0
                </otherwise>
            </choose>
        )
        ORDER BY ss.endtime DESC, ss.id DESC
        <if test="pageSize != null and pageSize > 0">
            LIMIT #{pageSize}
            <if test="pageNum != null and pageNum >= 0">
                OFFSET #{pageNum}
            </if>
        </if>
    </select>
    <select id="getDataByTime" resultMap="ServiceSubtaskResult">
        select id,
ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskPreachformMapper.xml
@@ -299,6 +299,24 @@
    </update>
    <!-- åŽŸ deleteServiceSubtaskPreachformById:<update> ç½® del_flag=1 -->
    <!-- å¾®ä¿¡å…¬ä¼—号已领取未填写按默认选项完成:仅更新当前子任务的当前公众号行。
         åªå…è®¸ä»Žå·²å‘送(2)/已领取(3)迁移到完成(9),避免并发或重复补偿覆盖其他状态。 -->
    <update id="updateWechatPreachformComplete" parameterType="com.ruoyi.task.domain.ServiceSubtaskPreachform">
        update service_subtask_preachform
        set sendstate = '9',
            remark = case
                         when remark is null or remark = '' then #{remark}
                         else concat(remark, ',', #{remark})
                     end,
            update_time = now()
        where id = #{id}
          and subid = #{subid}
          and orgid = #{orgid}
          and preachform = #{preachform}
          and sendstate in ('2', '3')
    </update>
    <update id="deleteServiceSubtaskPreachformById" parameterType="Long">
        update service_subtask_preachform
        <trim prefix="SET" suffixOverrides=",">
smartor/pom.xml
@@ -89,12 +89,6 @@
            <artifactId>nls-sdk-recognizer</artifactId>
            <version>2.2.1</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.nls</groupId>
            <artifactId>nls-sdk-recognizer</artifactId>
            <version>2.2.1</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.nls</groupId>
smartor/src/main/java/com/smartor/common/SpecificArchiveConstants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package com.smartor.common;
/**
 * ä¸“科档案生成相关常量。
 */
public final class SpecificArchiveConstants {
    /**
     * ä¸½æ°´å¸‚妇幼保健院机构ID。
     * é—¨è¯ŠæŽ¥è¯Šå®Œæˆè®°å½•采集后自动生成妇科/儿科档案仅对该机构生效。
     */
    public static final String LISHUI_MCH_ORG_ID = "47226079133110211G1001";
    /**
     * ä¸½æ°´åœ°åŒºæ— é™¢åŒºæ¦‚念,默认院区ID。
     */
    public static final String DEFAULT_CAMPUS_ID = "1";
    private SpecificArchiveConstants() {
    }
}
smartor/src/main/java/com/smartor/service/IPatSpecificArchiveGenerateService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package com.smartor.service;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.outpatientvisit.domain.PatMedOuthosp;
/**
 * ä¸“科档案生成Service接口。
 *
 * <p>根据门诊接诊完成记录和 {@code pat_specific_archive_generate_config}
 * é…ç½®ç”Ÿæˆå¦‡ç§‘档案或儿科档案。</p>
 */
public interface IPatSpecificArchiveGenerateService {
    /**
     * æ ¹æ®é—¨è¯Šè®°å½•生成专科档案。
     *
     * @param patMedOuthosp é—¨è¯ŠæŽ¥è¯Šå®Œæˆè®°å½•
     * @param patArchive    æ‚£è€…档案,可为空;为空时按门诊记录中的患者信息回查
     * @return æœ¬æ¬¡ç”Ÿæˆ/更新的档案数量
     */
    int generateByOuthosp(PatMedOuthosp patMedOuthosp, PatArchive patArchive);
}
smartor/src/main/java/com/smartor/service/impl/PatMedInhospServiceImpl.java
@@ -931,6 +931,8 @@
        serviceSubtask.setLeaveldeptname(patMedInhosp1.getLeaveldeptname());
        serviceSubtask.setLeavehospitaldistrictcode(patMedInhosp1.getLeavehospitaldistrictcode());
        serviceSubtask.setLeavehospitaldistrictname(patMedInhosp1.getLeavehospitaldistrictname());
        serviceSubtask.setLeaveicd10code(patMedInhosp1.getLeaveicd10code());
        serviceSubtask.setLeavediagname(patMedInhosp1.getLeavediagname());
        serviceSubtask.setTemplateid(serviceTask.getTemplateid());
        serviceSubtask.setTemplatename(serviceTask.getTemplatename());
smartor/src/main/java/com/smartor/service/impl/PatSpecificArchiveGenerateServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,329 @@
package com.smartor.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.outpatientvisit.domain.PatMedOuthosp;
import com.ruoyi.patarchive.domain.PatSpecificArchiveGenerateConfig;
import com.ruoyi.patarchive.mapper.PatArchiveMapper;
import com.ruoyi.patarchive.service.IPatSpecificArchiveGenerateConfigService;
import com.smartor.common.SpecificArchiveConstants;
import com.smartor.domain.PatChildArchiveDetail;
import com.smartor.domain.PatMaternityArchiveDetail;
import com.smartor.service.IPatChildArchiveService;
import com.smartor.service.IPatMaternityArchiveService;
import com.smartor.service.IPatSpecificArchiveGenerateService;
import lombok.extern.slf4j.Slf4j;
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.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
 * ä¸“科档案生成Service业务层处理。
 *
 * <p>只处理丽水市妇幼保健院(orgid = 47226079133110211G1001)的门诊接诊完成记录,
 * æŒ‰ {@code pat_specific_archive_generate_config} é…ç½®åŒ¹é…ç§‘室和诊断后,
 * ç”Ÿæˆå¯¹åº”类型的妇科档案或儿科档案。</p>
 */
@Slf4j
@Service
public class PatSpecificArchiveGenerateServiceImpl implements IPatSpecificArchiveGenerateService {
    /** éƒ¨é—¨åˆ†ç±»ï¼šå¦‡ç§‘ */
    private static final String DEPT_CATEGORY_GYNECOLOGY = "1";
    /** éƒ¨é—¨åˆ†ç±»ï¼šå„¿ç§‘ */
    private static final String DEPT_CATEGORY_PEDIATRICS = "2";
    /** éƒ¨é—¨ç±»åž‹ï¼šç§‘室 */
    private static final String DEPT_TYPE_DEPARTMENT = "1";
    /** æ¥æºç±»åž‹ï¼šé—¨è¯Šè®°å½• */
    private static final String SOURCE_TYPE_OUTHOSP = "1";
    @Autowired
    private IPatSpecificArchiveGenerateConfigService patSpecificArchiveGenerateConfigService;
    @Autowired
    private IPatMaternityArchiveService patMaternityArchiveService;
    @Autowired
    private IPatChildArchiveService patChildArchiveService;
    @Autowired
    private PatArchiveMapper patArchiveMapper;
    @Override
    public int generateByOuthosp(PatMedOuthosp patMedOuthosp, PatArchive patArchive) {
        if (patMedOuthosp == null) {
            return 0;
        }
        // ä»…丽水市妇幼保健院生效,避免影响其他机构
        if (!SpecificArchiveConstants.LISHUI_MCH_ORG_ID.equals(patMedOuthosp.getOrgid())) {
            return 0;
        }
        if (StringUtils.isBlank(patMedOuthosp.getOuthospno())) {
            log.warn("生成专科档案跳过:门诊编号为空,serialnum={}", patMedOuthosp.getSerialnum());
            return 0;
        }
        PatArchive targetArchive = resolvePatArchive(patMedOuthosp, patArchive);
        if (targetArchive == null && patMedOuthosp.getPatid() != null) {
            targetArchive = patArchiveMapper.selectPatArchiveByPatid(patMedOuthosp.getPatid());
        }
        Long patid = targetArchive != null ? targetArchive.getId() : patMedOuthosp.getPatid();
        if (patid == null) {
            log.warn("生成专科档案跳过:未找到患者档案,outhospno={},patno={}",
                    patMedOuthosp.getOuthospno(), patMedOuthosp.getPatno());
            return 0;
        }
        String campusid = StringUtils.isNotBlank(patMedOuthosp.getCampusid())
                ? patMedOuthosp.getCampusid() : "1";
        List<PatSpecificArchiveGenerateConfig> configs =
                patSpecificArchiveGenerateConfigService.selectEffectiveConfigList(patMedOuthosp.getOrgid(), campusid);
        if (CollectionUtils.isEmpty(configs)) {
            log.debug("生成专科档案跳过:机构未配置档案生成规则,orgid={},campusid={}",
                    patMedOuthosp.getOrgid(), campusid);
            return 0;
        }
        // åŒä¸€æ¡é—¨è¯Šè®°å½•、同一部门分类、同一档案类型只生成一次
        Set<String> generatedKeys = new HashSet<>();
        int generatedCount = 0;
        for (PatSpecificArchiveGenerateConfig config : configs) {
            if (!matchConfig(config, patMedOuthosp)) {
                continue;
            }
            String archiveType = config.getArchiveType();
            String deptCategory = config.getDeptCategory();
            if (StringUtils.isBlank(archiveType) || StringUtils.isBlank(deptCategory)) {
                log.warn("生成专科档案跳过:配置不完整,configId={},deptCategory={},archiveType={}",
                        config.getId(), deptCategory, archiveType);
                continue;
            }
            String generatedKey = deptCategory + "|" + archiveType;
            if (!generatedKeys.add(generatedKey)) {
                continue;
            }
            try {
                if (DEPT_CATEGORY_GYNECOLOGY.equals(deptCategory)) {
                    PatMaternityArchiveDetail detail =
                            buildMaternityDetail(patMedOuthosp, targetArchive, config, campusid);
                    patMaternityArchiveService.insertOrUpdateMaternityArchive(detail);
                    generatedCount++;
                } else if (DEPT_CATEGORY_PEDIATRICS.equals(deptCategory)) {
                    PatChildArchiveDetail detail =
                            buildChildDetail(patMedOuthosp, targetArchive, config, campusid);
                    patChildArchiveService.insertOrUpdateChildArchive(detail);
                    generatedCount++;
                } else {
                    log.warn("生成专科档案跳过:未知部门分类,configId={},deptCategory={}",
                            config.getId(), deptCategory);
                }
            } catch (Exception e) {
                log.error("生成专科档案失败,configId={},outhospno={},deptCategory={},archiveType={}",
                        config.getId(), patMedOuthosp.getOuthospno(), deptCategory, archiveType, e);
            }
        }
        return generatedCount;
    }
    /**
     * åˆ¤æ–­é—¨è¯Šè®°å½•是否命中配置。
     *
     * <p>部门条件与诊断条件为 OR å…³ç³»ï¼šä»»ä¸€æ¡ä»¶å‘½ä¸­å³ç”Ÿæˆï¼›ä¸¤ä¸ªé›†åˆéƒ½ä¸ºç©ºæ—¶é…ç½®æ— æ•ˆã€‚</p>
     */
    private boolean matchConfig(PatSpecificArchiveGenerateConfig config, PatMedOuthosp patMedOuthosp) {
        List<String> deptCodes = parseCodes(config.getDeptCodes());
        List<String> icd10Codes = parseCodes(config.getIcd10Codes());
        if (CollectionUtils.isEmpty(deptCodes) && CollectionUtils.isEmpty(icd10Codes)) {
            return false;
        }
        boolean deptMatched = false;
        if (CollectionUtils.isNotEmpty(deptCodes)) {
            // é—¨è¯Šè®°å½•只有科室,没有病区;dept_type=2(病区)不参与门诊匹配
            if (DEPT_TYPE_DEPARTMENT.equals(config.getDeptType())) {
                deptMatched = containsIgnoreCase(deptCodes, patMedOuthosp.getDeptcode());
            }
        }
        boolean diagnosisMatched = false;
        if (CollectionUtils.isNotEmpty(icd10Codes)) {
            String icd10code = patMedOuthosp.getIcd10code();
            String diagname = patMedOuthosp.getDiagname();
            diagnosisMatched = icd10Codes.stream().anyMatch(code -> {
                if (equalsIgnoreCase(code, icd10code)) {
                    return true;
                }
                // éƒ¨åˆ† HIS æ•°æ®åªæœ‰è¯Šæ–­åç§°æ²¡æœ‰è¯Šæ–­ç ï¼Œæ­¤æ—¶å…è®¸æŒ‰è¯Šæ–­åç§°å…œåº•匹配
                return StringUtils.isBlank(icd10code) && equalsIgnoreCase(code, diagname);
            });
        }
        return deptMatched || diagnosisMatched;
    }
    private PatMaternityArchiveDetail buildMaternityDetail(PatMedOuthosp patMedOuthosp, PatArchive patArchive,
                                                           PatSpecificArchiveGenerateConfig config, String campusid) {
        PatMaternityArchiveDetail detail = new PatMaternityArchiveDetail();
        detail.setArchiveType(config.getArchiveType());
        detail.setSourceType(SOURCE_TYPE_OUTHOSP);
        fillCommonDetail(detail, patMedOuthosp, patArchive, campusid);
        if (patArchive != null) {
            detail.setAddress(patArchive.getPlaceOfResidence());
        }
        return detail;
    }
    private PatChildArchiveDetail buildChildDetail(PatMedOuthosp patMedOuthosp, PatArchive patArchive,
                                                   PatSpecificArchiveGenerateConfig config, String campusid) {
        PatChildArchiveDetail detail = new PatChildArchiveDetail();
        detail.setArchiveType(config.getArchiveType());
        detail.setSourceType(SOURCE_TYPE_OUTHOSP);
        fillCommonChildDetail(detail, patMedOuthosp, patArchive, campusid);
        detail.setParentPhone(patArchive != null
                ? firstNotBlank(patArchive.getTelcode(), patMedOuthosp.getTelcode())
                : patMedOuthosp.getTelcode());
        if (patArchive != null) {
            detail.setParentAddress(patArchive.getPlaceOfResidence());
        }
        // è¥å…»æ€§ç–¾ç—…档案同步诊断信息,方便列表直接展示
        if ("1".equals(config.getArchiveType())) {
            detail.setNutritionIcd10Code(patMedOuthosp.getIcd10code());
            detail.setNutritionDiagName(patMedOuthosp.getDiagname());
        }
        return detail;
    }
    private void fillCommonDetail(PatMaternityArchiveDetail detail, PatMedOuthosp patMedOuthosp,
                                  PatArchive patArchive, String campusid) {
        detail.setOuthospno(patMedOuthosp.getOuthospno());
        detail.setSerialnum(patMedOuthosp.getSerialnum());
        detail.setPatid(patArchive != null ? patArchive.getId() : patMedOuthosp.getPatid());
        detail.setPatname(patArchive != null
                ? firstNotBlank(patArchive.getName(), patMedOuthosp.getPatname()) : patMedOuthosp.getPatname());
        detail.setBirthdate(patArchive != null ? patArchive.getBirthdate() : null);
        detail.setIdcardno(patArchive != null
                ? firstNotBlank(patArchive.getIdcardno(), patMedOuthosp.getIdcardno()) : patMedOuthosp.getIdcardno());
        detail.setPhone(patArchive != null
                ? firstNotBlank(patArchive.getTelcode(), patMedOuthosp.getTelcode()) : patMedOuthosp.getTelcode());
        detail.setSex(patArchive != null && patArchive.getSex() != null
                ? patArchive.getSex() : patMedOuthosp.getSex());
        detail.setDiagTime(patMedOuthosp.getAdmitdate());
        detail.setArchiveTime(new Date());
        detail.setDoctorCode(patMedOuthosp.getDrcode());
        detail.setDoctorName(patMedOuthosp.getDrname());
        detail.setManagementDoctorCode(patMedOuthosp.getDrcode());
        detail.setManagementDoctorName(patMedOuthosp.getDrname());
        detail.setFirstDoctorCode(patMedOuthosp.getDrcode());
        detail.setFirstDoctorName(patMedOuthosp.getDrname());
        detail.setDeptcode(patMedOuthosp.getDeptcode());
        detail.setDeptname(patMedOuthosp.getDeptname());
        detail.setIcd10code(patMedOuthosp.getIcd10code());
        detail.setDiagname(patMedOuthosp.getDiagname());
        detail.setRegisterUser(patMedOuthosp.getDrname());
        detail.setOrgid(patMedOuthosp.getOrgid());
        detail.setCampusid(campusid);
        detail.setDelFlag("0");
    }
    private void fillCommonChildDetail(PatChildArchiveDetail detail, PatMedOuthosp patMedOuthosp,
                                       PatArchive patArchive, String campusid) {
        detail.setOuthospno(patMedOuthosp.getOuthospno());
        detail.setSerialnum(patMedOuthosp.getSerialnum());
        detail.setPatid(patArchive != null ? patArchive.getId() : patMedOuthosp.getPatid());
        detail.setPatname(patArchive != null
                ? firstNotBlank(patArchive.getName(), patMedOuthosp.getPatname()) : patMedOuthosp.getPatname());
        detail.setBirthdate(patArchive != null ? patArchive.getBirthdate() : null);
        detail.setIdcardno(patArchive != null
                ? firstNotBlank(patArchive.getIdcardno(), patMedOuthosp.getIdcardno()) : patMedOuthosp.getIdcardno());
        detail.setPhone(patArchive != null
                ? firstNotBlank(patArchive.getTelcode(), patMedOuthosp.getTelcode()) : patMedOuthosp.getTelcode());
        detail.setSex(patArchive != null && patArchive.getSex() != null
                ? patArchive.getSex() : patMedOuthosp.getSex());
        detail.setDiagTime(patMedOuthosp.getAdmitdate());
        detail.setArchiveTime(new Date());
        detail.setDoctorCode(patMedOuthosp.getDrcode());
        detail.setDoctorName(patMedOuthosp.getDrname());
        detail.setManagementDoctorCode(patMedOuthosp.getDrcode());
        detail.setManagementDoctorName(patMedOuthosp.getDrname());
        detail.setDeptcode(patMedOuthosp.getDeptcode());
        detail.setDeptname(patMedOuthosp.getDeptname());
        detail.setIcd10code(patMedOuthosp.getIcd10code());
        detail.setDiagname(patMedOuthosp.getDiagname());
        detail.setRegisterUser(patMedOuthosp.getDrname());
        detail.setOrgid(patMedOuthosp.getOrgid());
        detail.setCampusid(campusid);
        detail.setDelFlag("0");
    }
    private PatArchive resolvePatArchive(PatMedOuthosp patMedOuthosp, PatArchive patArchive) {
        if (patArchive != null && patArchive.getId() != null) {
            return patArchive;
        }
        PatArchive query = new PatArchive();
        if (StringUtils.isNotBlank(patMedOuthosp.getPatno())) {
            query.setPatientno(patMedOuthosp.getPatno());
        } else if (StringUtils.isNotBlank(patMedOuthosp.getIdcardno())) {
            query.setIdcardno(patMedOuthosp.getIdcardno());
        } else {
            return null;
        }
        query.setOrgid(patMedOuthosp.getOrgid());
        List<PatArchive> archives = patArchiveMapper.selectPatArchiveList(query);
        return CollectionUtils.isNotEmpty(archives) ? archives.get(0) : null;
    }
    private List<String> parseCodes(String raw) {
        if (StringUtils.isBlank(raw)) {
            return Collections.emptyList();
        }
        String value = raw.trim();
        try {
            JSONArray array = JSON.parseArray(value);
            if (array != null) {
                List<String> result = new ArrayList<>();
                for (Object item : array) {
                    if (item != null && StringUtils.isNotBlank(String.valueOf(item))) {
                        result.add(String.valueOf(item).trim());
                    }
                }
                return result;
            }
        } catch (Exception e) {
            log.warn("解析档案生成配置集合失败,按逗号分隔处理:{}", raw);
        }
        return Arrays.stream(value.split(","))
                .map(String::trim)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toList());
    }
    private String firstNotBlank(String first, String second) {
        return StringUtils.isNotBlank(first) ? first : second;
    }
    private boolean containsIgnoreCase(List<String> source, String target) {
        if (StringUtils.isBlank(target)) {
            return false;
        }
        return source.stream().anyMatch(item -> equalsIgnoreCase(item, target));
    }
    private boolean equalsIgnoreCase(String first, String second) {
        return first != null && second != null && first.trim().equalsIgnoreCase(second.trim());
    }
}
smartor/src/main/java/com/smartor/service/impl/ServiceExternalServiceImpl.java
@@ -19,9 +19,8 @@
import com.ruoyi.system.service.ISysConfigService;
import com.smartor.domain.*;
import com.smartor.mapper.*;
import com.smartor.service.IPatChildArchiveService;
import com.smartor.service.IPatMaternityArchiveService;
import com.smartor.service.IPatMedOuthospService;
import com.smartor.service.IPatSpecificArchiveGenerateService;
import com.smartor.service.IServiceExternalService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
@@ -69,6 +68,11 @@
@Slf4j
@Service
public class ServiceExternalServiceImpl implements IServiceExternalService {
    /**
     * é¾™æ³‰äººæ°‘医院机构ID
     */
    private static final String LONGQUAN_PEOPLE_HOSPITAL_ORG_ID = "47240004533118111A1001";
    @Autowired
    private SysDept2Mapper sysDeptMapper;
@@ -100,9 +104,7 @@
    @Autowired
    private ISysConfigService configService;
    @Autowired
    private IPatMaternityArchiveService patMaternityArchiveService;
    @Autowired
    private IPatChildArchiveService patChildArchiveService;
    private IPatSpecificArchiveGenerateService patSpecificArchiveGenerateService;
    @Autowired
    private PatMedOuthospMapper patMedOuthospMapper;
    @Autowired
@@ -1842,9 +1844,66 @@
        } else {
            patMedInhosp.setCreateTime(new Date());
            i = inhospVisitFacade.insertPatMedInhosp(patMedInhosp);
            if (i == 1) {
                //龙泉人民医院:新增住院信息时处理患者再入院
                stopReadmissionFollowUpTasks(patMedInhosp);
            }
        }
        if (i == 1) return true;
        return false;
    }
    /**
     * æ‚£è€…再入院处理:患者再次入院后,上一次出院生成的待执行长期随访任务不再执行。
     * ç›®å‰ä»…对龙泉人民医院生效,避免影响其他机构。
     *
     * @param patMedInhosp æœ¬æ¬¡æ–°å¢žçš„住院信息
     */
    private void stopReadmissionFollowUpTasks(PatMedInhosp patMedInhosp) {
        if (!LONGQUAN_PEOPLE_HOSPITAL_ORG_ID.equals(patMedInhosp.getOrgid())) {
            return;
        }
        if (Objects.isNull(patMedInhosp.getPatid()) || Objects.isNull(patMedInhosp.getStarttime())) {
            log.warn("龙泉人民医院患者再入院处理跳过,patid或入院时间为空,inhospno={}", patMedInhosp.getInhospno());
            return;
        }
        try {
            ServiceSubtaskEntity subtask = new ServiceSubtaskEntity();
            subtask.setPatid(patMedInhosp.getPatid());
            subtask.setOrgid(LONGQUAN_PEOPLE_HOSPITAL_ORG_ID);
            subtask.setSendstate(2L);
            List<ServiceSubtask> serviceSubtaskList = serviceSubtaskMapper.selectServiceSubtaskList(subtask);
            if (CollectionUtils.isEmpty(serviceSubtaskList)) {
                return;
            }
            int stopCount = 0;
            for (ServiceSubtask serviceSubtask : serviceSubtaskList) {
                //不是长期任务,不处理
                if (Objects.isNull(serviceSubtask.getLongSendTime())) {
                    continue;
                }
                //出院时间晚于或等于本次入院时间的任务,不属于本次入院前的待随访任务
                if (Objects.isNull(serviceSubtask.getEndtime())
                        || !serviceSubtask.getEndtime().before(patMedInhosp.getStarttime())) {
                    continue;
                }
                serviceSubtask.setSendstate(4L);
                serviceSubtask.setIsVisitAgain(2);
                serviceSubtask.setResult("error");
                serviceSubtask.setRemark(StringUtils.isNotEmpty(serviceSubtask.getRemark())
                        ? serviceSubtask.getRemark() + ";患者再入院" : "患者再入院");
                serviceSubtask.setUpdateTime(new Date());
                if (Boolean.TRUE.equals(serviceSubtaskMapper.updateServiceSubtask(serviceSubtask))) {
                    stopCount++;
                }
            }
            log.info("龙泉人民医院患者再入院处理完成,patid={},本次入院时间={},停止待执行长期任务{}条",
                    patMedInhosp.getPatid(), patMedInhosp.getStarttime(), stopCount);
        } catch (Exception e) {
            //再入院任务处理失败不影响本次住院信息入库
            log.error("龙泉人民医院患者再入院处理异常,patid={},inhospno={}",
                    patMedInhosp.getPatid(), patMedInhosp.getInhospno(), e);
        }
    }
    //新增随访任务同步
@@ -2480,61 +2539,20 @@
        return 1;
    }
    //新增患者专科档案基本信息
    /**
     * å®ŒæˆæŽ¥è¯Šæ—¶æŒ‰ä¸“科档案生成配置生成妇科/儿科档案。
     *
     * <p>生成逻辑内部只对指定机构生效,未命中配置或生成失败均不影响接诊主流程。</p>
     */
    private void genePatSpecificArchive(PatMedOuthosp patMedOuthosp, PatArchive patArchive) {
        String deptCategory = judgeDeptCategory(patMedOuthosp.getDeptcode(), patMedOuthosp.getOrgid());
        boolean isGynecology = "1".equals(deptCategory);  // æ˜¯å¦å¦‡ç§‘
        boolean isPediatrics = "2".equals(deptCategory);  // æ˜¯å¦å„¿ç§‘
        if (isGynecology) {
            // èµ°å¦‡ç§‘档案生成逻辑
            PatMaternityArchiveDetail patMaternityArchiveDetail
                    = DtoConversionUtils.sourceToTarget(patMedOuthosp, PatMaternityArchiveDetail.class);
            patMaternityArchiveDetail.setSourceType("1");
            patMaternityArchiveDetail.setPhone(patArchive.getTelcode());
            patMaternityArchiveDetail.setPatid(patArchive.getId());
            patMaternityArchiveDetail.setPatname(patArchive.getName());
            patMaternityArchiveDetail.setBirthdate(patArchive.getBirthdate());
            patMaternityArchiveDetail.setSex(patArchive.getSex());
            patMaternityArchiveDetail.setAddress(patArchive.getPlaceOfResidence());
            patMaternityArchiveDetail.setOuthospno(patMedOuthosp.getOuthospno());
            patMaternityArchiveDetail.setSerialnum(patMedOuthosp.getSerialnum());
            patMaternityArchiveDetail.setIcd10code(patMedOuthosp.getIcd10code());
            patMaternityArchiveDetail.setDeptcode(patMedOuthosp.getDeptcode());
            patMaternityArchiveDetail.setDeptname(patMedOuthosp.getDeptname());
            patMaternityArchiveDetail.setDoctorName(patMedOuthosp.getDrname());
            patMaternityArchiveDetail.setDoctorCode(patMedOuthosp.getDrcode());
            patMaternityArchiveDetail.setManagementDoctorName(patMedOuthosp.getDrname());
            patMaternityArchiveDetail.setManagementDoctorCode(patMedOuthosp.getDrcode());
            patMaternityArchiveDetail.setOrgid(patMedOuthosp.getOrgid());
            patMaternityArchiveDetail.setCampusid(patMedOuthosp.getCampusid());
            patMaternityArchiveService.insertOrUpdateMaternityArchive(patMaternityArchiveDetail);
        } else if (isPediatrics) {
            // èµ°å„¿ç§‘档案生成逻辑
            PatChildArchiveDetail patChildArchiveDetail
                    = DtoConversionUtils.sourceToTarget(patMedOuthosp, PatChildArchiveDetail.class);
            patChildArchiveDetail.setSourceType("1");
            patChildArchiveDetail.setPhone(patArchive.getTelcode());
            patChildArchiveDetail.setPatid(patArchive.getId());
            patChildArchiveDetail.setPatname(patArchive.getName());
            patChildArchiveDetail.setBirthdate(patArchive.getBirthdate());
            patChildArchiveDetail.setSex(patArchive.getSex());
//            patChildArchiveDetail.setAddress(patArchive.getPlaceOfResidence());
            patChildArchiveDetail.setOuthospno(patMedOuthosp.getOuthospno());
            patChildArchiveDetail.setSerialnum(patMedOuthosp.getSerialnum());
            patChildArchiveDetail.setIcd10code(patMedOuthosp.getIcd10code());
            patChildArchiveDetail.setDeptcode(patMedOuthosp.getDeptcode());
            patChildArchiveDetail.setDeptname(patMedOuthosp.getDeptname());
            patChildArchiveDetail.setDoctorName(patMedOuthosp.getDrname());
            patChildArchiveDetail.setDoctorCode(patMedOuthosp.getDrcode());
            patChildArchiveDetail.setManagementDoctorName(patMedOuthosp.getDrname());
            patChildArchiveDetail.setManagementDoctorCode(patMedOuthosp.getDrcode());
            patChildArchiveDetail.setOrgid(patMedOuthosp.getOrgid());
            patChildArchiveDetail.setCampusid(patMedOuthosp.getCampusid());
            patChildArchiveService.insertOrUpdateChildArchive(patChildArchiveDetail);
        } else {
            // æœªå‘½ä¸­é…ç½®ï¼Œä¸ç”Ÿæˆæ¡£æ¡ˆ
        try {
            int generated = patSpecificArchiveGenerateService.generateByOuthosp(patMedOuthosp, patArchive);
            if (generated > 0) {
                log.info("完成接诊自动生成专科档案成功,outhospno={},生成数量={}",
                        patMedOuthosp.getOuthospno(), generated);
            }
        } catch (Exception e) {
            log.error("完成接诊自动生成专科档案失败,outhospno={}", patMedOuthosp.getOuthospno(), e);
        }
    }
sql/pat_specific_archive_generate_config_lishui_mch_20261104.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,95 @@
-- =============================================================================
-- ä¸½æ°´å¸‚妇幼保健院 ä¸“科档案生成配置(依据五个完成接诊入参)
-- orgid : 47226079133110211G1001
-- date  : 2026-11-04
-- è¯´æ˜Ž  :
--   1. å®ŒæˆæŽ¥è¯Šæ¶ˆæ¯ JZ_MZ_WanChengJZ å†™å…¥ pat_med_outhosp åŽï¼ŒæŒ‰æœ¬é…ç½®ç”Ÿæˆæ¡£æ¡ˆã€‚
--   2. dept_codes / icd10_codes å‡ä¸º JSON æ•°ç»„字符串。
--   3. åŒ¹é…å…³ç³»ä¸º OR:部门条件或诊断条件任一命中即生成。
--   4. è„šæœ¬æŒ‰ remark åšå¹‚等,重复执行不会重复插入。
-- =============================================================================
-- 1. å¦‡ç§‘-早孕关爱
--    å…¥å‚:JiuZhenKSID=010104010601(早孕关爱门诊),主诊断 Icd10=O26.900x206(孕19周)
INSERT INTO pat_specific_archive_generate_config
(dept_type, dept_category, archive_type, dept_codes, icd10_codes, remark,
 orgid, campusid, create_by, create_time, del_flag)
SELECT '1', '1', '1', '["010104010601"]', '["O26.900x206"]',
       '丽水妇幼-早孕关爱(完成接诊入参)',
       '47226079133110211G1001', '1', 'system', NOW(), '0'
FROM DUAL
WHERE NOT EXISTS (
    SELECT 1 FROM pat_specific_archive_generate_config
    WHERE orgid = '47226079133110211G1001'
      AND remark = '丽水妇幼-早孕关爱(完成接诊入参)'
);
-- 2. å¦‡ç§‘-围绝经期 MHI
--    å…¥å‚:JiuZhenKSID=010102010207(妇科内分泌门诊),主诊断 Icd10=N95.900
INSERT INTO pat_specific_archive_generate_config
(dept_type, dept_category, archive_type, dept_codes, icd10_codes, remark,
 orgid, campusid, create_by, create_time, del_flag)
SELECT '1', '1', '2', '["010102010207"]', '["N95.900"]',
       '丽水妇幼-围绝经期MHI(完成接诊入参)',
       '47226079133110211G1001', '1', 'system', NOW(), '0'
FROM DUAL
WHERE NOT EXISTS (
    SELECT 1 FROM pat_specific_archive_generate_config
    WHERE orgid = '47226079133110211G1001'
      AND remark = '丽水妇幼-围绝经期MHI(完成接诊入参)'
);
-- 3. å„¿ç§‘-营养性疾病
--    å…¥å‚:主诊断 Icd10=E46.x00x003(营养不良)
--    è¯´æ˜Žï¼šè¯¥æ ·ä¾‹ç§‘室为“儿童心理/语言/行为发育门诊”,科室不具备区分度,因此只按诊断匹配。
INSERT INTO pat_specific_archive_generate_config
(dept_type, dept_category, archive_type, dept_codes, icd10_codes, remark,
 orgid, campusid, create_by, create_time, del_flag)
SELECT '1', '2', '1', NULL, '["E46.x00x003"]',
       '丽水妇幼-儿科营养性疾病(完成接诊入参)',
       '47226079133110211G1001', '1', 'system', NOW(), '0'
FROM DUAL
WHERE NOT EXISTS (
    SELECT 1 FROM pat_specific_archive_generate_config
    WHERE orgid = '47226079133110211G1001'
      AND remark = '丽水妇幼-儿科营养性疾病(完成接诊入参)'
);
-- 4. å„¿ç§‘-学习困难
--    å…¥å‚:主诊断 Icd10=F81.900(学习技能发育障碍),子诊断含 F90.000(活动与注意失调)
--    è¯´æ˜Žï¼šè¯¥æ ·ä¾‹ç§‘室为“儿童保健门诊”,科室不具备区分度,因此只按诊断匹配。
INSERT INTO pat_specific_archive_generate_config
(dept_type, dept_category, archive_type, dept_codes, icd10_codes, remark,
 orgid, campusid, create_by, create_time, del_flag)
SELECT '1', '2', '2', NULL, '["F81.900","F90.000"]',
       '丽水妇幼-儿科学习困难(完成接诊入参)',
       '47226079133110211G1001', '1', 'system', NOW(), '0'
FROM DUAL
WHERE NOT EXISTS (
    SELECT 1 FROM pat_specific_archive_generate_config
    WHERE orgid = '47226079133110211G1001'
      AND remark = '丽水妇幼-儿科学习困难(完成接诊入参)'
);
-- 5. å„¿ç§‘-高危儿专案
--    å…¥å‚:JiuZhenKSID=010104010405(早产儿/高危儿门诊),主诊断 Icd10=P96.800x904(高危儿)
INSERT INTO pat_specific_archive_generate_config
(dept_type, dept_category, archive_type, dept_codes, icd10_codes, remark,
 orgid, campusid, create_by, create_time, del_flag)
SELECT '1', '2', '3', '["010104010405"]', '["P96.800x904"]',
       '丽水妇幼-儿科高危儿专案(完成接诊入参)',
       '47226079133110211G1001', '1', 'system', NOW(), '0'
FROM DUAL
WHERE NOT EXISTS (
    SELECT 1 FROM pat_specific_archive_generate_config
    WHERE orgid = '47226079133110211G1001'
      AND remark = '丽水妇幼-儿科高危儿专案(完成接诊入参)'
);
-- æ ¸éªŒï¼šåº”返回 5 æ¡é…ç½®
SELECT id, dept_category, archive_type, dept_codes, icd10_codes, remark
FROM pat_specific_archive_generate_config
WHERE orgid = '47226079133110211G1001'
  AND del_flag = '0'
  AND remark LIKE '丽水妇幼-%(完成接诊入参)'
ORDER BY dept_category, archive_type;