From b36a92c981c6650b04b14886c21d1f658fb3940f Mon Sep 17 00:00:00 2001 From: tianyongbao Date: Sun, 26 Apr 2026 21:19:09 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=94=B5=E8=AF=9D=E6=8A=A5=E8=AD=A6?= =?UTF-8?q?=EF=BC=8C=E5=9B=9E=E6=89=A7=E8=B6=85=E6=97=B6=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/VmsMnsConsumerServiceImpl.java | 4 ++-- .../service/impl/VmsNoticeServiceImpl.java | 14 ++++++++++--- .../impl/WarnCallNoticeServiceImpl.java | 20 ++++++++++++++----- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/intc-modules/intc-iot/src/main/java/com/intc/iot/service/impl/VmsMnsConsumerServiceImpl.java b/intc-modules/intc-iot/src/main/java/com/intc/iot/service/impl/VmsMnsConsumerServiceImpl.java index 5d3a65c..0d34d5c 100644 --- a/intc-modules/intc-iot/src/main/java/com/intc/iot/service/impl/VmsMnsConsumerServiceImpl.java +++ b/intc-modules/intc-iot/src/main/java/com/intc/iot/service/impl/VmsMnsConsumerServiceImpl.java @@ -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秒),小于此值则重新获取,防止服务器时间差异 */ diff --git a/intc-modules/intc-iot/src/main/java/com/intc/iot/service/impl/VmsNoticeServiceImpl.java b/intc-modules/intc-iot/src/main/java/com/intc/iot/service/impl/VmsNoticeServiceImpl.java index fe842ed..9a284ac 100644 --- a/intc-modules/intc-iot/src/main/java/com/intc/iot/service/impl/VmsNoticeServiceImpl.java +++ b/intc-modules/intc-iot/src/main/java/com/intc/iot/service/impl/VmsNoticeServiceImpl.java @@ -43,13 +43,21 @@ public class VmsNoticeServiceImpl implements VmsNoticeService { if (vmsClient == null) { synchronized (this) { if (vmsClient == null) { + // 必须使用 VMS 专用 AK(vms.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); } } } diff --git a/intc-modules/intc-iot/src/main/java/com/intc/iot/service/impl/WarnCallNoticeServiceImpl.java b/intc-modules/intc-iot/src/main/java/com/intc/iot/service/impl/WarnCallNoticeServiceImpl.java index a289c35..fd1cd7e 100644 --- a/intc-modules/intc-iot/src/main/java/com/intc/iot/service/impl/WarnCallNoticeServiceImpl.java +++ b/intc-modules/intc-iot/src/main/java/com/intc/iot/service/impl/WarnCallNoticeServiceImpl.java @@ -57,10 +57,20 @@ public class WarnCallNoticeServiceImpl implements WarnCallNoticeService { /** * 回执超时时间(分钟) * 从呼叫实际发出(callTime 已修正为真实发出时间)到收到 VMS 回执的最长等待时间。 - * 正常链路:通话约 30s + VMS 生成回执约 1~2min,3 分钟内完成。 - * 设为 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) {