|
|
@@ -0,0 +1,233 @@
|
|
|
+package com.ruoyi.web.controller.chenyanlogin.service.impl;
|
|
|
+
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.alibaba.fastjson.annotation.JSONField;
|
|
|
+import lombok.Data;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.core.ParameterizedTypeReference;
|
|
|
+import org.springframework.http.*;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+import org.springframework.util.DigestUtils;
|
|
|
+import org.springframework.web.client.RestTemplate;
|
|
|
+
|
|
|
+import java.io.Serializable;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+@Component
|
|
|
+public class ZhwlCaller {
|
|
|
+
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(ZhwlCaller.class);
|
|
|
+
|
|
|
+// @Value("${cloud.platform.appKey}")
|
|
|
+ private String appKey = "Kg2pE5V0";
|
|
|
+
|
|
|
+// @Value("${cloud.platform.appSecret}")
|
|
|
+ private String appSecret = "7a3bbfd6656e1bdef7290b714c1d1c73578a4ce2";
|
|
|
+
|
|
|
+// @Value("${cloud.platform.baseUrl}")
|
|
|
+ private String baseUrl = "https://webapi.nbiotyun.com"; // e.g., http://domain/api/v1
|
|
|
+
|
|
|
+ private final RestTemplate restTemplate = new RestTemplate();
|
|
|
+
|
|
|
+ // 接口路径常量(便于管理)
|
|
|
+ private static final String GET_DEVICE_LIST_PATH = "/api/v1/dev/getList";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 公共方法:构建带认证信息的请求头
|
|
|
+ *
|
|
|
+ * @param path 原始请求路径(如:/dev/getList),用于签名
|
|
|
+ * @param timestamp 时间戳(毫秒)
|
|
|
+ * @return HttpHeaders
|
|
|
+ */
|
|
|
+ private HttpHeaders buildCommonHeaders(String path, long timestamp) {
|
|
|
+ HttpHeaders headers = new HttpHeaders();
|
|
|
+ headers.setContentType(MediaType.APPLICATION_JSON);
|
|
|
+ headers.set("appKey", appKey);
|
|
|
+ headers.set("timestamp", String.valueOf(timestamp));
|
|
|
+
|
|
|
+ // 签名:MD5(path + timestamp + appSecret)
|
|
|
+ String signatureSource = path + timestamp + appSecret;
|
|
|
+ String signature = DigestUtils.md5DigestAsHex(signatureSource.getBytes(StandardCharsets.UTF_8));
|
|
|
+ headers.set("signature", signature);
|
|
|
+ log.debug("signatureSource:{}", signatureSource);
|
|
|
+ log.debug("headers:{}", JSON.toJSONString(headers));
|
|
|
+ return headers;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 通用 POST 请求模板方法(可扩展)
|
|
|
+ */
|
|
|
+ private ResponseEntity<String> postForString(String endpointPath, Object requestBody) {
|
|
|
+ String url = baseUrl + endpointPath;
|
|
|
+ long timestamp = System.currentTimeMillis();
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 序列化请求体
|
|
|
+ String jsonBody = JSON.toJSONString(requestBody);
|
|
|
+
|
|
|
+ // 构建请求头
|
|
|
+ HttpHeaders headers = buildCommonHeaders(endpointPath, timestamp);
|
|
|
+ headers.setContentType(MediaType.APPLICATION_JSON);
|
|
|
+ // 明确接受 JSON,但即使返回 text/html 也能拿到字符串
|
|
|
+ headers.setAccept(Collections.singletonList(MediaType.ALL)); // 接受任意类型
|
|
|
+
|
|
|
+ // 构建请求实体
|
|
|
+ HttpEntity<String> entity = new HttpEntity<>(jsonBody, headers);
|
|
|
+
|
|
|
+ // 使用 String.class 接收响应体,不管 Content-Type 是什么
|
|
|
+ ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
|
|
|
+ log.debug(response.toString());
|
|
|
+ return response;
|
|
|
+// return new ResponseEntity<>(null);
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException("调用接口失败: " + url, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ private ResponseEntity<Map<String, Object>> postForMap(String endpointPath, Object requestBody) {
|
|
|
+ String url = baseUrl + endpointPath;
|
|
|
+ long timestamp = System.currentTimeMillis();
|
|
|
+
|
|
|
+ try {
|
|
|
+ String jsonBody = JSON.toJSONString(requestBody);
|
|
|
+ HttpHeaders headers = buildCommonHeaders(endpointPath, timestamp);
|
|
|
+ headers.setContentType(MediaType.APPLICATION_JSON);
|
|
|
+ headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
|
|
+
|
|
|
+ HttpEntity<String> entity = new HttpEntity<>(jsonBody, headers);
|
|
|
+
|
|
|
+ ParameterizedTypeReference<Map<String, Object>> typeRef =
|
|
|
+ new ParameterizedTypeReference<Map<String, Object>>() {};
|
|
|
+
|
|
|
+ ResponseEntity<Map<String, Object>> response = restTemplate.exchange(url, HttpMethod.POST, entity, typeRef);
|
|
|
+
|
|
|
+ return response;
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException("调用接口失败: " + url, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // ==================== 接口调用方法区 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询设备列表
|
|
|
+ */
|
|
|
+ public DeviceRespDTO getDeviceList(DeviceListQueryVO queryVO) {
|
|
|
+ ResponseEntity<String> response = postForString(GET_DEVICE_LIST_PATH, queryVO);
|
|
|
+ //1. 检查 HTTP 状态码
|
|
|
+ if (!response.getStatusCode().is2xxSuccessful()) {
|
|
|
+ log.info("HTTP {} - 请求失败,响应内容: {}",response.getStatusCode() ,response.getBody());
|
|
|
+ return new DeviceRespDTO();
|
|
|
+ }
|
|
|
+
|
|
|
+ String body = response.getBody();
|
|
|
+ if (body == null || body.trim().isEmpty()) {
|
|
|
+ log.info("云平台返回空响应体");
|
|
|
+ return new DeviceRespDTO();
|
|
|
+ }
|
|
|
+
|
|
|
+ //2. 打印原始响应(调试用,上线可关闭)
|
|
|
+ log.debug("原始响应: {}", body);
|
|
|
+
|
|
|
+ try {
|
|
|
+ //3. 先解析外层 JSON
|
|
|
+ Map<String, Object> map = JSON.parseObject(body, Map.class);
|
|
|
+
|
|
|
+ //4. 提取 code、success
|
|
|
+ String code = (String) map.get("code");
|
|
|
+ Boolean success = (Boolean) map.get("success");
|
|
|
+
|
|
|
+ Object messageObj = map.get("message");
|
|
|
+ if (!(messageObj instanceof Map)) {
|
|
|
+ throw new RuntimeException("响应中的 message 字段不是对象类型: " + messageObj);
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> messageMap = (Map<String, Object>) messageObj;
|
|
|
+
|
|
|
+ //5. 构建返回 DTO
|
|
|
+ DeviceRespDTO respDTO = new DeviceRespDTO();
|
|
|
+ respDTO.setPageSize((Integer) messageMap.get("pageSize"));
|
|
|
+ respDTO.setPageCount((Integer) messageMap.get("pageCount"));
|
|
|
+ respDTO.setCurrentPage((Integer) messageMap.get("currentPage"));
|
|
|
+ respDTO.setTotal((Integer) messageMap.get("total"));
|
|
|
+
|
|
|
+ //6. 解析 data 列表(关键:用 Fastjson 转成 List<DeviceItemDTO>)
|
|
|
+ Object dataList = messageMap.get("data");
|
|
|
+ if (dataList instanceof List) {
|
|
|
+ respDTO.setData(JSON.parseArray(JSON.toJSONString(dataList), DeviceRespDTO.DeviceItemDTO.class));
|
|
|
+ } else {
|
|
|
+ respDTO.setData(new ArrayList<>());
|
|
|
+ }
|
|
|
+
|
|
|
+ return respDTO;
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException("解析响应数据失败,响应内容: " + body, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @Data
|
|
|
+ public static class DeviceListQueryVO {
|
|
|
+
|
|
|
+ private String deviceModelName;
|
|
|
+
|
|
|
+ private Integer pageNum = 10;
|
|
|
+
|
|
|
+ private Integer pageSize = 1; // 最大值可在业务层校验 1-100
|
|
|
+
|
|
|
+ private String startTime;
|
|
|
+ private String endTime;
|
|
|
+
|
|
|
+ private Integer fullFlag = 0; // 0: 本级, 1: 所有
|
|
|
+
|
|
|
+ private Integer state; // 设备状态
|
|
|
+ private Integer registerSign; // 入网注册标志
|
|
|
+ }
|
|
|
+
|
|
|
+ @Data
|
|
|
+ public static class DeviceRespDTO {
|
|
|
+
|
|
|
+ private List<DeviceItemDTO> data;
|
|
|
+
|
|
|
+ private Integer pageSize;
|
|
|
+ private Integer pageCount;
|
|
|
+ private Integer currentPage;
|
|
|
+ private Integer total;
|
|
|
+
|
|
|
+ @Data
|
|
|
+ public static class DeviceItemDTO implements Serializable {
|
|
|
+ private Long deviceId;
|
|
|
+ private String deviceImei;
|
|
|
+ private String deviceImsi;
|
|
|
+ private String iccid;
|
|
|
+ private String deviceTypeName;
|
|
|
+ private String deviceModelName;
|
|
|
+ private Integer registerSign;
|
|
|
+ private Integer state;
|
|
|
+ private Double latitude;
|
|
|
+ private Double longitude;
|
|
|
+ private String installAddress;
|
|
|
+ private String contact;
|
|
|
+ private String phonenumber;
|
|
|
+ private String companyName;
|
|
|
+ private String floorName;
|
|
|
+ private String buildingName;
|
|
|
+ private String roomName;
|
|
|
+ private String region;
|
|
|
+
|
|
|
+ @JSONField(name = "createTime")
|
|
|
+ private String createTime;
|
|
|
+
|
|
|
+ @JSONField(name = "updateTime")
|
|
|
+ private String updateTime;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|