package com.ruoyi.phone.config;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.phone.api.PhoneApi;
import com.ruoyi.phone.api.PhoneFacade;
import com.ruoyi.phone.domain.PhoneCallBackSHVO;
import com.ruoyi.phone.domain.PhoneCallBackYQVO;
import com.ruoyi.phone.domain.PullTaskVO;
import java.util.List;
/**
* 电话模块开关自检 —— 纯 {@code main},不依赖 JUnit / Spring / 数据库。
*
*
为什么不用 JUnit
* 与任务模块的 {@code TaskSwitchCheck}、满意度模块的 {@code SatisfactionSwitchCheck} 同款:
* 本仓离线仓库缺 {@code surefire-junit3:2.12.4},走
* {@code mvn -o -q -pl ruoyi-phone -am -DskipTests test-compile} + {@code java -cp} 这条路。
*
* 断言覆盖
*
* - sys_config 优先于 yml;
* - yml 兜底;参数行缺失 → 回落 yml;
* - 非法值 → 按"不是 false 即启用";
* - fail-open:读不到配置服务时按启用,且不抛异常;
* - 10 秒缓存与 invalidateCache;
* - 拦截器:开启放行、关闭抛 {@link ServiceException} 且 message 含
* {@code phone.enabled=false} 与 {@code 接口不可用}(并验证 request 为 null 不炸);
* - 守卫路径恰好 8 条(6 条在原 {@code /smartor/serviceSubtask/**} 下 +
* 2 条三汇 {@code /smartor/shphone/**}),且"归电话的 6 条"与
* {@code TaskWebConfig#excludedPhonePaths()} 一一对应;
* - 门面:{@link PhoneFacade} 是 {@link PhoneApi};关闭时各方法降级为空值/空列表且不抛异常。
*
*/
public final class PhoneSwitchCheck {
private static int failed = 0;
private static int passed = 0;
private static void check(String key, Object expected, Object actual) {
boolean ok = expected == null ? actual == null : expected.equals(actual);
if (ok) {
passed++;
System.out.println(" [OK] " + key + " = " + actual);
} else {
failed++;
System.out.println(" [FAIL] " + key + " 期望=" + expected + " 实际=" + actual);
}
}
private static PhoneProperties props(boolean enabled) {
PhoneProperties p = new PhoneProperties();
p.setEnabled(enabled);
return p;
}
public static void main(String[] args) {
System.out.println("========== 电话模块开关自检 ==========");
System.out.println("-- 1) sys_config 优先于 yml --");
check("sys_config=true, yml=false -> enabled", true,
new PhoneSwitch(props(false), "true").isEnabled());
check("sys_config=false, yml=true -> enabled", false,
new PhoneSwitch(props(true), "false").isEnabled());
System.out.println("-- 2) yml 兜底 --");
check("sys_config 行缺失, yml=true -> enabled", true,
new PhoneSwitch(props(true), (String) null).isEnabled());
check("sys_config 行缺失, yml=false -> enabled", false,
new PhoneSwitch(props(false), (String) null).isEnabled());
System.out.println("-- 3) 非法值按「不是 false 即启用」--");
check("sys_config=yes -> enabled", true, new PhoneSwitch(props(false), "yes").isEnabled());
check("sys_config=FALSE(大小写) -> enabled", false,
new PhoneSwitch(props(true), "FALSE").isEnabled());
System.out.println("-- 4) fail-open:取不到配置服务时不抛异常且按 yml --");
boolean threw = false;
boolean value = false;
try {
value = new PhoneSwitch(props(true), (String) null).isEnabled();
} catch (Exception e) {
threw = true;
}
check("无配置服务时不抛异常", false, threw);
check("无配置服务时按 yml=true", true, value);
System.out.println("-- 5) 10 秒缓存与 invalidateCache --");
PhoneSwitch sw = new PhoneSwitch(props(false), "false");
check("初始 enabled", false, sw.isEnabled());
sw.invalidateCache();
check("invalidateCache 后仍读同一配置值", false, sw.isEnabled());
System.out.println("-- 6) 拦截器 --");
PhoneAvailabilityInterceptor on =
new PhoneAvailabilityInterceptor(new PhoneSwitch(props(true), "true"));
boolean ok = false;
try {
ok = on.preHandle(null, null, null);
} catch (Exception ignored) {
}
check("启用时放行", true, ok);
PhoneAvailabilityInterceptor off =
new PhoneAvailabilityInterceptor(new PhoneSwitch(props(true), "false"));
ServiceException caught = null;
try {
// request 传 null:验证拦截器对 null request 安全(测试会这么调)
off.preHandle(null, null, null);
} catch (ServiceException e) {
caught = e;
}
check("关闭时抛 ServiceException", true, caught != null);
String msg = caught == null ? "" : caught.getMessage();
check("message 含 phone.enabled=false", true, msg.contains("phone.enabled=false"));
check("message 含 接口不可用", true, msg.contains("接口不可用"));
System.out.println("-- 7) 守卫路径 --");
String[] guarded = PhoneWebConfig.guardedPathPatterns();
check("守卫路径条数", 8, guarded.length);
check("含 phoneCallBack", true, contains(guarded, "/smartor/serviceSubtask/phoneCallBack"));
check("含 phoneCallBackYQ", true, contains(guarded, "/smartor/serviceSubtask/phoneCallBackYQ"));
check("含 taskPull", true, contains(guarded, "/smartor/serviceSubtask/taskPull"));
check("含 saveQuestionAnswerPhone", true, contains(guarded, "/smartor/serviceSubtask/saveQuestionAnswerPhone"));
check("含 recordAcceptshanghai", true, contains(guarded, "/smartor/serviceSubtask/recordAcceptshanghai"));
check("含 recordAccept", true, contains(guarded, "/smartor/serviceSubtask/recordAccept"));
check("含 getFirstQues", true, contains(guarded, "/smartor/shphone/getFirstQues"));
check("含 processResust", true, contains(guarded, "/smartor/shphone/processResust"));
// 关键:不得误伤任务模块自己的随访端点与外呼实例配置端点
check("不含 patItem", false, contains(guarded, "/smartor/serviceSubtask/patItem"));
check("不含 addOutboundInstance", false, contains(guarded, "/smartor/shphone/addOutboundInstance"));
System.out.println("-- 7.1) 归电话的 6 条(任务模块要排除的那 6 条)--");
String[] owned = PhoneWebConfig.subtaskPathsOwnedByPhone();
check("条数", 6, owned.length);
for (String p : owned) {
check("在守卫清单里: " + p, true, contains(guarded, p));
}
System.out.println("-- 8) 门面降级 --");
PhoneApi facadeOff = new PhoneFacade(props(true), "false", null);
check("关闭时 isEnabled", false, facadeOff.isEnabled());
List disabledTasks = facadeOff.taskPull();
check("关闭时 taskPull 返回空列表", true, disabledTasks != null && disabledTasks.isEmpty());
check("关闭时 saveQuestionAnswerPhone 返回 0", 0, facadeOff.saveQuestionAnswerPhone(null));
PhoneCallBackYQVO disabledYq = facadeOff.phoneCallBackYQ(null);
check("关闭时 phoneCallBackYQ 返回非 null 空对象", true, disabledYq != null);
PhoneCallBackSHVO disabledSh = facadeOff.getFirstQues(null);
check("关闭时 getFirstQues 返回非 null 空对象", true, disabledSh != null);
boolean facadeThrew = false;
try {
facadeOff.phoneCallBack(null);
facadeOff.recordAccept(null);
facadeOff.processResust(null);
} catch (Exception e) {
facadeThrew = true;
}
check("关闭时门面调用不抛异常", false, facadeThrew);
PhoneApi facadeOn = new PhoneFacade(props(true), "true", null);
check("开启时 isEnabled", true, facadeOn.isEnabled());
// 实现缺失(测试传 null)也按"不可用"降级,不抛异常
boolean onThrew = false;
try {
facadeOn.phoneCallBack(null);
} catch (Exception e) {
onThrew = true;
}
check("无实现时不抛异常", false, onThrew);
System.out.println("==================================================");
System.out.println("通过 " + passed + " 项,失败 " + failed + " 项");
if (failed > 0) {
System.exit(1);
}
}
private static boolean contains(String[] array, String value) {
if (array == null) {
return false;
}
for (String s : array) {
if (s.equals(value)) {
return true;
}
}
return false;
}
private PhoneSwitchCheck() {
}
}