Просмотр исходного кода

修复设备协议层关键缺陷

- 修复 iotInstanceId 静态字段注入失败导致 RRPC 调用缺少实例 ID 的问题
- 修复结算时 Duration.toSecondsPart() 只记录秒余数而非总秒数的 bug
- 修复 AliyunLotClient 初始化竞态条件,改用 @PostConstruct 同步初始化
- OrderUpdateEventHandler 增加 productKey/deviceName 条件过滤
- 清理已废弃的 MnsHandler 及 AliyunLotMnsConfig(含硬编码凭据)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
skyline 1 день назад
Родитель
Сommit
83208c6547

+ 7 - 4
car-wash-service/src/main/java/com/kym/service/aliyun/lot/AliyunLotClient.java

@@ -4,8 +4,7 @@ import com.aliyun.iot20180120.models.*;
 import com.aliyun.tea.TeaModel;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.event.ContextRefreshedEvent;
-import org.springframework.context.event.EventListener;
+import jakarta.annotation.PostConstruct;
 import org.springframework.stereotype.Component;
 
 import java.util.Arrays;
@@ -24,13 +23,17 @@ public class AliyunLotClient {
     private static com.aliyun.iot20180120.Client rRpcClient;
 
     private static final Base64.Encoder encoder = Base64.getEncoder();
-    @Value("${aliyun.lot.amqp.iotInstanceId}")
     private static String iotInstanceId;
     @Value("${aliyun.lot.amqp.accessKey}")
     private String accessKey;
     @Value("${aliyun.lot.amqp.accessSecret}")
     private String accessSecret;
 
+    @Value("${aliyun.lot.amqp.iotInstanceId}")
+    public void setIotInstanceId(String iotInstanceId) {
+        AliyunLotClient.iotInstanceId = iotInstanceId;
+    }
+
     /**
      * 调用Iot20180120客户端发送请求
      * 2.CreateSubscribeRelation 创建MNS或AMQP服务端订阅
@@ -148,7 +151,7 @@ public class AliyunLotClient {
     /**
      * 初始化IoT(Iot20180120)客户端
      */
-    @EventListener(classes = {ContextRefreshedEvent.class})
+    @PostConstruct
     public void init() throws Exception {
         com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
                 .setRegionId("cn-shanghai")

+ 0 - 23
car-wash-service/src/main/java/com/kym/service/aliyun/lot/AliyunLotMnsConfig.java

@@ -1,23 +0,0 @@
-package com.kym.service.aliyun.lot;
-
-import lombok.Data;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * 阿里云物联网平台配置
- *
- * @author skyline
- */
-@ConfigurationProperties(prefix = "aliyun.lot.mns")
-@Configuration
-@Data
-public class AliyunLotMnsConfig {
-    public String accessKey;
-    public String accessSecret;
-    public String consumerGroupId;
-    public String iotInstanceId;
-    public String clientId;
-    public String ampqHost;
-    public String mnsHost;
-}

+ 0 - 196
car-wash-service/src/main/java/com/kym/service/aliyun/lot/MnsHandler.java

@@ -1,196 +0,0 @@
-package com.kym.service.aliyun.lot;
-
-import cn.hutool.core.util.CharsetUtil;
-import com.alibaba.fastjson2.JSONObject;
-import com.alibaba.fastjson2.TypeReference;
-import com.aliyun.mns.client.CloudAccount;
-import com.aliyun.mns.client.CloudQueue;
-import com.aliyun.mns.client.MNSClient;
-import com.aliyun.mns.model.Message;
-import com.kym.entity.awoara.Event;
-import com.kym.entity.awoara.MessageBody;
-import com.kym.service.awoara.factory.AwoaraEventHandlerFactory;
-import jakarta.annotation.PreDestroy;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.text.StringEscapeUtils;
-import org.springframework.context.event.ContextRefreshedEvent;
-import org.springframework.context.event.EventListener;
-import org.springframework.scheduling.annotation.Async;
-import org.springframework.stereotype.Component;
-
-import java.util.Base64;
-import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-
-/**
- * MNS消息处理
- *
- * @author skyline
- */
-//@Component 备注:弃用MNS改用AMQP
-@Slf4j
-public class MnsHandler {
-
-    /**
-     * 使用 Java 21 虚拟线程执行器(推荐)
-     * 虚拟线程非常轻量,适合I/O密集型的消息处理
-     */
-    private final static ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();
-
-    /**
-     * 传统线程池(备用)
-     */
-//    private final static ExecutorService executorService = new ThreadPoolExecutor(
-//            2, 4, 60, TimeUnit.SECONDS,
-//            new LinkedBlockingQueue<>(10000));
-
-    // 增加饱和策略(建议用CallerRunsPolicy)
-//    private final static ThreadPoolExecutor executorService = new ThreadPoolExecutor(
-//            4, 8, 60, TimeUnit.SECONDS,
-//            new LinkedBlockingQueue<>(10000),
-//            new ThreadPoolExecutor.CallerRunsPolicy()); // 关键修改点
-
-
-    /**
-     * 是否做 base64 编码
-     */
-    private static final Boolean IS_BASE64 = false;
-    /**
-     * BASE64解码器
-     */
-    private static Base64.Decoder decoder = Base64.getDecoder();
-    String queueName = "aliyun-iot-k1olfsszoYB";
-    // 遵循阿里云规范,env设置ak、sk。
-    CloudAccount account = new CloudAccount("LTAI5tNhD2KFuLUN1hMEukmS", "dtVF6na8Hp9W8DmAoWI9k24VXwjNyM", "http://1757940634296846.mns.cn-shanghai.aliyuncs.com");
-    //this client need only initialize once
-    MNSClient client = account.getMNSClient();
-    CloudQueue queue = client.getQueueRef(queueName);
-
-
-    private static void batchReceive(CloudQueue queue) {
-        executorService.execute(() -> {
-            while (!Thread.currentThread().isInterrupted()) {
-                try {
-                    longPollingBatchReceive(queue);
-                } catch (Exception e) {
-                    log.error("消息处理异常", e);
-                    // 增加恢复逻辑(如休眠后重试)
-                    try {
-                        TimeUnit.SECONDS.sleep(5);
-                    } catch (InterruptedException ex) {
-                        break;
-                    }
-                } finally {
-                    // 虚拟线程执行器不支持传统线程池的监控方法
-                    // 虚拟线程是轻量级的,无需关注池大小和队列
-                    log.debug("虚拟线程执行器正在处理消息...");
-                }
-            }
-        });
-    }
-
-    /**
-     * 长轮询批量获取消息
-     *
-     * @param queue
-     */
-    private static void longPollingBatchReceive(CloudQueue queue) {
-        log.info("=============开始 长轮询批量获取消息=============");
-
-        // 一次性拉取最多xx条消息
-        int batchSize = 15;
-        // 长轮询时间为 xx s
-        int waitSeconds = 15;
-
-        List<Message> messages = queue.batchPopMessage(batchSize, waitSeconds);
-        if (messages != null && !messages.isEmpty()) {
-            for (Message message : messages) {
-                printMsgAndDelete(queue, message);
-            }
-        }
-        log.info("=============结束 长轮询批量获取消息=============");
-    }
-
-    /**
-     * 获取单条消息
-     *
-     * @param queue
-     */
-    private static void singleReceive(CloudQueue queue) {
-        log.info("=============start singleReceive=============");
-
-        Message popMsg = queue.popMessage();
-        printMsgAndDelete(queue, popMsg);
-
-        log.info("=============end singleReceive=============");
-    }
-
-    /**
-     * 删除队列消息
-     *
-     * @param queue
-     * @param popMsg
-     */
-    private static void printMsgAndDelete(CloudQueue queue, Message popMsg) {
-        if (popMsg != null) {
-            log.info("message handle: " + popMsg.getReceiptHandle());
-            log.info("message body: " + (IS_BASE64 ? popMsg.getMessageBody() : popMsg.getMessageBodyAsRawString()));
-            log.info("message id: " + popMsg.getMessageId());
-            log.info("message dequeue count:" + popMsg.getDequeueCount());
-
-            try {
-                handleMessage(popMsg);
-                queue.deleteMessage(popMsg.getReceiptHandle());
-            } catch (Exception e) {
-                log.error("Failed to process message: " + popMsg.getMessageId(), e);
-            }
-            log.info("delete message successfully.");
-        }
-    }
-
-
-    static void handleMessage(Message popMsg) {
-        var base64 = popMsg.getMessageBodyAsRawString();
-        // 对messageBody进行base64解码
-        var messageBodyStr = new String(decoder.decode(base64), CharsetUtil.CHARSET_UTF_8);
-
-        // 对messageBodyStr中的payload进行base64解码后转换成MessageBody对象
-        var json = JSONObject.parseObject(messageBodyStr);
-
-        json.put("payload", JSONObject.parseObject(new String(decoder.decode(json.get("payload").toString()), CharsetUtil.CHARSET_UTF_8)));
-        log.info("消息内容:{}", json.toJSONString());
-        if ("upload".equals(json.getString("messagetype"))) {
-            var event = json.getJSONObject("payload").getString("event");
-            var eventHandler = AwoaraEventHandlerFactory.getEventHandler(event);
-            var message = parseMessageBody(json.toJSONString(), Event.getClazz(event));
-            eventHandler.handle(message);
-        } else {
-            log.info("message type is not upload");
-        }
-    }
-
-    private static <T> MessageBody<T> parseMessageBody(String jsonStr, Class<T> clazz) {
-        // 去掉json字符串中的转义符
-        var json = StringEscapeUtils.unescapeJava(jsonStr);
-        return JSONObject.parseObject(json, new TypeReference<>(clazz) {
-
-        });
-    }
-
-
-    @EventListener(classes = {ContextRefreshedEvent.class})
-    @Async
-    public void init() {
-        batchReceive(queue);
-    }
-
-    @PreDestroy
-    public void shutdown() {
-        client.close(); // 关闭MNS客户端
-        executorService.shutdown();
-    }
-
-
-}

+ 7 - 1
car-wash-service/src/main/java/com/kym/service/awoara/event/handle/OrderUpdateEventHandler.java

@@ -38,10 +38,14 @@ public class OrderUpdateEventHandler implements AwoaraEventHandler<OrderInfoObje
     public void handle(MessageBody<OrderInfoObject> message) {
         log.info(message.toString());
         log.info("收到订单更新事件");
+
+        var topic = message.getTopic().split("/");
+        var productKey = topic[1];
+        var deviceName = topic[2];
+
         var orderInfo = message.getPayload().getData().getOrder_info();
         log.info("订单:{},更新信息:{}", orderInfo.getOrder_id(), orderInfo);
 
-        // 更新订单信息
         washOrderService.lambdaUpdate()
                 .set(WashOrder::getOpenType, orderInfo.getOpen_type())
                 .set(WashOrder::getCloseType, orderInfo.getClose_type())
@@ -55,6 +59,8 @@ public class OrderUpdateEventHandler implements AwoaraEventHandler<OrderInfoObje
                 .set(WashOrder::getIdleRemainTime, orderInfo.getIdle_remain_time())
                 .set(WashOrder::getDetail, JSON.toJSONString(orderInfo.getDetail()))
                 .eq(WashOrder::getOrderId, orderInfo.getOrder_id())
+                .eq(WashOrder::getProductKey, productKey)
+                .eq(WashOrder::getDeviceName, deviceName)
                 .update();
 
     }

+ 1 - 1
car-wash-service/src/main/java/com/kym/service/impl/OrderSettlementServiceImpl.java

@@ -85,7 +85,7 @@ public class OrderSettlementServiceImpl implements OrderSettlementService {
                 .setDiscountMoney(orderInfo.getDiscount_money())
                 .setDetail(orderInfo.getDetail())
                 .setEndTime(LocalDateTime.now())
-                .setTotalSeconds(Duration.between(washOrder.getStartTime(), LocalDateTime.now()).toSecondsPart())
+                .setTotalSeconds(Duration.between(washOrder.getStartTime(), LocalDateTime.now()).toSeconds())
                 .setRechargePayment(rechargePayment)
                 .setGrantsPayment(grantsPayment)
                 .setPayStatus(WashOrder.PAY_STATUS_已支付)