package com.ruoyi.publicity.controller;
|
|
import com.ruoyi.common.core.service.IConfigService;
|
import com.ruoyi.publicity.api.PublicityFacade;
|
import io.swagger.annotations.Api;
|
import io.swagger.annotations.ApiOperation;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.ObjectProvider;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RestController;
|
|
import java.util.LinkedHashMap;
|
import java.util.Map;
|
|
/**
|
* 宣教模块的 HTTP 入口:<b>宣教生成入口 + 开关/状态查询</b>。
|
*
|
* <h3>为什么这些端点是"新增"的(与出入院模块的关键区别)</h3>
|
* 出入院模块的 controller 是<b>把 ruoyi-admin 已有的端点搬进来</b>(URL 一个不变);
|
* 而宣教在本仓库里<b>从来没有过 controller</b> —— 它原本只是
|
* {@code PatMedInhospServiceImpl#dealOutHospInfo} 内部的两段调用
|
* (入院宣教 L331-409、药品宣教 L411-444),迁出后也只到 Service 层为止。
|
* 因此本类的 URL 是<b>新定义</b>的,前端若要手动触发需要接新地址;
|
* 但"主流程自动触发"不受影响(那是 {@code RyTask} / {@code dealOutHospInfo} 走的门面调用)。
|
*
|
* <h3>端点清单</h3>
|
* <pre>
|
* POST /smartor/publicity/dealInHospPublicity 入院宣教(科室 type=4 / 病区 type=5)
|
* POST /smartor/publicity/dealMedicalPublicity 药品宣教(仅 sltd 站点,type=0)
|
* GET /smartor/publicity/status 开关与子开关状态(只读、不受开关拦截)
|
* </pre>
|
*
|
* <h3>⚠️ 开关的层级(两个开关,别混)</h3>
|
* <ol>
|
* <li>{@code sys_config: publicity.enabled} —— <b>模块级</b>开关,由
|
* {@link PublicityFacade#isEnabled()} 在运行时读(10 秒缓存)。
|
* 它<b>只</b>决定 {@link PublicityFacade} 是否把调用转发下去;
|
* 关闭时两个生成端点返回 0 并打 WARN。</li>
|
* <li>{@code sys_config: in.helibrary.switch} —— <b>入院宣教自身的业务开关</b>,
|
* 只有值等于 {@code "2"} 时入院宣教才真的执行。它在
|
* {@code PublicityServiceImpl} 内部读取,与模块开关无关。</li>
|
* </ol>
|
* 本类的 {@code /status} 会把两者分别如实报出来 —— 这两个开关是排查
|
* "为什么没发宣教"时最容易看错的地方。
|
*
|
* <h3>与出入院模块拦截器的一致性</h3>
|
* 两个生成端点由 {@code PublicityAvailabilityInterceptor} 在模块关闭时拦下并抛
|
* 明确的业务异常(而不是静默返回 0),与 {@code inhospvisit} 的做法一致;
|
* {@code /status} <b>刻意不挂拦截器</b> —— 模块关掉时你更需要能查到状态。
|
*/
|
@Slf4j
|
@Api(description = "宣教")
|
@RestController
|
@RequestMapping("/smartor/publicity")
|
public class PublicityController {
|
|
/** 出院随访「通知类任务」提前几天发(与出入院模块读同一个键) */
|
private static final String VISIT_EARLY_DAY_KEY = "visit.early.day";
|
|
/** 入院宣教自身的业务开关:只有等于 "2" 才执行(与 PublicityServiceImpl 内一致) */
|
private static final String HELIBRARY_SWITCH_KEY = "in.helibrary.switch";
|
|
/** 入院宣教业务开关的"开"值 */
|
private static final String HELIBRARY_SWITCH_ON = "2";
|
|
/**
|
* 本模块常驻门面。
|
* <p>
|
* ⚠️ 注入门面而不是 {@code PublicityService}:门面永远存在,
|
* 即使模块被开关关掉或没被打进应用,本 Controller 也<b>不会启动失败</b>,
|
* 只是调用降级并打 WARN。
|
*/
|
@Autowired
|
private PublicityFacade publicityFacade;
|
|
/**
|
* 参数配置服务(ruoyi-common 的接口,实现在 ruoyi-system)。
|
* <p>
|
* ⚠️ 用 {@code ObjectProvider} 惰性取:本模块只依赖 ruoyi-common,
|
* 在独立部署/测试等"容器里没有实现"的场景下不能因此启动失败。
|
*/
|
@Autowired
|
private ObjectProvider<IConfigService> configServiceProvider;
|
|
/**
|
* 入院宣教:科室维度(type=4)+ 病区维度(type=5)。
|
* <p>
|
* 逐行对应原 {@code PatMedInhospServiceImpl#dealOutHospInfo} L331-409 的调用部分:
|
* 原实现在主流程里直接调 {@code dealInHospPublicity(config)},此处只是把它暴露成端点。
|
* <p>
|
* 返回处理记录数(与原实现的日志用值同源),便于手工触发时看到结果。
|
*/
|
@ApiOperation("入院宣教(科室 type=4 / 病区 type=5)")
|
@PostMapping("/dealInHospPublicity")
|
public int dealInHospPublicity() {
|
return publicityFacade.dealInHospPublicity(readVisitEarlyDay());
|
}
|
|
/**
|
* 药品宣教:按患者的 {@code medicalCode} 逐个药品编码匹配长期宣教任务(type=0)。
|
* <p>
|
* 仅 {@code spring.profiles.active == "sltd"} 时执行,其它站点由
|
* {@code PublicityServiceImpl} 直接返回 0(站点判断<b>不在</b>本 Controller,
|
* 与原实现位置一致)。
|
*/
|
@ApiOperation("药品宣教(仅 sltd 站点,type=0)")
|
@PostMapping("/dealMedicalPublicity")
|
public int dealMedicalPublicity() {
|
return publicityFacade.dealMedicalPublicity(readVisitEarlyDay());
|
}
|
|
/**
|
* 开关与子开关状态(<b>只读</b>,不触发任何业务)。
|
* <p>
|
* 专为排查"为什么没发宣教"而设:把两个层级的开关、以及"实现是否装配"
|
* 分别报出来,避免把"模块关了"误判成"没配长期宣教任务"。
|
*/
|
@ApiOperation("查询宣教模块开关状态")
|
@GetMapping("/status")
|
public Map<String, Object> status() {
|
Map<String, Object> m = new LinkedHashMap<>();
|
boolean moduleEnabled = publicityFacade.isEnabled();
|
m.put("moduleEnabled", moduleEnabled);
|
m.put("switchKey", "publicity.enabled");
|
m.put("switchValue", readConfig("publicity.enabled"));
|
String helibrary = readConfig(HELIBRARY_SWITCH_KEY);
|
m.put("inHospPublicitySwitchKey", HELIBRARY_SWITCH_KEY);
|
m.put("inHospPublicitySwitchValue", helibrary);
|
m.put("inHospPublicityOn", HELIBRARY_SWITCH_ON.equals(helibrary));
|
m.put("note", "moduleEnabled=false 时两个生成端点会被拦截;"
|
+ "inHospPublicityOn=false 时入院宣教整块不执行,与模块开关无关");
|
return m;
|
}
|
|
/**
|
* 读 {@code sys_config: visit.early.day}(与出入院模块 {@code InhospVisitController} 同一做法)。
|
* <p>
|
* 取不到(没有实现 / 读库异常)时返回 {@code null} 并打 WARN ——
|
* 原实现在这种情况下拿到的也是 {@code null},故语义一致。
|
*/
|
private String readVisitEarlyDay() {
|
return readConfig(VISIT_EARLY_DAY_KEY);
|
}
|
|
/**
|
* 读任意 {@code sys_config} 键;没有实现或读失败时返回 {@code null} 并打 WARN。
|
*/
|
private String readConfig(String key) {
|
if (configServiceProvider == null) {
|
return null;
|
}
|
try {
|
IConfigService svc = configServiceProvider.stream().findFirst().orElse(null);
|
if (svc == null) {
|
log.warn("【宣教】容器里没有 IConfigService 实现,{} 按未配置(null)处理。", key);
|
return null;
|
}
|
return svc.selectConfigByKey(key);
|
} catch (Exception e) {
|
log.warn("【宣教】读取 {} 失败,按未配置(null)处理:{}", key, e.toString());
|
return null;
|
}
|
}
|
}
|