From c9b6ccdee4dd0d0364ef204f55892e9c960a19a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E7=AB=B9=E7=BA=A2?= Date: Tue, 13 May 2025 18:41:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=AD=A3=E6=96=B0=E8=8F=9C=E5=93=81token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cool/store/utils/SignatureUtils.java | 51 +++++- .../java/com/cool/store/dto/FoodTokenDTO.java | 14 ++ .../cool/store/service/ThirdFoodService.java | 22 +++ .../service/impl/ThirdFoodServiceImpl.java | 148 ++++++++++++++++++ .../controller/webb/PCTestController.java | 9 +- .../webc/MiniShopAccountController.java | 11 ++ .../resources/application-online.properties | 2 + .../resources/application-test.properties | 2 + 8 files changed, 256 insertions(+), 3 deletions(-) create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/dto/FoodTokenDTO.java create mode 100644 coolstore-partner-service/src/main/java/com/cool/store/service/ThirdFoodService.java create mode 100644 coolstore-partner-service/src/main/java/com/cool/store/service/impl/ThirdFoodServiceImpl.java diff --git a/coolstore-partner-common/src/main/java/com/cool/store/utils/SignatureUtils.java b/coolstore-partner-common/src/main/java/com/cool/store/utils/SignatureUtils.java index bf47a372a..0c542aa9d 100644 --- a/coolstore-partner-common/src/main/java/com/cool/store/utils/SignatureUtils.java +++ b/coolstore-partner-common/src/main/java/com/cool/store/utils/SignatureUtils.java @@ -1,14 +1,17 @@ package com.cool.store.utils; +import cn.hutool.core.util.RandomUtil; import com.alibaba.fastjson.JSONObject; import com.google.gson.JsonObject; import lombok.extern.slf4j.Slf4j; +import okhttp3.internal.http.HttpMethod; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; import java.util.*; /** @@ -18,6 +21,9 @@ import java.util.*; */ @Slf4j public class SignatureUtils { + + + private static final String privateKey = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCML5dEU4e6BAopiSjxeA2ZNs3VeIXoWe8HpHHGhZP1xE+4vbPv3L4Z7XexeoPMiBcluyGKSRl5jwuvYvcF9A2nGPhxVugYOxpC+TuGVu3TE7a2E/+1iMFfdNCYgqMxfnTe2Svo+X5anc5AcpKar2+AYcdZ4emMQLwrJkEB9endpWPruqC7dCUmAwxByMi525fdipFEDsAIf9ozkBuqwyQa0/RwQ+fyOT87pJ58z7gLXcZxW8YaXNAS5hmdA6/0GqZQQyDTXxZ/nDPpeIIk8PC8VNrgllCxwDtyDMWfUg8+yTlHAjsCX7MGRM3Gd3QcfqrvlIK/Lvn/9qAWcY3KOU1JAgMBAAECggEADwvnokmN6qEW3yTt7FcMrJqFhslgJBONFKapoJuDSVCIFwXji5YHoHxMydOi1F/yDhZzD+bvMU0xfaJzbaPnmD0Vo4KmuD0VwUUDhtSLQ8ncpVfprRgv2zlMT2ZMMlqGnEm2jRPEZGEGO4QgqLI2V1MHZOYa3XcwSzhYp2d38dZvPxWnF9mDt26BbM5Eb3D4oAb+J8rOVsbXEbVTdVTsJmhLOUEK84r2fUCLqHALAOc/za28ToupYD/QRWT6K5nWKDclDWn2tnz3MQJrEYMVvGDVCryqltljd2XB4MXnBZxEYDZyonxxI5M1Avb9mHUyy2wMYSeTUocSnVjgCZPnFQKBgQC34xs/Q0lDTpRdS0a8odzey4jew/nfo8TqzAa2/vomRHkOAoXyqci71tCos5836hxprkhvj1KMauB/rFzCZYfr5a2UMM70p/vuYv5JhTMDStTVx9OPcydZDeK77OY39qPhkW4Tm10BBUodzVV0YhxbCfRmSRSF2FuZCSLdd5bmpQKBgQDDKTUYuA+s/cFt0QsKkRRBm/ONQnETCCXo87NP9RzspcydkULnmuRMLWP4zqHN5TccL0MyI7hyHZ7AwU3qoi4JXFo8k5rRcSdwaBy99B+ZKF159l7chVHE8p4tYSmxwoIOIbpfXA2nfp+zaknt6lb8DSeCw8lewsEZUwmU++bu1QKBgB5M5t16yeS0WxAwajyE828m3KvQYSkvxC/oZKbcxv1tRQFA+y9sOK5zDDjwOCkvg3UKidcQJ8sJhWqkTTifIFGHrB2CGWgJEiNK3pngv/In74A2iC7YC8DJ3/WVaK4KDyEJJLlIXX/gjkDw58bgr8l0j3gXTLFtYFKMRdl2bkAdAoGAHwhNZhnWfEZ5XHHyFIMPOoOGzb5z5nfUN1+Q8rTiHYsbpy5y+gr6JFbPX7/ToL7C7ZtiTvW5HYQyOypBrn6NXXWu75T0+DrK76fOcjIAWEU/3+XaHayhXPTLTCH4FEj+jyqzzPgBP9H1qPzMtqUdocShtLxXJ9dGBzXZ/zOy++UCgYAHgaZC5Jz8+p5Ka7YtPZXpDXt1gUh2bmzIcAOBlizE+ElWZN/skybumos3lP8JOXdB65jEuXI9V0rc6n/KpOLY2Xfy6CX78RpWVXPc30D10DxUVI9hyEU+Xg4t9rwOAVhZaOwjhaEqpdvDrjMmX0bBVwCI2QXt7GnjUPqibNyEbQ=="; /** * 生成 HmacSHA256 签名 @@ -37,6 +43,47 @@ public class SignatureUtils { } } + /** + * 正新菜品市场系统 验签 + * @param httpMethod + * @param uri + * @param requestBody + * @param timeStamp + * @param random + * @return + */ + public static String sign(String httpMethod, String uri, String requestBody, String timeStamp, String random) throws Exception { + log.info("timeStamp+random:{}",timeStamp + "/" + random); + String plainText = new StringJoiner("&") + .add("CRM") + .add(httpMethod.toUpperCase()) + .add(uri) + .add(requestBody) + .add(timeStamp) + .add(random) + .toString(); + // 签名算法 + Signature signature = Signature.getInstance("SHA256WithRSA"); + + PrivateKey privateKey = readPrivateKey(SignatureUtils.privateKey); + // 签名 + signature.initSign(privateKey); + signature.update(plainText.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(signature.sign()); + } + + + private static PrivateKey readPrivateKey(String key) throws Exception { + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + byte[] keys = Base64.getDecoder().decode(key); + try { + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keys); + return keyFactory.generatePrivate(keySpec); + } catch (InvalidKeySpecException e) { + throw new IllegalStateException("私钥⽆效:", e); + } + } + /** * 生成待签名字符串(参数按字母排序 + appkey + timestamp) * @param params 请求参数Map(需提前过滤空值) diff --git a/coolstore-partner-model/src/main/java/com/cool/store/dto/FoodTokenDTO.java b/coolstore-partner-model/src/main/java/com/cool/store/dto/FoodTokenDTO.java new file mode 100644 index 000000000..8097cb624 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/dto/FoodTokenDTO.java @@ -0,0 +1,14 @@ +package com.cool.store.dto; + +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/5/13 17:02 + * @Version 1.0 + */ +@Data +public class FoodTokenDTO { + + private String mobile ; +} diff --git a/coolstore-partner-service/src/main/java/com/cool/store/service/ThirdFoodService.java b/coolstore-partner-service/src/main/java/com/cool/store/service/ThirdFoodService.java new file mode 100644 index 000000000..c16a436e1 --- /dev/null +++ b/coolstore-partner-service/src/main/java/com/cool/store/service/ThirdFoodService.java @@ -0,0 +1,22 @@ +package com.cool.store.service; + +import com.cool.store.dto.FoodTokenDTO; +import com.cool.store.dto.GetAccessTokenDTO; + +/** + * 正新菜品市场api对接-埃林哲-对接人徐哲 + * @Author suzhuhong + * @Date 2025/5/13 15:20 + * @Version 1.0 + */ +public interface ThirdFoodService { + + + /** + * 获取正新菜品市场token + * @param dto + * @return + */ + String getFoodToken(FoodTokenDTO dto); + +} diff --git a/coolstore-partner-service/src/main/java/com/cool/store/service/impl/ThirdFoodServiceImpl.java b/coolstore-partner-service/src/main/java/com/cool/store/service/impl/ThirdFoodServiceImpl.java new file mode 100644 index 000000000..e812a1ea6 --- /dev/null +++ b/coolstore-partner-service/src/main/java/com/cool/store/service/impl/ThirdFoodServiceImpl.java @@ -0,0 +1,148 @@ +package com.cool.store.service.impl; + +import cn.hutool.core.util.RandomUtil; +import com.alibaba.fastjson.JSONObject; +import com.cool.store.dto.FoodTokenDTO; +import com.cool.store.dto.GetAccessTokenDTO; +import com.cool.store.enums.ErrorCodeEnum; +import com.cool.store.exception.ServiceException; +import com.cool.store.response.oppty.OpportunityApiResponse; +import com.cool.store.service.ThirdFoodService; +import com.cool.store.utils.SignatureUtils; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.net.URI; +import java.util.Map; + +/** + * @Author suzhuhong + * @Date 2025/5/13 15:21 + * @Version 1.0 + */ +@Service +@Slf4j +public class ThirdFoodServiceImpl implements ThirdFoodService { + + @Value("${zx.food.url}") + private String apiUrl; + + + @Resource + OkHttpClient okHttpClient; + @Resource + ObjectMapper objectMapper; + + + @Override + public String getFoodToken(FoodTokenDTO dto) { + // 1. 发送POST请求 + String url = apiUrl + "/interface/v1/user/getToken"; + return executeApiCall(url, dto, String.class); + } + + + private T executeApiCall(String url, Object requestBody, Class responseType) { + // 1. 打印请求前日志 + //logRequest(url, requestBody); + + try { + Request request = buildRequest(requestBody, url); + + try (Response response = okHttpClient.newCall(request).execute()) { + // 2. 获取原始响应内容 + String responseBody = response.body().string(); + + // 3. 打印响应日志 + logResponse(url, response.code(), responseBody); + + if (!response.isSuccessful()) { + throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, + "HTTP请求失败,状态码: " + response.code()); + } + + // 4. 解析响应 + JavaType javaType = objectMapper.getTypeFactory() + .constructParametricType(OpportunityApiResponse.class, responseType); + + OpportunityApiResponse apiResponse = objectMapper.readValue(responseBody, javaType); + + if (apiResponse.getCode() != 200) { + throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, apiResponse.getMsg()); + } + return apiResponse.getData(); + } + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + log.error("API调用异常 - URL: {}, 错误: {}", url, e.getMessage(), e); + throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, "接口调用异常: " + e.getMessage()); + } + } + + + private Request buildRequest(Object requestBody, String url) { + String timestamp = String.valueOf(System.currentTimeMillis()); + String random = RandomUtil.randomString(20); + + String signString = null; + try { + signString = SignatureUtils.sign("POST","/v1/user/getToken", objectMapper.writeValueAsString(requestBody),timestamp,random); + } catch (Exception e) { + throw new ServiceException(ErrorCodeEnum.THIRD_API_SIGN_ERROR,"加密失败"); + } + + log.info("签名生成 - 签名结果: {}", signString); + + RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json"), + JSONObject.toJSONString(requestBody) + ); + + return new Request.Builder() + .url(url) + .post(body) + .addHeader("X-ZhengXin-Sign", signString) + .addHeader("X-ZhengXin-SignTime", timestamp) + .addHeader("X-ZhengXin-SignRandom", random) + .addHeader("X-ZhengXin-appId", "CRM") + .build(); + } + + private void logResponse(String url, int statusCode, String responseBody) { + if (log.isInfoEnabled()) { + try { + // 尝试美化JSON输出 + Object json = objectMapper.readValue(responseBody, Object.class); + String prettyResponse = objectMapper.writerWithDefaultPrettyPrinter() + .writeValueAsString(json); + + log.info("\n======= 响应开始 =======\n" + + "API地址: {}\n" + + "HTTP状态码: {}\n" + + "响应内容: {}\n" + + "======= 响应结束 =======", + url, + statusCode, + prettyResponse); + } catch (Exception e) { + // 非JSON响应或解析失败时直接输出原始内容 + log.info("\n======= 响应开始 =======\n" + + "API地址: {}\n" + + "HTTP状态码: {}\n" + + "原始响应: {}\n" + + "======= 响应结束 =======", + url, + statusCode, + responseBody); + } + } + } +} diff --git a/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCTestController.java b/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCTestController.java index 7b47e3e7d..8778e7a33 100644 --- a/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCTestController.java +++ b/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCTestController.java @@ -1,6 +1,7 @@ package com.cool.store.controller.webb; import com.cool.store.dao.*; +import com.cool.store.dto.FoodTokenDTO; import com.cool.store.dto.GetAccessTokenDTO; import com.cool.store.dto.ModifyPasswordDTO; import com.cool.store.entity.*; @@ -295,6 +296,12 @@ public class PCTestController { public ResponseResult> getAuthRegionIdAndSubRegionIdByUserId(@RequestParam(value = "userId", required = true) String userId) { return ResponseResult.success(userAuthMappingService.getAuthRegionIdAndSubRegionIdByUserId(userId)); } - + @Resource + ThirdFoodService thirdFoodService; + @ApiOperation("获取菜品市场token") + @PostMapping("/getFoodToken") + public ResponseResult getFoodToken(@RequestBody @Validated FoodTokenDTO dto) { + return ResponseResult.success(thirdFoodService.getFoodToken(dto)); + } } diff --git a/coolstore-partner-web/src/main/java/com/cool/store/controller/webc/MiniShopAccountController.java b/coolstore-partner-web/src/main/java/com/cool/store/controller/webc/MiniShopAccountController.java index 0811548aa..c2a3def10 100644 --- a/coolstore-partner-web/src/main/java/com/cool/store/controller/webc/MiniShopAccountController.java +++ b/coolstore-partner-web/src/main/java/com/cool/store/controller/webc/MiniShopAccountController.java @@ -1,5 +1,6 @@ package com.cool.store.controller.webc; +import com.cool.store.dto.FoodTokenDTO; import com.cool.store.dto.GetAccessTokenDTO; import com.cool.store.dto.ModifyPasswordDTO; import com.cool.store.dto.ShopAccount.ShopAccountDTO; @@ -7,6 +8,7 @@ import com.cool.store.request.GetPasswordDTO; import com.cool.store.response.ResponseResult; import com.cool.store.service.PushService; import com.cool.store.service.ShopAccountService; +import com.cool.store.service.ThirdFoodService; import com.cool.store.service.ThirdXinGuanJiaService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -36,6 +38,9 @@ public class MiniShopAccountController { @Resource ThirdXinGuanJiaService thirdXinGuanJiaService; + @Resource + ThirdFoodService thirdFoodService; + @ApiOperation("根据门店shopId查询平台账号") @GetMapping("/getShopAccountByShopId") @@ -68,6 +73,12 @@ public class MiniShopAccountController { return ResponseResult.success(pushService.getXzgToken(dto)); } + @ApiOperation("获取菜品市场token") + @PostMapping("/getFoodToken") + public ResponseResult getFoodToken(@RequestBody @Validated FoodTokenDTO dto) { + return ResponseResult.success(thirdFoodService.getFoodToken(dto)); + } + @ApiOperation("获取加密子串") @PostMapping("/getEncryptedSubstring") public ResponseResult getEncryptedSubstring(@RequestBody GetPasswordDTO dto) { diff --git a/coolstore-partner-web/src/main/resources/application-online.properties b/coolstore-partner-web/src/main/resources/application-online.properties index 93a39ac1b..2df7c5b1b 100644 --- a/coolstore-partner-web/src/main/resources/application-online.properties +++ b/coolstore-partner-web/src/main/resources/application-online.properties @@ -118,6 +118,8 @@ zx.big.data.appSecret=35b8b9a400b4430fa022190be0913cd6 xzg.api.auth.url=http://webapi.zhengxinfood.com +zx.food.url=https://datacenter.zhengxinshipin.com + cool.api.appKey=k8J7fG2qR5tY9vX3 cool.api.secret=wP4sN6dL8zK2xM9c diff --git a/coolstore-partner-web/src/main/resources/application-test.properties b/coolstore-partner-web/src/main/resources/application-test.properties index 7636ac99c..ef4d1eb1e 100644 --- a/coolstore-partner-web/src/main/resources/application-test.properties +++ b/coolstore-partner-web/src/main/resources/application-test.properties @@ -121,6 +121,8 @@ yls.api.auth.secret=3b56198f096d4009072c927c96fbc8b6 #新掌柜账号 xzg.api.auth.url=http://webapi.zhengxinfood.com +zx.food.url=https://datacenter.zhengxinfood.com + cool.api.appKey=k8J7fG2qR5tY9vX3 cool.api.secret=wP4sN6dL8zK2xM9c