package com.ruoyi.common.core.service; import com.ruoyi.common.core.service.dto.InHospPublicityRowDto; import com.ruoyi.common.core.service.dto.PublicityTaskMatchDto; import java.util.List; /** * 宣教模块所需的只读/回写数据端口(接口在 ruoyi-common,实现由 smartor 提供)。 * *

为什么要有这个端口

* 宣教模块({@code ruoyi-publicity})要做两件事: *
    *
  1. 查"这个科室/病区/药品有没有配长期宣教任务"({@code service_taskdept} / {@code service_taskmedical});
  2. *
  3. 扫"哪些在院患者还没做入院宣教"({@code pat_med_inhosp} 的 {@code in_deptcheck_flag} / * {@code in_wardcheck_flag}),查不到患者档案时把标志位置 "2" 并追加原因。
  4. *
* 这些表和 SQL 全在 smartor。若让 {@code ruoyi-publicity} 直接依赖 smartor, * 就会形成 {@code smartor ↔ ruoyi-publicity} 循环依赖(smartor 要依赖它来触发宣教)。 * 若把 SQL 抄一份到新模块,就会出现"同一份口径两处维护"的隐患 * —— 这正是满意度模块当初极力避免的坑。 *

* 所以:接口放 ruoyi-common,实现({@code PublicityDataPortBridge})留在 smartor, * 宣教模块运行时按类型查找实现。这与 * {@link VisitTaskCreateApi} / {@link PublicitySubtaskApi} 是同一手法。 * *

调用方务必降级

* 取不到实现时按"没有宣教要做"处理并打 WARN,绝不能让出入院随访主流程抛异常。 */ public interface PublicityDataPort { /** * 扫在院患者(原 {@code patMedInhospMapper.selectPatMedInhospList} 的两段查询)。 *

* 迁移对应 {@code PatMedInhospServiceImpl#dealOutHospInfo}: *

* 两段是独立查询,调用方分两次调(不要合并成一次"OR"查询,那会改变结果集)。 * * @param checkFlagColumn {@code "in_deptcheck_flag"} 或 {@code "in_wardcheck_flag"}(只允许这两个值) * @return 在院患者行;无数据返回空列表 */ List findInHospForPublicity(String checkFlagColumn); /** * 按科室/病区查长期宣教任务ID(原 {@code serviceTaskdeptMapper.selectServiceTaskdeptList})。 *

* 迁移对应 L347-353({@code deptType="1"}、{@code serviceType="17"})与 * L390-396({@code deptType="2"}、{@code serviceType="17"})。 * SQL 条件:{@code del_flag = 0}、{@code longtask = 1}、{@code dept_code = ?}、 * {@code dept_type = ?}、{@code service_type = '17'}、{@code orgid = ?}。 * * @param deptCode 科室编码(deptType=1)或病区编码(deptType=2) * @param deptType "1" = 科室, "2" = 病区 * @param orgid 机构ID * @param inhospid 该条记录对应的出入院记录ID(回填进返回值,方便调用方回调 inhospvisit) * @return 命中的任务(可能多条);无数据返回空列表 */ List findDeptPublicityTaskIds(String deptCode, String deptType, String orgid, Long inhospid); /** * 按药品编码 + 名称 + 公司查长期宣教任务ID(原 {@code serviceTaskmedicalMapper.selectServiceTaskmedicalList})。 *

* 迁移对应原代码 L424-434。原实现设了三个字段: *

     * serviceTaskmedical.setMedicalCode(split[i]);
     * if (i < medicalCompanyArray.length) {
     *     serviceTaskmedical.setMedicalCompany(medicalCompanyArray[i]);
     * } else {
     *     log.warn("...下标越界...");
     *     serviceTaskmedical.setMedicalName("");   // ← 注意:越界时设的是 name,不是 company
     * }
     * 
*

* ⚠️ 越界分支是个历史笔误,但会真实影响 SQL,因此必须原样保留: * 越界时 {@code medical_name} 被置为空串(而非 null), * 而 SQL 条件是 {@code } —— 空串会跳过该条件; * 同时 {@code medical_company} 保持为 null(未被赋值),SQL 条件同样跳过。 * 也就是说:越界时两个条件都不过滤(等价于只按 medical_code 查)。 *

* 所以本方法必须把 {@code medicalName} 也传进来 —— 不能让 Bridge 自作主张把空串转成 null * 或反过来(那会改变 SQL 条件是否生效)。 * * @param medicalCode 药品编码 * @param medicalName 药品名称;越界分支传空串;正常分支传 null * @param medicalCompany 药品公司;越界分支传 null;正常分支传数组里的值 * @param inhospid 该条记录对应的出入院记录ID * @return 命中的任务(可能多条);无数据返回空列表 */ List findMedicalPublicityTaskIds(String medicalCode, String medicalName, String medicalCompany, Long inhospid); /** * 患者档案是否存在(原 {@code patArchiveMapper.selectPatArchiveByPatid(patid)} 的判空用途)。 *

* 迁移对应 L338-344 / L382-388。原实现取的是完整 {@code PatArchive}, * 但宣教这一层只用到"是不是 null"(真正的患者信息填充发生在 inhospvisit 组子任务时), * 所以这里只返回存在性。 * * @param patid 患者档案ID(即 {@code PatMedInhosp.patid} / {@code PatArchive.id},都是 Long) * @return true = 存在且未删除 */ boolean existsPatArchive(Long patid); /** * 把入院宣教的标志位置为给定值,并可同时写 {@code long_task_reason}。 *

* 迁移对应 L340-342 / L357-359 / L367-370(科室维度)与 L384-386 / L400-402(病区维度)。 * *

⚠️ 原实现有"覆盖"与"追加"两种写法,必须区分(所以这里给的是 {@code reasonMode})

* * * * * * * * * * * * * * *
场景原代码语义
患者基本信息为空(科室/病区共用){@code setLongTaskReason("患者基本信息为空")}覆盖(直接赋值,丢掉旧值)
科室未配宣教任务{@code setLongTaskReason(旧值 + "该患者所在科室未配置入院长期宣教任务;")}
* 其中"旧值"是按 inhospid 重查一次拿到的
追加(且不判旧值是否为 null)
病区未配宣教任务{@code setLongTaskReason(旧值 + "该患者所在病区未配置入院长期宣教任务;")}追加(同样不判 null;旧值为 null 时会拼出 {@code "null该患者所在病区..."})
科室处理异常{@code setLongTaskReason("人员入院科室处理出异常了")}覆盖;且原代码不设 longTaskReason 前缀、也不重查
* 因为"覆盖 / 追加"是两种不同语义,本方法用一个布尔区分,而不是让调用方拼字符串 * —— 拼字符串会让 {@code null} 前缀这种原缺陷被"顺手修掉",那就成了行为变更。 * * @param inhospid 出入院记录ID * @param flagColumn {@code "in_deptcheck_flag"} 或 {@code "in_wardcheck_flag"}(只允许这两个值) * @param flagValue 要写入的标志位值(原实现写 "2") * @param reason 要写入 {@code long_task_reason} 的文案;为 null 表示不动该字段 * @param appendMode true = 追加({@code 旧值 + reason},旧值按 inhospid 重查,不判 null); * false = 覆盖(直接写 {@code reason}) * @return true = 更新成功 */ boolean updatePublicityFlag(Long inhospid, String flagColumn, String flagValue, String reason, boolean appendMode); /** * 按字段更新出入院记录(可一次写多个 flag + {@code long_task_reason} + {@code remark})。 * *

为什么要有这个"宽"接口

* 出院随访编排层({@code InhospTaskOrchestrator} 的 3 个 {@code *ForPatient})在写失败标志位时, * 原实现有三种不同写法,写进去的列集合也不同: * * 窄接口 {@link #updatePublicityFlag} 表达不了"一次写多个 flag", * 所以加这个通道。调用方按原逻辑决定给哪些字段赋值,null = 不改 * (对应原 XML 每个字段各自的 {@code })。 * * @param update 更新草稿({@code inhospid} 必填) * @return true = 影响行数 > 0 */ boolean updateVisitInhosp(com.ruoyi.common.core.service.dto.VisitInhospUpdateDto update); /** * 按离院科室/病区查「任务执行规则」(原 {@code ServiceTaskRuleMapper#selectServiceTaskRuleByDeptOrWard})。 * *

为什么放在这个端口上

* 该查询服务的是出院随访的规则编排({@code addSubTaskByRule}), * 语义上不属于"宣教"。但本端口已经是"随访侧访问 smartor 只读配置"的统一入口 * (它已经提供 {@code findInHospForPublicity} / {@code findDeptPublicityTaskIds} 等), * 再为一张单表加第四个端口不划算。这里在注释里写明用途,避免后人误以为它只服务宣教。 *

* 迁移对应 {@code PatMedInhospServiceImpl#addSubTaskByRule} L1704-1707: *

     * ServiceTaskRule ruleQuery = new ServiceTaskRule();
     * ruleQuery.setDeptcode(patMedInhosp1.getLeaveldeptcode());
     * ruleQuery.setWardcode(patMedInhosp1.getLeavehospitaldistrictcode());
     * ServiceTaskRule rule = serviceTaskRuleMapper.selectServiceTaskRuleByDeptOrWard(ruleQuery);
     * 
* 注意:该查询最多返回一条(原实现直接取单个对象)。 * * @param leaveldeptcode 离院科室编码 * @param leavehospitaldistrictcode 离院病区编码 * @return 规则;未配置返回 {@code null} */ com.ruoyi.common.core.service.dto.VisitTaskRuleDto findTaskRule(String leaveldeptcode, String leavehospitaldistrictcode); }