Merge branch 'master' into cc_20251029_smz

This commit is contained in:
wangff
2025-11-10 10:42:07 +08:00
69 changed files with 3140 additions and 16 deletions

View File

@@ -0,0 +1,126 @@
package com.cool.store.builder;
import com.cool.store.config.weixin.WechatMiniappProperties;
import com.cool.store.dto.wechat.WechatTemplateMessageDTO;
import com.cool.store.enums.wechat.WechatTemplateEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* @Author suzhuhong
* @Date 2025/10/10 14:34
* @Version 1.0
*/
@Component
public class TemplateMessageBuilder {
@Autowired
private WechatMiniappProperties wechatMiniappProperties;
/**
* 构建普通模板消息
*/
public WechatTemplateMessageDTO buildNormalTemplate(String openId,
WechatTemplateEnum template,
Map<String, Object> data) {
WechatTemplateMessageDTO messageDTO = new WechatTemplateMessageDTO();
messageDTO.setToUser(openId);
messageDTO.setTemplateId(template.getTemplateId());
// 设置URL如果data中包含url
if (data.containsKey("url")) {
messageDTO.setUrl((String) data.get("url"));
}
// 构建模板数据
messageDTO.setData(buildTemplateData(data));
return messageDTO;
}
/**
* 构建小程序跳转模板消息
*/
public WechatTemplateMessageDTO buildMiniappTemplate(String openId,
WechatTemplateEnum template,
Map<String, Object> data,
String miniAppPagePath) {
WechatTemplateMessageDTO messageDTO = new WechatTemplateMessageDTO();
messageDTO.setToUser(openId);
messageDTO.setTemplateId(template.getTemplateId());
// 设置小程序跳转
WechatTemplateMessageDTO.MiniprogramDTO miniProgram = new WechatTemplateMessageDTO.MiniprogramDTO();
miniProgram.setAppid(wechatMiniappProperties.getAppId());
miniProgram.setPagepath(miniAppPagePath != null ? miniAppPagePath : wechatMiniappProperties.getDefaultPagePath());
messageDTO.setMiniprogram(miniProgram);
// 设置备用URL如果data中包含url
if (data.containsKey("url")) {
messageDTO.setUrl((String) data.get("url"));
}
// 构建模板数据
messageDTO.setData(buildTemplateData(data));
return messageDTO;
}
/**
* 构建小程序跳转模板消息带备用URL
*/
public WechatTemplateMessageDTO buildMiniAppTemplateWithUrl(String openId,
WechatTemplateEnum template,
Map<String, Object> data,
String miniAppPagePath,
String backupUrl) {
WechatTemplateMessageDTO messageDTO = buildMiniappTemplate(openId, template, data, miniAppPagePath);
// 设置备用URL
if (backupUrl != null && !backupUrl.trim().isEmpty()) {
messageDTO.setUrl(backupUrl);
}
return messageDTO;
}
/**
* 构建模板数据
*/
private Map<String, WechatTemplateMessageDTO.TemplateDataItemDTO> buildTemplateData(Map<String, Object> data) {
Map<String, WechatTemplateMessageDTO.TemplateDataItemDTO> templateData = new HashMap<>();
data.forEach((key, value) -> {
if (!"url".equals(key) && value != null) {
WechatTemplateMessageDTO.TemplateDataItemDTO item =
new WechatTemplateMessageDTO.TemplateDataItemDTO(
value.toString(),
getColorByField(key)
);
templateData.put(key, item);
}
});
return templateData;
}
/**
* 根据字段名获取颜色
*/
private String getColorByField(String fieldName) {
switch (fieldName) {
case "amount":
case "refundAmount":
case "couponValue":
case "character_string2":
return "#FF0000"; // 金额类字段用红色
case "orderNo":
case "expressNo":
return "#173177"; // 编号类字段用蓝色
default:
return "#333333"; // 默认用深灰色
}
}
}

View File

@@ -0,0 +1,32 @@
package com.cool.store.config.weixin;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @Author suzhuhong
* @Date 2025/10/10 14:41
* @Version 1.0
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "wechat.miniapp")
public class WechatMiniappProperties {
/**
* 小程序appId
*/
private String appId;
/**
* 小程序页面路径
*/
private String defaultPagePath ;
/**
* 是否使用小程序跳转
*/
private boolean enabled = false;
}

View File

@@ -0,0 +1,37 @@
package com.cool.store.config.weixin;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @Author suzhuhong
* @Date 2025/10/10 14:29
* @Version 1.0
* 微信服务号配置
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "wechat.mp")
public class WechatMpProperties {
/**
* 公众号appId
*/
private String appId;
/**
* 公众号appSecret
*/
private String appSecret;
/**
* 获取access_token的URL
*/
private String accessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token";
/**
* 发送模板消息的URL
*/
private String sendTemplateMessageUrl = "https://api.weixin.qq.com/cgi-bin/message/template/send";
}

View File

@@ -0,0 +1,157 @@
package com.cool.store.handler;
import com.alibaba.fastjson.JSONObject;
import com.cool.store.dao.PartnerUserWechatBindDAO;
import com.cool.store.dto.wechat.WechatUserInfoDTO;
import com.cool.store.service.wechat.WechatTemplateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import javax.annotation.Resource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
/**
* @Author suzhuhong
* @Date 2025/10/14 14:56
* @Version 1.0
*/
@Component
@Slf4j
public class WeChatHandler {
@Resource
PartnerUserWechatBindDAO partnerUserWechatBindDAO;
@Resource
WechatTemplateService wechatTemplateService;
public Map<String, Object> parseXmlToMap(String xmlContent) throws Exception {
Map<String, Object> result = new HashMap<>();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new InputSource(new StringReader(xmlContent)));
NodeList nodes = document.getDocumentElement().getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
String tagName = node.getNodeName();
String textContent = node.getTextContent();
result.put(tagName, textContent);
}
}
return result;
}
public String processMessage(Map<String, Object> messageMap) {
String msgType = (String) messageMap.get("MsgType");
String event = (String) messageMap.get("Event");
switch (msgType) {
case "event":
return handleEvent(messageMap);
// case "text":
// return handleTextMessage(message);
//
// case "image":
// return handleImageMessage(message);
default:
// 其他类型的消息直接回复success
return "success";
}
}
private String handleEvent(Map<String, Object> messageMap) {
String event = (String) messageMap.get("Event");
String fromUserName = (String) messageMap.get("FromUserName");
String toUserName = (String) messageMap.get("ToUserName");
switch (event) {
case "subscribe":
// 关注事件 - 绑定用户
return handleSubscribeEvent(fromUserName,toUserName);
case "unsubscribe":
// 取消关注事件 - 解绑用户
return handleUnsubscribeEvent(fromUserName,toUserName);
default:
return buildWelcomeReply(fromUserName, toUserName);
}
}
private String handleSubscribeEvent(String fromUserName,String toUserName) {
try {
//根据openId 获取用户信息
WechatUserInfoDTO userInfo = wechatTemplateService.getUserInfo(fromUserName, null);
log.info("handleSubscribeEvent: {}", JSONObject.toJSONString(userInfo));
//根据unionId 更新服务号ID
if (userInfo != null) {
partnerUserWechatBindDAO.updateByUnionId(userInfo.getUnionid(),fromUserName);
}
// 立即回复欢迎消息
return buildWelcomeReply(fromUserName, toUserName);
} catch (Exception e) {
// 即使处理失败也要返回success
return buildWelcomeReply(fromUserName, toUserName);
}
}
/**
* 处理取消关注事件
*/
private String handleUnsubscribeEvent(String fromUserName,String toUserName) {
// 异步处理用户解绑
//userBindingService.unbindOfficialAccountUser(openId);
return "success";
}
private String buildSuccessReply(String fromUser, String toUser) {
return String.format(
"<xml>" +
"<ToUserName><![CDATA[%s]]></ToUserName>" +
"<FromUserName><![CDATA[%s]]></FromUserName>" +
"<CreateTime>%d</CreateTime>" +
"<MsgType><![CDATA[text]]></MsgType>" +
"<Content><![CDATA[success]]></Content>" +
"</xml>",
fromUser, toUser, System.currentTimeMillis() / 1000
);
}
private String buildWelcomeReply(String fromUser, String toUser) {
return String.format(
"<xml>" +
"<ToUserName><![CDATA[%s]]></ToUserName>" +
"<FromUserName><![CDATA[%s]]></FromUserName>" +
"<CreateTime>%d</CreateTime>" +
"<MsgType><![CDATA[text]]></MsgType>" +
"<Content><![CDATA[欢迎关注!您已成功绑定通知服务,可以接收小程序的重要消息通知。]]></Content>" +
"</xml>",
fromUser, toUser, System.currentTimeMillis() / 1000
);
}
}

View File

@@ -1,10 +1,11 @@
package com.cool.store.service;
import com.cool.store.dto.FoodTokenDTO;
import com.cool.store.dto.GetAccessTokenDTO;
import com.cool.store.dto.store.StoreUserPositionDTO;
import com.cool.store.dto.recipe.RecipeSpLaunchDTO;
import com.cool.store.dto.store.StoreUserUpdateDTO;
import com.cool.store.request.recipe.RevenueDataRequest;
import com.cool.store.response.caipin.StoreUserResponse;
import com.cool.store.vo.recipe.RevenueDataVO;
import java.util.List;
@@ -31,4 +32,17 @@ public interface ThirdFoodService {
*/
StoreUserResponse pushStoreUser(List<StoreUserUpdateDTO> storeUserUpdateDTOList);
/**
* 查询门店营收实收数据
* @param request 营收数据Request
* @return 营收数据VO列表
*/
List<RevenueDataVO> getRevenueData(RevenueDataRequest request);
/**
* 查询菜品服务包上新数据
* @param request 请求request
* @return 服务包菜品上新DTO
*/
RecipeSpLaunchDTO getRecipeServiceLaunch(RevenueDataRequest request);
}

View File

@@ -9,9 +9,12 @@ import com.cool.store.dto.notice.MessageTemplateCountDTO;
import com.cool.store.dto.notice.NoticeDTO;
import com.cool.store.dto.store.AuthStoreUserDTO;
import com.cool.store.dto.store.StoreAreaDTO;
import com.cool.store.dto.wechat.ServiceAccountOpenIdDTO;
import com.cool.store.entity.*;
import com.cool.store.enums.ErrorCodeEnum;
import com.cool.store.enums.notice.*;
import com.cool.store.enums.wechat.WechatTemplateDetailEnum;
import com.cool.store.enums.wechat.WechatTemplateEnum;
import com.cool.store.exception.ServiceException;
import com.cool.store.mapper.StoreGroupMappingMapper;
import com.cool.store.mapper.StoreMapper;
@@ -19,12 +22,16 @@ import com.cool.store.request.notice.*;
import com.cool.store.response.bigdata.ApiResponse;
import com.cool.store.service.MessageTemplateService;
import com.cool.store.service.StoreService;
import com.cool.store.service.wechat.WechatTemplateService;
import com.cool.store.utils.CoolDateUtils;
import com.cool.store.utils.RedisUtilPool;
import com.cool.store.utils.poi.DateUtils;
import com.cool.store.vo.PartnerUserInfoVO;
import com.cool.store.vo.notice.*;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
@@ -67,10 +74,14 @@ public class MessageTemplateServiceImpl implements MessageTemplateService {
@Resource
MatterConfigDAO matterConfigDAO;
@Resource
WechatTemplateService wechatTemplateService;
@Resource
RedisUtilPool redisUtilPool;
@Resource
TaskExecutor noticeThreadPool;
@Resource
HyPartnerUserInfoDAO hyPartnerUserInfoDAO;
@Resource
MessageIssueService messageIssueService;
@@ -210,10 +221,38 @@ public class MessageTemplateServiceImpl implements MessageTemplateService {
JSONObject.toJSONString(request.getStoreInfoList()),
JSONObject.toJSONString(request.getUserInfoList()),
userId);
//发送通知
Set<String> userIds = authUser.values().stream().flatMap(Collection::stream).collect(Collectors.toSet());
//分批 查询用户信息
List<String> openIdList = new ArrayList<>();
Lists.partition(new ArrayList<>(userIds), 100).forEach(x->{
List<EnterpriseUserDO> userInfoByUserIds = enterpriseUserDAO.getUserInfoByUserIds(x);
//取出用户的手机号,过滤掉空的手机号
List<String> mobileList = userInfoByUserIds.stream().filter(user -> StringUtils.isNotBlank(user.getMobile())).map(EnterpriseUserDO::getMobile).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(mobileList)){
List<ServiceAccountOpenIdDTO> serviceAccountOpenIdDTOS = hyPartnerUserInfoDAO.selectLastBindRecord(mobileList);
if (CollectionUtils.isNotEmpty(serviceAccountOpenIdDTOS)){
//服务号ID
openIdList.addAll(serviceAccountOpenIdDTOS.stream().map(ServiceAccountOpenIdDTO::getServiceAccountOpenId).collect(Collectors.toList()));
}
}
});
MessageTemplateDO messageTemplateDO = list.get(0);
Map<String, Object> data = new HashMap<>();
data.put(WechatTemplateDetailEnum.THING6.getCode(), messageTemplateDO.getMessageTitle());
data.put(WechatTemplateDetailEnum.TIME33.getCode(), DateUtils.parseDateToStr(DateUtils.SPECIAL_DATE_START, new Date()));
data.put(WechatTemplateDetailEnum.CHARACTER_STRING14.getCode(), messageTemplateDO.getMessageCode());
openIdList.forEach(x->{
wechatTemplateService.sendMiniAppTemplate(x, WechatTemplateEnum.NEW_QUESTION_NOTICE,data,"pages/notification/index");
});
// 即时消息下发
messageIssueService.issueMessage(realtimeMessageList);
} catch (Exception e) {
log.info("发布流程异常,已取消发布");
log.info("发布流程异常 e:{}",e.getMessage());
} finally {
releaseLocks(lockKeys);
log.info("发布流程结束已释放Redis锁");

View File

@@ -59,6 +59,8 @@ public class ShopAccountServiceImpl implements ShopAccountService {
ThirdXinGuanJiaService thirdXinGuanJiaService;
@Resource
private LineInfoDAO lineInfoDAO;
@Resource
private StoreDao storeDao;
@Override
public List<ShopAccountDTO> getShopAccountByShopId(Long shopId) {
@@ -311,7 +313,17 @@ public class ShopAccountServiceImpl implements ShopAccountService {
//查询老店关联表数据
OldShopDO oldShopDO = oldShopDAO.getByCode(shopCode);
if (Objects.isNull(oldShopDO)) {
throw new ServiceException(ErrorCodeEnum.GET_YLS_CODE_FAIL);
StoreDO storeDO = storeDao.getByStoreNum(shopCode);
if (Objects.isNull(storeDO)) {
throw new ServiceException(ErrorCodeEnum.GET_YLS_CODE_FAIL);
}
oldShopDO = OldShopDO.builder()
.shopCode(shopCode)
.shopName(storeDO.getStoreName())
.mobile(storeDO.getTelephone())
.ylsShopCode("ZXA8_" + shopCode)
.build();
oldShopDAO.insertSelective(oldShopDO);
}
return oldShopDO.getYlsShopCode();
}

View File

@@ -399,6 +399,7 @@ public class StoreServiceImpl implements StoreService {
dto.setJoinMode(JoinModeEnum.getByCode(store.getJoinModel()));
dto.setBrand(FranchiseBrandEnum.getDescByCode(store.getJoinBrand()));
dto.setOrderMiniProgramName(store.getMiniProgramOrderStoreName());
dto.setStatus(StoreStatusEnum.getName(store.getStoreStatus()));
if (store.getRegionId() != null){
dto.setManagerSupervisionName(regionMap.get(store.getRegionId()));
}

View File

@@ -1,17 +1,23 @@
package com.cool.store.service.impl;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONObject;
import com.cool.store.dao.StoreDao;
import com.cool.store.dto.FoodTokenDTO;
import com.cool.store.dto.GetAccessTokenDTO;
import com.cool.store.dto.store.StoreUserPositionDTO;
import com.cool.store.dto.recipe.RecipeSpLaunchDTO;
import com.cool.store.dto.recipe.RevenueDataDTO;
import com.cool.store.dto.recipe.RevenueDataQueryDTO;
import com.cool.store.dto.store.StoreUserUpdateDTO;
import com.cool.store.entity.StoreDO;
import com.cool.store.enums.ErrorCodeEnum;
import com.cool.store.exception.ServiceException;
import com.cool.store.request.recipe.RevenueDataRequest;
import com.cool.store.response.caipin.StoreUserResponse;
import com.cool.store.response.oppty.OpportunityApiResponse;
import com.cool.store.service.ThirdFoodService;
import com.cool.store.utils.BeanUtil;
import com.cool.store.utils.SignatureUtils;
import com.cool.store.vo.recipe.RevenueDataVO;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -24,9 +30,9 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @Author suzhuhong
@@ -45,6 +51,8 @@ public class ThirdFoodServiceImpl implements ThirdFoodService {
OkHttpClient okHttpClient;
@Resource
ObjectMapper objectMapper;
@Resource
private StoreDao storeDao;
@Override
@@ -60,6 +68,29 @@ public class ThirdFoodServiceImpl implements ThirdFoodService {
return executeApiCall(url, dto, StoreUserResponse.class);
}
@Override
public List<RevenueDataVO> getRevenueData(RevenueDataRequest request) {
StoreDO storeDO = storeDao.getByStoreId(request.getStoreId());
if (Objects.isNull(storeDO)) {
throw new ServiceException(ErrorCodeEnum.STORE_NOT_FIND);
}
RevenueDataQueryDTO queryDTO = new RevenueDataQueryDTO(storeDO.getStoreNum(), request.getBusinessDateFrom(), request.getBusinessDateTo());
String url = "/v1/store/business";
List<RevenueDataDTO> list = executeApiCall(url, queryDTO, List.class);
return BeanUtil.toList(list, RevenueDataVO.class, CopyOptions.create().setFieldMapping(Collections.singletonMap("storeCode", "storeNum")));
}
@Override
public RecipeSpLaunchDTO getRecipeServiceLaunch(RevenueDataRequest request) {
StoreDO storeDO = storeDao.getByStoreId(request.getStoreId());
if (Objects.isNull(storeDO)) {
throw new ServiceException(ErrorCodeEnum.STORE_NOT_FIND);
}
RevenueDataQueryDTO queryDTO = new RevenueDataQueryDTO(storeDO.getStoreNum(), request.getBusinessDateFrom(), request.getBusinessDateTo());
String url = "/v1/store/business/spRecipeList";
return executeApiCall(url, queryDTO, RecipeSpLaunchDTO.class);
}
private <T> T executeApiCall(String url, Object requestBody, Class<T> responseType) {
// 1. 打印请求前日志

View File

@@ -111,9 +111,16 @@ public class WechatMiniAppServiceImpl implements WechatMiniAppService {
PartnerUserWechatBindDO bindDO = new PartnerUserWechatBindDO();
bindDO.setBindTime(new Date());
bindDO.setOpenId(openid);
bindDO.setUnionId(unionId);
bindDO.setPartnerId(hyPartnerUserInfoDO.getPartnerId());
bindDO.setCreateTime(new Date());
partnerUserWechatBindDAO.insertSelective(bindDO);
}else {
//维护unionId 针对老数据没有unionId
if (zlPartnerUserBindDO.getUnionId()==null){
zlPartnerUserBindDO.setUnionId(unionId);
partnerUserWechatBindDAO.update(zlPartnerUserBindDO);
}
}
BeanUtil.copyProperties(hyPartnerUserInfoDO, userInfoVO);
fillLineInfo(userInfoVO, hyPartnerUserInfoDO.getPartnerId());

View File

@@ -0,0 +1,67 @@
package com.cool.store.service.impl.xinfa;
import com.cool.store.dto.huoma.*;
import com.cool.store.enums.SpecialTagEnum;
import com.cool.store.response.ResponseResult;
import com.cool.store.service.xinfa.XinFaBusinessService;
import com.cool.store.service.xinfa.XinFaDeviceService;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Author suzhuhong
* @Date 2025/11/5 16:17
* @Version 1.0
*/
@Service
public class XinFaBusinessServiceImpl implements XinFaBusinessService {
@Resource
XinFaDeviceService xinFaDeviceService;
@Override
public List<StoreXinFaDeviceDetail> getStoreXinFaDeviceDetail(String storeNum) {
return xinFaDeviceService.getStoreXinFaDeviceDetail(storeNum);
}
@Override
public List<TagDetailDTO> getAccountAllTags(String storeNum,String deviceName) {
//如果是广告机,不需要展示标签
if (deviceName.contains("广告机")){
return new ArrayList<>();
}
List<TagDetailDTO> accountAllTags = xinFaDeviceService.getAccountAllTags(storeNum);
if (accountAllTags != null){
return accountAllTags.stream()
.filter(tag -> tag.getName() != null &&
SpecialTagEnum.getAllTagNames().contains(tag.getName()))
.sorted(Comparator.comparing(tag -> SpecialTagEnum.getAllTagNames().indexOf(tag.getName())))
.collect(Collectors.toList());
}
return null;
}
@Override
public List<ProgramResponseDTO> getProgramList(ProgramReqDTO programReqDTO) {
//如果没传tag 查所有 根据更新时间倒序排
programReqDTO.setSort("desc");
programReqDTO.setDate("updateTime");
if (CollectionUtils.isEmpty(programReqDTO.getTagIds())){
//设备名称包含 广告机 只查询电子价目表
programReqDTO.setTagIds(xinFaDeviceService.getAccountSpecialTagIds(programReqDTO.getStoreCode(), SpecialTagEnum.getElectronicPriceTagName()));
}
List<ProgramResponseDTO> programList = xinFaDeviceService.getProgramList(programReqDTO);
return programList;
}
@Override
public Boolean publishProgram(PublishDTO publishDTO) {
return xinFaDeviceService.publish(publishDTO);
}
}

View File

@@ -0,0 +1,149 @@
package com.cool.store.service.wechat;
import com.cool.store.builder.TemplateMessageBuilder;
import com.cool.store.config.weixin.WechatMpProperties;
import com.cool.store.dto.wechat.AccessTokenDTO;
import com.cool.store.dto.wechat.WechatTemplateMessageDTO;
import com.cool.store.dto.wechat.WechatUserInfoDTO;
import com.cool.store.enums.wechat.WechatTemplateEnum;
import com.cool.store.utils.OkHttpUtil;
import com.cool.store.utils.RedisUtilPool;
import com.cool.store.utils.poi.StringUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.Map;
/**
* @Author suzhuhong
* @Date 2025/10/10 14:15
* @Version 1.0
*/
@Slf4j
@Service
public class WechatTemplateService {
private static final String ACCESS_TOKEN_KEY = "wechat_service_account_access_token";
@Autowired
private WechatMpProperties wechatMpProperties;
@Autowired
private TemplateMessageBuilder templateMessageBuilder;
@Autowired
private OkHttpUtil okHttpUtil;
@Autowired
private ObjectMapper objectMapper;
@Resource
private RedisUtilPool redisUtilPool;
public String getAccessToken() {
String cachedToken = redisUtilPool.getString(ACCESS_TOKEN_KEY);
if (StringUtils.isNotEmpty(cachedToken)) {
log.info("从 Redis 获取 access_token: {}", cachedToken);
return cachedToken;
}
String url = String.format("%s?grant_type=client_credential&appid=%s&secret=%s",
wechatMpProperties.getAccessTokenUrl(),
wechatMpProperties.getAppId(),
wechatMpProperties.getAppSecret());
try {
String result = okHttpUtil.doGet(url);
log.info("获取access_token响应: {}", result);
if (StringUtils.isNotEmpty( result)){
AccessTokenDTO responseDTO = objectMapper.readValue(result, AccessTokenDTO.class);
String accessToken = responseDTO.getAccess_token();
// 将获取到的 token 存入 Redis设置过期时间微信 token 有效期 7200 秒,这里设置 7000 秒)
if (StringUtils.isNotEmpty(accessToken)) {
redisUtilPool.setString(ACCESS_TOKEN_KEY, accessToken, 7000);
log.info("将 access_token 存入 Redis: {}", accessToken);
}
return accessToken;
}
return null;
} catch (IOException e) {
log.error("获取access_token失败", e);
} catch (Exception e) {
log.error("解析access_token响应失败", e);
}
return null;
}
public WechatUserInfoDTO getUserInfo(String openId, String lang) {
String accessToken = getAccessToken();
//默认中国
lang = StringUtils.isEmpty(lang)?"zh_CN":lang;
if (accessToken == null) {
log.error("获取access_token失败");
return null;
}
String url = String.format("https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s",
accessToken, openId);
if (lang != null && !lang.trim().isEmpty()) {
url += "&lang=" + lang;
}
try {
String result = okHttpUtil.doGet(url);
log.debug("获取用户信息响应: {}", result);
WechatUserInfoDTO userInfo = objectMapper.readValue(result, WechatUserInfoDTO.class);
return userInfo;
} catch (IOException e) {
log.error("获取用户信息失败", e);
return null;
} catch (Exception e) {
log.error("解析用户信息响应失败", e);
return null;
}
}
public boolean sendNormalTemplate(String openId, WechatTemplateEnum template, Map<String, Object> data) {
WechatTemplateMessageDTO messageDTO = templateMessageBuilder.buildNormalTemplate(openId, template, data);
return sendTemplateMessage(messageDTO);
}
public boolean sendMiniAppTemplate(String openId, WechatTemplateEnum template,
Map<String, Object> data, String miniAppPagePath) {
WechatTemplateMessageDTO messageDTO = templateMessageBuilder.buildMiniappTemplate(
openId, template, data, miniAppPagePath);
return sendTemplateMessage(messageDTO);
}
public boolean sendMiniAppTemplateWithUrl(String openId, WechatTemplateEnum template,
Map<String, Object> data, String miniAppPagePath, String backupUrl) {
WechatTemplateMessageDTO messageDTO = templateMessageBuilder.buildMiniAppTemplateWithUrl(
openId, template, data, miniAppPagePath, backupUrl);
return sendTemplateMessage(messageDTO);
}
private boolean sendTemplateMessage(WechatTemplateMessageDTO messageDTO) {
String accessToken = getAccessToken();
if (accessToken == null) {
log.error("获取access_token失败无法发送模板消息");
return false;
}
String url = String.format("%s?access_token=%s",
wechatMpProperties.getSendTemplateMessageUrl(), accessToken);
log.info("发送模板消息: {}", messageDTO);
try {
String result = okHttpUtil.doPostJson(url, messageDTO);
log.info("发送模板消息响应: {}", result);
return Boolean.TRUE;
} catch (Exception e) {
log.error("解析模板消息响应失败", e);
return false;
}
}
}

View File

@@ -0,0 +1,48 @@
package com.cool.store.service.xinfa;
import com.cool.store.dto.huoma.*;
import com.cool.store.response.ResponseResult;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
/**
* @Author suzhuhong
* @Date 2025/11/5 16:15
* @Version 1.0
*/
public interface XinFaBusinessService {
/**
* 获取门店信发设备列表
* @param storeNum
* @return
*/
List<StoreXinFaDeviceDetail> getStoreXinFaDeviceDetail(String storeNum);
/**
* 获取账号下有哪些标签
* @param storeNum
* @return
*/
List<TagDetailDTO> getAccountAllTags(String storeNum,String deviceName);
/**
* 获取账号下有哪些节目、
* 通过门店在哪个账号下 确定账号
* @param programReqDTO
* @return
*/
List<ProgramResponseDTO> getProgramList(ProgramReqDTO programReqDTO);
/**
* 发布信发设备
* @param publishDTO
* @return
*/
Boolean publishProgram(PublishDTO publishDTO);
}

View File

@@ -0,0 +1,491 @@
package com.cool.store.service.xinfa;
import com.alibaba.fastjson.JSONObject;
import com.cool.store.constants.RedisConstant;
import com.cool.store.dto.huoma.*;
import com.cool.store.enums.ErrorCodeEnum;
import com.cool.store.enums.SpecialTagEnum;
import com.cool.store.exception.ServiceException;
import com.cool.store.response.ResponseResult;
import com.cool.store.utils.RedisUtilPool;
import com.cool.store.utils.poi.constant.Constants;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;
/**
* @Author suzhuhong
* @Date 2025/11/4 15:47
* @Version 1.0
*/
@Service
@Slf4j
public class XinFaDeviceService {
@Value("${huoMa.direct.stores.account}")
private String huoMaDirectStoresAccount;
@Value("${huoMa.direct.stores.password}")
private String huoMaDirectStoresPassword;
@Value("${huoMa.franchise.stores.account}")
private String huoMaFranchiseStoresAccount;
@Value("${huoMa.franchise.stores.password}")
private String huoMaFranchiseStoresPassword;
@Value("${huoMa.restaurant.stores.account}")
private String huoMaRestaurantStoresAccount;
@Value("${huoMa.restaurant.stores.password}")
private String huoMaRestaurantStoresPassword;
@Resource
RedisUtilPool redisUtilPool;
@Value("${huoMa.token.url}")
private String huoMaTokenUrl;
@Value("${huoMa.get.point.terminal.url}")
private String huoMaGetPointTerminalUrl;
@Value("${huoMa.id.url}")
private String huoMaGetStoreIdUrl;
@Value("${huoMa.store.device.detail.url}")
private String huoMaGetStoreXinFaDeviceDetailUrl ;
@Value("${huoMa.get.tag.url}")
private String huoMaGetTagUrl;
@Value("${huoMa.get.program.url}")
private String huoMaGetProgramUrl;
@Value("${huoMa.get.publish.url}")
private String huoMaGetPublishUrl;
private final Map<String, HuoMaAccountDTO> accountMap = new HashMap<>();
@PostConstruct
public void initAccountMap() {
accountMap.put("restaurant", new HuoMaAccountDTO(huoMaRestaurantStoresAccount, huoMaRestaurantStoresPassword));
accountMap.put("direct", new HuoMaAccountDTO(huoMaDirectStoresAccount, huoMaDirectStoresPassword));
accountMap.put("franchise", new HuoMaAccountDTO(huoMaFranchiseStoresAccount, huoMaFranchiseStoresPassword));
}
public String getStoreToken(String account, String password) {
String key = MessageFormat.format(RedisConstant.HUO_MA_TOKEN, account);
String accessToken = redisUtilPool.getString(key);
if (accessToken != null) {
return accessToken;
}
Map<String, String> requestBody = new HashMap<>();
requestBody.put("account", account);
requestBody.put("password", password);
String responseBody = sendPostRequest(JSONObject.toJSONString(requestBody), huoMaTokenUrl);
try{
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(responseBody);
String token = rootNode.path("data").path("token").asText();
//缓存60秒
redisUtilPool.setString(key, token,60*60);
return token;
}catch (Exception e){
log.error("解析获取token失败,url:{},responseBody:{}",huoMaTokenUrl, responseBody);
}
return null;
}
public List<StoreEquipmentDTO> getStoreEquipmentDataByStoreNumList(List<String> storeNumList, String token) {
Map<String,List<String>> requestBody = new HashMap<>();
requestBody.put("codeList", storeNumList);
String responseBody = sendPostRequestByToken(JSONObject.toJSONString(requestBody), huoMaGetPointTerminalUrl,token);
try{
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(responseBody);
return mapper.convertValue(rootNode.get("data"),
mapper.getTypeFactory().constructCollectionType(List.class, StoreEquipmentDTO.class));
}catch (Exception e){
log.error("解析获取data失败,url:{},responseBody:{}",huoMaTokenUrl, responseBody);
}
return Collections.emptyList();
}
public Integer getStoreIdByStoreNum(String storeNum, String token) {
String houMaStoreId = redisUtilPool.hashGet(RedisConstant.HUO_MA_STORE_ID, storeNum);
if (houMaStoreId != null) {
try {
return Integer.valueOf(houMaStoreId);
} catch (NumberFormatException e) {
// 如果缓存中的数据格式不正确,继续执行正常逻辑
log.warn("Redis缓存中的门店ID格式不正确storeNum: {}", storeNum);
}
}
StoreRequestDTO requestBody = new StoreRequestDTO("point_report", 0, 10, storeNum);
String responseBody = sendPostRequestByToken(JSONObject.toJSONString(requestBody), huoMaGetStoreIdUrl,token);
try{
Integer storeId = extractIdsFromResponse(responseBody);
redisUtilPool.hashSet(RedisConstant.HUO_MA_STORE_ID, storeNum, storeId.toString());
return storeId;
}catch (Exception e){
log.error("解析获取data失败,url:{},responseBody:{}",huoMaTokenUrl, responseBody);
}
return null;
}
public List<StoreXinFaDeviceDetail> getStoreXinFaDeviceDetail(String storeNum) {
String source = redisUtilPool.hashGet(RedisConstant.HUOMA_STORE_DEVICE_RESOURCE_KEY, storeNum);
if (StringUtils.isNotBlank(source)) {
HuoMaAccountDTO huoMaAccountDTO = accountMap.get(source);
if (Objects.nonNull(huoMaAccountDTO)) {
String token = getStoreToken(huoMaAccountDTO.getAccount(), huoMaAccountDTO.getPassword());
List<StoreXinFaDeviceDetail> deviceDetailDetail = getStoreXinFaDeviceDetailDetail(storeNum, token);
if (CollectionUtils.isNotEmpty(deviceDetailDetail)) {
return deviceDetailDetail;
}
}
}
for (Map.Entry<String, HuoMaAccountDTO> entry : accountMap.entrySet()) {
HuoMaAccountDTO huoMaAccountDTO = entry.getValue();
if (!huoMaAccountDTO.getIsQuery()) {
String token = getStoreToken(huoMaAccountDTO.getAccount(), huoMaAccountDTO.getPassword());
List<StoreXinFaDeviceDetail> deviceDetailDetail = getStoreXinFaDeviceDetailDetail(storeNum, token);
if (CollectionUtils.isNotEmpty(deviceDetailDetail)) {
redisUtilPool.hashSet(RedisConstant.HUOMA_STORE_DEVICE_RESOURCE_KEY, storeNum, entry.getKey(), Constants.REFRESH_TOKEN_EXPIRE);
return deviceDetailDetail;
}
}
}
return Collections.emptyList();
}
public List<StoreXinFaDeviceDetail> getStoreXinFaDeviceDetailDetail(String storeNum, String token) {
Integer storeIdByStoreNum = getStoreIdByStoreNum(storeNum, token);
return getStoreXinFaDeviceDetailByPointId(storeIdByStoreNum, token);
}
public List<TagDetailDTO> getAccountAllTags(String storeNum){
String source = redisUtilPool.hashGet(RedisConstant.HUOMA_STORE_DEVICE_RESOURCE_KEY, storeNum);
//获取标签 必须要知道门店属于哪个账号下 获取对应账号下的标签 如果获取不到 直接返回空
if (StringUtils.isEmpty(source)){
log.info("门店没有找到对应的账号,storeNum: {}", storeNum);
return new ArrayList<>();
}
HuoMaAccountDTO huoMaAccountDTO = accountMap.get(source);
if (Objects.nonNull(huoMaAccountDTO)){
huoMaAccountDTO.setIsQuery(true);
String token = getStoreToken(huoMaAccountDTO.getAccount(), huoMaAccountDTO.getPassword());
TagDTO tagDTO = new TagDTO("DEFAULT", 0, 30,"program");
String responseBody = null;
try{
responseBody = sendPostRequestByToken(JSONObject.toJSONString(tagDTO), huoMaGetTagUrl,token);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(responseBody);
// 直接转换整个数组
return mapper.convertValue(
rootNode.path("data").path("content"),
mapper.getTypeFactory().constructCollectionType(List.class, TagDetailDTO.class)
);
}catch (Exception e){
log.error("getAccountAllTags解析获取data失败,url:{},responseBody:{}",huoMaTokenUrl, responseBody);
}
}
return new ArrayList<>();
}
/**
* 获取账号下指定标签的id 只需要一下四个
* @param storeNum
* @return
*/
public List<Integer> getAccountSpecialTagIds( String storeNum,List<String> tagList) {
List<TagDetailDTO> accountAllTags = getAccountAllTags(storeNum);
if (accountAllTags != null) {
return accountAllTags.stream()
.filter(tag -> tag.getName() != null &&
tagList.contains(tag.getName()))
.map(TagDetailDTO::getId)
.collect(Collectors.toList());
}
return new ArrayList<>();
}
public List<ProgramResponseDTO> getProgramList(ProgramReqDTO programReqDTO) {
if (StringUtils.isEmpty(programReqDTO.getStoreCode())){
log.info("门店没有找到对应的账号,storeNum: {}", programReqDTO.getStoreCode());
return new ArrayList<>();
}
//先查询门店是属于哪个账号下
String source = redisUtilPool.hashGet(RedisConstant.HUOMA_STORE_DEVICE_RESOURCE_KEY, programReqDTO.getStoreCode());
//获取标签 必须要知道门店属于哪个账号下 获取对应账号下的标签 如果获取不到 直接返回空
if (StringUtils.isEmpty(source)){
log.info("门店没有找到对应的账号,storeNum: {}", programReqDTO.getStoreCode());
return new ArrayList<>();
}
HuoMaAccountDTO huoMaAccountDTO = accountMap.get(source);
if (huoMaAccountDTO!=null){
String token = getStoreToken(huoMaAccountDTO.getAccount(), huoMaAccountDTO.getPassword());
String responseBody = null;
try{
responseBody = sendPostRequestByToken(JSONObject.toJSONString(programReqDTO), huoMaGetProgramUrl,token);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(responseBody);
// 直接转换整个数组
return mapper.convertValue(
rootNode.path("data").path("content"),
mapper.getTypeFactory().constructCollectionType(List.class, ProgramResponseDTO.class)
);
}catch (Exception e){
log.error("getProgramList 解析获取data失败,url:{},responseBody:{}",huoMaTokenUrl, responseBody);
}
}
return new ArrayList<>();
}
public Boolean publish(PublishDTO publishDTO){
if (StringUtils.isEmpty(publishDTO.getStoreCode())){
log.info("门店没有找到对应的账号,发布失败,storeNum: {}", publishDTO.getStoreCode());
return Boolean.FALSE;
}
String source = redisUtilPool.hashGet(RedisConstant.HUOMA_STORE_DEVICE_RESOURCE_KEY, publishDTO.getStoreCode());
//获取标签 必须要知道门店属于哪个账号下 获取对应账号下的标签 如果获取不到 直接返回空
if (StringUtils.isEmpty(source)){
log.info("门店没有找到对应的账号,storeNum: {}", publishDTO.getStoreCode());
return Boolean.FALSE;
}
HuoMaAccountDTO huoMaAccountDTO = accountMap.get(source);
if (huoMaAccountDTO!=null){
String token = getStoreToken(huoMaAccountDTO.getAccount(), huoMaAccountDTO.getPassword());
String responseBody = null;
try{
responseBody = sendPostRequestByToken(JSONObject.toJSONString(publishDTO), huoMaGetPublishUrl,token);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(responseBody);
int code = rootNode.get("code").asInt();
if (code != 0) {
throw new RuntimeException("发布失败");
}
// 直接转换整个数组
log.info("发布成功 deviceId:{},storeCode:{}",JSONObject.toJSONString(publishDTO.getDeviceIdList()), publishDTO.getStoreCode() );
return Boolean.TRUE;
}catch (Exception e){
log.error("发布失败, url:{}, responseBody:{}", huoMaTokenUrl, responseBody);
}
}
return Boolean.FALSE;
}
public List<StoreXinFaDeviceDetail> getStoreXinFaDeviceDetailByPointId(Integer pointId, String token) {
if (pointId != null){
StoreXinFaDetailRequestDTO storeXinFaDetailRequestDTO = new StoreXinFaDetailRequestDTO(0, 10, pointId);
String responseBody = null;
try{
responseBody = sendPostRequestByToken(JSONObject.toJSONString(storeXinFaDetailRequestDTO), huoMaGetStoreXinFaDeviceDetailUrl,token);
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(responseBody);
// 直接转换整个数组
return mapper.convertValue(
rootNode.path("data").path("content"),
mapper.getTypeFactory().constructCollectionType(List.class, StoreXinFaDeviceDetail.class)
);
}catch (Exception e){
log.error("getStoreXinFaDeviceDetailByPointId解析获取data失败,url:{},responseBody:{}",huoMaTokenUrl, responseBody);
}
return null;
}
return null;
}
private Integer extractIdsFromResponse(String jsonResponse) throws IOException {
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonResponse);
// 遍历dataList数组提取ID
JsonNode dataList = rootNode.path("data").path("dataList");
for (JsonNode item : dataList) {
if (item.has("ID")) {
//取出第一个id
return item.get("ID").asInt();
}
}
return null;
}
private String sendPostRequest(String requestBody, String requestUrl) {
log.info("开始发送请求,url:{},requestBody:{}", requestUrl, requestBody);
int maxRetries = 3;
for (int i = 0; i < maxRetries; i++) {
try {
Request request = new Request.Builder()
.url(requestUrl)
.post(RequestBody.create(MediaType.parse("application/json"), requestBody))
.build();
return sendPost(requestUrl, request);
} catch (Exception e) {
log.warn("请求失败,第{}次重试,错误: {}", i + 1, e.getMessage());
if (i == maxRetries - 1) {
throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, "请求重试失败: " + e.getMessage());
}
// 重试前等待
try {
Thread.sleep(1000 * (i + 1));
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, "重试被中断");
}
}
}
throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, "请求重试失败");
}
private String sendPostRequestByToken(String requestBody, String requestUrl, String token) {
log.info("开始发送请求,url:{},requestBody:{}", requestUrl, requestBody);
int maxRetries = 3;
for (int i = 0; i < maxRetries; i++) {
try {
Request request = new Request.Builder()
.url(requestUrl)
.post(RequestBody.create(MediaType.parse("application/json"), requestBody))
.addHeader("token", token)
.build();
String result = sendPost(requestUrl, request);
// 检查是否token失效
if (isTokenExpired(result)) {
log.warn("Token已失效正在清除缓存并重试第{}次", i + 1);
// 直接清除对应账号的token缓存
token = clearAccountTokenCacheForToken(token);
// 抛出异常触发重试
throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, "Token失效需要重试");
}
return result;
} catch (ServiceException e) {
log.warn("请求失败,第{}次重试,错误: {}", i + 1, e.getMessage());
if (i == maxRetries - 1) {
throw e;
}
try {
Thread.sleep(1000 * (i + 1));
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, "重试被中断");
}
} catch (Exception e) {
log.warn("请求异常,第{}次重试,错误: {}", i + 1, e.getMessage());
if (i == maxRetries - 1) {
throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, "请求重试失败: " + e.getMessage());
}
try {
Thread.sleep(1000 * (20*i + 1));
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, "重试被中断");
}
}
}
throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, "请求重试失败");
}
private boolean isTokenExpired(String responseBody) {
try {
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(responseBody);
int code = rootNode.path("code").asInt(-1);
String msg = rootNode.path("msg").asText("");
return code == 501;
} catch (Exception e) {
log.error("检查token失效状态时发生错误: {}", e.getMessage());
return false;
}
}
private String clearAccountTokenCacheForToken(String token) {
try {
// 遍历可能的账号,找到对应的缓存并删除
String[] accounts = {
huoMaDirectStoresAccount,
huoMaFranchiseStoresAccount,
huoMaRestaurantStoresAccount
};
for (String account : accounts) {
if (account != null) {
String key = MessageFormat.format(RedisConstant.HUO_MA_TOKEN, account);
String cachedToken = redisUtilPool.getString(key);
if (token.equals(cachedToken)) {
redisUtilPool.delKey(key);
log.info("已清除账号 {} 的token缓存", account);
// 根据账号类型获取对应的密码并重新获取token
String password = getPasswordForAccount(account);
if (password != null) {
String newToken = getStoreToken(account, password);
if (newToken != null) {
log.info("已为账号 {} 重新获取新的token", account);
return newToken;
}
}
break;
}
}
}
} catch (Exception e) {
log.error("清除token缓存或重新获取token失败: {}", e.getMessage());
}
return null;
}
private String getPasswordForAccount(String account) {
if (account.equals(huoMaDirectStoresAccount)) {
return huoMaDirectStoresPassword;
} else if (account.equals(huoMaFranchiseStoresAccount)) {
return huoMaFranchiseStoresPassword;
} else if (account.equals(huoMaRestaurantStoresAccount)) {
return huoMaRestaurantStoresPassword;
}
return null;
}
@NotNull
private static String sendPost(String requestUrl, Request request) {
try (Response response = new OkHttpClient().newCall(request).execute()) {
log.info("发起请求 time{}", System.currentTimeMillis());
if (!response.isSuccessful()) {
log.info("HTTP请求失败msg: " + response.message());
throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR,
"HTTP请求失败状态码: " + response.code());
}
String responseBody = response.body().string();
log.info("请求成功responseBody:{}", JSONObject.toJSONString(responseBody));
return responseBody;
} catch (ServiceException e) {
throw e;
} catch (Exception e) {
log.error("API调用异常 - URL: {}, 错误: {}", requestUrl, e.getMessage(), e);
throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, "接口调用异常: " + e.getMessage());
}
}
}

View File

@@ -220,4 +220,6 @@ public class Constants
public static final String WANG_LEI_JOB_NUMBER = "19060164";
public static final int REFRESH_TOKEN_EXPIRE = 60*60*24*30;
}