diff --git a/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/service/rule/clean/processor/ButtonEventRuleProcessor.java b/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/service/rule/clean/processor/ButtonEventRuleProcessor.java new file mode 100644 index 0000000..9d655b9 --- /dev/null +++ b/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/service/rule/clean/processor/ButtonEventRuleProcessor.java @@ -0,0 +1,295 @@ +package com.viewsh.module.iot.service.rule.clean.processor; + +import com.viewsh.module.iot.core.integration.constants.CleanOrderTopics; +import com.viewsh.module.iot.dal.dataobject.integration.clean.ButtonEventConfig; +import com.viewsh.module.iot.dal.redis.clean.DeviceCurrentOrderRedisDAO; +import com.viewsh.module.iot.service.integration.clean.CleanOrderIntegrationConfigService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.messaging.support.MessageBuilder; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * 按键事件规则处理器 + *
+ * 监听设备按键事件上报,处理保洁员工牌的按键交互 + *
+ * 支持的按键类型: + * - 确认键(confirmKeyId):保洁员确认接收工单 + * - 查询键(queryKeyId):保洁员查询当前工单信息 + * + * @author AI + */ +@Component +@Slf4j +public class ButtonEventRuleProcessor { + + @Resource + private CleanOrderIntegrationConfigService configService; + + @Resource + private DeviceCurrentOrderRedisDAO deviceCurrentOrderRedisDAO; + + @Resource + private RocketMQTemplate rocketMQTemplate; + + /** + * 处理按键事件属性上报 + *
+ * 在设备属性上报处理流程中调用此方法 + * + * @param deviceId 设备ID + * @param identifier 属性标识符(如 button_event) + * @param propertyValue 属性值 + */ + public void processPropertyChange(Long deviceId, String identifier, Object propertyValue) { + // 1. 检查是否是按键事件属性 + if (!"button_event".equals(identifier)) { + return; + } + + log.debug("[ButtonEvent] 收到按键事件:deviceId={}, value={}", deviceId, propertyValue); + + // 2. 获取配置 + CleanOrderIntegrationConfigService.AreaDeviceConfigWrapper configWrapper = + getConfigWrapper(deviceId); + + if (configWrapper == null || configWrapper.getConfig() == null) { + log.debug("[ButtonEvent] 设备无配置:deviceId={}", deviceId); + return; + } + + ButtonEventConfig buttonConfig = configWrapper.getConfig().getButtonEvent(); + if (buttonConfig == null || !buttonConfig.getEnabled()) { + log.debug("[ButtonEvent] 未启用按键事件处理:deviceId={}", deviceId); + return; + } + + // 3. 解析按键ID + Integer buttonId = parseButtonId(propertyValue); + if (buttonId == null) { + log.warn("[ButtonEvent] 按键ID解析失败:deviceId={}, value={}", deviceId, propertyValue); + return; + } + + log.debug("[ButtonEvent] 按键解析成功:deviceId={}, buttonId={}", deviceId, buttonId); + + // 4. 匹配按键类型并处理 + if (buttonId.equals(buttonConfig.getConfirmKeyId())) { + // 确认键 + handleConfirmButton(configWrapper, buttonId); + } else if (buttonId.equals(buttonConfig.getQueryKeyId())) { + // 查询键 + handleQueryButton(configWrapper, buttonId); + } else { + log.debug("[ButtonEvent] 未配置的按键:deviceId={}, buttonId={}", deviceId, buttonId); + } + } + + /** + * 处理确认按键 + *
+ * 保洁员按下确认键,确认接收工单 + */ + private void handleConfirmButton(CleanOrderIntegrationConfigService.AreaDeviceConfigWrapper configWrapper, + Integer buttonId) { + Long deviceId = configWrapper.getDeviceId(); + + log.info("[ButtonEvent] 确认键按下:deviceId={}, buttonId={}", deviceId, buttonId); + + // 1. 查询设备当前工单 + DeviceCurrentOrderRedisDAO.OrderCacheInfo currentOrderJson = deviceCurrentOrderRedisDAO.getCurrentOrder(deviceId); + if (currentOrderJson == null) { + log.warn("[ButtonEvent] 设备无当前工单,跳过确认:deviceId={}", deviceId); + return; + } + + // 2. 解析工单ID(简单解析,生产环境可用 JSON 库) + Long orderId = extractOrderIdFromJson(currentOrderJson.getOrderId().toString()); + if (orderId == null) { + log.warn("[ButtonEvent] 工单ID解析失败:deviceId={}, json={}", deviceId, currentOrderJson); + return; + } + + // 3. 防重复检查(短时间内同一工单的确认操作去重) + 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; + } + + // 4. 发布工单确认事件 + publishConfirmEvent(configWrapper, orderId, buttonId); + + log.info("[ButtonEvent] 发布工单确认事件:deviceId={}, orderId={}", deviceId, orderId); + } + + /** + * 处理查询按键 + *
+ * 保洁员按下查询键,查询当前工单信息
+ */
+ private void handleQueryButton(CleanOrderIntegrationConfigService.AreaDeviceConfigWrapper configWrapper,
+ Integer buttonId) {
+ Long deviceId = configWrapper.getDeviceId();
+
+ log.info("[ButtonEvent] 查询键按下:deviceId={}, buttonId={}", deviceId, buttonId);
+
+ // 1. 查询设备当前工单
+ DeviceCurrentOrderRedisDAO.OrderCacheInfo currentOrderJson = deviceCurrentOrderRedisDAO.getCurrentOrder(deviceId);
+ if (currentOrderJson == null) {
+ log.info("[ButtonEvent] 设备无当前工单:deviceId={}", deviceId);
+ // 发布查询结果事件(无工单)
+ publishQueryEvent(configWrapper, null, buttonId, "当前无工单");
+ return;
+ }
+
+ // 2. 解析工单信息
+ Long orderId = extractOrderIdFromJson(currentOrderJson.getOrderId().toString());
+ if (orderId == null) {
+ log.warn("[ButtonEvent] 工单ID解析失败:deviceId={}, json={}", deviceId, currentOrderJson);
+ return;
+ }
+
+ // 3. 发布查询事件
+ publishQueryEvent(configWrapper, orderId, buttonId, "查询当前工单");
+
+ log.info("[ButtonEvent] 发布工单查询事件:deviceId={}, orderId={}", deviceId, orderId);
+ }
+
+ /**
+ * 发布工单确认事件
+ */
+ private void publishConfirmEvent(CleanOrderIntegrationConfigService.AreaDeviceConfigWrapper configWrapper,
+ Long orderId, Integer buttonId) {
+ try {
+ Map
+ * 简单实现,生产环境建议使用 Jackson 或 Gson
+ */
+ private Long extractOrderIdFromJson(String json) {
+ try {
+ // 简单解析:查找 "orderId":12345
+ int orderIdIndex = json.indexOf("\"orderId\"");
+ if (orderIdIndex == -1) {
+ return null;
+ }
+
+ int colonIndex = json.indexOf(":", orderIdIndex);
+ if (colonIndex == -1) {
+ return null;
+ }
+
+ int endIndex = json.indexOf(",", colonIndex);
+ if (endIndex == -1) {
+ endIndex = json.indexOf("}", colonIndex);
+ }
+
+ if (endIndex == -1) {
+ return null;
+ }
+
+ String idStr = json.substring(colonIndex + 1, endIndex).trim();
+ return Long.parseLong(idStr);
+ } catch (Exception e) {
+ log.error("[ButtonEvent] 解析工单ID失败:json={}", json, e);
+ return null;
+ }
+ }
+
+ /**
+ * 获取配置包装器
+ */
+ private CleanOrderIntegrationConfigService.AreaDeviceConfigWrapper getConfigWrapper(Long deviceId) {
+ return configService.getConfigWrapperByDeviceId(deviceId);
+ }
+
+ /**
+ * 解析按键ID
+ */
+ private Integer parseButtonId(Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ if (value instanceof Number) {
+ return ((Number) value).intValue();
+ }
+
+ if (value instanceof String) {
+ try {
+ return Integer.parseInt((String) value);
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ }
+
+ return null;
+ }
+
+ @Resource
+ private StringRedisTemplate stringRedisTemplate;
+}