fix: 电话报警,回执超时问题修复。

This commit is contained in:
tianyongbao
2026-04-26 21:19:09 +08:00
parent e98460af07
commit 237a322b7b
3 changed files with 28 additions and 10 deletions

View File

@@ -50,8 +50,8 @@ public class VmsMnsConsumerServiceImpl implements VmsMnsConsumerService {
/** MNS 消息类型(固定值) */
private static final String VMS_MNS_MESSAGE_TYPE = "VoiceReport";
/** MNS 默认线程数 */
private static final int MNS_THREAD_COUNT = 2;
/** MNS 默认线程数改为1避免多线程共享 mnsClient 时互相踢连接导致无限崩溃 */
private static final int MNS_THREAD_COUNT = 1;
private static final int POLL_WAIT_SECONDS = 30;
/** Token 过期缓冲时间120秒小于此值则重新获取防止服务器时间差异 */

View File

@@ -43,13 +43,21 @@ public class VmsNoticeServiceImpl implements VmsNoticeService {
if (vmsClient == null) {
synchronized (this) {
if (vmsClient == null) {
// 必须使用 VMS 专用 AKvms.access-key-id而非 IoT 平台 AK
// MNS 回执消费也使用 VMS 专用 AK两端必须一致否则回执无法匹配
AliyunIotProperties.VmsMnsConfig vmsCfg = aliyunIotProperties.getVms();
String akId = (vmsCfg.getAccessKeyId() != null && !vmsCfg.getAccessKeyId().isEmpty())
? vmsCfg.getAccessKeyId() : aliyunIotProperties.getAccessKeyId();
String akSecret = (vmsCfg.getAccessKeySecret() != null && !vmsCfg.getAccessKeySecret().isEmpty())
? vmsCfg.getAccessKeySecret() : aliyunIotProperties.getAccessKeySecret();
// VMS 服务固定在 cn-hangzhou与 IoT 平台 region 无关
DefaultProfile profile = DefaultProfile.getProfile(
"cn-hangzhou",
aliyunIotProperties.getAccessKeyId(),
aliyunIotProperties.getAccessKeySecret()
akId,
akSecret
);
vmsClient = new DefaultAcsClient(profile);
log.info("VMS 客户端初始化成功");
log.info("VMS 客户端初始化成功, akId={}", akId);
}
}
}

View File

@@ -57,10 +57,20 @@ public class WarnCallNoticeServiceImpl implements WarnCallNoticeService {
/**
* 回执超时时间(分钟)
* 从呼叫实际发出callTime 已修正为真实发出时间)到收到 VMS 回执的最长等待时间。
* 正常链路:通话约 30s + VMS 生成回执约 1~2min3 分钟内完成。
* 设为 5 分钟,兼顾回执延迟容忍与批次重试及时性(批次间隔 3 分钟
* 正常链路:通话约 30s + VMS 生成回执约 1~2min通常 3 分钟内完成。
* 但阿里云 VMS 在网络波动或平台繁忙时,回执延迟可达 5~10 分钟,因此设为 15 分钟。
* 这意味着同批次最多等待 15 分钟才会解除阻塞让下一条记录发出,
* 整体上 15 分钟内不会对同一设备重复拨打,是可接受的保守策略。
*/
private static final int CALLBACK_TIMEOUT_MINUTES = 5;
private static final int CALLBACK_TIMEOUT_MINUTES = 15;
/**
* 批次阻断查询窗口(分钟)
* 用于定时任务发出待发记录前,向前扫描同设备是否已有处于这个窗口内的阻塞记录。
* 计算依据:回执超时(15min) + 批次最大时长(3次重试 x 2个手机号 x 3min = 18min) + 缓冲 = 40min
* 确保给定批次的所有记录都在窗口内,防止批次尾部记录因窗口过短而脱革导致重复发出。
*/
private static final int BATCH_BLOCK_WINDOW_MINUTES = 40;
@Override
@Transactional(rollbackFor = Exception.class)
@@ -260,11 +270,11 @@ public class WarnCallNoticeServiceImpl implements WarnCallNoticeService {
for (AquWarnCallNotice notice : pendingList) {
try {
// 发出前先检查同设备同批次窗口内的状态
// 批次窗口 = 当前记录的 callTime 往前 CALLBACK_TIMEOUT_MINUTES 分钟(足够覆盖整个批次)
// 批次窗口 = 当前记录的 callTime 往前 BATCH_BLOCK_WINDOW_MINUTES 分钟(足够覆盖整个批次)
// - status=1等待回执中前一条已发出但回执未到不重复拨打等其超时后再发
// - status=3已成功已有成功回执跳过该条记录
if (notice.getDeviceId() != null) {
LocalDateTime batchWindowStart = notice.getCallTime().minusMinutes(CALLBACK_TIMEOUT_MINUTES);
LocalDateTime batchWindowStart = notice.getCallTime().minusMinutes(BATCH_BLOCK_WINDOW_MINUTES);
long blockingCount = warnCallNoticeMapper.countWaitingOrSuccessByDeviceAfter(
notice.getDeviceId(), batchWindowStart);
if (blockingCount > 0) {