package com.ruoyi.othervisit.api; import com.ruoyi.othervisit.config.OtherVisitProperties; import com.ruoyi.othervisit.config.OtherVisitSwitch; import com.ruoyi.othervisit.domain.BaseDictOperation; import com.ruoyi.othervisit.domain.PatMedOperation; import com.ruoyi.othervisit.domain.PatMedOperationItem; import com.ruoyi.othervisit.service.BaseDictOperationService; import com.ruoyi.othervisit.service.OperationItemService; import com.ruoyi.othervisit.service.OperationService; import com.ruoyi.othervisit.service.OperationVisitTaskService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.List; /** * 其他随访(手术随访)模块对外门面(常驻 Bean)。 * *

这是其它模块访问手术数据的唯一入口

* 按你定的跨模块数据访问原则:别的模块要用手术数据,只能从这个模块调用, * 而不是在自己模块里写 SQL。所以本门面包了两类能力: *
    *
  1. 随访编排:{@link #dealOperationInfo()};
  2. *
  3. 基础数据读写:{@code pat_med_operation} / {@code pat_med_operation_item} / * {@code base_dict_operation} 的增删改查。
  4. *
* 相应地,下面的调用方都必须改走本门面(本次一并改完): * * *

为什么用常驻 Bean(不用 {@code @ConditionalOnProperty})

* 与 {@code InhospVisitFacade} / {@code PublicityFacade} / {@code OuthospVisitFacade} 同一套设计: * * *

⚠️ 哪些方法不受开关管辖

* 下面所有「基础数据读写」方法都不判 {@code othervisit.enabled}, * 理由与出入院/门诊模块一致:手术记录是 HIS 采集写入的原始业务数据, * 关掉"生成随访"不该让 HIS 采集写不进手术表 —— 那是数据故障,与"今天不发随访"不是一回事。 * 只有 {@link #dealOperationInfo()} 受开关管辖。 */ @Slf4j @Component public class OtherVisitFacade { private final OtherVisitProperties properties; /** * 运行时开关({@code sys_config} 优先,{@link OtherVisitProperties} 兜底) */ private final OtherVisitSwitch otherVisitSwitch; /** * 手术随访编排(扫描未处理手术记录 → 匹配长期任务 → 写子任务)。 *

* ⚠️ 用 {@code ObjectProvider} 惰性取:容器里若没有它也不该让门面装配失败。 */ private final ObjectProvider taskServiceProvider; /** * 手术记录基础读写(本模块自己的组件,没有条件装配,故直接注入)。 */ private final OperationService operationService; /** * 手术明细基础读写。 */ private final OperationItemService operationItemService; /** * 手术基础信息读写。 */ private final BaseDictOperationService baseDictOperationService; /** * Spring 使用的构造器。 *

* ⚠️ 必须显式标注 {@code @Autowired}:本类有两个公开构造器 * (另一个是给测试/非 Spring 环境用的),若不标注,Spring 会因为"构造器不唯一" * 而退回查找无参构造器,从而在启动时报 {@code No default constructor found}。 * —— 这是满意度模块实机踩到的缺陷,此处刻意保留同样的写法与注释。 */ @Autowired public OtherVisitFacade(OtherVisitProperties properties, OtherVisitSwitch otherVisitSwitch, ObjectProvider taskServiceProvider, OperationService operationService, OperationItemService operationItemService, BaseDictOperationService baseDictOperationService) { this.properties = properties; this.otherVisitSwitch = otherVisitSwitch; this.taskServiceProvider = taskServiceProvider; this.operationService = operationService; this.operationItemService = operationItemService; this.baseDictOperationService = baseDictOperationService; } /** * 供测试/非 Spring 环境直接构造。 *

* 所有数据服务与编排服务都不传:测试构造下调用它们会打 WARN 并安全返回, * 保证开关校验类测试不必拉起数据库。 */ public OtherVisitFacade(OtherVisitProperties properties, String sysConfigValue) { this(properties, new OtherVisitSwitch(properties, sysConfigValue), null, null, null, null); } /** * 取运行时开关(供测试断言 / 拦截器复用) */ public OtherVisitSwitch getOtherVisitSwitch() { return otherVisitSwitch; } /** * 手术随访模块是否可用(开关打开) */ public boolean isEnabled() { return otherVisitSwitch.isEnabled(); } // ========================================================================== // 随访编排(受开关管辖) // ========================================================================== /** * 手术随访主扫描(供 {@code RyTask#dealOperationInfo} 调用)。 * *

⚠️ 本方法受 {@code othervisit.enabled} 管辖

* 它是"生成手术随访"的业务动作 —— 关掉开关就该停下。 * 关闭时返回 {@code 0} 并打 WARN(不抛异常,保证定时任务主流程不被拖垮)。 * *

返回值说明

* 正常时返回编排结果(原实现固定返回 1,不是处理条数); * 关闭或组件不可用时返回 0。原调用方 {@code RyTask} 忽略返回值, * 所以这个约定只影响日志与将来的排查。 */ public Integer dealOperationInfo() { if (!isEnabled()) { log.warn("【手术随访】模块未启用(参数配置 othervisit.enabled=false),dealOperationInfo 跳过。"); return 0; } OperationVisitTaskService svc = taskService(); if (svc == null) { log.warn("【手术随访】容器里没有 OperationVisitTaskService(或非 Spring 构造),dealOperationInfo 跳过。"); return 0; } return svc.dealOperationInfo(); } // ========================================================================== // 手术记录(pat_med_operation)—— 不受开关管辖 // ========================================================================== /** * 查询手术记录。 *

对应原 {@code IPatMedOperationService#selectPatMedOperationById}。 */ public PatMedOperation selectOperationById(Long id) { return operationService == null ? null : operationService.selectById(id); } /** * 查询手术记录列表。 *

对应原 {@code IPatMedOperationService#selectPatMedOperationList}。 */ public List selectOperationList(PatMedOperation query) { return operationService == null ? java.util.Collections.emptyList() : operationService.selectList(query); } /** * 新增手术记录。 *

对应原 {@code IPatMedOperationService#insertPatMedOperation}。 */ public int insertOperation(PatMedOperation row) { return operationService == null ? 0 : operationService.insert(row); } /** * 修改手术记录。 *

对应原 {@code IPatMedOperationService#updatePatMedOperation}。 */ public int updateOperation(PatMedOperation row) { return operationService == null ? 0 : operationService.update(row); } /** * 批量删除手术记录。 *

对应原 {@code IPatMedOperationService#deletePatMedOperationByIds}。 */ public int deleteOperationByIds(Long[] ids) { return operationService == null ? 0 : operationService.deleteByIds(ids); } /** * 删除手术记录。 *

对应原 {@code IPatMedOperationService#deletePatMedOperationById}。 */ public int deleteOperationById(Long id) { return operationService == null ? 0 : operationService.deleteById(id); } // ========================================================================== // 手术明细(pat_med_operation_item)—— 不受开关管辖 // ========================================================================== public PatMedOperationItem selectOperationItemById(Long id) { return operationItemService == null ? null : operationItemService.selectById(id); } public List selectOperationItemList(PatMedOperationItem query) { return operationItemService == null ? java.util.Collections.emptyList() : operationItemService.selectList(query); } /** * 按条件查手术明细({@code JOIN pat_med_inhosp} 的那条)。 *

* ⚠️ 与 {@link #selectOperationItemList(PatMedOperationItem)} 不是同一条 SQL,别混用。 */ public List selectOperationItemListByCondition(PatMedOperationItem query) { return operationItemService == null ? java.util.Collections.emptyList() : operationItemService.selectOperationItemList(query); } /** * 按条件查手术明细,同一 {@code opid} 下只取 {@code oplevelcode} 最大的一条。 *

调用方:出入院随访的手术分支({@code PatMedInhospServiceImpl})。 */ public List selectOperationItemListMaxLevel(PatMedOperationItem query) { return operationItemService == null ? java.util.Collections.emptyList() : operationItemService.selectOperationItemListMaxLevel(query); } public int insertOperationItem(PatMedOperationItem row) { return operationItemService == null ? 0 : operationItemService.insert(row); } public int insertOperationItems(List rows) { return operationItemService == null ? 0 : operationItemService.insertBatch(rows); } public int updateOperationItem(PatMedOperationItem row) { return operationItemService == null ? 0 : operationItemService.update(row); } /** * 按 {@code operationid} 更新手术明细。 *

⚠️ 不代设 {@code updateTime}(原直调 Mapper 的调用方自己负责)。 */ public int updateOperationItemByOperationid(PatMedOperationItem row) { return operationItemService == null ? 0 : operationItemService.updateByOperationid(row); } public int deleteOperationItemByIds(Long[] ids) { return operationItemService == null ? 0 : operationItemService.deleteByIds(ids); } public int deleteOperationItemById(Long id) { return operationItemService == null ? 0 : operationItemService.deleteById(id); } /** * 按 {@code opid} 物理删除手术明细。 */ public int deleteHardOperationItemByOpId(String opid) { return operationItemService == null ? 0 : operationItemService.deleteHardByOpId(opid); } /** * 按 {@code opid} 软删除手术明细。 */ public int deleteSoftOperationItemByOpId(String opid) { return operationItemService == null ? 0 : operationItemService.deleteSoftByOpId(opid); } /** * 按 {@code operationid} 删除手术明细。 */ public int deleteOperationItemByOperationid(Long operationid) { return operationItemService == null ? 0 : operationItemService.deleteByOperationid(operationid); } // ========================================================================== // 手术基础信息(base_dict_operation)—— 不受开关管辖 // ========================================================================== public BaseDictOperation selectBaseDictOperationById(Long id) { return baseDictOperationService == null ? null : baseDictOperationService.selectById(id); } public List selectBaseDictOperationList(BaseDictOperation query) { return baseDictOperationService == null ? java.util.Collections.emptyList() : baseDictOperationService.selectList(query); } public int insertBaseDictOperation(BaseDictOperation row) { return baseDictOperationService == null ? 0 : baseDictOperationService.insert(row); } public int updateBaseDictOperation(BaseDictOperation row) { return baseDictOperationService == null ? 0 : baseDictOperationService.update(row); } public int deleteBaseDictOperationByIds(Long[] ids) { return baseDictOperationService == null ? 0 : baseDictOperationService.deleteByIds(ids); } public int deleteBaseDictOperationById(Long id) { return baseDictOperationService == null ? 0 : baseDictOperationService.deleteById(id); } // ========================================================================== private OperationVisitTaskService taskService() { if (taskServiceProvider == null) { return null; } try { return taskServiceProvider.stream().findFirst().orElse(null); } catch (Exception e) { log.warn("【手术随访】获取 OperationVisitTaskService 失败,按不可用处理:{}", e.toString()); return null; } } }