feat(ops,iot): 工单语音播报循环机制 + 统一按键逻辑
核心改动: - 新增循环播报机制:DISPATCHED 状态持续播报"工单来啦"直到按键确认 - 统一按键逻辑:confirmKeyId 和 queryKeyId 都路由到同一处理逻辑, 根据工单状态智能判断行为(确认/查询/无工单提示) - ARRIVED/COMPLETED 状态静默不播报,CANCELLED 保留取消播报 - 修复 P0:确认去重后按键不再静默,改为发查询事件给反馈 - 修复 P0:PAUSED 状态(P0打断)时停止被打断工单的循环播报 - 修复 P1:handleCompleted 补全 deviceId 兜底逻辑 - 修复 P1:stopLoop 只移除循环消息,保留非循环消息 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -74,74 +74,62 @@ public class ButtonEventRuleProcessor {
|
||||
|
||||
log.debug("[ButtonEvent] 按键解析成功:deviceId={}, buttonId={}", deviceId, buttonId);
|
||||
|
||||
// 4. 匹配按键类型并处理
|
||||
if (buttonId.equals(buttonConfig.getConfirmKeyId())) {
|
||||
// 确认键
|
||||
handleConfirmButton(deviceId, buttonId);
|
||||
} else if (buttonId.equals(buttonConfig.getQueryKeyId())) {
|
||||
// 查询键
|
||||
handleQueryButton(deviceId, buttonId);
|
||||
// 4. 匹配按键类型并处理(确认键和查询键统一路由到同一逻辑)
|
||||
if (buttonId.equals(buttonConfig.getConfirmKeyId())
|
||||
|| buttonId.equals(buttonConfig.getQueryKeyId())) {
|
||||
// 所有已知按键统一走绿色按键逻辑(根据工单状态智能判断行为)
|
||||
handleGreenButton(deviceId, buttonId);
|
||||
} else {
|
||||
log.debug("[ButtonEvent] 未配置的按键:deviceId={}, buttonId={}", deviceId, buttonId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理确认按键
|
||||
* 处理绿色按键(统一按键逻辑)
|
||||
* <p>
|
||||
* 保洁员按下确认键,确认接收工单
|
||||
* 根据当前工单状态智能判断行为:
|
||||
* - 无工单:发布查询事件(Ops 端播报"没有工单")
|
||||
* - DISPATCHED:发布确认事件(触发确认状态转换 + 停止循环 + 播报地点)
|
||||
* - CONFIRMED/ARRIVED:发布查询事件(播报地点)
|
||||
* - 其他状态:发布查询事件(兜底处理)
|
||||
*/
|
||||
private void handleConfirmButton(Long deviceId, Integer buttonId) {
|
||||
|
||||
log.info("[ButtonEvent] 确认键按下:deviceId={}, buttonId={}", deviceId, buttonId);
|
||||
|
||||
// 1. 查询设备当前工单
|
||||
BadgeDeviceStatusRedisDAO.OrderInfo currentOrder = badgeDeviceStatusRedisDAO.getCurrentOrder(deviceId);
|
||||
if (currentOrder == null) {
|
||||
log.warn("[ButtonEvent] 设备无当前工单,跳过确认:deviceId={}", deviceId);
|
||||
return;
|
||||
}
|
||||
|
||||
Long orderId = currentOrder.getOrderId();
|
||||
|
||||
// 2. 防重复检查(短时间内同一工单的确认操作去重)
|
||||
String dedupKey = String.format("iot:clean:button:dedup:confirm:%s:%s", deviceId, orderId);
|
||||
Boolean firstTime = stringRedisTemplate.opsForValue()
|
||||
.setIfAbsent(dedupKey, "1", 10, java.util.concurrent.TimeUnit.SECONDS);
|
||||
|
||||
if (!firstTime) {
|
||||
log.info("[ButtonEvent] 确认操作重复,跳过:deviceId={}, orderId={}", deviceId, orderId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 发布工单确认事件
|
||||
publishConfirmEvent(deviceId, orderId, buttonId);
|
||||
|
||||
log.info("[ButtonEvent] 发布工单确认事件:deviceId={}, orderId={}", deviceId, orderId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理查询按键
|
||||
* <p>
|
||||
* 保洁员按下查询键,查询当前工单信息
|
||||
*/
|
||||
private void handleQueryButton(Long deviceId, Integer buttonId) {
|
||||
|
||||
log.info("[ButtonEvent] 查询键按下:deviceId={}, buttonId={}", deviceId, buttonId);
|
||||
private void handleGreenButton(Long deviceId, Integer buttonId) {
|
||||
log.info("[ButtonEvent] 绿色按键按下:deviceId={}, buttonId={}", deviceId, buttonId);
|
||||
|
||||
// 1. 查询设备当前工单
|
||||
BadgeDeviceStatusRedisDAO.OrderInfo currentOrder = badgeDeviceStatusRedisDAO.getCurrentOrder(deviceId);
|
||||
if (currentOrder == null) {
|
||||
// 无工单 → 发布查询事件(Ops 端播报"没有工单")
|
||||
log.info("[ButtonEvent] 设备无当前工单:deviceId={}", deviceId);
|
||||
// 发布查询结果事件(无工单)
|
||||
publishQueryEvent(deviceId, null, buttonId, "当前无工单");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 发布查询事件
|
||||
publishQueryEvent(deviceId, currentOrder.getOrderId(), buttonId, "查询当前工单");
|
||||
Long orderId = currentOrder.getOrderId();
|
||||
String orderStatus = currentOrder.getStatus();
|
||||
|
||||
log.info("[ButtonEvent] 发布工单查询事件:deviceId={}, orderId={}", deviceId, currentOrder.getOrderId());
|
||||
// 2. 根据工单状态智能分派
|
||||
if ("DISPATCHED".equals(orderStatus)) {
|
||||
// DISPATCHED → 发布确认事件(触发确认 + 停止循环 + 播报地点)
|
||||
// 防重复检查
|
||||
String dedupKey = String.format("iot:clean:button:dedup:confirm:%s:%s", deviceId, orderId);
|
||||
Boolean firstTime = stringRedisTemplate.opsForValue()
|
||||
.setIfAbsent(dedupKey, "1", 10, java.util.concurrent.TimeUnit.SECONDS);
|
||||
|
||||
if (!Boolean.TRUE.equals(firstTime)) {
|
||||
// 重复确认不再静默,改为发查询事件给保洁员反馈(播报地点)
|
||||
log.info("[ButtonEvent] 确认操作重复,转为查询:deviceId={}, orderId={}", deviceId, orderId);
|
||||
publishQueryEvent(deviceId, orderId, buttonId, "重复确认,查询当前工单");
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfirmEvent(deviceId, orderId, buttonId);
|
||||
log.info("[ButtonEvent] DISPATCHED状态,发布确认事件:deviceId={}, orderId={}", deviceId, orderId);
|
||||
} else {
|
||||
// CONFIRMED / ARRIVED / 其他状态 → 发布查询事件(播报地点)
|
||||
publishQueryEvent(deviceId, orderId, buttonId, "查询当前工单");
|
||||
log.info("[ButtonEvent] {}状态,发布查询事件:deviceId={}, orderId={}", orderStatus, deviceId, orderId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -74,6 +74,12 @@ public class CleanNotificationConstants {
|
||||
|
||||
// ==================== 新工单播报 ====================
|
||||
|
||||
/**
|
||||
* 新工单循环播报(铃声式,类似来电提醒)
|
||||
* 用于 DISPATCHED 状态的持续播报,直到保洁员按键确认
|
||||
*/
|
||||
public static final String NEW_ORDER_RING = "工单来啦";
|
||||
|
||||
/**
|
||||
* 新工单播报(简短版)
|
||||
*/
|
||||
@@ -89,7 +95,7 @@ public class CleanNotificationConstants {
|
||||
* 新工单播报(完整版)
|
||||
* 参数: {areaName} - 区域名称, {orderTitle} - 工单标题(截断)
|
||||
*/
|
||||
public static final String NEW_ORDER_FULL = "新工单来啦,请按1键进行确认";
|
||||
public static final String NEW_ORDER_FULL = "新工单来啦,请按键进行确认";
|
||||
|
||||
// ==================== 工单确认播报 ====================
|
||||
|
||||
@@ -188,7 +194,7 @@ public class CleanNotificationConstants {
|
||||
/**
|
||||
* 按键查询播报(无工单时)
|
||||
*/
|
||||
public static final String QUERY_NO_ORDER = "暂无待办工单";
|
||||
public static final String QUERY_NO_ORDER = "没有工单";
|
||||
|
||||
/**
|
||||
* 按键查询播报(待办数量提示)
|
||||
|
||||
@@ -273,10 +273,16 @@ public class CleanOrderEventListener {
|
||||
recordOrderConfirmedLog(orderId, deviceId, event);
|
||||
|
||||
if (deviceId != null) {
|
||||
// 发送确认成功语音播报(使用统一模板)
|
||||
// 1. 停止循环播报
|
||||
voiceBroadcastService.stopLoop(deviceId);
|
||||
|
||||
// 2. 播报地点信息
|
||||
OpsOrderDO order = opsOrderMapper.selectById(orderId);
|
||||
String confirmMessage = CleanNotificationConstants.VoiceBuilder.buildOrderConfirmed(order);
|
||||
playVoice(deviceId, confirmMessage, orderId);
|
||||
String areaName = CleanNotificationConstants.VoiceBuilder.getAreaName(
|
||||
order != null ? order.getLocation() : null);
|
||||
String locationMessage = String.format(
|
||||
CleanNotificationConstants.VoiceTemplate.QUERY_HAS_ORDER, areaName);
|
||||
playVoice(deviceId, locationMessage, orderId);
|
||||
}
|
||||
|
||||
log.info("[CleanOrderEventListener] 工单已确认: orderId={}, deviceId={}", orderId, deviceId);
|
||||
@@ -304,11 +310,9 @@ public class CleanOrderEventListener {
|
||||
// 2. 计算并更新响应时长(下发→到岗,排除暂停时间)
|
||||
updateResponseSeconds(orderId);
|
||||
|
||||
// 3. 语音播报提醒开始作业
|
||||
// 3. 停止循环播报(处理信标自动到岗 DISPATCHED→ARRIVED 场景)+ 静默(不播报)
|
||||
if (deviceId != null) {
|
||||
OpsOrderDO order = opsOrderMapper.selectById(orderId);
|
||||
String arrivedMessage = CleanNotificationConstants.VoiceBuilder.buildOrderArrived(order);
|
||||
playVoice(deviceId, arrivedMessage, orderId);
|
||||
voiceBroadcastService.stopLoop(deviceId);
|
||||
}
|
||||
|
||||
// 4. 记录到岗业务日志
|
||||
@@ -328,11 +332,26 @@ public class CleanOrderEventListener {
|
||||
public void handlePaused(OrderStateChangedEvent event) {
|
||||
Long orderId = event.getOrderId();
|
||||
|
||||
// 记录暂停开始时间到扩展表
|
||||
// 1. 停止循环播报(P0打断时,被打断的工单可能正在循环播报"工单来啦")
|
||||
Long deviceId = event.getPayloadLong("assigneeId");
|
||||
if (deviceId == null) {
|
||||
deviceId = event.getOperatorId();
|
||||
}
|
||||
if (deviceId == null) {
|
||||
OpsOrderDO order = opsOrderMapper.selectById(orderId);
|
||||
if (order != null) {
|
||||
deviceId = order.getAssigneeId();
|
||||
}
|
||||
}
|
||||
if (deviceId != null) {
|
||||
voiceBroadcastService.stopLoop(deviceId);
|
||||
}
|
||||
|
||||
// 2. 记录暂停开始时间到扩展表
|
||||
recordPauseStartTime(orderId);
|
||||
|
||||
// 设备状态由 BadgeDeviceStatusEventListener 统一处理
|
||||
log.info("[CleanOrderEventListener] 暂停时间已记录: orderId={}", orderId);
|
||||
log.info("[CleanOrderEventListener] 暂停处理完成: orderId={}, deviceId={}", orderId, deviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -342,10 +361,21 @@ public class CleanOrderEventListener {
|
||||
public void handleCompleted(OrderStateChangedEvent event) {
|
||||
Long orderId = event.getOrderId();
|
||||
|
||||
// 获取 deviceId(优先从 payload 获取,其次从工单获取)
|
||||
// 获取 deviceId(优先从 payload 获取,其次从工单 assigneeId 兜底)
|
||||
Long deviceId = event.getPayloadLong("deviceId");
|
||||
if (deviceId == null) {
|
||||
OpsOrderDO order = opsOrderMapper.selectById(orderId);
|
||||
if (order != null) {
|
||||
deviceId = order.getAssigneeId();
|
||||
}
|
||||
}
|
||||
|
||||
// 记录完成时间并计算完成时长
|
||||
// 兜底:停止循环播报(安全调用)
|
||||
if (deviceId != null) {
|
||||
voiceBroadcastService.stopLoop(deviceId);
|
||||
}
|
||||
|
||||
// 记录完成时间并计算完成时长(静默,不播报)
|
||||
LocalDateTime completedTime = LocalDateTime.now();
|
||||
OpsOrderCleanExtDO ext = cleanExtMapper.selectByOpsOrderId(orderId);
|
||||
|
||||
@@ -404,8 +434,9 @@ public class CleanOrderEventListener {
|
||||
updateExt.setCompletedTime(LocalDateTime.now());
|
||||
cleanExtMapper.insertOnDuplicateKeyUpdate(updateExt);
|
||||
|
||||
// 2. 语音播报通知保洁员工单已取消
|
||||
// 2. 停止循环播报 + 语音播报通知保洁员工单已取消(取消是异常情况,需要通知)
|
||||
if (deviceId != null) {
|
||||
voiceBroadcastService.stopLoop(deviceId);
|
||||
playVoice(deviceId, CleanNotificationConstants.VoiceTemplate.ORDER_CANCELLED, orderId);
|
||||
}
|
||||
|
||||
@@ -446,25 +477,23 @@ public class CleanOrderEventListener {
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步执行工单完成后的通知和派单
|
||||
* 异步执行工单完成后的派单
|
||||
* <p>
|
||||
* 先发送完成通知,再立即触发下一个任务派发。
|
||||
* 语音播报顺序和间隔由 TTS 队列({@link com.viewsh.module.ops.environment.service.voice.TtsQueueConsumer})
|
||||
* 按设备维度控制,同一设备前后播报间隔由 ops.tts.queue.interval-ms 配置。
|
||||
* 完成状态静默(不播报),直接触发下一个任务派发。
|
||||
* 如果下一个任务派发成功,会触发 handleDispatched 启动新的循环播报。
|
||||
*/
|
||||
@Async("ops-task-executor")
|
||||
public void asyncCompleteAndDispatchNext(Long orderId, Long deviceId) {
|
||||
// 1. 发送完成通知(入TTS队列)
|
||||
sendOrderCompletedNotification(orderId, deviceId);
|
||||
// 静默:不发送完成通知
|
||||
|
||||
// 2. 自动推送下一个任务(新任务通知也入TTS队列,由队列控制播报间隔)
|
||||
// 自动推送下一个任务(新任务通知会触发 handleDispatched 启动循环播报)
|
||||
cleanOrderService.autoDispatchNextOrder(orderId, deviceId);
|
||||
}
|
||||
|
||||
// ==================== 通知方法 ====================
|
||||
|
||||
/**
|
||||
* 发送新工单通知(语音播报 + 站内信)
|
||||
* 发送新工单通知(循环语音播报 + 站内信)
|
||||
*/
|
||||
@Async("ops-task-executor")
|
||||
public void sendNewOrderNotification(Long deviceId, Long orderId) {
|
||||
@@ -477,9 +506,9 @@ public class CleanOrderEventListener {
|
||||
|
||||
log.info("[新工单通知] deviceId={}, orderId={}", deviceId, orderId);
|
||||
|
||||
// 1. 语音播报(使用统一模板构建器)
|
||||
String voiceMessage = CleanNotificationConstants.VoiceBuilder.buildNewOrder(order, true);
|
||||
playVoice(deviceId, voiceMessage, orderId);
|
||||
// 1. 启动循环语音播报"工单来了"
|
||||
voiceBroadcastService.broadcastLoop(deviceId,
|
||||
CleanNotificationConstants.VoiceTemplate.NEW_ORDER_RING, orderId);
|
||||
|
||||
// 2. 发送站内信(暂时发送到管理员)
|
||||
sendNotifyMessage(1L,
|
||||
|
||||
@@ -46,6 +46,8 @@ public class TtsQueueConsumer {
|
||||
|
||||
private static final String LOCK_KEY_PREFIX = "ops:tts:lock:";
|
||||
|
||||
private static final String LOOP_KEY_PREFIX = "ops:tts:loop:";
|
||||
|
||||
@Value("${ops.tts.queue.enabled:true}")
|
||||
private boolean queueEnabled;
|
||||
|
||||
@@ -200,6 +202,21 @@ public class TtsQueueConsumer {
|
||||
deviceBroadcastLock.put(deviceId, System.currentTimeMillis());
|
||||
log.info("[TTS队列] 播报成功: deviceId={}, text={}",
|
||||
deviceId, message.getText());
|
||||
|
||||
// 循环消息:检查 Redis 循环标记,若存在则重新入队
|
||||
if (message.isLoopable() && isLoopActive(deviceId)) {
|
||||
TtsQueueMessage loopMsg = TtsQueueMessage.loopMessage(
|
||||
deviceId, message.getText(), message.getOrderId());
|
||||
try {
|
||||
String loopJson = objectMapper.writeValueAsString(loopMsg);
|
||||
redisTemplate.opsForList().rightPush(queueKey, loopJson);
|
||||
redisTemplate.expire(queueKey, 1, TimeUnit.HOURS);
|
||||
log.debug("[TTS队列] 循环消息重新入队: deviceId={}", deviceId);
|
||||
} catch (Exception ex) {
|
||||
log.warn("[TTS队列] 循环消息重新入队失败: deviceId={}", deviceId, ex);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// 播报失败,释放锁(允许立即重试)
|
||||
@@ -335,6 +352,99 @@ public class TtsQueueConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 循环播报控制 ====================
|
||||
|
||||
/**
|
||||
* 启动循环播报标记
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @param orderId 工单ID
|
||||
*/
|
||||
public void startLoop(Long deviceId, Long orderId) {
|
||||
String loopKey = LOOP_KEY_PREFIX + deviceId;
|
||||
redisTemplate.opsForValue().set(loopKey, String.valueOf(orderId), 1, TimeUnit.HOURS);
|
||||
log.info("[TTS队列] 启动循环播报: deviceId={}, orderId={}", deviceId, orderId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止循环播报
|
||||
* <p>
|
||||
* 删除循环标记,并从队列中移除循环消息(保留非循环消息)
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
*/
|
||||
public void stopLoop(Long deviceId) {
|
||||
String loopKey = LOOP_KEY_PREFIX + deviceId;
|
||||
Boolean deleted = redisTemplate.delete(loopKey);
|
||||
// 从队列中移除循环消息,保留非循环消息(如取消播报、待办播报等)
|
||||
int removed = removeLoopMessages(deviceId);
|
||||
log.info("[TTS队列] 停止循环播报: deviceId={}, loopKeyDeleted={}, removedMessages={}", deviceId, deleted, removed);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从队列中移除所有循环消息,保留非循环消息
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @return 移除的消息数量
|
||||
*/
|
||||
private int removeLoopMessages(Long deviceId) {
|
||||
String queueKey = getQueueKey(deviceId);
|
||||
int removedCount = 0;
|
||||
|
||||
try {
|
||||
Long size = redisTemplate.opsForList().size(queueKey);
|
||||
if (size == null || size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 取出所有消息,过滤掉循环消息后重新入队
|
||||
java.util.List<Object> allMessages = new java.util.ArrayList<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
Object msg = redisTemplate.opsForList().leftPop(queueKey);
|
||||
if (msg == null) {
|
||||
break;
|
||||
}
|
||||
allMessages.add(msg);
|
||||
}
|
||||
|
||||
// 过滤:保留非循环消息
|
||||
for (Object msgObj : allMessages) {
|
||||
TtsQueueMessage message = parseMessage(msgObj);
|
||||
if (message != null && message.isLoopable()) {
|
||||
removedCount++;
|
||||
} else {
|
||||
// 非循环消息放回队列
|
||||
redisTemplate.opsForList().rightPush(queueKey, msgObj);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果队列非空,刷新 TTL
|
||||
if (removedCount < allMessages.size()) {
|
||||
redisTemplate.expire(queueKey, 1, TimeUnit.HOURS);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warn("[TTS队列] 移除循环消息失败,降级为清空队列: deviceId={}", deviceId, e);
|
||||
// 降级:清空整个队列,确保循环一定停止
|
||||
clearQueue(deviceId);
|
||||
}
|
||||
|
||||
return removedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查循环播报是否激活
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @return 是否激活
|
||||
*/
|
||||
public boolean isLoopActive(Long deviceId) {
|
||||
String loopKey = LOOP_KEY_PREFIX + deviceId;
|
||||
return Boolean.TRUE.equals(redisTemplate.hasKey(loopKey));
|
||||
}
|
||||
|
||||
// ==================== 队列查询 ====================
|
||||
|
||||
/**
|
||||
* 获取队列长度
|
||||
*/
|
||||
|
||||
@@ -88,6 +88,11 @@ public class TtsQueueMessage implements Serializable {
|
||||
*/
|
||||
private Integer maxRetry;
|
||||
|
||||
/**
|
||||
* 是否为循环消息(消费后重新入队,实现持续播报)
|
||||
*/
|
||||
private Boolean loopable;
|
||||
|
||||
/**
|
||||
* 创建按序消息(FIFO,rightPush 追加到尾部)
|
||||
* <p>
|
||||
@@ -126,6 +131,33 @@ public class TtsQueueMessage implements Serializable {
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建循环消息(消费后自动重新入队,实现持续播报)
|
||||
* <p>
|
||||
* ttsFlag=0x09(紧急通知,带显示),priority=5(普通优先级)
|
||||
* 消费后如果 Redis 中存在循环标记,则重新入队继续播报
|
||||
*/
|
||||
public static TtsQueueMessage loopMessage(Long deviceId, String text, Long orderId) {
|
||||
return TtsQueueMessage.builder()
|
||||
.deviceId(deviceId)
|
||||
.text(text)
|
||||
.ttsFlag(TTS_FLAG_URGENT)
|
||||
.orderId(orderId)
|
||||
.priority(PRIORITY_NORMAL)
|
||||
.createTime(System.currentTimeMillis())
|
||||
.retryCount(0)
|
||||
.maxRetry(2)
|
||||
.loopable(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为循环消息
|
||||
*/
|
||||
public boolean isLoopable() {
|
||||
return Boolean.TRUE.equals(loopable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加重试次数
|
||||
*/
|
||||
|
||||
@@ -101,6 +101,47 @@ public class VoiceBroadcastService {
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 循环播报模式 ====================
|
||||
|
||||
/**
|
||||
* 启动循环播报(消费后自动重新入队,持续播报直到 stopLoop)
|
||||
* <p>
|
||||
* 用于新工单推送场景:设备持续播报"工单来了"直到保洁员按键确认。
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @param text 播报文本
|
||||
* @param orderId 工单ID
|
||||
*/
|
||||
public void broadcastLoop(Long deviceId, String text, Long orderId) {
|
||||
if (deviceId == null || text == null) {
|
||||
return;
|
||||
}
|
||||
if (queueEnabled) {
|
||||
// 设置循环标记
|
||||
ttsQueueConsumer.startLoop(deviceId, orderId);
|
||||
// 入队循环消息
|
||||
enqueueOrFallback(TtsQueueMessage.loopMessage(deviceId, text, orderId));
|
||||
} else {
|
||||
broadcastDirect(deviceId, text, TtsQueueMessage.TTS_FLAG_URGENT, orderId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止循环播报
|
||||
* <p>
|
||||
* 删除循环标记并清空该设备的播报队列。安全调用,无循环标记时也不会报错。
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
*/
|
||||
public void stopLoop(Long deviceId) {
|
||||
if (deviceId == null) {
|
||||
return;
|
||||
}
|
||||
if (queueEnabled) {
|
||||
ttsQueueConsumer.stopLoop(deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 直接播报模式(特殊场景) ====================
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user