package com.ruoyi.phone.api;
|
|
import com.ruoyi.phone.config.PhoneProperties;
|
import com.ruoyi.phone.config.PhoneSwitch;
|
import com.ruoyi.phone.domain.PhoneCallBackSHVO;
|
import com.ruoyi.phone.domain.PhoneCallBackVO;
|
import com.ruoyi.phone.domain.PhoneCallBackYQVO;
|
import com.ruoyi.phone.domain.PhoneCallRecordVO;
|
import com.ruoyi.phone.domain.PhoneCallReqYQVO;
|
import com.ruoyi.phone.domain.PhoneCallResultYQVO;
|
import com.ruoyi.phone.domain.PullTaskVO;
|
import com.ruoyi.phone.service.IPhoneService;
|
import com.ruoyi.task.domain.ServiceSubTaskDetailReq;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Component;
|
|
import java.util.Collections;
|
import java.util.List;
|
|
/**
|
* 电话模块对外门面(<b>常驻 Bean</b>)。
|
*
|
* <h3>为什么必须"常驻"</h3>
|
* 本门面<b>永远</b>是 Spring Bean,不随开关状态增删。这样调用方的
|
* {@code @Autowired PhoneApi} 在任何开关状态下都<b>不会启动失败</b>
|
* —— 与 {@code TaskFacade} / {@code SatisfactionFacade} 是同一条设计约束
|
* (历史教训:用 {@code @ConditionalOnProperty} 摘 Bean 会让调用方启动即失败,
|
* 且只在启动时求值一次,做不到"改完参数 10 秒生效")。
|
*
|
* <h3>关闭时的行为</h3>
|
* 开关({@code sys_config: phone.enabled})关闭时,本门面各方法<b>返回空值/空列表并打 WARN</b>,
|
* <b>不抛异常</b>:电话是随访的一条发送通道,模块被关掉时不能让调用方(随访编排、定时任务)
|
* 跟着炸。HTTP 侧另有 {@code PhoneAvailabilityInterceptor} 负责"明确报错",
|
* 两者分工:<b>端点 → 明确报错;模块内调用 → 静默降级</b>。
|
*
|
* <h3>使用示例</h3>
|
* <pre>
|
* @Autowired
|
* private PhoneApi phoneApi;
|
*
|
* if (phoneApi.isEnabled()) {
|
* List<PullTaskVO> tasks = phoneApi.taskPull();
|
* }
|
* </pre>
|
*/
|
@Slf4j
|
@Component
|
public class PhoneFacade implements PhoneApi {
|
|
/**
|
* 运行时开关({@code sys_config} 优先,{@link PhoneProperties} 兜底)
|
*/
|
private final PhoneSwitch phoneSwitch;
|
|
/**
|
* 模块内实现(唯一实现 {@code PhoneServiceImpl})。
|
* <p>
|
* 与满意度门面不同,这里不需要 {@code ObjectProvider} + 自引用过滤:
|
* 本类<b>不实现</b> {@code IPhoneService},不存在"Spring 把自引用当兜底候选"导致的无限递归;
|
* 实现 Bean 也不受任何条件装配控制,恒定存在。
|
* <p>
|
* 允许为 {@code null}:仅供测试/非 Spring 环境构造时使用(此时所有方法按"不可用"降级)。
|
*/
|
private final IPhoneService phoneService;
|
|
/**
|
* Spring 使用的构造器。
|
* <p>
|
* ⚠️ <b>显式标注 {@code @Autowired}</b>:本类有两个公开构造器(另一个给测试用),
|
* 标注后 Spring 不会退回查找无参构造器。该规则已由
|
* {@code sql/_probe/verify_spring_beans.py} 静态把关。
|
*/
|
@Autowired
|
public PhoneFacade(PhoneSwitch phoneSwitch, IPhoneService phoneService) {
|
this.phoneSwitch = phoneSwitch;
|
this.phoneService = phoneService;
|
}
|
|
/**
|
* 供<b>测试/非 Spring 环境</b>直接构造:{@code sysConfigValue} 就是 {@code sys_config}
|
* 里 {@code phone.enabled} 的值,{@code null} 表示"参数行不存在";{@code phoneService} 可为 {@code null}。
|
*/
|
public PhoneFacade(PhoneProperties properties, String sysConfigValue, IPhoneService phoneService) {
|
this(new PhoneSwitch(properties, sysConfigValue), phoneService);
|
}
|
|
/**
|
* 取运行时开关(供拦截器复用同一实例、也供测试断言)
|
*/
|
public PhoneSwitch getPhoneSwitch() {
|
return phoneSwitch;
|
}
|
|
@Override
|
public boolean isEnabled() {
|
return phoneSwitch.isEnabled();
|
}
|
|
@Override
|
public void phoneCallBack(PhoneCallBackVO phoneCallBackVO) {
|
if (!available("phoneCallBack")) {
|
return;
|
}
|
phoneService.phoneCallBack(phoneCallBackVO);
|
}
|
|
@Override
|
public PhoneCallBackYQVO phoneCallBackYQ(PhoneCallReqYQVO phoneCallReqYQVO) {
|
if (!available("phoneCallBackYQ")) {
|
return new PhoneCallBackYQVO();
|
}
|
return phoneService.phoneCallBackYQ(phoneCallReqYQVO);
|
}
|
|
@Override
|
public List<PullTaskVO> taskPull() {
|
if (!available("taskPull")) {
|
return Collections.emptyList();
|
}
|
return phoneService.taskPull();
|
}
|
|
@Override
|
public Integer saveQuestionAnswerPhone(ServiceSubTaskDetailReq serviceSubTaskDetailReq) {
|
if (!available("saveQuestionAnswerPhone")) {
|
return 0;
|
}
|
return phoneService.saveQuestionAnswerPhone(serviceSubTaskDetailReq);
|
}
|
|
@Override
|
public Integer recordAccept(PhoneCallRecordVO phoneCallRecordVO) {
|
if (!available("recordAccept")) {
|
return 0;
|
}
|
return phoneService.recordAccept(phoneCallRecordVO);
|
}
|
|
@Override
|
public PhoneCallBackSHVO getFirstQues(PhoneCallReqYQVO phoneCallReqYQVO) {
|
if (!available("getFirstQues")) {
|
return new PhoneCallBackSHVO();
|
}
|
return phoneService.getFirstQues(phoneCallReqYQVO);
|
}
|
|
@Override
|
public PhoneCallBackSHVO processResust(PhoneCallResultYQVO phoneCallResultYQVO) {
|
if (!available("processResust")) {
|
return new PhoneCallBackSHVO();
|
}
|
return phoneService.processResust(phoneCallResultYQVO);
|
}
|
|
/**
|
* 开关 + 实现可用性判断,不可用时打 WARN 并让调用方降级
|
*/
|
private boolean available(String method) {
|
if (!phoneSwitch.isEnabled()) {
|
log.warn("【电话】模块已关闭(参数配置 phone.enabled=false),{} 本次调用降级为空结果", method);
|
return false;
|
}
|
if (phoneService == null) {
|
log.warn("【电话】未找到电话模块实现(IPhoneService 缺失),{} 本次调用降级为空结果", method);
|
return false;
|
}
|
return true;
|
}
|
}
|