package com.xiaopeng.order.utils;
|
|
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSONArray;
|
import com.alibaba.fastjson2.JSONObject;
|
import com.xiaopeng.common.core.utils.StringUtils;
|
import com.xiaopeng.common.redis.service.RedisService;
|
import com.xiaopeng.order.config.MinistryTransportProperties;
|
import com.xiaopeng.order.model.dto.RegulationOrderCreateInfoDTO;
|
import com.xiaopeng.order.model.dto.RegulationOrderMatchInfoDTO;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.core.io.ByteArrayResource;
|
import org.springframework.http.HttpEntity;
|
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.MediaType;
|
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
import org.springframework.stereotype.Component;
|
import org.springframework.util.LinkedMultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
import org.springframework.web.client.RestTemplate;
|
|
import java.nio.charset.StandardCharsets;
|
import java.security.MessageDigest;
|
import java.time.LocalDateTime;
|
import java.time.format.DateTimeFormatter;
|
import java.util.concurrent.TimeUnit;
|
|
@Slf4j
|
@Component
|
public class MinistryOfTransportOrderReporter {
|
|
public static final DateTimeFormatter DATA_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
|
private static final DateTimeFormatter FILE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-ddHHmmssSSS");
|
|
private static final String TOKEN_CACHE_KEY = "ministry_of_transport:token";
|
|
private static final long TOKEN_EXPIRE_SECONDS = 20 * 60;
|
|
private static final long TOKEN_REFRESH_THRESHOLD_SECONDS = 5 * 60;
|
|
private final RedisService redisService;
|
|
private final MinistryTransportProperties ministryTransportProperties;
|
|
private final RestTemplate restTemplate;
|
|
public MinistryOfTransportOrderReporter(RedisService redisService,
|
MinistryTransportProperties ministryTransportProperties) {
|
this.redisService = redisService;
|
this.ministryTransportProperties = ministryTransportProperties;
|
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
|
requestFactory.setConnectTimeout(10000);
|
requestFactory.setReadTimeout(10000);
|
this.restTemplate = new RestTemplate(requestFactory);
|
}
|
|
public String pushOrderCreateInfo(RegulationOrderCreateInfoDTO item) {
|
return pushOrderInfo(item, "DDFQ");
|
}
|
|
public String pushOrderMatchInfo(RegulationOrderMatchInfoDTO item) {
|
return pushOrderInfo(item, "DDCG");
|
}
|
|
private String pushOrderInfo(Object item, String command) {
|
String token = getToken();
|
if (StringUtils.isBlank(token)) {
|
throw new RuntimeException("交通部token为空");
|
}
|
JSONObject body = new JSONObject();
|
JSONArray items = new JSONArray();
|
items.add(JSON.toJSON(item));
|
body.put("items", items);
|
String filename = ministryTransportProperties.getPlatform() + "_ORDER_" + command + "_REQ_" + LocalDateTime.now().format(FILE_TIME_FORMATTER) + ".json";
|
return uploadJsonFile(filename, body.toJSONString(), token);
|
}
|
|
private String getToken() {
|
String cachedToken = redisService.getCacheObject(TOKEN_CACHE_KEY);
|
if (StringUtils.isNotBlank(cachedToken)) {
|
Long expireTime = redisService.getExpire(TOKEN_CACHE_KEY);
|
if (expireTime != null && expireTime > TOKEN_REFRESH_THRESHOLD_SECONDS) {
|
return cachedToken;
|
}
|
}
|
JSONObject body = new JSONObject();
|
body.put("platform", ministryTransportProperties.getPlatform());
|
body.put("key", ministryTransportProperties.getKey());
|
String filename = ministryTransportProperties.getPlatform() + "_TOKEN_ACCESS_REQ_" + LocalDateTime.now().format(FILE_TIME_FORMATTER) + ".json";
|
String response = uploadJsonFile(filename, body.toJSONString(), ministryTransportProperties.getPlatform());
|
String token = parseToken(response);
|
redisService.setCacheObject(TOKEN_CACHE_KEY, token, TOKEN_EXPIRE_SECONDS, TimeUnit.SECONDS);
|
return token;
|
}
|
|
private String uploadJsonFile(String filename, String jsonBody, String auth) {
|
byte[] bytes = jsonBody.getBytes(StandardCharsets.UTF_8);
|
HttpHeaders headers = new HttpHeaders();
|
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
headers.add("binfile-md5", md5Hex(bytes));
|
headers.add("binfile-auth", auth);
|
headers.add("binfile-gzip", "false");
|
headers.add("binfile-reqlen", String.valueOf(bytes.length));
|
|
HttpHeaders fileHeaders = new HttpHeaders();
|
fileHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
|
HttpEntity<ByteArrayResource> fileEntity = new HttpEntity<>(new ByteArrayResource(bytes) {
|
@Override
|
public String getFilename() {
|
return filename;
|
}
|
}, fileHeaders);
|
|
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
body.add("binFile", fileEntity);
|
body.add("filename", filename);
|
|
ResponseEntity<String> response = restTemplate.postForEntity(
|
ministryTransportProperties.getUrl(),
|
new HttpEntity<>(body, headers),
|
String.class
|
);
|
String responseBody = response.getBody() == null ? "" : response.getBody();
|
log.info("交通部订单接口响应 status={}, filename={}, responseLength={}",
|
response.getStatusCodeValue(), filename, responseBody.length());
|
return responseBody;
|
}
|
|
private String parseToken(String tokenResponse) {
|
if (StringUtils.isBlank(tokenResponse)) {
|
throw new RuntimeException("交通部token响应为空");
|
}
|
JSONObject response = JSON.parseObject(tokenResponse);
|
String token = response.getString("token");
|
if (StringUtils.isNotBlank(token)) {
|
return token;
|
}
|
JSONArray items = response.getJSONArray("items");
|
if (items != null && !items.isEmpty()) {
|
token = items.getJSONObject(0).getString("token");
|
if (StringUtils.isNotBlank(token)) {
|
return token;
|
}
|
}
|
throw new RuntimeException("交通部token响应中未找到token: " + tokenResponse);
|
}
|
|
private String md5Hex(byte[] bytes) {
|
try {
|
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
byte[] digest = md5.digest(bytes);
|
StringBuilder sb = new StringBuilder(digest.length * 2);
|
for (byte b : digest) {
|
sb.append(Character.forDigit((b >> 4) & 0xF, 16));
|
sb.append(Character.forDigit(b & 0xF, 16));
|
}
|
return sb.toString();
|
} catch (Exception e) {
|
throw new RuntimeException("计算交通部请求MD5失败", e);
|
}
|
}
|
}
|