From a54644b8bdfdb62060bee60f66eac82c1c5cbd70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E7=AB=B9=E7=BA=A2?= Date: Tue, 1 Apr 2025 21:36:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=A4=96=E9=83=A8=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E6=8E=A5=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cool/store/enums/ErrorCodeEnum.java | 4 +- .../com/cool/store/utils/CoolDateUtils.java | 31 +++ .../com/cool/store/utils/GeoHashUtils.java | 44 ++++ .../java/com/cool/store/utils/JsonUtils.java | 24 ++ .../com/cool/store/utils/SignatureUtils.java | 73 ++++++ .../request/bigdata/ProfitDataRequest.java | 16 ++ .../request/oppty/BerthOperationRequest.java | 31 +++ .../cool/store/request/oppty/CityRequest.java | 19 ++ .../store/request/oppty/FollowRequest.java | 23 ++ .../request/oppty/InspectionRequest.java | 20 ++ .../request/oppty/OpportunityBaseRequest.java | 27 +++ .../oppty/OpportunityDetailRequest.java | 15 ++ .../request/oppty/OpportunityListRequest.java | 108 +++++++++ .../request/oppty/StoreOperationRequest.java | 57 +++++ .../cool/store/request/oppty/UserRequest.java | 22 ++ .../response/bigdata/ActDataResponse.java | 19 ++ .../store/response/bigdata/ApiResponse.java | 15 ++ .../response/bigdata/ProfitDataResponse.java | 16 ++ .../response/bigdata/ProfitRateResponse.java | 16 ++ .../response/oppty/BerthInfoResponse.java | 31 +++ .../store/response/oppty/BrandResponse.java | 34 +++ .../store/response/oppty/CityResponse.java | 20 ++ .../oppty/OpportunityApiResponse.java | 18 ++ .../oppty/OpportunityDetailResponse.java | 33 +++ .../oppty/OpportunityInfoPageResponse.java | 24 ++ .../oppty/OpportunityInfoResponse.java | 26 +++ .../store/response/oppty/PageResponse.java | 22 ++ .../response/oppty/StoreInfoResponse.java | 42 ++++ .../store/response/oppty/UserResponse.java | 22 ++ .../cool/store/config/rest/JacksonConfig.java | 20 ++ .../cool/store/config/rest/OkHttpConfig.java | 110 +++++++++ .../store/service/ThirdBigDataService.java | 57 +++++ .../service/ThirdOpportunityService.java | 70 ++++++ .../service/impl/ThirdBigDataServiceImpl.java | 209 +++++++++++++++++ .../impl/ThirdOpportunityServiceImpl.java | 217 ++++++++++++++++++ .../controller/webc/ThirdApiController.java | 124 ++++++++++ .../resources/application-test.properties | 12 +- 37 files changed, 1669 insertions(+), 2 deletions(-) create mode 100644 coolstore-partner-common/src/main/java/com/cool/store/utils/GeoHashUtils.java create mode 100644 coolstore-partner-common/src/main/java/com/cool/store/utils/JsonUtils.java create mode 100644 coolstore-partner-common/src/main/java/com/cool/store/utils/SignatureUtils.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/request/bigdata/ProfitDataRequest.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/request/oppty/BerthOperationRequest.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/request/oppty/CityRequest.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/request/oppty/FollowRequest.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/request/oppty/InspectionRequest.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/request/oppty/OpportunityBaseRequest.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/request/oppty/OpportunityDetailRequest.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/request/oppty/OpportunityListRequest.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/request/oppty/StoreOperationRequest.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/request/oppty/UserRequest.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/response/bigdata/ActDataResponse.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/response/bigdata/ApiResponse.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/response/bigdata/ProfitDataResponse.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/response/bigdata/ProfitRateResponse.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/response/oppty/BerthInfoResponse.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/response/oppty/BrandResponse.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/response/oppty/CityResponse.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/response/oppty/OpportunityApiResponse.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/response/oppty/OpportunityDetailResponse.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/response/oppty/OpportunityInfoPageResponse.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/response/oppty/OpportunityInfoResponse.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/response/oppty/PageResponse.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/response/oppty/StoreInfoResponse.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/response/oppty/UserResponse.java create mode 100644 coolstore-partner-service/src/main/java/com/cool/store/config/rest/JacksonConfig.java create mode 100644 coolstore-partner-service/src/main/java/com/cool/store/config/rest/OkHttpConfig.java create mode 100644 coolstore-partner-service/src/main/java/com/cool/store/service/ThirdBigDataService.java create mode 100644 coolstore-partner-service/src/main/java/com/cool/store/service/ThirdOpportunityService.java create mode 100644 coolstore-partner-service/src/main/java/com/cool/store/service/impl/ThirdBigDataServiceImpl.java create mode 100644 coolstore-partner-service/src/main/java/com/cool/store/service/impl/ThirdOpportunityServiceImpl.java create mode 100644 coolstore-partner-web/src/main/java/com/cool/store/controller/webc/ThirdApiController.java diff --git a/coolstore-partner-common/src/main/java/com/cool/store/enums/ErrorCodeEnum.java b/coolstore-partner-common/src/main/java/com/cool/store/enums/ErrorCodeEnum.java index 7722a1750..7c8790b24 100644 --- a/coolstore-partner-common/src/main/java/com/cool/store/enums/ErrorCodeEnum.java +++ b/coolstore-partner-common/src/main/java/com/cool/store/enums/ErrorCodeEnum.java @@ -236,7 +236,9 @@ public enum ErrorCodeEnum { UPDATE_INVESTMENT_MANAGER_FAIL(131005,"当前用户已经为该门店招商经理",null), CONFIRM_THE_APPROVER(131006,"您提交的铺位暂时找不到选址审批人,请联系系统管理员配置选址审批权限后再提交铺位审批",null), - TALLY_BOOK_NOT_EXIST(180001, "记账本数据不存在", null) + TALLY_BOOK_NOT_EXIST(180001, "记账本数据不存在", null), + + THIRD_API_ERROR(151001,"第三方服务异常->{0}",null), ; diff --git a/coolstore-partner-common/src/main/java/com/cool/store/utils/CoolDateUtils.java b/coolstore-partner-common/src/main/java/com/cool/store/utils/CoolDateUtils.java index bb1e2a4dd..88060a25c 100644 --- a/coolstore-partner-common/src/main/java/com/cool/store/utils/CoolDateUtils.java +++ b/coolstore-partner-common/src/main/java/com/cool/store/utils/CoolDateUtils.java @@ -9,6 +9,8 @@ import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAdjusters; import java.util.Calendar; import java.util.Date; @@ -32,6 +34,13 @@ public class CoolDateUtils { public static final String DATE_FORMAT_SEC_5 = "yyyy.MM.dd HH:mm"; public static final String DATE_FORMAT_SEC_6 = "yyyy.MM.dd"; public static final String DATE_FORMAT_SEC_7 = "yyyy/MM/dd HH:mm"; + + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + private static final DateTimeFormatter getDateFormatter(String format){ + return DateTimeFormatter.ofPattern(format); + } + /** * 几天后的当前 * @param d @@ -128,4 +137,26 @@ public class CoolDateUtils { } + /** + * 获取当前日期字符串 (yyyy-MM-dd) + */ + public static String getCurrentDate() { + return LocalDate.now().format(getDateFormatter(DATE_FORMAT_DAY)); + } + + /** + * 获取上月同一天的日期字符串 (yyyy-MM-dd) + * 如果上个月没有同一天(如当前是3月31日,但2月没有31日),则返回上个月的最后一天 + */ + public static String getSameDayLastMonth() { + LocalDate today = LocalDate.now(); + LocalDate lastMonthSameDay = today.minusMonths(1); + + // 处理跨月情况(如3月31日,2月没有31日) + if (lastMonthSameDay.getDayOfMonth() != today.getDayOfMonth()) { + lastMonthSameDay = lastMonthSameDay.with(TemporalAdjusters.lastDayOfMonth()); + } + + return lastMonthSameDay.format(getDateFormatter(DATE_FORMAT_DAY)); + } } diff --git a/coolstore-partner-common/src/main/java/com/cool/store/utils/GeoHashUtils.java b/coolstore-partner-common/src/main/java/com/cool/store/utils/GeoHashUtils.java new file mode 100644 index 000000000..dbdb3ac03 --- /dev/null +++ b/coolstore-partner-common/src/main/java/com/cool/store/utils/GeoHashUtils.java @@ -0,0 +1,44 @@ +package com.cool.store.utils; +import java.util.BitSet; +/** + * @Author suzhuhong + * @Date 2025/4/1 20:39 + * @Version 1.0 + */ + +public class GeoHashUtils { + private static final String BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz"; + private static final int[] BITS = {16, 8, 4, 2, 1}; + + public static String encode(double lat, double lon, int precision) { + BitSet latBits = getBits(lat, -90, 90); + BitSet lonBits = getBits(lon, -180, 180); + + StringBuilder hash = new StringBuilder(); + for (int i = 0; i < precision; i++) { + int index = 0; + for (int j = 0; j < 5; j++) { + boolean lonBit = lonBits.get(i * 5 + j); + boolean latBit = latBits.get(i * 5 + j); + index = (index << 1) | (lonBit ? 1 : 0); + index = (index << 1) | (latBit ? 1 : 0); + } + hash.append(BASE32.charAt(index)); + } + return hash.toString(); + } + + private static BitSet getBits(double val, double min, double max) { + BitSet bits = new BitSet(); + for (int i = 0; i < 30; i++) { + double mid = (min + max) / 2; + if (val >= mid) { + bits.set(i); + min = mid; + } else { + max = mid; + } + } + return bits; + } +} \ No newline at end of file diff --git a/coolstore-partner-common/src/main/java/com/cool/store/utils/JsonUtils.java b/coolstore-partner-common/src/main/java/com/cool/store/utils/JsonUtils.java new file mode 100644 index 000000000..bc9e6e42d --- /dev/null +++ b/coolstore-partner-common/src/main/java/com/cool/store/utils/JsonUtils.java @@ -0,0 +1,24 @@ +package com.cool.store.utils; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import java.util.Map; + +/** + * @Author suzhuhong + * @Date 2025/3/26 19:39 + * @Version 1.0 + */ + +public class JsonUtils { + private static final Gson gson = new Gson(); + + public static String toJson(Object obj) { + return gson.toJson(obj); + } + + public static Map parseJsonToMap(String json) { + return gson.fromJson(json, new TypeToken>() {}.getType()); + } +} 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 new file mode 100644 index 000000000..07f69a9dd --- /dev/null +++ b/coolstore-partner-common/src/main/java/com/cool/store/utils/SignatureUtils.java @@ -0,0 +1,73 @@ +package com.cool.store.utils; + +import lombok.extern.slf4j.Slf4j; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.*; + +/** + * @Author suzhuhong + * @Date 2025/3/26 19:32 + * @Version 1.0 + */ +@Slf4j +public class SignatureUtils { + + /** + * 生成 HmacSHA256 签名 + * @param data 待签名字符串 + * @param secret 密钥 + * @return 签名(Hex小写) + */ + public static String hmacSha256(String data, String secret) { + try { + Mac mac = Mac.getInstance("HmacSHA256"); + SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); + mac.init(secretKey); + byte[] hash = mac.doFinal(data.getBytes(StandardCharsets.UTF_8)); + return bytesToHex(hash).toLowerCase(); + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + throw new RuntimeException("HmacSHA256签名失败", e); + } + } + + /** + * 生成待签名字符串(参数按字母排序 + appkey + timestamp) + * @param params 请求参数Map(需提前过滤空值) + * @param appKey 应用Key + * @param timestamp 时间戳(毫秒) + */ + public static String buildSignString(Map params, String appKey, long timestamp) { + TreeMap sortedParams = new TreeMap<>(params); + StringBuilder sb = new StringBuilder(); + + // 拼接排序后的参数 + sortedParams.forEach((key, value) ->{ + if (Objects.isNull(value)||(value instanceof Double &&((Double) value).intValue()==0)){ + log.info("0或者空值不参与签名"); + }else { + if (value instanceof Double){ + sb.append(key).append("=").append(((Double) value).intValue()).append("&"); + }else { + sb.append(key).append("=").append(value).append("&"); + } + } + }); + + sb.append("appkey=").append(appKey).append("×tamp=").append(timestamp); + + return sb.toString(); + } + + private static String bytesToHex(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/request/bigdata/ProfitDataRequest.java b/coolstore-partner-model/src/main/java/com/cool/store/request/bigdata/ProfitDataRequest.java new file mode 100644 index 000000000..1cdd1c619 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/request/bigdata/ProfitDataRequest.java @@ -0,0 +1,16 @@ +package com.cool.store.request.bigdata; + +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/3/27 17:49 + * @Version 1.0 + */ +@Data +public class ProfitDataRequest { + + private String store_code; + + private String bill_date; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/BerthOperationRequest.java b/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/BerthOperationRequest.java new file mode 100644 index 000000000..ca4a9c466 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/BerthOperationRequest.java @@ -0,0 +1,31 @@ +package com.cool.store.request.oppty; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/4/1 20:30 + * @Version 1.0 + */ +@Data +public class BerthOperationRequest { + @ApiModelProperty(" 操作类型: 1(新增), 2(更新), 3(删除)") + private String opType; + @ApiModelProperty("机会点编号") + private String code; + @ApiModelProperty("关注人用户编号") + private String userId; + @ApiModelProperty("关注用户手机号") + private String mobile; + @ApiModelProperty("关注人用户名") + private String userName; + @ApiModelProperty("铺位ID") + private Integer berthId; + @ApiModelProperty("铺位名称") + private String name; + @ApiModelProperty("铺位地址") + private String address; + @ApiModelProperty("铺位经纬度") + private String location; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/CityRequest.java b/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/CityRequest.java new file mode 100644 index 000000000..a2c9261a6 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/CityRequest.java @@ -0,0 +1,19 @@ +package com.cool.store.request.oppty; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/4/1 16:15 + * @Version 1.0 + */ +@Data +public class CityRequest { + + + @ApiModelProperty("父ID") + private Integer pid; + + +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/FollowRequest.java b/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/FollowRequest.java new file mode 100644 index 000000000..7010b3576 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/FollowRequest.java @@ -0,0 +1,23 @@ +package com.cool.store.request.oppty; + +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/3/26 18:50 + * @Version 1.0 + */ +@Data +public class FollowRequest { + + private Integer opType; + + private String code; + + private String userId; + + private String mobile; + + private String userName; + +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/InspectionRequest.java b/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/InspectionRequest.java new file mode 100644 index 000000000..cb6729b27 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/InspectionRequest.java @@ -0,0 +1,20 @@ +package com.cool.store.request.oppty; + +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/4/1 20:05 + * @Version 1.0 + */ +@Data +public class InspectionRequest { + private String code; + + private String userId; + + private String mobile; + + private String userName; + +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/OpportunityBaseRequest.java b/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/OpportunityBaseRequest.java new file mode 100644 index 000000000..ae2e1900c --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/OpportunityBaseRequest.java @@ -0,0 +1,27 @@ +package com.cool.store.request.oppty; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @Author suzhuhong + * @Date 2025/3/26 18:47 + * @Version 1.0 + */ +@Data +public class OpportunityBaseRequest { + + @NotNull(message = "notice不能为空") + private Integer notice; + + @NotNull(message = "timestamp不能为空") + private Long timestamp; + + @NotBlank(message = "key不能为空") + private String key; + + private String sign; + +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/OpportunityDetailRequest.java b/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/OpportunityDetailRequest.java new file mode 100644 index 000000000..9a3d4ad36 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/OpportunityDetailRequest.java @@ -0,0 +1,15 @@ +package com.cool.store.request.oppty; + +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/3/26 17:23 + * @Version 1.0 + */ +@Data +public class OpportunityDetailRequest{ + + private String code; + +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/OpportunityListRequest.java b/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/OpportunityListRequest.java new file mode 100644 index 000000000..2659f8cdc --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/OpportunityListRequest.java @@ -0,0 +1,108 @@ +package com.cool.store.request.oppty; + + +import lombok.Data; + + +/** + * @Author suzhuhong + * @Date 2025/3/26 14:38 + * @Version 1.0 + */ +@Data + +public class OpportunityListRequest{ + private String latLng; + private String code; + private Integer province; + private Integer city; + private Integer district; + private String userId; + private Integer orderType = 1; + private Integer inspectionStatus ; + private Integer pageNum = 1; + private Integer pageSize = 20; + + + public String getLatLng() { + return latLng; + } + + public void setLatLng(String latLng) { + this.latLng = latLng; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public Integer getProvince() { + return province; + } + + public void setProvince(Integer province) { + this.province = province; + } + + public Integer getCity() { + return city; + } + + public void setCity(Integer city) { + this.city = city; + } + + public Integer getDistrict() { + return district; + } + + public void setDistrict(Integer district) { + this.district = district; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public Integer getOrderType() { + return orderType; + } + + public void setOrderType(Integer orderType) { + this.orderType = orderType; + } + + public Integer getInspectionStatus() { + return inspectionStatus; + } + + public void setInspectionStatus(Integer inspectionStatus) { + this.inspectionStatus = inspectionStatus; + } + + public Integer getPageNum() { + return pageNum; + } + + public void setPageNum(Integer pageNum) { + this.pageNum = pageNum; + } + + public Integer getPageSize() { + return pageSize; + } + + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + } + + +} \ No newline at end of file diff --git a/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/StoreOperationRequest.java b/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/StoreOperationRequest.java new file mode 100644 index 000000000..6d317cbf2 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/StoreOperationRequest.java @@ -0,0 +1,57 @@ +package com.cool.store.request.oppty; + +import lombok.Data; +/** + * @Author suzhuhong + * @Date 2025/4/1 20:31 + * @Version 1.0 + */ + + +@Data +public class StoreOperationRequest { + /** + * 操作类型:1-新开门店,2-更新门店,3-闭店 + */ + private String opType; + + /** + * 机会点编号 + */ + private String code; + + /** + * 门店ID + */ + private Integer storeId; + + /** + * 门店编号 + */ + private String storeSn; + + /** + * 门店模式:1-加盟,2-强管 + */ + private Integer storeModel; + + /** + * 门店品牌(默认值:正新鸡排) + */ + private String storeBrand = "正新鸡排"; + + /** + * 门店名称 + */ + private String storeName; + + /** + * 门店地址 + */ + private String address; + + /** + * 门店经纬度geohash + */ + private String location; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/UserRequest.java b/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/UserRequest.java new file mode 100644 index 000000000..c1b548220 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/request/oppty/UserRequest.java @@ -0,0 +1,22 @@ +package com.cool.store.request.oppty; + +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/3/26 18:53 + * @Version 1.0 + */ +@Data +public class UserRequest { + + private String userId; + + private String mobile; + + private String userName; + + + + +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/response/bigdata/ActDataResponse.java b/coolstore-partner-model/src/main/java/com/cool/store/response/bigdata/ActDataResponse.java new file mode 100644 index 000000000..85a080e78 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/response/bigdata/ActDataResponse.java @@ -0,0 +1,19 @@ +package com.cool.store.response.bigdata; + +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/4/1 13:42 + * @Version 1.0 + */ +@Data +public class ActDataResponse { + + private String act_total_amt; + + private String Store_code; + + private String act_amt; + +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/response/bigdata/ApiResponse.java b/coolstore-partner-model/src/main/java/com/cool/store/response/bigdata/ApiResponse.java new file mode 100644 index 000000000..1a7e66acd --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/response/bigdata/ApiResponse.java @@ -0,0 +1,15 @@ +package com.cool.store.response.bigdata; + +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/4/1 10:49 + * @Version 1.0 + */ +@Data +public class ApiResponse { + private String msg; + private int code; + private T data; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/response/bigdata/ProfitDataResponse.java b/coolstore-partner-model/src/main/java/com/cool/store/response/bigdata/ProfitDataResponse.java new file mode 100644 index 000000000..c4deff435 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/response/bigdata/ProfitDataResponse.java @@ -0,0 +1,16 @@ +package com.cool.store.response.bigdata; + +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/3/27 17:46 + * @Version 1.0 + */ +@Data +public class ProfitDataResponse { + private String store_code; + private Double profit_total_amt; + private Double profit_amt; + private Double profit_rate; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/response/bigdata/ProfitRateResponse.java b/coolstore-partner-model/src/main/java/com/cool/store/response/bigdata/ProfitRateResponse.java new file mode 100644 index 000000000..226f7da4b --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/response/bigdata/ProfitRateResponse.java @@ -0,0 +1,16 @@ +package com.cool.store.response.bigdata; + +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/4/1 13:25 + * @Version 1.0 + */ +@Data +public class ProfitRateResponse { + private String profit_amt ; + private String store_code ; + private String profit_rate; + private String bill_date; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/BerthInfoResponse.java b/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/BerthInfoResponse.java new file mode 100644 index 000000000..db1660a25 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/BerthInfoResponse.java @@ -0,0 +1,31 @@ +package com.cool.store.response.oppty; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/3/26 14:33 + * @Version 1.0 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class BerthInfoResponse { + /** + * 铺位ID + */ + private Integer berthId; + /** + * 铺位名称 + */ + private String name; + /** + * 铺位地址 + */ + private String address; + /** + * 铺位经纬度 + */ + private String geohash; + +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/BrandResponse.java b/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/BrandResponse.java new file mode 100644 index 000000000..85b861e9a --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/BrandResponse.java @@ -0,0 +1,34 @@ +package com.cool.store.response.oppty; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/3/26 14:29 + * @Version 1.0 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class BrandResponse { + /** + * 品牌编号 + */ + private String brandCode; + /** + * 品牌名称 + */ + private String brandName; + /** + * 品牌Logo base64编码 + */ + private String brandLogo; + /** + * 该品牌门店所在位置 + */ + private String geohash; + /** + * 品牌数量 + */ + private Integer count; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/CityResponse.java b/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/CityResponse.java new file mode 100644 index 000000000..fda607c61 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/CityResponse.java @@ -0,0 +1,20 @@ +package com.cool.store.response.oppty; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/4/1 16:16 + * @Version 1.0 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class CityResponse { + + private Integer code; + private Integer name; + private Integer pid; + private String location; + +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/OpportunityApiResponse.java b/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/OpportunityApiResponse.java new file mode 100644 index 000000000..f91202d63 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/OpportunityApiResponse.java @@ -0,0 +1,18 @@ +package com.cool.store.response.oppty; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/4/1 10:49 + * @Version 1.0 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class OpportunityApiResponse { + private String msg; + private int code; + private Boolean success; + private T data; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/OpportunityDetailResponse.java b/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/OpportunityDetailResponse.java new file mode 100644 index 000000000..92f979ce3 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/OpportunityDetailResponse.java @@ -0,0 +1,33 @@ +package com.cool.store.response.oppty; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; +import org.apache.catalina.User; + +import java.util.List; + +/** + * @Author suzhuhong + * @Date 2025/3/26 17:24 + * @Version 1.0 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class OpportunityDetailResponse { + + private String code; // 机会点编号 + private Integer level; // 等级 + private String name; // 名称 + private String address; // 地址 + private String geohash; // GeoHash + private Integer province; // 省份编码 + private Integer city; // 城市编码 + private Integer district; // 区县编码 + private UserResponse bdUser; // 拓展人信息 + private List atUsers; // 关注人列表 + private List brandStats; // 周边品牌 + private StoreInfoResponse storeInfo; // 门店信息 + private List berthInfo; + + +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/OpportunityInfoPageResponse.java b/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/OpportunityInfoPageResponse.java new file mode 100644 index 000000000..072733f9d --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/OpportunityInfoPageResponse.java @@ -0,0 +1,24 @@ +package com.cool.store.response.oppty; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +import java.util.List; + +/** + * @Author suzhuhong + * @Date 2025/4/1 18:40 + * @Version 1.0 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class OpportunityInfoPageResponse { + + + private PageResponse page; + + private List pageData; + + + +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/OpportunityInfoResponse.java b/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/OpportunityInfoResponse.java new file mode 100644 index 000000000..ed91ec889 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/OpportunityInfoResponse.java @@ -0,0 +1,26 @@ +package com.cool.store.response.oppty; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +import java.util.List; + +/** + * @Author suzhuhong + * @Date 2025/3/26 14:40 + * @Version 1.0 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class OpportunityInfoResponse { + private String code; + private Integer level; + private String name; + private String address; + private String geohash; + private Integer province; + private Integer city; + private Integer district; + private Integer berthNum; + private List atUser; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/PageResponse.java b/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/PageResponse.java new file mode 100644 index 000000000..56aececc9 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/PageResponse.java @@ -0,0 +1,22 @@ +package com.cool.store.response.oppty; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/3/26 14:27 + * @Version 1.0 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class PageResponse { + + private Integer currentPage; + private Integer pageSize; + private Integer total; + private Integer count; + private Boolean first; + private Boolean last; + +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/StoreInfoResponse.java b/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/StoreInfoResponse.java new file mode 100644 index 000000000..64e4f786d --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/StoreInfoResponse.java @@ -0,0 +1,42 @@ +package com.cool.store.response.oppty; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/3/26 14:31 + * @Version 1.0 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class StoreInfoResponse { + /** + * 门店ID + */ + private Integer storeId; + /** + * 门店编号 + */ + private String storeSn; + /** + * 门店模式:1:加盟 2:强管 + */ + private Integer storeModel; + /** + * 门店品牌 预留,不传默认正新鸡排 + */ + private String storeBrand; + /** + * 门店名称 + */ + private String storeName; + /** + * 门店地址 + */ + private String address; + /** + * 门店经纬度 + */ + private String geohash; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/UserResponse.java b/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/UserResponse.java new file mode 100644 index 000000000..db78fa0e2 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/response/oppty/UserResponse.java @@ -0,0 +1,22 @@ +package com.cool.store.response.oppty; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/3/26 14:28 + * @Version 1.0 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class UserResponse { + private String userId; + private String mobile; + private String userName; + private String atTime; + + private String inspectionStatus; + + private String inspectionTime; +} diff --git a/coolstore-partner-service/src/main/java/com/cool/store/config/rest/JacksonConfig.java b/coolstore-partner-service/src/main/java/com/cool/store/config/rest/JacksonConfig.java new file mode 100644 index 000000000..201084bba --- /dev/null +++ b/coolstore-partner-service/src/main/java/com/cool/store/config/rest/JacksonConfig.java @@ -0,0 +1,20 @@ +package com.cool.store.config.rest; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @Author suzhuhong + * @Date 2025/4/1 10:54 + * @Version 1.0 + */ +@Configuration +public class JacksonConfig { + + @Bean + public ObjectMapper objectMapper() { + ObjectMapper mapper = new ObjectMapper(); + return mapper; + } +} diff --git a/coolstore-partner-service/src/main/java/com/cool/store/config/rest/OkHttpConfig.java b/coolstore-partner-service/src/main/java/com/cool/store/config/rest/OkHttpConfig.java new file mode 100644 index 000000000..f3d7d753b --- /dev/null +++ b/coolstore-partner-service/src/main/java/com/cool/store/config/rest/OkHttpConfig.java @@ -0,0 +1,110 @@ +package com.cool.store.config.rest; + +import okhttp3.OkHttpClient; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import javax.net.ssl.*; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * @Author suzhuhong + * @Date 2025/4/1 9:56 + * @Version 1.0 + */ + +@Configuration +public class OkHttpConfig { + + private static final String TRUSTED_DOMAIN = "zhengxinfood.com"; + private static final int CONNECT_TIMEOUT = 30; + private static final int READ_TIMEOUT = 30; + private static final int WRITE_TIMEOUT = 30; + + @Bean + public OkHttpClient okHttpClient() { + return createCustomTrustOkHttpClient(TRUSTED_DOMAIN); + } + + private OkHttpClient createCustomTrustOkHttpClient(String trustedDomain) { + try { + // 获取系统默认TrustManager + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init((KeyStore) null); + TrustManager[] defaultTrustManagers = trustManagerFactory.getTrustManagers(); + X509TrustManager defaultTrustManager = (X509TrustManager) defaultTrustManagers[0]; + + // 创建自定义TrustManager + X509TrustManager customTrustManager = new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + defaultTrustManager.checkClientTrusted(chain, authType); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + try { + defaultTrustManager.checkServerTrusted(chain, authType); + } catch (CertificateException e) { + if (!isCertificateForDomain(chain[0], trustedDomain)) { + throw new CertificateException("证书不匹配目标域名: " + trustedDomain); + } + } + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return defaultTrustManager.getAcceptedIssuers(); + } + }; + + // 配置SSLContext + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, new TrustManager[]{customTrustManager}, new SecureRandom()); + + return new OkHttpClient.Builder() + .sslSocketFactory(sslContext.getSocketFactory(), customTrustManager) + .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS) + .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS) + .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS) + .build(); + } catch (Exception e) { + throw new RuntimeException("创建OkHttpClient失败", e); + } + } + + private boolean isCertificateForDomain(X509Certificate certificate, String domain) { + try { + // 检查CN (Common Name) + String principal = certificate.getSubjectX500Principal().getName(); + if (principal.contains("CN=" + domain) || principal.contains("CN=*." + domain)) { + return true; + } + + // 检查SAN (Subject Alternative Names) + Collection> sans = certificate.getSubjectAlternativeNames(); + if (sans != null) { + for (List san : sans) { + // 类型2是DNS名称 + if (san.get(0).equals(2)) { + String sanValue = (String) san.get(1); + if (sanValue.equals(domain) || sanValue.endsWith("." + domain)) { + return true; + } + } + } + } + return false; + } catch (Exception e) { + return false; + } + } +} \ No newline at end of file diff --git a/coolstore-partner-service/src/main/java/com/cool/store/service/ThirdBigDataService.java b/coolstore-partner-service/src/main/java/com/cool/store/service/ThirdBigDataService.java new file mode 100644 index 000000000..4e690ee3c --- /dev/null +++ b/coolstore-partner-service/src/main/java/com/cool/store/service/ThirdBigDataService.java @@ -0,0 +1,57 @@ +package com.cool.store.service; + +import com.cool.store.request.bigdata.ProfitDataRequest; +import com.cool.store.request.oppty.CityRequest; +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.oppty.CityResponse; + +import java.util.List; + +/** + * @Author suzhuhong + * @Date 2025/3/27 17:37 + * @Version 1.0 + */ +public interface ThirdBigDataService { + + + /** + * 查询昨日毛利 + * @param request + * @return + */ + ProfitDataResponse getProfitData(ProfitDataRequest request); + + /** + * 查询当月毛利 + * @param requestBody + * @return + */ + ProfitDataResponse getCurMonthProfitData(ProfitDataRequest requestBody); + + /** + * 上月毛利 + * @param requestBody + * @return + */ + ProfitDataResponse getPreMonthProfitData(ProfitDataRequest requestBody); + + + /** + *getMonthProfitTrend + * @param requestBody + * @return + */ + List getMonthProfitTrend(ProfitDataRequest requestBody); + + /** + * 实收数据 + * @param requestBody + * @return + */ + ActDataResponse getActData(ProfitDataRequest requestBody); + + +} diff --git a/coolstore-partner-service/src/main/java/com/cool/store/service/ThirdOpportunityService.java b/coolstore-partner-service/src/main/java/com/cool/store/service/ThirdOpportunityService.java new file mode 100644 index 000000000..baeef4fde --- /dev/null +++ b/coolstore-partner-service/src/main/java/com/cool/store/service/ThirdOpportunityService.java @@ -0,0 +1,70 @@ +package com.cool.store.service; + +import com.cool.store.request.oppty.*; +import com.cool.store.response.oppty.CityResponse; +import com.cool.store.response.oppty.OpportunityDetailResponse; +import com.cool.store.response.oppty.OpportunityInfoPageResponse; + +import java.util.List; + +/** + * @Author suzhuhong + * @Date 2025/3/26 14:50 + * @Version 1.0 + * 正新 第三方机会点服务 + */ +public interface ThirdOpportunityService { + + + /** + * 机会点列表 我的机会点列表 + * @param request + * @return + */ + OpportunityInfoPageResponse listOpportunities(OpportunityListRequest request); + + + /** + * 查询机会点详情 + * @param opportunityDetailRequest + * @return + */ + OpportunityDetailResponse getOpportunitiesDetail(OpportunityDetailRequest opportunityDetailRequest); + + /** + * 关注机会点 + * @param request + * @return + */ + String followOpportunity(FollowRequest request); + + /** + * 考察机会点 + * @param request + * @return + */ + String inspectionOpportunity(InspectionRequest request); + + /** + * 铺位关注机会点 + * @param requestBody + * @return + */ + String berthOperation(BerthOperationRequest requestBody); + + /** + * 门店关注机会点 + * @param requestBody + * @return + */ + String storeOperation(StoreOperationRequest requestBody); + + /** + * 省市区列表 + * @param request + * @return + */ + List cityList(CityRequest request); + + +} diff --git a/coolstore-partner-service/src/main/java/com/cool/store/service/impl/ThirdBigDataServiceImpl.java b/coolstore-partner-service/src/main/java/com/cool/store/service/impl/ThirdBigDataServiceImpl.java new file mode 100644 index 000000000..76c74541b --- /dev/null +++ b/coolstore-partner-service/src/main/java/com/cool/store/service/impl/ThirdBigDataServiceImpl.java @@ -0,0 +1,209 @@ +package com.cool.store.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.cool.store.enums.ErrorCodeEnum; +import com.cool.store.exception.ServiceException; +import com.cool.store.request.bigdata.ProfitDataRequest; +import com.cool.store.request.oppty.CityRequest; +import com.cool.store.response.bigdata.ActDataResponse; +import com.cool.store.response.bigdata.ApiResponse; +import com.cool.store.response.bigdata.ProfitDataResponse; +import com.cool.store.response.bigdata.ProfitRateResponse; +import com.cool.store.response.oppty.CityResponse; +import com.cool.store.response.oppty.OpportunityApiResponse; +import com.cool.store.response.oppty.OpportunityDetailResponse; +import com.cool.store.service.ThirdBigDataService; +import com.cool.store.utils.JsonUtils; +import com.cool.store.utils.SignatureUtils; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * @Author suzhuhong + * @Date 2025/3/27 17:37 + * @Version 1.0 + */ +@Service +@Slf4j +public class ThirdBigDataServiceImpl implements ThirdBigDataService { + + @Value("${zx.big.data.url}") + private String apiUrl; + + @Value("${zx.big.data.appKey}") + private String appKey; + + @Value("${zx.big.data.appSecret}") + private String appSecret; + + @Resource + ObjectMapper objectMapper; + + @Resource + OkHttpClient okHttpClient; + + + @Override + public ProfitDataResponse getProfitData(ProfitDataRequest requestBody) { + // 1. 发送POST请求 + String url = apiUrl + "api/web/v1/act/yesd_profit"; + return executeApiCall(url, requestBody, ProfitDataResponse.class); + } + + + @Override + public ProfitDataResponse getCurMonthProfitData(ProfitDataRequest requestBody) { + // 1. 发送POST请求 + String url = apiUrl + "api/web/v1/act/curr_month_profit"; + return executeApiCall(url, requestBody, ProfitDataResponse.class); + } + + @Override + public ProfitDataResponse getPreMonthProfitData(ProfitDataRequest requestBody) { + // 1. 发送POST请求 + String url = apiUrl + "api/web/v1/act/prev_month_profit"; + return executeApiCall(url, requestBody, ProfitDataResponse.class); + } + + @Override + public List getMonthProfitTrend(ProfitDataRequest requestBody) { + // 1. 发送POST请求 + String url = apiUrl + "api/web/v1/act/month_profit_trend"; + return executeApiCall(url, requestBody, List.class); + } + + @Override + public ActDataResponse getActData(ProfitDataRequest requestBody) { + // 1. 发送POST请求 + String url = apiUrl + "api/web/v1/income/store_act"; + return executeApiCall(url, requestBody, ActDataResponse.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() != 0) { + 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) { + Map params = JsonUtils.parseJsonToMap(JSONObject.toJSONString(requestBody)); + long timestamp = System.currentTimeMillis(); + + String signString = SignatureUtils.buildSignString(params, appKey, timestamp); + String sign = SignatureUtils.hmacSha256(signString, appSecret); + + log.debug("签名生成 - 原始字符串: {}, 签名结果: {}", signString, sign); + + RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json"), + JSONObject.toJSONString(requestBody) + ); + + return new Request.Builder() + .url(url) + .post(body) + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json") + .addHeader("sign", sign) + .addHeader("timestamp", String.valueOf(timestamp)) + .addHeader("appkey", appKey) + .build(); + } + + /** + * 记录请求日志 + */ + private void logRequest(String url, Object requestBody) { + if (log.isInfoEnabled()) { + try { + log.info("\n======= 请求开始 =======\n" + + "API地址: {}\n" + + "请求参数: {}\n" + + "======= 请求结束 =======", + url, + objectMapper.writerWithDefaultPrettyPrinter() + .writeValueAsString(requestBody)); + } catch (JsonProcessingException e) { + log.warn("日志JSON序列化失败", e); + } + } + } + + /** + * 记录响应日志 + */ + 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-service/src/main/java/com/cool/store/service/impl/ThirdOpportunityServiceImpl.java b/coolstore-partner-service/src/main/java/com/cool/store/service/impl/ThirdOpportunityServiceImpl.java new file mode 100644 index 000000000..ea62088b3 --- /dev/null +++ b/coolstore-partner-service/src/main/java/com/cool/store/service/impl/ThirdOpportunityServiceImpl.java @@ -0,0 +1,217 @@ +package com.cool.store.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.cool.store.enums.ErrorCodeEnum; +import com.cool.store.exception.ServiceException; +import com.cool.store.request.oppty.*; +import com.cool.store.response.oppty.*; +import com.cool.store.service.ThirdOpportunityService; +import com.cool.store.utils.JsonUtils; +import com.cool.store.utils.SignatureUtils; +import com.fasterxml.jackson.core.JsonProcessingException; +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.io.IOException; +import java.lang.reflect.ParameterizedType; +import java.util.List; +import java.util.Map; + +/** + * @Author suzhuhong + * @Date 2025/3/26 15:20 + * @Version 1.0 + */ +@Service +@Slf4j +public class ThirdOpportunityServiceImpl implements ThirdOpportunityService { + + @Value("${zx.opportunity.url}") + private String apiUrl; + + @Value("${third.party.appKey}") + private String appKey; + + @Value("${third.party.appSecret}") + private String appSecret; + + @Resource + OkHttpClient okHttpClient; + + @Resource + ObjectMapper objectMapper; + + + @Override + public OpportunityInfoPageResponse listOpportunities(OpportunityListRequest requestBody) { + // 1. 发送POST请求 + String url = apiUrl+"open/oppty/v1/listOppty"; + return executeApiCall(url, requestBody, OpportunityInfoPageResponse.class); + } + + @Override + public OpportunityDetailResponse getOpportunitiesDetail(OpportunityDetailRequest requestBody) { + String url = apiUrl + "open/oppty/v1/opptyDetail"; + return executeApiCall(url, requestBody, OpportunityDetailResponse.class); + } + + + @Override + public String followOpportunity(FollowRequest requestBody) { + String url = apiUrl+"open/oppty/v1/saveAtUser"; + return executeApiCall(url, requestBody, String.class); + } + + @Override + public String inspectionOpportunity(InspectionRequest requestBody) { + String url = apiUrl+"open/oppty/v1/inspection"; + return executeApiCall(url, requestBody, String.class); + } + + @Override + public String berthOperation(BerthOperationRequest requestBody) { + String url = apiUrl+"open/oppty/v1/addBerthInfo"; + return executeApiCall(url, requestBody, String.class); + + } + + @Override + public String storeOperation(StoreOperationRequest requestBody) { + String url = apiUrl+"open/oppty/v1/addStore"; + return executeApiCall(url, requestBody, String.class); + } + + @Override + public List cityList(CityRequest requestBody) { + // 1. 发送POST请求 + String url = apiUrl+"open/oppty/v1/listCity"; + return executeApiCall(url, requestBody, List.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) { + Map params = JsonUtils.parseJsonToMap(JSONObject.toJSONString(requestBody)); + long timestamp = System.currentTimeMillis() / 1000; + + String signString = SignatureUtils.buildSignString(params, appKey, timestamp); + String sign = SignatureUtils.hmacSha256(signString, appSecret); + + log.debug("签名生成 - 原始字符串: {}, 签名结果: {}", signString, sign); + + RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json"), + JSONObject.toJSONString(requestBody) + ); + + return new Request.Builder() + .url(url) + .post(body) + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json") + .addHeader("sign", sign) + .addHeader("timestamp", String.valueOf(timestamp)) + .addHeader("appkey", appKey) + .build(); + } + + /** + * 记录请求日志 + */ + private void logRequest(String url, Object requestBody) { + if (log.isInfoEnabled()) { + try { + log.info("\n======= 请求开始 =======\n" + + "API地址: {}\n" + + "请求参数: {}\n" + + "======= 请求结束 =======", + url, + objectMapper.writerWithDefaultPrettyPrinter() + .writeValueAsString(requestBody)); + } catch (JsonProcessingException e) { + log.warn("日志JSON序列化失败", e); + } + } + } + + /** + * 记录响应日志 + */ + 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/webc/ThirdApiController.java b/coolstore-partner-web/src/main/java/com/cool/store/controller/webc/ThirdApiController.java new file mode 100644 index 000000000..4a364c718 --- /dev/null +++ b/coolstore-partner-web/src/main/java/com/cool/store/controller/webc/ThirdApiController.java @@ -0,0 +1,124 @@ +package com.cool.store.controller.webc; + +import com.cool.store.request.bigdata.ProfitDataRequest; +import com.cool.store.request.oppty.*; +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.oppty.CityResponse; +import com.cool.store.response.oppty.OpportunityDetailResponse; +import com.cool.store.response.oppty.OpportunityInfoPageResponse; +import com.cool.store.service.ThirdBigDataService; +import com.cool.store.service.ThirdOpportunityService; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + +/** + * @Author suzhuhong + * @Date 2025/4/1 20:49 + * @Version 1.0 + */ +@Slf4j +@RestController +@RequestMapping("/mini/third/api") +public class ThirdApiController { + + @Resource + ThirdOpportunityService thirdOpportunityService; + @Resource + ThirdBigDataService thirdBigDataService; + + @PostMapping("/list") + @ApiOperation("机会点列表/我的机会点") + public ResponseResult listOpportunities(@Valid @RequestBody OpportunityListRequest request) { + return ResponseResult.success(thirdOpportunityService.listOpportunities(request)); + } + + + @PostMapping("/getOpportunitiesDetail") + @ApiOperation("机会点详情") + public ResponseResult getOpportunitiesDetail(@Valid @RequestBody OpportunityDetailRequest request) { + return ResponseResult.success(thirdOpportunityService.getOpportunitiesDetail(request)); + } + + + @PostMapping("/followOpportunity") + @ApiOperation("关注/取消关注 机会点") + public ResponseResult followOpportunity(@Valid @RequestBody FollowRequest request) { + return ResponseResult.success(thirdOpportunityService.followOpportunity(request)); + } + + @PostMapping("/inspectionOpportunity") + @ApiOperation("考察机会点") + public ResponseResult inspectionOpportunity(@Valid @RequestBody InspectionRequest request) { + return ResponseResult.success(thirdOpportunityService.inspectionOpportunity(request)); + } + + @PostMapping("/berthOperation") + @ApiOperation("铺位关注机会点") + public ResponseResult berthOperation(@Valid @RequestBody BerthOperationRequest request) { + return ResponseResult.success(thirdOpportunityService.berthOperation(request)); + } + + @PostMapping("/storeOperation") + @ApiOperation("门店关注机会点") + public ResponseResult storeOperation(@Valid @RequestBody StoreOperationRequest request) { + return ResponseResult.success(thirdOpportunityService.storeOperation(request)); + } + + @PostMapping("/cityList") + @ApiOperation("省市区列表 查询省传0") + public ResponseResult> listOpportunities(@Valid @RequestBody CityRequest request) { + // 调用第三方接口 + return ResponseResult.success(thirdOpportunityService.cityList(request)); + } + + @PostMapping("/getProfitData") + @ApiOperation("昨天毛利") + public ResponseResult listOpportunities(@Valid @RequestBody ProfitDataRequest request) { + // 调用第三方接口 + return ResponseResult.success(thirdBigDataService.getProfitData(request)); + } + + @PostMapping("/getCurMonthProfitData") + @ApiOperation("本月毛利") + public ResponseResult getCurMonthProfitData(@Valid @RequestBody ProfitDataRequest request) { + // 调用第三方接口 + return ResponseResult.success(thirdBigDataService.getCurMonthProfitData(request)); + } + + + @PostMapping("/getPreMonthProfitData") + @ApiOperation("上月毛利") + public ResponseResult getPreMonthProfitData(@Valid @RequestBody ProfitDataRequest request) { + // 调用第三方接口 + return ResponseResult.success(thirdBigDataService.getPreMonthProfitData(request)); + } + + + @PostMapping("/getMonthProfitTrend") + @ApiOperation("毛利趋势") + public ResponseResult> getMonthProfitTrend(@Valid @RequestBody ProfitDataRequest request) { + // 调用第三方接口 + return ResponseResult.success(thirdBigDataService.getMonthProfitTrend(request)); + } + + @PostMapping("/getActData") + @ApiOperation("门店实收") + public ResponseResult getActData(@Valid @RequestBody ProfitDataRequest request) { + // 调用第三方接口 + return ResponseResult.success(thirdBigDataService.getActData(request)); + } + + + +} diff --git a/coolstore-partner-web/src/main/resources/application-test.properties b/coolstore-partner-web/src/main/resources/application-test.properties index 165cbf8a5..f919ccc27 100644 --- a/coolstore-partner-web/src/main/resources/application-test.properties +++ b/coolstore-partner-web/src/main/resources/application-test.properties @@ -28,7 +28,7 @@ isv.domain=https://abstore-isv.coolstore.cn/isv #rocketmq \u914D\u7F6E rocketmq.accessKey=LTAI5tJbgtyoHUvofTaeP1RP rocketmq.secretKey=myg755iCx0j4PyQkHMaUVeOr0bw1tA -rocketmq.nameSrvAdder=http://MQ_INST_1947409023213164_BX3sLZnA.cn-hangzhou.mq-internal.aliyuncs.com:8080 +rocketmq.nameSrvAdder=http://MQ_INST_1947409023213164_BX3sLZnA.cn-hangzhou.mq.aliyuncs.com:80 rocketmq.topic=simple_message #oss配置 @@ -82,3 +82,13 @@ enterprise.dingCorpId=wpayJeDAAAhGIFgUJpJN-zg39JuNbYhg ##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 + + +third.party.appKey=IGSAEQoakR2HEaYx +third.party.appSecret=aPsA99K1obFeFm3m +zx.opportunity.url=https://snp.wenmatech.com/ + +#大数据地址 +zx.big.data.url=https://ds.zhengxinfood.com/ +zx.big.data.appKey=ff203b5567744feaaae49fb86f58c5bf +zx.big.data.appSecret=35b8b9a400b4430fa022190be0913cd6