feat:外部接口接入

This commit is contained in:
苏竹红
2025-04-02 09:16:43 +08:00
parent a54644b8bd
commit d4cb86b415
7 changed files with 333 additions and 5 deletions

View File

@@ -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();
} }
} }

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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));
}
} }

View File

@@ -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&noticeType=zx&corpId=%s&appType=%s&eid=%s qywx.task.notice.url2=https://tstore-h5.coolstore.cn/?corpId=%s&appType=%s#/notice?target=%s&noticeType=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