liujie
2026-06-09 c96fbe6b0aeb413a17c429133ee6d81d138144b9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package com.xiaopeng.vehicleConvoy.util;
 
 
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.xiaopeng.api.dto.vehicleConvoy.BaseImOpenDto;
import com.xiaopeng.api.dto.vehicleConvoy.ImOpenQueryStateDto;
import com.xiaopeng.api.dto.vehicleConvoy.VehicleTemperatureDto;
import com.xiaopeng.api.feignClient.vehicleConvoy.VehicleControlClient;
import com.xiaopeng.api.vo.vehicleConvoy.VehicleStateVo;
import com.xiaopeng.api.vo.vehicleConvoy.VehiclesCommandVo;
import com.xiaopeng.vehicleConvoy.service.AsyncVehicleStateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
 
/**
 * 车辆控制工具类
 */
@Slf4j
@Component
public class VehicleControlUtil {
 
    public static final String OPEN_ID_ROBOT_TAXI = "robot_taxi";
    @Resource
    private VehicleControlClient vehicleControlClient;
    @Resource
    private AsyncVehicleStateService asyncVehicleStateService;
    // 车辆控制接口appid
    @Value("${vehicle.control.appId}")
    private String appid;
    // 车辆控制接口appSecret
    @Value("${vehicle.control.appSecret}")
    private String appSecret;
 
 
    /**
     * 查询车辆内温度
     *
     * @param uid 用户id
     * @param vin 车辆vin
     *            测试车辆:L1NNSGHA5PB000204
     * @return 车辆温度信息
     */
    public VehicleTemperatureDto queryVehicleTemperature(String uid, String vin) {
        String nonce = System.currentTimeMillis() + "";
        Set<String> stateList = new HashSet<>();
        stateList.add("hvacInnerTemp");
        stateList.add("trunkAjarCloseSt");
        Map<String, String> map = new HashMap<>();
        map.put("nonce", nonce);
        map.put("openId", OPEN_ID_ROBOT_TAXI);
        map.put("accessToken", "");
        map.put("body", "");
        map.put("vin", vin);
        map.put("stateList", StrUtil.join(",", stateList));
        String sign = SignatureUtils.getSignatureSHA1(appid, map, appSecret);
        log.info("查询车内温度 请求参数:uid={}, vin={}", uid, vin);
        ImOpenQueryStateDto baseImOpenDto = vehicleControlClient.queryVehicleTemperature(appid, nonce, "", sign, OPEN_ID_ROBOT_TAXI, vin, stateList, uid);
        log.info("查询车内温度 结果:{}", JSON.toJSONString(baseImOpenDto));
        if (null == baseImOpenDto || null == baseImOpenDto.getData()) {
            throw new RuntimeException("查询车内温度失败");
        }
        if (0 != baseImOpenDto.getCode()) {
            throw new RuntimeException("查询车内温度失败:" + baseImOpenDto.getMsg());
        }
        VehicleTemperatureDto vehicleTemperatureDto = baseImOpenDto.getData();
        vehicleTemperatureDto.setVin(vin);
        return vehicleTemperatureDto;
    }
 
    /**
     * 同步查询车辆后备箱状态
     * 下发一次指令,等待回调结果(1秒超时)
     *
     * @param uid 用户ID
     * @param vin 车辆VIN码
     * @return 查询后备箱开关状态(打开状态=true,关闭状态=false)
     * @throws RuntimeException 查询失败或1秒超时
     */
    public Boolean queryTrunkDoorStateSync(Long uid, String vin, boolean reportEnable) {
        log.info("开始同步查询车辆后备箱状态: uid={}, vin={} (1秒超时)", uid, vin);
        if (!reportEnable) {
            VehicleTemperatureDto vehicleTemperatureDto = queryVehicleTemperature(String.valueOf(uid), vin);
            VehicleStateVo vehicleStateVo = vehicleTemperatureDto.getStateMap().get("trunkAjarCloseSt");
            return null != vehicleStateVo && vehicleStateVo.getValue().equals("1");
        }
        try {
            // 使用异步服务查询状态,内部有1秒超时处理
            CompletableFuture<VehicleTemperatureDto> future = asyncVehicleStateService
                    .queryVehicleStateAsync(String.valueOf(uid), vin);
 
            // 等待回调结果,由AsyncVehicleStateService内部处理超时
            VehicleTemperatureDto result = future.get();
 
            if (result == null) {
                throw new RuntimeException("查询车辆状态失败: 未获取到状态数据");
            }
 
            log.info("同步查询车辆后备箱状态成功: uid={}, vin={}", uid, vin);
            VehicleStateVo vehicleStateVo = result.getStateMap().get("trunkAjarCloseSt");
            log.info("同步查询车辆后备箱状态成功: uid={}, vin={}, vehicleStateVo={}", uid, vin, JSON.toJSONString(vehicleStateVo));
            return null != vehicleStateVo && vehicleStateVo.getValue().equals("5");
 
        } catch (java.util.concurrent.ExecutionException e) {
            Throwable cause = e.getCause();
            if (cause instanceof RuntimeException && cause.getMessage().contains("查询超时")) {
                log.error("查询车辆后备箱状态超时: uid={}, vin={} (1秒超时)", uid, vin);
                throw new RuntimeException("查询车辆状态超时,请稍后重试");
            } else {
                log.error("同步查询车辆后备箱状态失败: uid={}, vin={}", uid, vin, cause);
                throw new RuntimeException("查询车辆状态失败: " + (cause != null ? cause.getMessage() : e.getMessage()));
            }
        } catch (Exception e) {
            log.error("同步查询车辆后备箱状态失败: uid={}, vin={}", uid, vin, e);
            throw new RuntimeException("查询车辆状态失败: " + e.getMessage());
        }
    }
 
 
    /**
     * 车辆控制操作
     * <p>
     * 注意:签名计算由 VehicleControlSignatureInterceptor 拦截器处理
     * 拦截器会在 Feign 序列化 body 后,使用真实的 body 重新计算签名
     *
     * @param commandVo 车辆控制命令参数
     * @return msgId 消息id,对比消息回调通知
     * 测试车辆:L1NNSGHA5PB000204
     */
    public String vehiclesCommand(VehiclesCommandVo commandVo, Long uid) {
        String nonce = System.currentTimeMillis() + "";
        BaseImOpenDto baseImOpenDto = vehicleControlClient.vehiclesCommand(
                appid,
                nonce,
                "",
                "",
                OPEN_ID_ROBOT_TAXI,
                commandVo,
                uid
        );
 
        if (null == baseImOpenDto || null == baseImOpenDto.getData()) {
            throw new RuntimeException("车辆控制失败");
        }
        if (0 != baseImOpenDto.getCode()) {
            throw new RuntimeException("车辆控制失败:" + baseImOpenDto.getMsg());
        }
        return baseImOpenDto.getData().getMsgId();
    }
}