feat:外部接口接入
This commit is contained in:
@@ -6,7 +6,11 @@ import javax.crypto.Mac;
|
|||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,6 +21,11 @@ import java.util.*;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class SignatureUtils {
|
public class SignatureUtils {
|
||||||
|
|
||||||
|
private static final String HMAC_ALGORITHM = "HmacSHA256";
|
||||||
|
private static final DateTimeFormatter RFC_1123_FORMATTER =
|
||||||
|
DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH)
|
||||||
|
.withZone(ZoneId.of("GMT"));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成 HmacSHA256 签名
|
* 生成 HmacSHA256 签名
|
||||||
* @param data 待签名字符串
|
* @param data 待签名字符串
|
||||||
@@ -25,8 +34,8 @@ public class SignatureUtils {
|
|||||||
*/
|
*/
|
||||||
public static String hmacSha256(String data, String secret) {
|
public static String hmacSha256(String data, String secret) {
|
||||||
try {
|
try {
|
||||||
Mac mac = Mac.getInstance("HmacSHA256");
|
Mac mac = Mac.getInstance(HMAC_ALGORITHM);
|
||||||
SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
|
SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), HMAC_ALGORITHM);
|
||||||
mac.init(secretKey);
|
mac.init(secretKey);
|
||||||
byte[] hash = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
|
byte[] hash = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
|
||||||
return bytesToHex(hash).toLowerCase();
|
return bytesToHex(hash).toLowerCase();
|
||||||
@@ -35,6 +44,57 @@ public class SignatureUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成与Postman一致的签名头
|
||||||
|
* @param username API用户名
|
||||||
|
* @param secret API密钥
|
||||||
|
* @param requestBody 请求体JSON字符串
|
||||||
|
* @return 包含所有签名头的Map
|
||||||
|
*/
|
||||||
|
public static Map<String, String> generateHeaders(String username,
|
||||||
|
String secret,
|
||||||
|
String requestBody) {
|
||||||
|
// 1. 生成RFC 1123格式时间戳
|
||||||
|
String date = ZonedDateTime.now(ZoneId.of("GMT")).format(RFC_1123_FORMATTER);
|
||||||
|
|
||||||
|
|
||||||
|
// 2. 计算请求体摘要
|
||||||
|
String digest = "SHA-256=" + calculateDigest(requestBody);
|
||||||
|
|
||||||
|
// 3. 构建签名字符串
|
||||||
|
String signingString = "x-date: " + date + "\n" + "digest: " + digest;
|
||||||
|
|
||||||
|
// 4. 计算HMAC签名
|
||||||
|
String signature = hmacSha256( signingString,secret);
|
||||||
|
|
||||||
|
// 5. 组装Authorization头
|
||||||
|
String authorization = String.format(
|
||||||
|
"hmac username=\"%s\", algorithm=\"hmac-sha256\", headers=\"x-date digest\", signature=\"%s\"",
|
||||||
|
username,
|
||||||
|
signature
|
||||||
|
);
|
||||||
|
|
||||||
|
// 6. 返回所有头信息
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("x-date", date);
|
||||||
|
headers.put("Digest", digest);
|
||||||
|
headers.put("Authorization", authorization);
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String calculateDigest(String data) {
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||||
|
byte[] hash = md.digest(data.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return Base64.getEncoder().encodeToString(hash);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Digest计算失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成待签名字符串(参数按字母排序 + appkey + timestamp)
|
* 生成待签名字符串(参数按字母排序 + appkey + timestamp)
|
||||||
* @param params 请求参数Map(需提前过滤空值)
|
* @param params 请求参数Map(需提前过滤空值)
|
||||||
@@ -70,4 +130,7 @@ public class SignatureUtils {
|
|||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.cool.store.request.huoma;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author suzhuhong
|
||||||
|
* @Date 2025/4/1 23:15
|
||||||
|
* @Version 1.0
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ShopBasicInfoRequest {
|
||||||
|
|
||||||
|
private String shop_sn;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.cool.store.response.huoma;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author suzhuhong
|
||||||
|
* @Date 2025/4/1 23:19
|
||||||
|
* @Version 1.0
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ShopBaseInfoResponse {
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
private String name;
|
||||||
|
private String sn;
|
||||||
|
private String address;
|
||||||
|
private String province;
|
||||||
|
private String city;
|
||||||
|
private String region;
|
||||||
|
private String lnglat;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.cool.store.service;
|
||||||
|
|
||||||
|
import com.cool.store.request.huoma.ShopBasicInfoRequest;
|
||||||
|
import com.cool.store.response.huoma.ShopBaseInfoResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正新-火码api
|
||||||
|
* @Author suzhuhong
|
||||||
|
* @Date 2025/4/1 23:14
|
||||||
|
* @Version 1.0
|
||||||
|
*/
|
||||||
|
public interface HuoMaService {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getShopInfo
|
||||||
|
* @param requestBody
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
ShopBaseInfoResponse getShopInfo(ShopBasicInfoRequest requestBody);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package com.cool.store.service.impl;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.cool.store.request.huoma.ShopBasicInfoRequest;
|
||||||
|
import com.cool.store.response.bigdata.ApiResponse;
|
||||||
|
import com.cool.store.response.huoma.ShopBaseInfoResponse;
|
||||||
|
import com.cool.store.response.oppty.OpportunityApiResponse;
|
||||||
|
import com.cool.store.service.HuoMaService;
|
||||||
|
import com.cool.store.utils.SignatureUtils;
|
||||||
|
import com.fasterxml.jackson.databind.JavaType;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import okhttp3.*;
|
||||||
|
import org.apache.poi.ss.formula.functions.T;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author suzhuhong
|
||||||
|
* @Date 2025/4/1 23:20
|
||||||
|
* @Version 1.0
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class HuoMaServiceImpl implements HuoMaService {
|
||||||
|
|
||||||
|
@Value("${api.auth.url}")
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
@Value("${api.auth.username}")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@Value("${api.auth.secret}")
|
||||||
|
private String secret;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
OkHttpClient okHttpClient;
|
||||||
|
@Resource
|
||||||
|
ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ShopBaseInfoResponse getShopInfo(ShopBasicInfoRequest requestBody) {
|
||||||
|
String apiUrl = url + "/dzgV1/shop/getShopBasicInfo";
|
||||||
|
|
||||||
|
Map<String, String> authHeaders = SignatureUtils.generateHeaders(
|
||||||
|
username, secret, JSONObject.toJSONString(requestBody)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. 构建请求体
|
||||||
|
RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json"),
|
||||||
|
JSONObject.toJSONString(requestBody));
|
||||||
|
|
||||||
|
// 3. 构建请求
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(apiUrl)
|
||||||
|
.post(body)
|
||||||
|
.addHeader("Content-Type", "application/json")
|
||||||
|
.addHeader("x-Date", authHeaders.get("x-date"))
|
||||||
|
.addHeader("Digest", authHeaders.get("Digest"))
|
||||||
|
.addHeader("Authorization", authHeaders.get("Authorization"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 4. 执行请求
|
||||||
|
try (Response response = okHttpClient.newCall(request).execute()) {
|
||||||
|
|
||||||
|
String responseBody = response.body().string();
|
||||||
|
|
||||||
|
JavaType javaType = objectMapper.getTypeFactory()
|
||||||
|
.constructParametricType(ApiResponse.class, ShopBaseInfoResponse.class);
|
||||||
|
|
||||||
|
ApiResponse<ShopBaseInfoResponse> apiResponse = objectMapper.readValue(responseBody, javaType);
|
||||||
|
|
||||||
|
if (!response.isSuccessful()) {
|
||||||
|
throw new IOException("请求失败: " + response.code());
|
||||||
|
}
|
||||||
|
return apiResponse.getData();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.cool.store.controller.webb;
|
package com.cool.store.controller.webb;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.cool.store.constants.CommonConstants;
|
import com.cool.store.constants.CommonConstants;
|
||||||
import com.cool.store.dao.*;
|
import com.cool.store.dao.*;
|
||||||
import com.cool.store.entity.*;
|
import com.cool.store.entity.*;
|
||||||
@@ -8,16 +9,31 @@ import com.cool.store.enums.QWMessageEnum;
|
|||||||
import com.cool.store.enums.SMSMsgEnum;
|
import com.cool.store.enums.SMSMsgEnum;
|
||||||
import com.cool.store.job.XxlJobHandler;
|
import com.cool.store.job.XxlJobHandler;
|
||||||
import com.cool.store.mq.util.HttpRestTemplateService;
|
import com.cool.store.mq.util.HttpRestTemplateService;
|
||||||
|
import com.cool.store.request.bigdata.ProfitDataRequest;
|
||||||
|
import com.cool.store.request.huoma.ShopBasicInfoRequest;
|
||||||
|
import com.cool.store.request.oppty.*;
|
||||||
import com.cool.store.request.xfsgFirstOrderListRequest;
|
import com.cool.store.request.xfsgFirstOrderListRequest;
|
||||||
import com.cool.store.response.ResponseResult;
|
import com.cool.store.response.ResponseResult;
|
||||||
|
import com.cool.store.response.bigdata.ActDataResponse;
|
||||||
|
import com.cool.store.response.bigdata.ProfitDataResponse;
|
||||||
|
import com.cool.store.response.bigdata.ProfitRateResponse;
|
||||||
|
import com.cool.store.response.huoma.ShopBaseInfoResponse;
|
||||||
|
import com.cool.store.response.oppty.CityResponse;
|
||||||
|
import com.cool.store.response.oppty.OpportunityDetailResponse;
|
||||||
|
import com.cool.store.response.oppty.OpportunityInfoPageResponse;
|
||||||
|
import com.cool.store.response.oppty.OpportunityInfoResponse;
|
||||||
import com.cool.store.response.xfsgFirstOderListResponse;
|
import com.cool.store.response.xfsgFirstOderListResponse;
|
||||||
import com.cool.store.service.DataHandleService;
|
import com.cool.store.service.*;
|
||||||
import com.cool.store.service.ShopService;
|
|
||||||
import com.cool.store.service.impl.CommonService;
|
import com.cool.store.service.impl.CommonService;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import io.swagger.annotations.ApiResponse;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import javax.validation.Valid;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -119,4 +135,104 @@ public class PCTestController {
|
|||||||
|
|
||||||
return ResponseResult.success(Boolean.TRUE);
|
return ResponseResult.success(Boolean.TRUE);
|
||||||
}
|
}
|
||||||
|
@Resource
|
||||||
|
ThirdOpportunityService thirdOpportunityService;
|
||||||
|
@Resource
|
||||||
|
ThirdBigDataService thirdBigDataService;
|
||||||
|
@Resource
|
||||||
|
HuoMaService huoMaService;
|
||||||
|
|
||||||
|
@PostMapping("/list")
|
||||||
|
@ApiOperation("机会点列表/我的机会点")
|
||||||
|
public ResponseResult<OpportunityInfoPageResponse> listOpportunities(@Valid @RequestBody OpportunityListRequest request) {
|
||||||
|
OpportunityInfoPageResponse list = thirdOpportunityService.listOpportunities(request);
|
||||||
|
return ResponseResult.success(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/getOpportunitiesDetail")
|
||||||
|
@ApiOperation("机会点详情")
|
||||||
|
public ResponseResult<OpportunityDetailResponse> getOpportunitiesDetail(@Valid @RequestBody OpportunityDetailRequest request) {
|
||||||
|
OpportunityDetailResponse list = thirdOpportunityService.getOpportunitiesDetail(request);
|
||||||
|
return ResponseResult.success(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/followOpportunity")
|
||||||
|
@ApiOperation("关注/取消关注 机会点")
|
||||||
|
public ResponseResult<String> followOpportunity(@Valid @RequestBody FollowRequest request) {
|
||||||
|
return ResponseResult.success(thirdOpportunityService.followOpportunity(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/inspectionOpportunity")
|
||||||
|
@ApiOperation("考察机会点")
|
||||||
|
public ResponseResult<String> inspectionOpportunity(@Valid @RequestBody InspectionRequest request) {
|
||||||
|
return ResponseResult.success(thirdOpportunityService.inspectionOpportunity(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/berthOperation")
|
||||||
|
@ApiOperation("铺位关注机会点")
|
||||||
|
public ResponseResult<String> berthOperation(@Valid @RequestBody BerthOperationRequest request) {
|
||||||
|
return ResponseResult.success(thirdOpportunityService.berthOperation(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/storeOperation")
|
||||||
|
@ApiOperation("门店关注机会点")
|
||||||
|
public ResponseResult<String> storeOperation(@Valid @RequestBody StoreOperationRequest request) {
|
||||||
|
return ResponseResult.success(thirdOpportunityService.storeOperation(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/cityList")
|
||||||
|
@ApiOperation("省市区列表 查询省传0")
|
||||||
|
public ResponseResult<List<CityResponse>> listOpportunities(@Valid @RequestBody CityRequest request) {
|
||||||
|
// 调用第三方接口
|
||||||
|
List<CityResponse> list = thirdOpportunityService.cityList(request);
|
||||||
|
return ResponseResult.success(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/getProfitData")
|
||||||
|
@ApiOperation("昨天毛利")
|
||||||
|
public ResponseResult<ProfitDataResponse> listOpportunities(@Valid @RequestBody ProfitDataRequest request) {
|
||||||
|
// 调用第三方接口
|
||||||
|
return ResponseResult.success(thirdBigDataService.getProfitData(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/getCurMonthProfitData")
|
||||||
|
@ApiOperation("本月毛利")
|
||||||
|
public ResponseResult<ProfitDataResponse> getCurMonthProfitData(@Valid @RequestBody ProfitDataRequest request) {
|
||||||
|
// 调用第三方接口
|
||||||
|
return ResponseResult.success(thirdBigDataService.getCurMonthProfitData(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/getPreMonthProfitData")
|
||||||
|
@ApiOperation("上月毛利")
|
||||||
|
public ResponseResult<ProfitDataResponse> getPreMonthProfitData(@Valid @RequestBody ProfitDataRequest request) {
|
||||||
|
// 调用第三方接口
|
||||||
|
return ResponseResult.success(thirdBigDataService.getPreMonthProfitData(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/getMonthProfitTrend")
|
||||||
|
@ApiOperation("毛利趋势")
|
||||||
|
public ResponseResult<List<ProfitRateResponse>> getMonthProfitTrend(@Valid @RequestBody ProfitDataRequest request) {
|
||||||
|
// 调用第三方接口
|
||||||
|
return ResponseResult.success(thirdBigDataService.getMonthProfitTrend(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/getActData")
|
||||||
|
@ApiOperation("门店实收")
|
||||||
|
public ResponseResult<ActDataResponse> getActData(@Valid @RequestBody ProfitDataRequest request) {
|
||||||
|
// 调用第三方接口
|
||||||
|
return ResponseResult.success(thirdBigDataService.getActData(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/getShopInfo")
|
||||||
|
@ApiOperation("火码 查询门店信息")
|
||||||
|
public ResponseResult<ShopBaseInfoResponse> getShopInfo(@Valid @RequestBody ShopBasicInfoRequest request) {
|
||||||
|
// 调用第三方接口
|
||||||
|
return ResponseResult.success(huoMaService.getShopInfo(request));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ enterprise.dingCorpId=wpayJeDAAAhGIFgUJpJN-zg39JuNbYhg
|
|||||||
##qywx.task.notice.url1=https://tstore-api.coolstore.cn/notice?corpId=%s&appType=%s&target=%s
|
##qywx.task.notice.url1=https://tstore-api.coolstore.cn/notice?corpId=%s&appType=%s&target=%s
|
||||||
qywx.task.notice.url2=https://tstore-h5.coolstore.cn/?corpId=%s&appType=%s#/notice?target=%s¬iceType=zx&corpId=%s&appType=%s&eid=%s
|
qywx.task.notice.url2=https://tstore-h5.coolstore.cn/?corpId=%s&appType=%s#/notice?target=%s¬iceType=zx&corpId=%s&appType=%s&eid=%s
|
||||||
|
|
||||||
|
#机会点
|
||||||
third.party.appKey=IGSAEQoakR2HEaYx
|
third.party.appKey=IGSAEQoakR2HEaYx
|
||||||
third.party.appSecret=aPsA99K1obFeFm3m
|
third.party.appSecret=aPsA99K1obFeFm3m
|
||||||
zx.opportunity.url=https://snp.wenmatech.com/
|
zx.opportunity.url=https://snp.wenmatech.com/
|
||||||
@@ -92,3 +92,7 @@ zx.opportunity.url=https://snp.wenmatech.com/
|
|||||||
zx.big.data.url=https://ds.zhengxinfood.com/
|
zx.big.data.url=https://ds.zhengxinfood.com/
|
||||||
zx.big.data.appKey=ff203b5567744feaaae49fb86f58c5bf
|
zx.big.data.appKey=ff203b5567744feaaae49fb86f58c5bf
|
||||||
zx.big.data.appSecret=35b8b9a400b4430fa022190be0913cd6
|
zx.big.data.appSecret=35b8b9a400b4430fa022190be0913cd6
|
||||||
|
|
||||||
|
api.auth.url=https://api.zhengxindzg.cn
|
||||||
|
api.auth.username=GkqgAhUJ7p9swJo
|
||||||
|
api.auth.secret=NzVrnS3OWiupdDY
|
||||||
|
|||||||
Reference in New Issue
Block a user