陈昶聿
2 天以前 cf8712a9c0816559f50644f15672e7359028c4c4
妇幼-住院完成生成
已修改21个文件
946 ■■■■■ 文件已修改
ruoyi-admin/src/main/java/com/ruoyi/web/component/RedisMqReceiver.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/api/TaskSubtaskApi.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/api/contract/TaskSubtaskApiImpl.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskServiceImpl.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskMapper.xml 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/common/SpecificArchiveConstants.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/PatChildArchive.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/PatChildArchiveDetail.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/PatMaternityArchive.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/PatMaternityArchiveDetail.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/IPatSpecificArchiveGenerateService.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/PatChildArchiveServiceImpl.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/PatMaternityArchiveServiceImpl.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/PatSpecificArchiveGenerateServiceImpl.java 601 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskAnswerServiceImpl.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/PatChildArchiveDetailMapper.xml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/PatChildArchiveMapper.xml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/PatMaternityArchiveDetailMapper.xml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/PatMaternityArchiveMapper.xml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/component/RedisMqReceiver.java
@@ -56,6 +56,7 @@
import com.ruoyi.task.domain.SendMagParam;
import com.ruoyi.task.service.IServiceSubtaskRecordService;
import com.ruoyi.task.mapper.ServiceSubtaskPreachformMapper;
import com.ruoyi.task.event.FollowupCompletedEventPublisher;
import com.ruoyi.patarchive.mapper.PatArchiveMapper;
@Slf4j
@Component//监听此队列
@@ -72,6 +73,9 @@
    @Value("${req_path}")
    private String req_path;
    @Autowired
    private FollowupCompletedEventPublisher followupCompletedEventPublisher;
    @Autowired
    private IServiceOutPathService iServiceOutPathService;
@@ -898,6 +902,9 @@
            serviceSubtask.setSendstate(6L);
        }
        serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
        if (Long.valueOf(6L).equals(serviceSubtask.getSendstate())) {
            followupCompletedEventPublisher.publish(serviceSubtask.getId());
        }
        // 更新当前发送方式明细;发送成功时允许当前行已经是 sendstate=2。
        currentPreachformRow.setSendstate(failSendstate);
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java
@@ -149,6 +149,12 @@
    private ICollectHISService ichService;
    @Autowired
    private IPatSpecificArchiveGenerateService patSpecificArchiveGenerateService;
    @Autowired
    private com.ruoyi.task.event.FollowupCompletedEventPublisher followupCompletedEventPublisher;
    @Autowired
    private ISysConfigService configService;
    @Autowired
@@ -1203,6 +1209,7 @@
                if (finishState) {
                    serviceSubtask.setSendstate(6L);
                    serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
                    followupCompletedEventPublisher.publish(serviceSubtask.getId());
                    continue;
                }
@@ -1715,6 +1722,7 @@
                //说明,在某种方式发送失败后,又成功了(例如短信或公众号,在规定时间外患者做完了,这个时候,状态改成了9)
                serviceSubtask.setSendstate(6L);
                serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
                followupCompletedEventPublisher.publish(serviceSubtask.getId());
                return "-1";
            }
            // 当sort等于列表size时,并且所有sendstate都不等于9,则返回-1
@@ -1942,6 +1950,9 @@
        serviceSubtask.setRemark(StringUtils.isNotEmpty(serviceSubtask.getRemark()) ? serviceSubtask.getRemark() + "," + remark : remark);
        log.info("serviceSubtask的入参是:{}",serviceSubtask);
        serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
        if (Long.valueOf(6L).equals(serviceSubtask.getSendstate())) {
            followupCompletedEventPublisher.publish(serviceSubtask.getId());
        }
        return true;
    }
@@ -2197,4 +2208,18 @@
        }
    }
    /**
     * 住院随访完成生成专科档案补偿任务。
     *
     * <p>扫描近 7 天已完成但未生成日志的住院随访,按配置补生成妇科/儿科档案。</p>
     */
    public void generateInhospFollowupArchive() {
        try {
            int generated = patSpecificArchiveGenerateService.compensateInhospFollowups();
            log.info("【generateInhospFollowupArchive】住院随访专科档案补偿完成,生成数量={}", generated);
        } catch (Exception e) {
            log.error("【generateInhospFollowupArchive】住院随访专科档案补偿失败", e);
        }
    }
}
ruoyi-task/src/main/java/com/ruoyi/task/api/TaskSubtaskApi.java
@@ -53,6 +53,19 @@
    List<ServiceSubtask> findSubtasks(ServiceSubtaskEntity query);
    /**
     * 查询已完成、指定机构/服务类型、完成时间在 startTime 之后的住院随访子任务。
     * 供住院随访档案补偿任务使用。
     *
     * @param orgid        机构ID
     * @param serviceTypes 服务类型白名单
     * @param startTime    完成时间下限
     * @param limit        最大条数
     * @return 子任务列表
     */
    List<ServiceSubtask> findCompletedInhospFollowups(String orgid, List<String> serviceTypes,
                                                      java.util.Date startTime, int limit);
    /**
     * 更新子任务(原 {@code ServiceSubtaskMapper#updateServiceSubtask},动态 SQL 只改非空字段)
     *
     * @param subtask 待更新对象(必须带 id)
ruoyi-task/src/main/java/com/ruoyi/task/api/contract/TaskSubtaskApiImpl.java
@@ -12,6 +12,7 @@
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
import com.ruoyi.task.service.IServiceSubtaskDetailTraceService;
import com.ruoyi.task.service.IServiceTaskService;
import com.ruoyi.task.event.FollowupCompletedEventPublisher;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -49,6 +50,9 @@
    @Autowired
    private IServiceSubtaskDetailTraceService traceService;
    @Autowired
    private FollowupCompletedEventPublisher followupCompletedEventPublisher;
    @Override
    public ServiceSubtask getSubtask(Long subId) {
        return serviceSubtaskMapper.selectServiceSubtaskById(subId);
@@ -60,8 +64,18 @@
    }
    @Override
    public List<ServiceSubtask> findCompletedInhospFollowups(String orgid, List<String> serviceTypes,
                                                             java.util.Date startTime, int limit) {
        return serviceSubtaskMapper.selectCompletedInhospFollowups(orgid, serviceTypes, startTime, limit);
    }
    @Override
    public int updateSubtask(ServiceSubtask subtask) {
        Boolean updated = serviceSubtaskMapper.updateServiceSubtask(subtask);
        if (Boolean.TRUE.equals(updated) && subtask.getSendstate() != null
                && subtask.getSendstate() == 6 && subtask.getId() != null) {
            followupCompletedEventPublisher.publish(subtask.getId());
        }
        return Boolean.TRUE.equals(updated) ? 1 : 0;
    }
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskMapper.java
@@ -48,6 +48,20 @@
    public List<ServiceSubtask> selectServiceSubtaskList(ServiceSubtaskEntity serviceSubtaskVO);
    /**
     * 查询已完成、且尚未被专科档案生成日志处理的住院随访子任务(补偿扫描用)。
     *
     * @param orgid        机构ID
     * @param serviceTypes 服务类型白名单
     * @param startTime    完成时间下限
     * @param limit        最大条数
     * @return 子任务集合
     */
    public List<ServiceSubtask> selectCompletedInhospFollowups(@Param("orgid") String orgid,
                                                               @Param("serviceTypes") List<String> serviceTypes,
                                                               @Param("startTime") Date startTime,
                                                               @Param("limit") int limit);
    /**
     * 分页第一阶段:只取当前页的子任务 id。
     * <p>
     * where / order 与 {@link #selectServiceSubtaskList} 共用同一份 XML 片段
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskServiceImpl.java
@@ -126,6 +126,7 @@
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.mapper.ext.SysDept2Mapper;
import com.ruoyi.task.api.contract.OperationSubtaskWriter;
import com.ruoyi.task.event.FollowupCompletedEventPublisher;
/**
 * 单一任务(随访)Service业务层处理
@@ -145,6 +146,9 @@
     */
    @Autowired
    private OperationSubtaskWriter operationSubtaskWriter;
    @Autowired
    private FollowupCompletedEventPublisher followupCompletedEventPublisher;
    /**
     * 满意度模块对外门面(常驻 Bean)。
@@ -1544,7 +1548,12 @@
        if (serviceSubtask.getSendstate() != null && serviceSubtask.getSendstate() == 6)
            serviceSubtask.setFinishtime(new Date());
        return serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
        Boolean updated = serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
        if (Boolean.TRUE.equals(updated) && serviceSubtask.getSendstate() != null
                && serviceSubtask.getSendstate() == 6 && serviceSubtask.getId() != null) {
            followupCompletedEventPublisher.publish(serviceSubtask.getId());
        }
        return updated;
    }
    @Override
@@ -4817,6 +4826,7 @@
            serviceSubtask.setSendstate(6L);
            serviceSubtask.setPreachform("1");
            serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
            followupCompletedEventPublisher.publish(serviceSubtask.getId());
        }
    }
@@ -4961,6 +4971,7 @@
        if (subtaskUpdated == 0) {
            throw new BaseException("微信公众号默认完成失败:子任务状态已变化,subid=" + current.getId());
        }
        followupCompletedEventPublisher.publish(current.getId());
        log.info("【微信公众号默认完成】subid:{},已按默认选项完成,填充题目数:{}", current.getId(), filled);
        return true;
ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskMapper.xml
@@ -1100,6 +1100,23 @@
        where id = #{id}
    </select>
    <select id="selectCompletedInhospFollowups" resultMap="ServiceSubtaskResult">
        <include refid="selectServiceSubtaskVo"/>
        where del_flag = '0'
          and sendstate = 6
          and hosp_type in ('2', '6')
          and orgid = #{orgid}
          and finishtime &gt;= #{startTime}
        <if test="serviceTypes != null and serviceTypes.size() > 0">
            and service_type in
            <foreach collection="serviceTypes" item="serviceType" open="(" separator="," close=")">
                #{serviceType}
            </foreach>
        </if>
        order by id asc
        limit #{limit}
    </select>
    <select id="selectServiceSubtaskByDiagname" parameterType="com.ruoyi.task.domain.ServiceSubtask"
            resultMap="ServiceSubtaskResult">
        select sendstate,starttime,endtime,nurse_name,phone,sex,task_name,sendname,leavediagname,visit_time FROM
smartor/src/main/java/com/smartor/common/SpecificArchiveConstants.java
@@ -16,6 +16,36 @@
     */
    public static final String DEFAULT_CAMPUS_ID = "1";
    /** 来源类型:门诊接诊完成 */
    public static final String SOURCE_TYPE_OUTHOSP = "1";
    /** 来源类型:住院随访完成 */
    public static final String SOURCE_TYPE_INHOSP_FOLLOWUP = "2";
    /** 来源类型:手工新增/手工导入 */
    public static final String SOURCE_TYPE_MANUAL = "3";
    /** 部门分类:妇科 */
    public static final String DEPT_CATEGORY_GYNECOLOGY = "1";
    /** 部门分类:儿科 */
    public static final String DEPT_CATEGORY_PEDIATRICS = "2";
    /** 生成结果:已生成 */
    public static final String RESULT_GENERATED = "1";
    /** 生成结果:患者已有对应档案,跳过 */
    public static final String RESULT_EXISTS = "2";
    /** 生成结果:未命中配置 */
    public static final String RESULT_NO_MATCH = "3";
    /** 生成结果:失败 */
    public static final String RESULT_FAILED = "4";
    /** 住院随访来源的默认配置ID(未命中配置时写日志用) */
    public static final int NO_CONFIG_ID = 0;
    private SpecificArchiveConstants() {
    }
}
smartor/src/main/java/com/smartor/domain/PatChildArchive.java
@@ -30,6 +30,23 @@
    @ApiModelProperty(value = "儿科档案类型: 1 营养性疾病2 学习困难3 高危儿专案")
    private String archiveType;
    /** 来源类型:1门诊接诊 2住院随访 3手工新增 */
    @Excel(name = "来源类型")
    @ApiModelProperty(value = "来源类型:1门诊接诊 2住院随访 3手工新增")
    private String sourceType;
    /** 来源业务ID:门诊记录ID/住院记录ID */
    @ApiModelProperty(value = "来源业务ID:门诊记录ID/住院记录ID")
    private Long sourceBizId;
    /** 来源业务编号:门诊号/住院号 */
    @ApiModelProperty(value = "来源业务编号:门诊号/住院号")
    private String sourceBizNo;
    /** 来源随访子任务ID:住院随访生成时记录触发档案生成的 service_subtask.id */
    @ApiModelProperty(value = "来源随访子任务ID:住院随访生成时记录触发档案生成的 service_subtask.id")
    private Long sourceSubId;
    /** 门诊编号 */
    @Excel(name = "门诊编号")
    @ApiModelProperty(value = "门诊编号")
smartor/src/main/java/com/smartor/domain/PatChildArchiveDetail.java
@@ -35,11 +35,23 @@
    @ApiModelProperty(value = "档案类型: 1 营养性疾病2 学习困难3 高危儿专案")
    private String archiveType;
    /** 来源类型: 1 门诊记录 */
    /** 来源类型:1门诊接诊 2住院随访 3手工新增 */
    @Excel(name = "来源类型")
    @ApiModelProperty(value = "来源类型: 1 门诊记录")
    @ApiModelProperty(value = "来源类型:1门诊接诊 2住院随访 3手工新增")
    private String sourceType;
    /** 来源业务ID:门诊记录ID/住院记录ID */
    @ApiModelProperty(value = "来源业务ID:门诊记录ID/住院记录ID")
    private Long sourceBizId;
    /** 来源业务编号:门诊号/住院号 */
    @ApiModelProperty(value = "来源业务编号:门诊号/住院号")
    private String sourceBizNo;
    /** 来源随访子任务ID:住院随访生成时记录触发档案生成的 service_subtask.id */
    @ApiModelProperty(value = "来源随访子任务ID:住院随访生成时记录触发档案生成的 service_subtask.id")
    private Long sourceSubId;
    /** 门诊编号 */
    @Excel(name = "门诊编号")
    @ApiModelProperty(value = "门诊编号")
smartor/src/main/java/com/smartor/domain/PatMaternityArchive.java
@@ -29,6 +29,23 @@
    @ApiModelProperty(value = "妇幼档案类型: 1 早孕关爱2 围绝经期MHI")
    private String archiveType;
    /** 来源类型:1门诊接诊 2住院随访 3手工新增 */
    @Excel(name = "来源类型")
    @ApiModelProperty(value = "来源类型:1门诊接诊 2住院随访 3手工新增")
    private String sourceType;
    /** 来源业务ID:门诊记录ID/住院记录ID */
    @ApiModelProperty(value = "来源业务ID:门诊记录ID/住院记录ID")
    private Long sourceBizId;
    /** 来源业务编号:门诊号/住院号 */
    @ApiModelProperty(value = "来源业务编号:门诊号/住院号")
    private String sourceBizNo;
    /** 来源随访子任务ID:住院随访生成时记录触发档案生成的 service_subtask.id */
    @ApiModelProperty(value = "来源随访子任务ID:住院随访生成时记录触发档案生成的 service_subtask.id")
    private Long sourceSubId;
    /** 门诊编号 */
    @Excel(name = "门诊编号")
    @ApiModelProperty(value = "门诊编号")
smartor/src/main/java/com/smartor/domain/PatMaternityArchiveDetail.java
@@ -34,11 +34,23 @@
    @ApiModelProperty(value = "档案类型: 1 早孕关爱2 围绝经期MHI")
    private String archiveType;
    /** 来源类型: 1 门诊记录 */
    /** 来源类型:1门诊接诊 2住院随访 3手工新增 */
    @Excel(name = "来源类型")
    @ApiModelProperty(value = "来源类型: 1 门诊记录")
    @ApiModelProperty(value = "来源类型:1门诊接诊 2住院随访 3手工新增")
    private String sourceType;
    /** 来源业务ID:门诊记录ID/住院记录ID */
    @ApiModelProperty(value = "来源业务ID:门诊记录ID/住院记录ID")
    private Long sourceBizId;
    /** 来源业务编号:门诊号/住院号 */
    @ApiModelProperty(value = "来源业务编号:门诊号/住院号")
    private String sourceBizNo;
    /** 来源随访子任务ID:住院随访生成时记录触发档案生成的 service_subtask.id */
    @ApiModelProperty(value = "来源随访子任务ID:住院随访生成时记录触发档案生成的 service_subtask.id")
    private Long sourceSubId;
    /** 门诊编号 */
    @Excel(name = "门诊编号")
    @ApiModelProperty(value = "门诊编号")
smartor/src/main/java/com/smartor/service/IPatSpecificArchiveGenerateService.java
@@ -19,4 +19,22 @@
     * @return 本次生成/更新的档案数量
     */
    int generateByOuthosp(PatMedOuthosp patMedOuthosp, PatArchive patArchive);
    /**
     * 根据住院随访子任务生成专科档案。
     *
     * <p>仅处理已完成的住院随访(hosp_type 2/6 + service_type 白名单),
     * 且仅对丽水市妇幼保健院生效。生成前按“患者 + 专科 + 档案类型”判断是否已有档案。</p>
     *
     * @param subId 随访子任务ID
     * @return 本次生成数量
     */
    int generateByInhospFollowup(Long subId);
    /**
     * 补偿扫描:查询近 7 天已完成但尚未处理的住院随访并尝试生成档案。
     *
     * @return 本次生成数量
     */
    int compensateInhospFollowups();
}
smartor/src/main/java/com/smartor/service/impl/PatChildArchiveServiceImpl.java
@@ -88,8 +88,9 @@
            log.error("insertOrUpdateChildArchive入参为空,请检查入参");
            throw new BaseException("儿科档案入参为空,请检查入参");
        }
        if (StringUtils.isEmpty(patChildArchiveDetail.getOuthospno())) {
            throw new BaseException("儿科档案入参门诊编号outhospno为空,请检查入参门诊编号");
        if (StringUtils.isEmpty(patChildArchiveDetail.getOuthospno())
                && StringUtils.isEmpty(patChildArchiveDetail.getInhospno())) {
            throw new BaseException("儿科档案入参门诊编号outhospno和住院编号inhospno不能同时为空");
        }
        if (StringUtils.isEmpty(patChildArchiveDetail.getArchiveType())) {
            throw new BaseException("儿科档案入参档案类型archiveType为空,请检查入参档案类型");
@@ -100,6 +101,7 @@
        key.setOrgid(patChildArchiveDetail.getOrgid());
        key.setPatid(patChildArchiveDetail.getPatid());
        key.setOuthospno(patChildArchiveDetail.getOuthospno());
        key.setInhospno(patChildArchiveDetail.getInhospno());
        key.setArchiveType(patChildArchiveDetail.getArchiveType());
        List<PatChildArchiveDetail> olds =
                patChildArchiveDetailService.selectPatChildArchiveDetailList(key);
@@ -117,6 +119,7 @@
            mainKey.setOrgid(patChildArchiveDetail.getOrgid());
            mainKey.setPatid(patChildArchiveDetail.getPatid());
            mainKey.setOuthospno(patChildArchiveDetail.getOuthospno());
            mainKey.setInhospno(patChildArchiveDetail.getInhospno());
            mainKey.setArchiveType(patChildArchiveDetail.getArchiveType());
            List<PatChildArchive> mainOlds =
                    patChildArchiveMapper.selectPatChildArchiveList(mainKey);
@@ -133,6 +136,17 @@
        }
        // 新增档案时补默认来源,更新时保留原来源
        if (existArchiveId == null && patChildArchiveDetail.getId() == null) {
            if (StringUtils.isEmpty(patChildArchiveDetail.getSourceType())) {
                patChildArchiveDetail.setSourceType("3");
            }
            if (StringUtils.isEmpty(patChildArchiveDetail.getSourceBizNo())) {
                patChildArchiveDetail.setSourceBizNo(StringUtils.isNotEmpty(patChildArchiveDetail.getOuthospno())
                        ? patChildArchiveDetail.getOuthospno() : patChildArchiveDetail.getInhospno());
            }
        }
        // 将详情中的公共字段复制到主表对象
        PatChildArchive patChildArchive = DtoConversionUtils.sourceToTarget(patChildArchiveDetail, PatChildArchive.class);
        if (patChildArchive == null) {
smartor/src/main/java/com/smartor/service/impl/PatMaternityArchiveServiceImpl.java
@@ -99,13 +99,15 @@
        patMaternityArchiveDetailVo.setOrgid(patMaternityArchiveDetail.getOrgid());
        patMaternityArchiveDetailVo.setPatid(patMaternityArchiveDetail.getPatid());
        patMaternityArchiveDetailVo.setOuthospno(patMaternityArchiveDetail.getOuthospno());
        patMaternityArchiveDetailVo.setInhospno(patMaternityArchiveDetail.getInhospno());
        patMaternityArchiveDetailVo.setArchiveType(patMaternityArchiveDetail.getArchiveType());
        List<PatMaternityArchiveDetail> patMaternityArchiveDetailOlds
                = patMaternityArchiveDetailService.selectPatMaternityArchiveDetailList(patMaternityArchiveDetailVo);
        if (StringUtils.isEmpty(patMaternityArchiveDetail.getOuthospno())) {
            throw new BaseException("妇科档案入参门诊编号outhospno为空,请检查入参门诊编号");
        if (StringUtils.isEmpty(patMaternityArchiveDetail.getOuthospno())
                && StringUtils.isEmpty(patMaternityArchiveDetail.getInhospno())) {
            throw new BaseException("妇科档案入参门诊编号outhospno和住院编号inhospno不能同时为空");
        }
        if (StringUtils.isEmpty(patMaternityArchiveDetail.getArchiveType())) {
            throw new BaseException("妇科档案入参档案类型archiveType为空,请检查入参档案类型");
@@ -116,6 +118,7 @@
        key.setOrgid(patMaternityArchiveDetail.getOrgid());
        key.setPatid(patMaternityArchiveDetail.getPatid());
        key.setOuthospno(patMaternityArchiveDetail.getOuthospno());
        key.setInhospno(patMaternityArchiveDetail.getInhospno());
        key.setArchiveType(patMaternityArchiveDetail.getArchiveType());
        List<PatMaternityArchiveDetail> olds =
                patMaternityArchiveDetailService.selectPatMaternityArchiveDetailList(key);
@@ -133,6 +136,7 @@
            mainKey.setOrgid(patMaternityArchiveDetail.getOrgid());
            mainKey.setPatid(patMaternityArchiveDetail.getPatid());
            mainKey.setOuthospno(patMaternityArchiveDetail.getOuthospno());
            mainKey.setInhospno(patMaternityArchiveDetail.getInhospno());
            mainKey.setArchiveType(patMaternityArchiveDetail.getArchiveType());
            List<PatMaternityArchive> mainOlds =
                    patMaternityArchiveMapper.selectPatMaternityArchiveList(mainKey);
@@ -156,6 +160,17 @@
            }
        }
        // 新增档案时补默认来源,更新时保留原来源
        if (existArchiveId == null && patMaternityArchiveDetail.getId() == null) {
            if (StringUtils.isEmpty(patMaternityArchiveDetail.getSourceType())) {
                patMaternityArchiveDetail.setSourceType("3");
            }
            if (StringUtils.isEmpty(patMaternityArchiveDetail.getSourceBizNo())) {
                patMaternityArchiveDetail.setSourceBizNo(StringUtils.isNotEmpty(patMaternityArchiveDetail.getOuthospno())
                        ? patMaternityArchiveDetail.getOuthospno() : patMaternityArchiveDetail.getInhospno());
            }
        }
        // 将详情中的公共字段复制到主表对象
        PatMaternityArchive patMaternityArchive = DtoConversionUtils.sourceToTarget(patMaternityArchiveDetail, PatMaternityArchive.class);
        if (patMaternityArchive == null) {
smartor/src/main/java/com/smartor/service/impl/PatSpecificArchiveGenerateServiceImpl.java
@@ -3,20 +3,31 @@
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.inhospvisit.api.InhospVisitFacade;
import com.ruoyi.inhospvisit.domain.PatMedInhosp;
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.ruoyi.task.api.TaskSubtaskApi;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.service.IServiceSubtaskService;
import com.smartor.common.SpecificArchiveConstants;
import com.smartor.domain.PatChildArchive;
import com.smartor.domain.PatChildArchiveDetail;
import com.smartor.domain.PatMaternityArchive;
import com.smartor.domain.PatMaternityArchiveDetail;
import com.smartor.domain.PatSpecificArchiveGenerateLog;
import com.smartor.service.IPatChildArchiveService;
import com.smartor.service.IPatMaternityArchiveService;
import com.smartor.service.IPatSpecificArchiveGenerateLogService;
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.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
@@ -26,27 +37,37 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
 * 专科档案生成Service业务层处理。
 *
 * <p>只处理丽水市妇幼保健院(orgid = 47226079133110211G1001)的门诊接诊完成记录,
 * 按 {@code pat_specific_archive_generate_config} 配置匹配科室和诊断后,
 * 生成对应类型的妇科档案或儿科档案。</p>
 * <p>只处理丽水市妇幼保健院(orgid = 47226079133110211G1001):</p>
 * <ul>
 *     <li>门诊接诊完成:按科室/诊断匹配配置后生成妇科/儿科档案;</li>
 *     <li>住院随访完成:按出院/在院科室、病区、诊断匹配配置后生成妇科/儿科档案,
 *     生成前按“患者 + 专科 + 档案类型”判断是否已有对应档案,已有则跳过。</li>
 * </ul>
 */
@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";
    /** 部门类型:病区 */
    private static final String DEPT_TYPE_WARD = "2";
    /** 就诊类型:出院 */
    private static final String HOSP_TYPE_DISCHARGE = "2";
    /** 就诊类型:住院/入院 */
    private static final String HOSP_TYPE_INHOSP = "6";
    /** 住院随访完成状态 */
    private static final Long SEND_STATE_FINISHED = 6L;
    /** 住院随访档案生成的最大重试次数 */
    private static final int MAX_RETRY = 3;
    /** Redis 锁前缀 */
    private static final String LOCK_PREFIX = "lock:archive:inhosp:";
    @Autowired
    private IPatSpecificArchiveGenerateConfigService patSpecificArchiveGenerateConfigService;
@@ -58,7 +79,32 @@
    private IPatChildArchiveService patChildArchiveService;
    @Autowired
    private IPatSpecificArchiveGenerateLogService patSpecificArchiveGenerateLogService;
    @Autowired
    private PatArchiveMapper patArchiveMapper;
    @Autowired
    private IServiceSubtaskService serviceSubtaskService;
    @Autowired
    private InhospVisitFacade inhospVisitFacade;
    @Autowired
    private TaskSubtaskApi taskSubtaskApi;
    @Autowired
    private RedisCache redisCache;
    /**
     * 住院随访服务类型白名单,默认出院随访、专科随访、入院随访、手术随访。
     */
    @Value("${archive.inhosp.followup.service-types:2,13,18,19}")
    private String inhospFollowupServiceTypes;
    /** 住院随访生成专科档案开关,默认开启,便于按机构灰度。 */
    @Value("${archive.inhosp.followup.enabled:true}")
    private boolean inhospFollowupEnabled;
    @Override
    public int generateByOuthosp(PatMedOuthosp patMedOuthosp, PatArchive patArchive) {
@@ -86,7 +132,7 @@
        }
        String campusid = StringUtils.isNotBlank(patMedOuthosp.getCampusid())
                ? patMedOuthosp.getCampusid() : "1";
                ? patMedOuthosp.getCampusid() : SpecificArchiveConstants.DEFAULT_CAMPUS_ID;
        List<PatSpecificArchiveGenerateConfig> configs =
                patSpecificArchiveGenerateConfigService.selectEffectiveConfigList(patMedOuthosp.getOrgid(), campusid);
        if (CollectionUtils.isEmpty(configs)) {
@@ -99,7 +145,8 @@
        Set<String> generatedKeys = new HashSet<>();
        int generatedCount = 0;
        for (PatSpecificArchiveGenerateConfig config : configs) {
            if (!matchConfig(config, patMedOuthosp)) {
            if (!matchConfig(config, patMedOuthosp.getDeptcode(), null,
                    patMedOuthosp.getIcd10code(), patMedOuthosp.getDiagname())) {
                continue;
            }
            String archiveType = config.getArchiveType();
@@ -116,12 +163,12 @@
            }
            try {
                if (DEPT_CATEGORY_GYNECOLOGY.equals(deptCategory)) {
                if (SpecificArchiveConstants.DEPT_CATEGORY_GYNECOLOGY.equals(deptCategory)) {
                    PatMaternityArchiveDetail detail =
                            buildMaternityDetail(patMedOuthosp, targetArchive, config, campusid);
                    patMaternityArchiveService.insertOrUpdateMaternityArchive(detail);
                    generatedCount++;
                } else if (DEPT_CATEGORY_PEDIATRICS.equals(deptCategory)) {
                } else if (SpecificArchiveConstants.DEPT_CATEGORY_PEDIATRICS.equals(deptCategory)) {
                    PatChildArchiveDetail detail =
                            buildChildDetail(patMedOuthosp, targetArchive, config, campusid);
                    patChildArchiveService.insertOrUpdateChildArchive(detail);
@@ -138,12 +185,276 @@
        return generatedCount;
    }
    @Override
    public int generateByInhospFollowup(Long subId) {
        if (!inhospFollowupEnabled || subId == null) {
            return 0;
        }
        ServiceSubtask subtask = serviceSubtaskService.selectServiceSubtaskById(subId);
        if (subtask == null || !SEND_STATE_FINISHED.equals(subtask.getSendstate())) {
            return 0;
        }
        if (!isInhospFollowup(subtask)) {
            return 0;
        }
        // 仅限丽水妇幼,机构为空时用住院记录/患者档案兜底
        PatMedInhosp inhosp = resolveInhosp(subtask);
        PatArchive patArchive = resolvePatArchive(subtask.getPatid(), inhosp);
        String orgid = firstNotBlank(subtask.getOrgid(),
                inhosp == null ? null : inhosp.getOrgid(),
                patArchive == null ? null : patArchive.getOrgid());
        if (!SpecificArchiveConstants.LISHUI_MCH_ORG_ID.equals(orgid)) {
            log.debug("住院随访生成专科档案跳过:非丽水妇幼机构,subId={},orgid={}", subId, orgid);
            return 0;
        }
        Long patid = firstNonNull(subtask.getPatid(),
                inhosp == null ? null : inhosp.getPatid(),
                patArchive == null ? null : patArchive.getId());
        if (patid == null) {
            log.warn("住院随访生成专科档案跳过:未找到患者,subId={},inhospid={}", subId, subtask.getInhospid());
            return 0;
        }
        if (patArchive == null) {
            patArchive = patArchiveMapper.selectPatArchiveByPatid(patid);
        }
        String campusid = firstNotBlank(subtask.getCampusid(),
                inhosp == null ? null : inhosp.getCampusid(),
                patArchive == null ? null : patArchive.getCampusid(),
                SpecificArchiveConstants.DEFAULT_CAMPUS_ID);
        boolean admission = HOSP_TYPE_INHOSP.equals(subtask.getHospType());
        String deptCode = admission
                ? firstNotBlank(inhosp == null ? null : inhosp.getDeptcode(), subtask.getDeptcode(),
                subtask.getVisitDeptCode(), subtask.getLeaveldeptcode())
                : firstNotBlank(inhosp == null ? null : inhosp.getLeaveldeptcode(), subtask.getLeaveldeptcode(),
                subtask.getVisitDeptCode(), inhosp == null ? null : inhosp.getDeptcode());
        String deptName = admission
                ? firstNotBlank(inhosp == null ? null : inhosp.getDeptname(), subtask.getDeptname(),
                subtask.getVisitDeptName(), subtask.getLeaveldeptname())
                : firstNotBlank(inhosp == null ? null : inhosp.getLeaveldeptname(), subtask.getLeaveldeptname(),
                subtask.getVisitDeptName(), inhosp == null ? null : inhosp.getDeptname());
        String wardCode = admission
                ? firstNotBlank(inhosp == null ? null : inhosp.getHospitaldistrictcode(),
                subtask.getHospitaldistrictcode(), subtask.getLeavehospitaldistrictcode())
                : firstNotBlank(inhosp == null ? null : inhosp.getLeavehospitaldistrictcode(),
                subtask.getLeavehospitaldistrictcode(), subtask.getHospitaldistrictcode());
        String wardName = admission
                ? firstNotBlank(inhosp == null ? null : inhosp.getHospitaldistrictname(),
                subtask.getHospitaldistrictname(), subtask.getLeavehospitaldistrictname())
                : firstNotBlank(inhosp == null ? null : inhosp.getLeavehospitaldistrictname(),
                subtask.getLeavehospitaldistrictname(), subtask.getHospitaldistrictname());
        String icd10Code = admission
                ? firstNotBlank(inhosp == null ? null : inhosp.getIcd10code(), subtask.getLeaveicd10code())
                : firstNotBlank(inhosp == null ? null : inhosp.getLeaveicd10code(),
                subtask.getLeaveicd10code(), inhosp == null ? null : inhosp.getIcd10code());
        String diagName = admission
                ? firstNotBlank(inhosp == null ? null : inhosp.getDiagname(), subtask.getDiagname())
                : firstNotBlank(inhosp == null ? null : inhosp.getLeavediagname(),
                subtask.getLeavediagname(), inhosp == null ? null : inhosp.getDiagname());
        InhospArchiveContext context = new InhospArchiveContext();
        context.subtask = subtask;
        context.inhosp = inhosp;
        context.patArchive = patArchive;
        context.patid = patid;
        context.orgid = orgid;
        context.campusid = campusid;
        context.deptCode = deptCode;
        context.deptName = deptName;
        context.wardCode = wardCode;
        context.wardName = wardName;
        context.icd10Code = icd10Code;
        context.diagName = diagName;
        context.diagTime = firstDate(subtask.getFinishtime(), inhosp == null ? null : inhosp.getEndtime());
        List<PatSpecificArchiveGenerateConfig> configs =
                patSpecificArchiveGenerateConfigService.selectEffectiveConfigList(orgid, campusid);
        if (CollectionUtils.isEmpty(configs)) {
            recordLog(context, null, null, null, SpecificArchiveConstants.RESULT_NO_MATCH, null,
                    "机构未配置档案生成规则");
            return 0;
        }
        Set<String> generatedKeys = new HashSet<>();
        int generatedCount = 0;
        boolean anyMatched = false;
        for (PatSpecificArchiveGenerateConfig config : configs) {
            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;
            }
            if (!matchConfig(config, deptCode, wardCode, icd10Code, diagName)) {
                continue;
            }
            anyMatched = true;
            String generatedKey = deptCategory + "|" + archiveType;
            if (!generatedKeys.add(generatedKey)) {
                continue;
            }
            if (alreadyProcessed(subId, config.getId())) {
                continue;
            }
            String lockKey = LOCK_PREFIX + orgid + ":" + patid + ":" + deptCategory + ":" + archiveType;
            boolean locked = false;
            try {
                locked = redisCache.setCacheObjectIfAbsent(lockKey, "1", 30, TimeUnit.SECONDS);
            } catch (Exception e) {
                // Redis 不可用时 fail-open,依赖生成日志和数据库查询兜底,避免档案生成整体停摆
                locked = true;
                log.warn("住院随访生成专科档案获取Redis锁异常,降级为无锁执行,subId={},lockKey={}", subId, lockKey, e);
            }
            if (!locked) {
                // 未拿到锁时由补偿任务重试,避免并发重复生成
                log.info("住院随访生成专科档案未获取到锁,等待补偿重试,subId={},lockKey={}", subId, lockKey);
                recordLog(context, config, deptCategory, archiveType, SpecificArchiveConstants.RESULT_FAILED,
                        null, "获取生成锁失败");
                continue;
            }
            try {
                Long existingArchiveId = findExistingArchiveId(orgid, patid, deptCategory, archiveType);
                if (existingArchiveId != null) {
                    recordLog(context, config, deptCategory, archiveType,
                            SpecificArchiveConstants.RESULT_EXISTS, existingArchiveId, "患者已有对应档案,跳过生成");
                    continue;
                }
                Long archiveId;
                if (SpecificArchiveConstants.DEPT_CATEGORY_GYNECOLOGY.equals(deptCategory)) {
                    PatMaternityArchiveDetail detail = buildInhospMaternityDetail(context, config);
                    archiveId = (long) patMaternityArchiveService.insertOrUpdateMaternityArchive(detail);
                } else if (SpecificArchiveConstants.DEPT_CATEGORY_PEDIATRICS.equals(deptCategory)) {
                    PatChildArchiveDetail detail = buildInhospChildDetail(context, config);
                    archiveId = (long) patChildArchiveService.insertOrUpdateChildArchive(detail);
                } else {
                    log.warn("住院随访生成专科档案跳过:未知部门分类,configId={},deptCategory={}",
                            config.getId(), deptCategory);
                    continue;
                }
                recordLog(context, config, deptCategory, archiveType,
                        SpecificArchiveConstants.RESULT_GENERATED, archiveId, "生成成功");
                generatedCount++;
            } catch (Exception e) {
                log.error("住院随访生成专科档案失败,subId={},configId={},deptCategory={},archiveType={}",
                        subId, config.getId(), deptCategory, archiveType, e);
                recordLog(context, config, deptCategory, archiveType,
                        SpecificArchiveConstants.RESULT_FAILED, null, truncateMsg(e.getMessage()));
            } finally {
                if (locked) {
                    try {
                        redisCache.deleteObject(lockKey);
                    } catch (Exception e) {
                        log.warn("住院随访生成专科档案释放Redis锁异常,lockKey={}", lockKey, e);
                    }
                }
            }
        }
        if (!anyMatched) {
            recordLog(context, null, null, null, SpecificArchiveConstants.RESULT_NO_MATCH, null,
                    "未命中科室/病区/诊断配置");
        }
        return generatedCount;
    }
    @Override
    public int compensateInhospFollowups() {
        if (!inhospFollowupEnabled) {
            return 0;
        }
        List<String> serviceTypes = parseCsv(inhospFollowupServiceTypes);
        if (CollectionUtils.isEmpty(serviceTypes)) {
            return 0;
        }
        Date startTime = new Date(System.currentTimeMillis() - 7L * 24 * 60 * 60 * 1000);
        List<ServiceSubtask> subtasks = taskSubtaskApi.findCompletedInhospFollowups(
                SpecificArchiveConstants.LISHUI_MCH_ORG_ID, serviceTypes, startTime, 200);
        if (CollectionUtils.isEmpty(subtasks)) {
            return 0;
        }
        int generated = 0;
        for (ServiceSubtask subtask : subtasks) {
            if (subtask.getId() == null
                    || patSpecificArchiveGenerateLogService.existsProcessedBySourceSub(
                    SpecificArchiveConstants.SOURCE_TYPE_INHOSP_FOLLOWUP, subtask.getId())) {
                continue;
            }
            try {
                generated += generateByInhospFollowup(subtask.getId());
            } catch (Exception e) {
                log.error("补偿生成住院随访专科档案失败,subId={}", subtask.getId(), e);
            }
        }
        return generated;
    }
    /**
     * 判断门诊记录是否命中配置。
     * 判断随访子任务是否属于需要生成档案的住院随访。
     */
    private boolean isInhospFollowup(ServiceSubtask subtask) {
        if (!HOSP_TYPE_DISCHARGE.equals(subtask.getHospType())
                && !HOSP_TYPE_INHOSP.equals(subtask.getHospType())) {
            return false;
        }
        if (StringUtils.isBlank(subtask.getServiceType())) {
            return false;
        }
        return parseCsv(inhospFollowupServiceTypes).contains(subtask.getServiceType().trim());
    }
    private boolean alreadyProcessed(Long subId, Long configId) {
        int cid = configId == null ? SpecificArchiveConstants.NO_CONFIG_ID : configId.intValue();
        PatSpecificArchiveGenerateLog old = patSpecificArchiveGenerateLogService
                .selectBySourceSubAndConfig(SpecificArchiveConstants.SOURCE_TYPE_INHOSP_FOLLOWUP, subId, cid);
        if (old == null) {
            return false;
        }
        if (SpecificArchiveConstants.RESULT_FAILED.equals(old.getProcessResult())) {
            return old.getRetryCount() != null && old.getRetryCount() >= MAX_RETRY;
        }
        return true;
    }
    /**
     * 查询患者是否已有对应专科档案。
     *
     * <p>用户确认口径:按“专科 + 档案类型”判断。妇科、儿科分表存储,因此妇科查妇科表,
     * 儿科查儿科表,再按档案类型区分。</p>
     */
    private Long findExistingArchiveId(String orgid, Long patid, String deptCategory, String archiveType) {
        if (SpecificArchiveConstants.DEPT_CATEGORY_GYNECOLOGY.equals(deptCategory)) {
            PatMaternityArchive query = new PatMaternityArchive();
            query.setOrgid(orgid);
            query.setPatid(patid);
            query.setArchiveType(archiveType);
            List<PatMaternityArchive> list = patMaternityArchiveService.selectPatMaternityArchiveList(query);
            return CollectionUtils.isNotEmpty(list) ? list.get(0).getId() : null;
        }
        if (SpecificArchiveConstants.DEPT_CATEGORY_PEDIATRICS.equals(deptCategory)) {
            PatChildArchive query = new PatChildArchive();
            query.setOrgid(orgid);
            query.setPatid(patid);
            query.setArchiveType(archiveType);
            List<PatChildArchive> list = patChildArchiveService.selectPatChildArchiveList(query);
            return CollectionUtils.isNotEmpty(list) ? list.get(0).getId() : null;
        }
        return null;
    }
    /**
     * 判断科室/病区、诊断是否命中配置。
     *
     * <p>部门条件与诊断条件为 OR 关系:任一条件命中即生成;两个集合都为空时配置无效。</p>
     */
    private boolean matchConfig(PatSpecificArchiveGenerateConfig config, PatMedOuthosp patMedOuthosp) {
    private boolean matchConfig(PatSpecificArchiveGenerateConfig config, String deptCode, String wardCode,
                                String icd10Code, String diagName) {
        List<String> deptCodes = parseCodes(config.getDeptCodes());
        List<String> icd10Codes = parseCodes(config.getIcd10Codes());
        if (CollectionUtils.isEmpty(deptCodes) && CollectionUtils.isEmpty(icd10Codes)) {
@@ -152,22 +463,21 @@
        boolean deptMatched = false;
        if (CollectionUtils.isNotEmpty(deptCodes)) {
            // 门诊记录只有科室,没有病区;dept_type=2(病区)不参与门诊匹配
            if (DEPT_TYPE_DEPARTMENT.equals(config.getDeptType())) {
                deptMatched = containsIgnoreCase(deptCodes, patMedOuthosp.getDeptcode());
                deptMatched = containsIgnoreCase(deptCodes, deptCode);
            } else if (DEPT_TYPE_WARD.equals(config.getDeptType())) {
                deptMatched = containsIgnoreCase(deptCodes, wardCode);
            }
        }
        boolean diagnosisMatched = false;
        if (CollectionUtils.isNotEmpty(icd10Codes)) {
            String icd10code = patMedOuthosp.getIcd10code();
            String diagname = patMedOuthosp.getDiagname();
            diagnosisMatched = icd10Codes.stream().anyMatch(code -> {
                if (equalsIgnoreCase(code, icd10code)) {
                if (equalsIgnoreCase(code, icd10Code)) {
                    return true;
                }
                // 部分 HIS 数据只有诊断名称没有诊断码,此时允许按诊断名称兜底匹配
                return StringUtils.isBlank(icd10code) && equalsIgnoreCase(code, diagname);
                return StringUtils.isBlank(icd10Code) && equalsIgnoreCase(code, diagName);
            });
        }
@@ -179,7 +489,9 @@
        PatMaternityArchiveDetail detail = new PatMaternityArchiveDetail();
        detail.setArchiveType(config.getArchiveType());
        detail.setFollowupPeriod(config.getFollowupPeriod());
        detail.setSourceType(SOURCE_TYPE_OUTHOSP);
        detail.setSourceType(SpecificArchiveConstants.SOURCE_TYPE_OUTHOSP);
        detail.setSourceBizId(patMedOuthosp.getId());
        detail.setSourceBizNo(patMedOuthosp.getOuthospno());
        fillCommonDetail(detail, patMedOuthosp, patArchive, campusid);
        if (patArchive != null) {
            detail.setAddress(patArchive.getPlaceOfResidence());
@@ -192,7 +504,9 @@
        PatChildArchiveDetail detail = new PatChildArchiveDetail();
        detail.setArchiveType(config.getArchiveType());
        detail.setFollowupPeriod(config.getFollowupPeriod());
        detail.setSourceType(SOURCE_TYPE_OUTHOSP);
        detail.setSourceType(SpecificArchiveConstants.SOURCE_TYPE_OUTHOSP);
        detail.setSourceBizId(patMedOuthosp.getId());
        detail.setSourceBizNo(patMedOuthosp.getOuthospno());
        fillCommonChildDetail(detail, patMedOuthosp, patArchive, campusid);
        detail.setParentPhone(patArchive != null
                ? firstNotBlank(patArchive.getTelcode(), patMedOuthosp.getTelcode())
@@ -205,6 +519,104 @@
            detail.setNutritionIcd10Code(patMedOuthosp.getIcd10code());
            detail.setNutritionDiagName(patMedOuthosp.getDiagname());
        }
        return detail;
    }
    private PatMaternityArchiveDetail buildInhospMaternityDetail(InhospArchiveContext context,
                                                                 PatSpecificArchiveGenerateConfig config) {
        PatMaternityArchiveDetail detail = new PatMaternityArchiveDetail();
        detail.setArchiveType(config.getArchiveType());
        detail.setFollowupPeriod(config.getFollowupPeriod());
        detail.setSourceType(SpecificArchiveConstants.SOURCE_TYPE_INHOSP_FOLLOWUP);
        detail.setSourceBizId(inInhospid(context));
        detail.setSourceBizNo(inInhospno(context));
        detail.setSourceSubId(context.subtask.getId());
        detail.setOuthospno(null);
        detail.setInhospno(inInhospno(context));
        detail.setSerialnum(inSerialnum(context));
        detail.setPatid(context.patid);
        detail.setPatname(firstNotBlank(context.patArchive == null ? null : context.patArchive.getName(),
                context.inhosp == null ? null : context.inhosp.getPatname()));
        detail.setBirthdate(context.patArchive == null ? null : context.patArchive.getBirthdate());
        detail.setIdcardno(firstNotBlank(context.patArchive == null ? null : context.patArchive.getIdcardno(),
                context.inhosp == null ? null : context.inhosp.getIdcardno()));
        detail.setPhone(firstNotBlank(context.patArchive == null ? null : context.patArchive.getTelcode(),
                context.inhosp == null ? null : context.inhosp.getTelcode()));
        detail.setSex(firstNonNull(context.patArchive == null ? null : context.patArchive.getSex(),
                context.inhosp == null ? null : context.inhosp.getSex(),
                context.subtask.getSex()));
        detail.setDiagTime(context.diagTime);
        detail.setArchiveTime(new Date());
        detail.setDoctorCode(inDrcode(context));
        detail.setDoctorName(inDrname(context));
        detail.setManagementDoctorCode(firstNotBlank(context.inhosp == null ? null : context.inhosp.getManagementDoctorCode(),
                inDrcode(context)));
        detail.setManagementDoctorName(firstNotBlank(context.inhosp == null ? null : context.inhosp.getManagementDoctor(),
                inDrname(context)));
        detail.setFirstDoctorCode(inDrcode(context));
        detail.setFirstDoctorName(inDrname(context));
        detail.setDeptcode(context.deptCode);
        detail.setDeptname(context.deptName);
        detail.setWardcode(context.wardCode);
        detail.setWardname(context.wardName);
        detail.setIcd10code(context.icd10Code);
        detail.setDiagname(context.diagName);
        detail.setAddress(context.patArchive == null ? null : context.patArchive.getPlaceOfResidence());
        detail.setRegisterUser("住院随访");
        detail.setOrgid(context.orgid);
        detail.setCampusid(context.campusid);
        detail.setDelFlag("0");
        return detail;
    }
    private PatChildArchiveDetail buildInhospChildDetail(InhospArchiveContext context,
                                                         PatSpecificArchiveGenerateConfig config) {
        PatChildArchiveDetail detail = new PatChildArchiveDetail();
        detail.setArchiveType(config.getArchiveType());
        detail.setFollowupPeriod(config.getFollowupPeriod());
        detail.setSourceType(SpecificArchiveConstants.SOURCE_TYPE_INHOSP_FOLLOWUP);
        detail.setSourceBizId(inInhospid(context));
        detail.setSourceBizNo(inInhospno(context));
        detail.setSourceSubId(context.subtask.getId());
        detail.setOuthospno(null);
        detail.setInhospno(inInhospno(context));
        detail.setSerialnum(inSerialnum(context));
        detail.setPatid(context.patid);
        detail.setPatname(firstNotBlank(context.patArchive == null ? null : context.patArchive.getName(),
                context.inhosp == null ? null : context.inhosp.getPatname()));
        detail.setBirthdate(context.patArchive == null ? null : context.patArchive.getBirthdate());
        detail.setIdcardno(firstNotBlank(context.patArchive == null ? null : context.patArchive.getIdcardno(),
                context.inhosp == null ? null : context.inhosp.getIdcardno()));
        detail.setPhone(firstNotBlank(context.patArchive == null ? null : context.patArchive.getTelcode(),
                context.inhosp == null ? null : context.inhosp.getTelcode()));
        detail.setSex(firstNonNull(context.patArchive == null ? null : context.patArchive.getSex(),
                context.inhosp == null ? null : context.inhosp.getSex(),
                context.subtask.getSex()));
        detail.setDiagTime(context.diagTime);
        detail.setArchiveTime(new Date());
        detail.setDoctorCode(inDrcode(context));
        detail.setDoctorName(inDrname(context));
        detail.setManagementDoctorCode(firstNotBlank(context.inhosp == null ? null : context.inhosp.getManagementDoctorCode(),
                inDrcode(context)));
        detail.setManagementDoctorName(firstNotBlank(context.inhosp == null ? null : context.inhosp.getManagementDoctor(),
                inDrname(context)));
        detail.setDeptcode(context.deptCode);
        detail.setDeptname(context.deptName);
        detail.setWardcode(context.wardCode);
        detail.setWardname(context.wardName);
        detail.setIcd10code(context.icd10Code);
        detail.setDiagname(context.diagName);
        detail.setParentPhone(firstNotBlank(context.patArchive == null ? null : context.patArchive.getTelcode(),
                context.inhosp == null ? null : context.inhosp.getTelcode()));
        detail.setParentAddress(context.patArchive == null ? null : context.patArchive.getPlaceOfResidence());
        if ("1".equals(config.getArchiveType())) {
            detail.setNutritionIcd10Code(context.icd10Code);
            detail.setNutritionDiagName(context.diagName);
        }
        detail.setRegisterUser("住院随访");
        detail.setOrgid(context.orgid);
        detail.setCampusid(context.campusid);
        detail.setDelFlag("0");
        return detail;
    }
@@ -289,6 +701,85 @@
        return CollectionUtils.isNotEmpty(archives) ? archives.get(0) : null;
    }
    private PatArchive resolvePatArchive(Long patid, PatMedInhosp inhosp) {
        if (patid != null) {
            PatArchive archive = patArchiveMapper.selectPatArchiveByPatid(patid);
            if (archive != null) {
                return archive;
            }
        }
        Long inhospPatid = inhosp == null ? null : inhosp.getPatid();
        return inhospPatid == null ? null : patArchiveMapper.selectPatArchiveByPatid(inhospPatid);
    }
    private PatMedInhosp resolveInhosp(ServiceSubtask subtask) {
        if (subtask.getInhospid() != null) {
            PatMedInhosp inhosp = inhospVisitFacade.selectPatMedInhospByInhospid(subtask.getInhospid());
            if (inhosp != null) {
                return inhosp;
            }
        }
        if (StringUtils.isNotBlank(subtask.getHospno())) {
            PatMedInhosp query = new PatMedInhosp();
            query.setInhospno(subtask.getHospno());
            query.setOrgid(subtask.getOrgid());
            List<PatMedInhosp> list = inhospVisitFacade.selectPatMedInhosp(query);
            if (CollectionUtils.isNotEmpty(list)) {
                return list.get(0);
            }
        }
        return null;
    }
    private void recordLog(InhospArchiveContext context, PatSpecificArchiveGenerateConfig config,
                           String deptCategory, String archiveType, String result, Long archiveId, String msg) {
        PatSpecificArchiveGenerateLog log = new PatSpecificArchiveGenerateLog();
        log.setSourceType(SpecificArchiveConstants.SOURCE_TYPE_INHOSP_FOLLOWUP);
        log.setSourceBizId(inInhospid(context));
        log.setSourceBizNo(inInhospno(context));
        log.setSourceSubId(context.subtask.getId());
        log.setPatid(context.patid);
        log.setOrgid(context.orgid);
        log.setCampusid(context.campusid);
        log.setDeptCategory(deptCategory);
        log.setArchiveType(archiveType);
        log.setConfigId(config == null || config.getId() == null
                ? SpecificArchiveConstants.NO_CONFIG_ID : config.getId().intValue());
        log.setArchiveId(archiveId);
        log.setProcessResult(result);
        log.setProcessMsg(msg);
        PatSpecificArchiveGenerateLog old = patSpecificArchiveGenerateLogService.selectBySourceSubAndConfig(
                log.getSourceType(), log.getSourceSubId(), log.getConfigId());
        int retry = old == null || old.getRetryCount() == null ? 0 : old.getRetryCount();
        if (SpecificArchiveConstants.RESULT_FAILED.equals(result)) {
            retry++;
        }
        log.setRetryCount(retry);
        patSpecificArchiveGenerateLogService.saveOrUpdate(log);
    }
    private Long inInhospid(InhospArchiveContext context) {
        return firstNonNull(context.inhosp == null ? null : context.inhosp.getInhospid(),
                context.subtask.getInhospid());
    }
    private String inInhospno(InhospArchiveContext context) {
        return firstNotBlank(context.inhosp == null ? null : context.inhosp.getInhospno(),
                context.subtask.getHospno());
    }
    private String inSerialnum(InhospArchiveContext context) {
        return context.inhosp == null ? null : context.inhosp.getSerialnum();
    }
    private String inDrcode(InhospArchiveContext context) {
        return firstNotBlank(context.inhosp == null ? null : context.inhosp.getDrcode(), context.subtask.getDrcode());
    }
    private String inDrname(InhospArchiveContext context) {
        return firstNotBlank(context.inhosp == null ? null : context.inhosp.getDrname(), context.subtask.getDrname());
    }
    private List<String> parseCodes(String raw) {
        if (StringUtils.isBlank(raw)) {
            return Collections.emptyList();
@@ -314,8 +805,42 @@
                .collect(Collectors.toList());
    }
    private String firstNotBlank(String first, String second) {
        return StringUtils.isNotBlank(first) ? first : second;
    private List<String> parseCsv(String raw) {
        if (StringUtils.isBlank(raw)) {
            return Collections.emptyList();
        }
        return Arrays.stream(raw.split(","))
                .map(String::trim)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toList());
    }
    private String firstNotBlank(String... values) {
        if (values == null) {
            return null;
        }
        for (String value : values) {
            if (StringUtils.isNotBlank(value)) {
                return value;
            }
        }
        return null;
    }
    private <T> T firstNonNull(T... values) {
        if (values == null) {
            return null;
        }
        for (T value : values) {
            if (value != null) {
                return value;
            }
        }
        return null;
    }
    private Date firstDate(Date first, Date second) {
        return first != null ? first : second;
    }
    private boolean containsIgnoreCase(List<String> source, String target) {
@@ -328,4 +853,30 @@
    private boolean equalsIgnoreCase(String first, String second) {
        return first != null && second != null && first.trim().equalsIgnoreCase(second.trim());
    }
    private String truncateMsg(String msg) {
        if (msg == null) {
            return null;
        }
        return msg.length() <= 900 ? msg : msg.substring(0, 900);
    }
    /**
     * 住院随访生成档案时用到的上下文。
     */
    private static class InhospArchiveContext {
        private ServiceSubtask subtask;
        private PatMedInhosp inhosp;
        private PatArchive patArchive;
        private Long patid;
        private String orgid;
        private String campusid;
        private String deptCode;
        private String deptName;
        private String wardCode;
        private String wardName;
        private String icd10Code;
        private String diagName;
        private Date diagTime;
    }
}
smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskAnswerServiceImpl.java
@@ -64,6 +64,7 @@
import com.ruoyi.task.domain.ServiceSubtaskPreachform;
import com.ruoyi.task.service.IServiceTaskService;
import com.ruoyi.task.mapper.ServiceSubtaskPreachformMapper;
import com.ruoyi.task.event.FollowupCompletedEventPublisher;
import com.ruoyi.patarchive.mapper.PatArchiveMapper;
/**
 * 任务问卷问题选项Service业务层处理
@@ -91,6 +92,9 @@
    @Autowired
    private ServiceSubtaskMapper serviceSubtaskMapper;
    @Autowired
    private FollowupCompletedEventPublisher followupCompletedEventPublisher;
    @Autowired
    private ServiceSubtaskPreachformMapper serviceSubtaskPreachformMapper;
@@ -476,6 +480,7 @@
                log.error("将该key和value从redis中,serviceSubtask的值为--------:{}", serviceSubtask);
                serviceSubtask.setScore(formatScore(scoreTotal));
                serviceSubtaskMapper.updateServiceSubtaskByCondition(serviceSubtask);
                followupCompletedEventPublisher.publish(serviceSubtask.getId());
                //将当前发送状态改成 9 完成(缓存 preachform 查询)
                String currentPreachform = serviceSubtask.getCurrentPreachform();
@@ -664,6 +669,7 @@
            serviceSubtask.setUpdateTime(new Date());
            serviceSubtask.setScore(formatScore(scoreTotal));
            serviceSubtaskMapper.updateServiceSubtaskByCondition(serviceSubtask);
            followupCompletedEventPublisher.publish(serviceSubtask.getId());
            //将当前发送状态改成9完成
            ServiceSubtaskPreachform serviceSubtaskPreachform = new ServiceSubtaskPreachform();
@@ -823,6 +829,7 @@
                serviceSubtask.setVisitTime(new Date());
                serviceSubtask.setScore(formatScore(scoreTotal));
                serviceSubtaskMapper.insertServiceSubtask(serviceSubtask);
                followupCompletedEventPublisher.publish(serviceSubtask.getId());
                //将当前发送状态改成9完成
                ServiceSubtaskPreachform serviceSubtaskPreachform = new ServiceSubtaskPreachform();
                serviceSubtaskPreachform.setPreachform(serviceSubtask.getCurrentPreachform());
smartor/src/main/resources/mapper/smartor/PatChildArchiveDetailMapper.xml
@@ -9,6 +9,9 @@
        <result property="archiveid" column="archiveid"/>
        <result property="archiveType" column="archive_type"/>
        <result property="sourceType" column="source_type"/>
        <result property="sourceBizId" column="source_biz_id"/>
        <result property="sourceBizNo" column="source_biz_no"/>
        <result property="sourceSubId" column="source_sub_id"/>
        <result property="outhospno" column="outhospno"/>
        <result property="inhospno" column="inhospno"/>
        <result property="serialnum" column="serialnum"/>
@@ -70,6 +73,9 @@
               archiveid,
               archive_type,
               source_type,
               source_biz_id,
               source_biz_no,
               source_sub_id,
               outhospno,
               inhospno,
               serialnum,
@@ -136,6 +142,9 @@
            <if test="archiveType != null and archiveType != ''">and archive_type = #{archiveType}</if>
            <if test="followupPeriod != null and followupPeriod != ''">and followup_period = #{followupPeriod}</if>
            <if test="sourceType != null and sourceType != ''">and source_type = #{sourceType}</if>
            <if test="sourceBizId != null">and source_biz_id = #{sourceBizId}</if>
            <if test="sourceBizNo != null and sourceBizNo != ''">and source_biz_no = #{sourceBizNo}</if>
            <if test="sourceSubId != null">and source_sub_id = #{sourceSubId}</if>
            <if test="outhospno != null and outhospno != ''">and outhospno = #{outhospno}</if>
            <if test="inhospno != null and inhospno != ''">and inhospno = #{inhospno}</if>
            <if test="serialnum != null and serialnum != ''">and serialnum = #{serialnum}</if>
@@ -187,6 +196,9 @@
            <if test="archiveid != null">archiveid,</if>
            <if test="archiveType != null">archive_type,</if>
            <if test="sourceType != null">source_type,</if>
            <if test="sourceBizId != null">source_biz_id,</if>
            <if test="sourceBizNo != null">source_biz_no,</if>
            <if test="sourceSubId != null">source_sub_id,</if>
            <if test="outhospno != null">outhospno,</if>
            <if test="inhospno != null">inhospno,</if>
            <if test="serialnum != null">serialnum,</if>
@@ -246,6 +258,9 @@
            <if test="archiveid != null">#{archiveid},</if>
            <if test="archiveType != null">#{archiveType},</if>
            <if test="sourceType != null">#{sourceType},</if>
            <if test="sourceBizId != null">#{sourceBizId},</if>
            <if test="sourceBizNo != null">#{sourceBizNo},</if>
            <if test="sourceSubId != null">#{sourceSubId},</if>
            <if test="outhospno != null">#{outhospno},</if>
            <if test="inhospno != null">#{inhospno},</if>
            <if test="serialnum != null">#{serialnum},</if>
@@ -309,6 +324,9 @@
            <if test="archiveid != null">archiveid = #{archiveid},</if>
            <if test="archiveType != null">archive_type = #{archiveType},</if>
            <if test="sourceType != null">source_type = #{sourceType},</if>
            <if test="sourceBizId != null">source_biz_id = #{sourceBizId},</if>
            <if test="sourceBizNo != null">source_biz_no = #{sourceBizNo},</if>
            <if test="sourceSubId != null">source_sub_id = #{sourceSubId},</if>
            <if test="outhospno != null">outhospno = #{outhospno},</if>
            <if test="inhospno != null">inhospno = #{inhospno},</if>
            <if test="serialnum != null">serialnum = #{serialnum},</if>
smartor/src/main/resources/mapper/smartor/PatChildArchiveMapper.xml
@@ -7,6 +7,10 @@
    <resultMap type="com.smartor.domain.PatChildArchive" id="PatChildArchiveResult">
        <result property="id" column="id"/>
        <result property="archiveType" column="archive_type"/>
        <result property="sourceType" column="source_type"/>
        <result property="sourceBizId" column="source_biz_id"/>
        <result property="sourceBizNo" column="source_biz_no"/>
        <result property="sourceSubId" column="source_sub_id"/>
        <result property="outhospno" column="outhospno"/>
        <result property="inhospno" column="inhospno"/>
        <result property="serialnum" column="serialnum"/>
@@ -52,6 +56,10 @@
    <sql id="selectPatChildArchiveVo">
        select id,
               archive_type,
               source_type,
               source_biz_id,
               source_biz_no,
               source_sub_id,
               outhospno,
               inhospno,
               serialnum,
@@ -101,6 +109,10 @@
        where 1=1
            and del_flag = '0'
            <if test="archiveType != null and archiveType != ''">and archive_type = #{archiveType}</if>
            <if test="sourceType != null and sourceType != ''">and source_type = #{sourceType}</if>
            <if test="sourceBizId != null">and source_biz_id = #{sourceBizId}</if>
            <if test="sourceBizNo != null and sourceBizNo != ''">and source_biz_no = #{sourceBizNo}</if>
            <if test="sourceSubId != null">and source_sub_id = #{sourceSubId}</if>
            <if test="outhospno != null and outhospno != ''">and outhospno = #{outhospno}</if>
            <if test="inhospno != null and inhospno != ''">and inhospno = #{inhospno}</if>
            <if test="serialnum != null and serialnum != ''">and serialnum = #{serialnum}</if>
@@ -152,6 +164,10 @@
        insert into pat_child_archive
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="archiveType != null">archive_type,</if>
            <if test="sourceType != null">source_type,</if>
            <if test="sourceBizId != null">source_biz_id,</if>
            <if test="sourceBizNo != null">source_biz_no,</if>
            <if test="sourceSubId != null">source_sub_id,</if>
            <if test="outhospno != null">outhospno,</if>
            <if test="inhospno != null">inhospno,</if>
            <if test="serialnum != null">serialnum,</if>
@@ -195,6 +211,10 @@
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="archiveType != null">#{archiveType},</if>
            <if test="sourceType != null">#{sourceType},</if>
            <if test="sourceBizId != null">#{sourceBizId},</if>
            <if test="sourceBizNo != null">#{sourceBizNo},</if>
            <if test="sourceSubId != null">#{sourceSubId},</if>
            <if test="outhospno != null">#{outhospno},</if>
            <if test="inhospno != null">#{inhospno},</if>
            <if test="serialnum != null">#{serialnum},</if>
@@ -242,6 +262,10 @@
        update pat_child_archive
        <trim prefix="SET" suffixOverrides=",">
            <if test="archiveType != null">archive_type = #{archiveType},</if>
            <if test="sourceType != null">source_type = #{sourceType},</if>
            <if test="sourceBizId != null">source_biz_id = #{sourceBizId},</if>
            <if test="sourceBizNo != null">source_biz_no = #{sourceBizNo},</if>
            <if test="sourceSubId != null">source_sub_id = #{sourceSubId},</if>
            <if test="outhospno != null">outhospno = #{outhospno},</if>
            <if test="inhospno != null">inhospno = #{inhospno},</if>
            <if test="serialnum != null">serialnum = #{serialnum},</if>
smartor/src/main/resources/mapper/smartor/PatMaternityArchiveDetailMapper.xml
@@ -9,6 +9,9 @@
        <result property="archiveid" column="archiveid"/>
        <result property="archiveType" column="archive_type"/>
        <result property="sourceType" column="source_type"/>
        <result property="sourceBizId" column="source_biz_id"/>
        <result property="sourceBizNo" column="source_biz_no"/>
        <result property="sourceSubId" column="source_sub_id"/>
        <result property="outhospno" column="outhospno"/>
        <result property="inhospno" column="inhospno"/>
        <result property="serialnum" column="serialnum"/>
@@ -67,6 +70,9 @@
               archiveid,
               archive_type,
               source_type,
               source_biz_id,
               source_biz_no,
               source_sub_id,
               outhospno,
               inhospno,
               serialnum,
@@ -129,6 +135,9 @@
            <if test="archiveid != null">and archiveid = #{archiveid}</if>
            <if test="archiveType != null and archiveType != ''">and archive_type = #{archiveType}</if>
            <if test="sourceType != null and sourceType != ''">and source_type = #{sourceType}</if>
            <if test="sourceBizId != null">and source_biz_id = #{sourceBizId}</if>
            <if test="sourceBizNo != null and sourceBizNo != ''">and source_biz_no = #{sourceBizNo}</if>
            <if test="sourceSubId != null">and source_sub_id = #{sourceSubId}</if>
            <if test="outhospno != null and outhospno != ''">and outhospno = #{outhospno}</if>
            <if test="inhospno != null and inhospno != ''">and inhospno = #{inhospno}</if>
            <if test="serialnum != null and serialnum != ''">and serialnum = #{serialnum}</if>
@@ -174,6 +183,9 @@
            <if test="archiveid != null">archiveid,</if>
            <if test="archiveType != null">archive_type,</if>
            <if test="sourceType != null">source_type,</if>
            <if test="sourceBizId != null">source_biz_id,</if>
            <if test="sourceBizNo != null">source_biz_no,</if>
            <if test="sourceSubId != null">source_sub_id,</if>
            <if test="outhospno != null">outhospno,</if>
            <if test="inhospno != null">inhospno,</if>
            <if test="serialnum != null">serialnum,</if>
@@ -230,6 +242,9 @@
            <if test="archiveid != null">#{archiveid},</if>
            <if test="archiveType != null">#{archiveType},</if>
            <if test="sourceType != null">#{sourceType},</if>
            <if test="sourceBizId != null">#{sourceBizId},</if>
            <if test="sourceBizNo != null">#{sourceBizNo},</if>
            <if test="sourceSubId != null">#{sourceSubId},</if>
            <if test="outhospno != null">#{outhospno},</if>
            <if test="inhospno != null">#{inhospno},</if>
            <if test="serialnum != null">#{serialnum},</if>
@@ -290,6 +305,9 @@
            <if test="archiveid != null">archiveid = #{archiveid},</if>
            <if test="archiveType != null">archive_type = #{archiveType},</if>
            <if test="sourceType != null">source_type = #{sourceType},</if>
            <if test="sourceBizId != null">source_biz_id = #{sourceBizId},</if>
            <if test="sourceBizNo != null">source_biz_no = #{sourceBizNo},</if>
            <if test="sourceSubId != null">source_sub_id = #{sourceSubId},</if>
            <if test="outhospno != null">outhospno = #{outhospno},</if>
            <if test="inhospno != null">inhospno = #{inhospno},</if>
            <if test="serialnum != null">serialnum = #{serialnum},</if>
smartor/src/main/resources/mapper/smartor/PatMaternityArchiveMapper.xml
@@ -7,6 +7,10 @@
    <resultMap type="com.smartor.domain.PatMaternityArchive" id="PatMaternityArchiveResult">
        <result property="id" column="id"/>
        <result property="archiveType" column="archive_type"/>
        <result property="sourceType" column="source_type"/>
        <result property="sourceBizId" column="source_biz_id"/>
        <result property="sourceBizNo" column="source_biz_no"/>
        <result property="sourceSubId" column="source_sub_id"/>
        <result property="outhospno" column="outhospno"/>
        <result property="inhospno" column="inhospno"/>
        <result property="serialnum" column="serialnum"/>
@@ -47,6 +51,10 @@
    <sql id="selectPatMaternityArchiveVo">
        select id,
               archive_type,
               source_type,
               source_biz_id,
               source_biz_no,
               source_sub_id,
               outhospno,
               inhospno,
               serialnum,
@@ -91,6 +99,10 @@
        where 1=1
            and del_flag = '0'
            <if test="archiveType != null and archiveType != ''">and archive_type = #{archiveType}</if>
            <if test="sourceType != null and sourceType != ''">and source_type = #{sourceType}</if>
            <if test="sourceBizId != null">and source_biz_id = #{sourceBizId}</if>
            <if test="sourceBizNo != null and sourceBizNo != ''">and source_biz_no = #{sourceBizNo}</if>
            <if test="sourceSubId != null">and source_sub_id = #{sourceSubId}</if>
            <if test="outhospno != null and outhospno != ''">and outhospno = #{outhospno}</if>
            <if test="inhospno != null and inhospno != ''">and inhospno = #{inhospno}</if>
            <if test="serialnum != null and serialnum != ''">and serialnum = #{serialnum}</if>
@@ -138,6 +150,10 @@
        insert into pat_maternity_archive
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="archiveType != null">archive_type,</if>
            <if test="sourceType != null">source_type,</if>
            <if test="sourceBizId != null">source_biz_id,</if>
            <if test="sourceBizNo != null">source_biz_no,</if>
            <if test="sourceSubId != null">source_sub_id,</if>
            <if test="outhospno != null">outhospno,</if>
            <if test="inhospno != null">inhospno,</if>
            <if test="serialnum != null">serialnum,</if>
@@ -176,6 +192,10 @@
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="archiveType != null">#{archiveType},</if>
            <if test="sourceType != null">#{sourceType},</if>
            <if test="sourceBizId != null">#{sourceBizId},</if>
            <if test="sourceBizNo != null">#{sourceBizNo},</if>
            <if test="sourceSubId != null">#{sourceSubId},</if>
            <if test="outhospno != null">#{outhospno},</if>
            <if test="inhospno != null">#{inhospno},</if>
            <if test="serialnum != null">#{serialnum},</if>
@@ -218,6 +238,10 @@
        update pat_maternity_archive
        <trim prefix="SET" suffixOverrides=",">
            <if test="archiveType != null">archive_type = #{archiveType},</if>
            <if test="sourceType != null">source_type = #{sourceType},</if>
            <if test="sourceBizId != null">source_biz_id = #{sourceBizId},</if>
            <if test="sourceBizNo != null">source_biz_no = #{sourceBizNo},</if>
            <if test="sourceSubId != null">source_sub_id = #{sourceSubId},</if>
            <if test="outhospno != null">outhospno = #{outhospno},</if>
            <if test="inhospno != null">inhospno = #{inhospno},</if>
            <if test="serialnum != null">serialnum = #{serialnum},</if>