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;
|
|
/**
|
* 其他随访(手术随访)模块对外门面(<b>常驻 Bean</b>)。
|
*
|
* <h3>这是其它模块访问手术数据的<b>唯一入口</b></h3>
|
* 按你定的跨模块数据访问原则:<b>别的模块要用手术数据,只能从这个模块调用,
|
* 而不是在自己模块里写 SQL</b>。所以本门面包了两类能力:
|
* <ol>
|
* <li><b>随访编排</b>:{@link #dealOperationInfo()};</li>
|
* <li><b>基础数据读写</b>:{@code pat_med_operation} / {@code pat_med_operation_item} /
|
* {@code base_dict_operation} 的增删改查。</li>
|
* </ol>
|
* 相应地,下面的调用方都必须改走本门面(本次一并改完):
|
* <ul>
|
* <li>{@code ruoyi-quartz} 的 {@code RyTask#dealOperationInfo} 与 {@code CollectHISServiceImpl};</li>
|
* <li>smartor 的 {@code ServiceExternalServiceImpl}(HIS 外部手术回传)、
|
* {@code PatMedInhospServiceImpl}(出入院随访的手术分支)、
|
* {@code ServiceSubtaskServiceImpl}。</li>
|
* </ul>
|
*
|
* <h3>为什么用常驻 Bean(不用 {@code @ConditionalOnProperty})</h3>
|
* 与 {@code InhospVisitFacade} / {@code PublicityFacade} / {@code OuthospVisitFacade} 同一套设计:
|
* <ul>
|
* <li><b>永远存在</b> —— 调用方({@code RyTask} 定时任务、{@code @Autowired})在任何开关状态下
|
* 都<b>不会启动失败</b>;</li>
|
* <li>开关在 {@code sys_config} 的 {@code othervisit.enabled},由
|
* {@link OtherVisitSwitch} <b>运行时</b>读取(10 秒进程内缓存),改完无需重启;</li>
|
* <li>关闭时返回安全值并打 WARN,<b>不抛异常</b>,保证调用方主流程照常完成。</li>
|
* </ul>
|
*
|
* <h3>⚠️ 哪些方法<b>不</b>受开关管辖</h3>
|
* 下面所有「基础数据读写」方法都<b>不</b>判 {@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;
|
|
/**
|
* 手术随访编排(扫描未处理手术记录 → 匹配长期任务 → 写子任务)。
|
* <p>
|
* ⚠️ 用 {@code ObjectProvider} 惰性取:容器里若没有它也不该让门面装配失败。
|
*/
|
private final ObjectProvider<OperationVisitTaskService> taskServiceProvider;
|
|
/**
|
* 手术记录基础读写(本模块自己的组件,没有条件装配,故直接注入)。
|
*/
|
private final OperationService operationService;
|
|
/**
|
* 手术明细基础读写。
|
*/
|
private final OperationItemService operationItemService;
|
|
/**
|
* 手术基础信息读写。
|
*/
|
private final BaseDictOperationService baseDictOperationService;
|
|
/**
|
* Spring 使用的构造器。
|
* <p>
|
* ⚠️ <b>必须显式标注 {@code @Autowired}</b>:本类有两个公开构造器
|
* (另一个是给测试/非 Spring 环境用的),若不标注,Spring 会因为"构造器不唯一"
|
* 而退回查找无参构造器,从而在启动时报 {@code No default constructor found}。
|
* —— 这是满意度模块<b>实机踩到</b>的缺陷,此处刻意保留同样的写法与注释。
|
*/
|
@Autowired
|
public OtherVisitFacade(OtherVisitProperties properties,
|
OtherVisitSwitch otherVisitSwitch,
|
ObjectProvider<OperationVisitTaskService> 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;
|
}
|
|
/**
|
* 供<b>测试/非 Spring 环境</b>直接构造。
|
* <p>
|
* 所有数据服务与编排服务都不传:测试构造下调用它们会打 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} 调用)。
|
*
|
* <h3>⚠️ 本方法<b>受</b> {@code othervisit.enabled} 管辖</h3>
|
* 它是"生成手术随访"的业务动作 —— 关掉开关就该停下。
|
* 关闭时返回 {@code 0} 并打 WARN(不抛异常,保证定时任务主流程不被拖垮)。
|
*
|
* <h3>返回值说明</h3>
|
* 正常时返回编排结果(原实现固定返回 1,<b>不是</b>处理条数);
|
* 关闭或组件不可用时返回 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)—— 不受开关管辖
|
// ==========================================================================
|
|
/**
|
* 查询手术记录。
|
* <p>对应原 {@code IPatMedOperationService#selectPatMedOperationById}。
|
*/
|
public PatMedOperation selectOperationById(Long id) {
|
return operationService == null ? null : operationService.selectById(id);
|
}
|
|
/**
|
* 查询手术记录列表。
|
* <p>对应原 {@code IPatMedOperationService#selectPatMedOperationList}。
|
*/
|
public List<PatMedOperation> selectOperationList(PatMedOperation query) {
|
return operationService == null ? java.util.Collections.emptyList() : operationService.selectList(query);
|
}
|
|
/**
|
* 新增手术记录。
|
* <p>对应原 {@code IPatMedOperationService#insertPatMedOperation}。
|
*/
|
public int insertOperation(PatMedOperation row) {
|
return operationService == null ? 0 : operationService.insert(row);
|
}
|
|
/**
|
* 修改手术记录。
|
* <p>对应原 {@code IPatMedOperationService#updatePatMedOperation}。
|
*/
|
public int updateOperation(PatMedOperation row) {
|
return operationService == null ? 0 : operationService.update(row);
|
}
|
|
/**
|
* 批量删除手术记录。
|
* <p>对应原 {@code IPatMedOperationService#deletePatMedOperationByIds}。
|
*/
|
public int deleteOperationByIds(Long[] ids) {
|
return operationService == null ? 0 : operationService.deleteByIds(ids);
|
}
|
|
/**
|
* 删除手术记录。
|
* <p>对应原 {@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<PatMedOperationItem> selectOperationItemList(PatMedOperationItem query) {
|
return operationItemService == null ? java.util.Collections.emptyList() : operationItemService.selectList(query);
|
}
|
|
/**
|
* 按条件查手术明细({@code JOIN pat_med_inhosp} 的那条)。
|
* <p>
|
* ⚠️ 与 {@link #selectOperationItemList(PatMedOperationItem)} <b>不是同一条 SQL</b>,别混用。
|
*/
|
public List<PatMedOperationItem> selectOperationItemListByCondition(PatMedOperationItem query) {
|
return operationItemService == null ? java.util.Collections.emptyList()
|
: operationItemService.selectOperationItemList(query);
|
}
|
|
/**
|
* 按条件查手术明细,同一 {@code opid} 下只取 {@code oplevelcode} 最大的一条。
|
* <p>调用方:出入院随访的手术分支({@code PatMedInhospServiceImpl})。
|
*/
|
public List<PatMedOperationItem> 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<PatMedOperationItem> rows) {
|
return operationItemService == null ? 0 : operationItemService.insertBatch(rows);
|
}
|
|
public int updateOperationItem(PatMedOperationItem row) {
|
return operationItemService == null ? 0 : operationItemService.update(row);
|
}
|
|
/**
|
* 按 {@code operationid} 更新手术明细。
|
* <p>⚠️ 不代设 {@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} <b>物理</b>删除手术明细。
|
*/
|
public int deleteHardOperationItemByOpId(String opid) {
|
return operationItemService == null ? 0 : operationItemService.deleteHardByOpId(opid);
|
}
|
|
/**
|
* 按 {@code opid} <b>软</b>删除手术明细。
|
*/
|
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<BaseDictOperation> 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;
|
}
|
}
|
}
|