package com.ruoyi.template.api; import com.ruoyi.common.core.service.TemplateModuleDisabledException; import com.ruoyi.template.config.TemplateProperties; import com.ruoyi.template.config.TemplateSwitch; import com.ruoyi.template.domain.HeLibrary; import com.ruoyi.template.domain.HeLibraryVO; import com.ruoyi.template.domain.IvrLibaExtemplatescript; import com.ruoyi.template.domain.IvrLibaTemplateVO; import com.ruoyi.template.mapper.IvrLibaExtemplatescriptMapper; import com.ruoyi.template.service.IHeLibraryService; import com.ruoyi.template.service.IIvrLibaTemplateService; 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.Collections; import java.util.List; /** * 模板模块对外门面(常驻 Bean)—— 其它模块访问模板数据的唯一入口。 * *
* ⚠️ 用 {@code ObjectProvider} 惰性取:本类有两个公开构造器,
* 且容器里若没有该服务也不该让门面装配失败。
*/
private final ObjectProvider
* ⚠️ 以 Mapper 形态注入:该项查询没有对应的 Service 方法,
* 原调用方就是直调 Mapper。门面只做纯转发(见 {@link #findIvrLibaExtemplatescripts})。
*/
private final ObjectProvider
* ⚠️ 必须显式标注 {@code @Autowired}:本类有两个公开构造器
* (另一个是给测试/非 Spring 环境用的),若不标注,Spring 会因为"构造器不唯一"
* 而退回查找无参构造器,从而在启动时报 {@code No default constructor found}。
* —— 这是满意度模块实机踩到的缺陷,此处刻意保留同样的写法与注释。
*/
@Autowired
public TemplateFacade(TemplateProperties properties,
TemplateSwitch templateSwitch,
ObjectProvider
* 不传任何服务:测试构造下调数据方法会返回安全值(null / 空列表)并打 WARN,
* 保证开关校验类测试不必拉起数据库。
*/
public TemplateFacade(TemplateProperties properties, String sysConfigValue) {
this(properties, new TemplateSwitch(properties, sysConfigValue), null, null, null);
}
/**
* 取运行时开关(供测试断言)
*/
public TemplateSwitch getTemplateSwitch() {
return templateSwitch;
}
/**
* 模块被关闭时的统一提示。
*
* ⚠️ 与 {@link com.ruoyi.template.config.TemplateAvailabilityInterceptor#DISABLED_MESSAGE}
* 必须逐字一致:同一个开关、同一个原因,不该因为走 HTTP 还是走门面而给出不同文案。
* 测试 {@code TemplateSwitchCheck} 会断言两者相等。
*/
public static final String DISABLED_MESSAGE =
"模板功能未启用(参数配置 template.enabled=false),该接口不可用";
/**
* 模板模块是否启用({@code sys_config: template.enabled},运行时热读)
*
* ⚠️ 这是**查询开关状态**的方法,刻意不受守卫:调用方需要它来预判
* "现在该不该调门面",如果它也抛异常就自我矛盾了。
*/
public boolean isEnabled() {
return templateSwitch.isEnabled();
}
/**
* 门面数据方法的统一入口守卫:模块关闭时抛
* {@link TemplateModuleDisabledException},由调用方决定如何降级。
*/
private void requireEnabled() {
if (!templateSwitch.isEnabled()) {
log.warn("【模板】模块已关闭(参数配置 template.enabled=false),拒绝门面访问");
throw new TemplateModuleDisabledException(DISABLED_MESSAGE);
}
}
// ==========================================================================
// 宣教资料库(he_library)
// ==========================================================================
/**
* 按主键查宣教资料库。
*
* 这是外部最需要的一个方法:原实现在 3 处直连
* {@code heLibraryMapper.selectHeLibraryById(serviceSubtask.getLibtemplateid())}
* ({@code RyTask}、{@code RedisMqReceiver}、{@code ServiceTaskServiceImpl}),
* 且只读 {@code getHetype()} / {@code getPreachcontent()} 两个字段。
*
* @param id 宣教资料库主键(原 {@code serviceSubtask.libtemplateid})
* @return 宣教资料库;不存在或服务不可用时返回 {@code null}
* @throws TemplateModuleDisabledException 模块被关闭({@code template.enabled=false})
*/
public HeLibrary findHeLibraryById(Long id) {
requireEnabled();
IHeLibraryService svc = heLibraryService();
if (svc == null) {
log.warn("【模板】IHeLibraryService 不可用(非 Spring 构造或装配缺失),findHeLibraryById({}) 返回 null。", id);
return null;
}
return svc.selectHeLibraryById(id);
}
/**
* 查宣教资料库列表(按条件)。
*
* @param query 查询条件(传 null 表示不限制)
* @return 列表;服务不可用时返回空列表(不是 null)
* @throws TemplateModuleDisabledException 模块被关闭({@code template.enabled=false})
*/
public List
* 调用方:{@code ChatWebSocketHandler}(聊天里按条件取语音话术)。
*
* @param query 查询条件
* @return 模板详情;查不到或服务不可用时返回 {@code null}
* @throws TemplateModuleDisabledException 模块被关闭({@code template.enabled=false})
*/
public IvrLibaTemplateVO findIvrLibaTemplateInfoByCondition(IvrLibaTemplateVO query) {
requireEnabled();
IIvrLibaTemplateService svc = ivrLibaTemplateService();
if (svc == null) {
log.warn("【模板】IIvrLibaTemplateService 不可用,findIvrLibaTemplateInfoByCondition 返回 null。");
return null;
}
return svc.selectInfoByCondition(query);
}
/**
* 按 ID 列表查语音库外部模板脚本(原 {@code IvrLibaExtemplatescriptMapper#queryIvrLibaExtemplatescriptList})。
*
* 调用方:{@code ChatWebSocketHandler}。
*
* ⚠️ 这是**唯一**以 Mapper 形态暴露的方法 —— 因为原调用方就是直调 Mapper,
* 而该查询没有对应的 Service 方法。纯转发,SQL 一行未改。
*
* @param ids 外部模板ID列表(原调用方把逗号串拆成 {@code List