From 77524330274c2d5e1238a4519763a9a1136cdc13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E7=AB=B9=E7=BA=A2?= Date: Mon, 13 Oct 2025 15:36:42 +0800 Subject: [PATCH 01/22] =?UTF-8?q?feat:=E5=BE=AE=E4=BF=A1=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../enums/wechat/WechatTemplateEnum.java | 72 +++++++ .../java/com/cool/store/utils/OkHttpUtil.java | 195 ++++++++++++++++++ .../cool/store/dto/wechat/AccessTokenDTO.java | 17 ++ .../dto/wechat/WechatTemplateMessageDTO.java | 90 ++++++++ .../store/builder/TemplateMessageBuilder.java | 126 +++++++++++ .../weixin/WechatMiniappProperties.java | 32 +++ .../config/weixin/WechatMpProperties.java | 37 ++++ .../service/wechat/WechatTemplateService.java | 155 ++++++++++++++ .../controller/webb/PCTestController.java | 20 ++ 9 files changed, 744 insertions(+) create mode 100644 coolstore-partner-common/src/main/java/com/cool/store/enums/wechat/WechatTemplateEnum.java create mode 100644 coolstore-partner-common/src/main/java/com/cool/store/utils/OkHttpUtil.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/AccessTokenDTO.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/WechatTemplateMessageDTO.java create mode 100644 coolstore-partner-service/src/main/java/com/cool/store/builder/TemplateMessageBuilder.java create mode 100644 coolstore-partner-service/src/main/java/com/cool/store/config/weixin/WechatMiniappProperties.java create mode 100644 coolstore-partner-service/src/main/java/com/cool/store/config/weixin/WechatMpProperties.java create mode 100644 coolstore-partner-service/src/main/java/com/cool/store/service/wechat/WechatTemplateService.java diff --git a/coolstore-partner-common/src/main/java/com/cool/store/enums/wechat/WechatTemplateEnum.java b/coolstore-partner-common/src/main/java/com/cool/store/enums/wechat/WechatTemplateEnum.java new file mode 100644 index 000000000..adba1821f --- /dev/null +++ b/coolstore-partner-common/src/main/java/com/cool/store/enums/wechat/WechatTemplateEnum.java @@ -0,0 +1,72 @@ +package com.cool.store.enums.wechat; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * @Author suzhuhong + * @Date 2025/10/10 14:39 + * @Version 1.0 + */ +public enum WechatTemplateEnum { + + ORDER_PAY_SUCCESS("ORDER_PAY_SUCCESS", "TM00001", "订单支付成功通知", + "您的订单已支付成功\n订单号:{{orderNo.DATA}}\n支付金额:{{amount.DATA}}元\n支付时间:{{payTime.DATA}}\n感谢您的购买!"), + + TEST("TEST", "T3sp5gBItHKD8oCeEiQMjn7JXpngFiz3dDcaArk84xY", "收到工单通知", + "测试模板"), + ; + + + private final String code; + private final String templateId; + private final String title; + private final String content; + + WechatTemplateEnum(String code, String templateId, String title, String content) { + this.code = code; + this.templateId = templateId; + this.title = title; + this.content = content; + } + + @JsonValue + public String getCode() { + return code; + } + + public String getTemplateId() { + return templateId; + } + + public String getTitle() { + return title; + } + + public String getContent() { + return content; + } + + /** + * 根据code获取枚举 + */ + public static WechatTemplateEnum getByCode(String code) { + for (WechatTemplateEnum template : values()) { + if (template.getCode().equals(code)) { + return template; + } + } + return null; + } + + /** + * 根据模板ID获取枚举 + */ + public static WechatTemplateEnum getByTemplateId(String templateId) { + for (WechatTemplateEnum template : values()) { + if (template.getTemplateId().equals(templateId)) { + return template; + } + } + return null; + } +} diff --git a/coolstore-partner-common/src/main/java/com/cool/store/utils/OkHttpUtil.java b/coolstore-partner-common/src/main/java/com/cool/store/utils/OkHttpUtil.java new file mode 100644 index 000000000..668b1b7a0 --- /dev/null +++ b/coolstore-partner-common/src/main/java/com/cool/store/utils/OkHttpUtil.java @@ -0,0 +1,195 @@ +package com.cool.store.utils; +import com.cool.store.enums.ErrorCodeEnum; +import com.cool.store.exception.ServiceException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import java.io.IOException; +import java.util.Map; + +/** + * @Author suzhuhong + * @Date 2025/10/10 14:21 + * @Version 1.0 + * OkHttp工具类 + */ +@Slf4j +@Component +public class OkHttpUtil { + + @Autowired + private OkHttpClient okHttpClient; + + @Autowired + private ObjectMapper objectMapper; + + private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); + + /** + * GET请求 + */ + public String doGet(String url) throws IOException { + return doGet(url, null); + } + + /** + * GET请求 - 带请求头 + */ + public String doGet(String url, Map headers) throws IOException { + Request.Builder builder = new Request.Builder().url(url); + + // 添加请求头 + if (headers != null && !headers.isEmpty()) { + headers.forEach(builder::addHeader); + } + + Request request = builder.build(); + + try (Response response = okHttpClient.newCall(request).execute()) { + if (!response.isSuccessful()) { + throw new IOException("Unexpected code: " + response); + } + ResponseBody body = response.body(); + return body != null ? body.string() : null; + } + } + + /** + * POST请求 - JSON数据 + */ + public String doPostJson(String url, Object data) throws IOException { + return doPostJson(url, data, null); + } + + /** + * POST请求 - JSON数据,带请求头 + */ + public String doPostJson(String url, Object data, Map headers) throws IOException { + //打印日志 + logRequest(url, data); + + String json = objectMapper.writeValueAsString(data); + RequestBody requestBody = RequestBody.create( JSON,json); + Request.Builder builder = new Request.Builder() + .url(url) + .post(requestBody); + + // 添加请求头 + if (headers != null && !headers.isEmpty()) { + headers.forEach(builder::addHeader); + } + + Request request = builder.build(); + + try (Response response = okHttpClient.newCall(request).execute()) { + if (!response.isSuccessful()) { + throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR, + "HTTP请求失败,状态码: " + response.code()); + } + ResponseBody body = response.body(); + + logResponse(url, response.code(), body.string()); + return body != null ? body.string() : null; + } + } + + /** + * 异步GET请求 + */ + public void doGetAsync(String url, Callback callback) { + doGetAsync(url, null, callback); + } + + /** + * 异步GET请求 - 带请求头 + */ + public void doGetAsync(String url, Map headers, Callback callback) { + Request.Builder builder = new Request.Builder().url(url); + + if (headers != null && !headers.isEmpty()) { + headers.forEach(builder::addHeader); + } + + Request request = builder.build(); + okHttpClient.newCall(request).enqueue(callback); + } + + /** + * 异步POST请求 + */ + public void doPostAsync(String url, Object data, Callback callback) { + doPostAsync(url, data, null, callback); + } + + /** + * 异步POST请求 - 带请求头 + */ + public void doPostAsync(String url, Object data, Map headers, Callback callback) { + try { + String json = objectMapper.writeValueAsString(data); + RequestBody requestBody = RequestBody.create( JSON,json); + + Request.Builder builder = new Request.Builder() + .url(url) + .post(requestBody); + + if (headers != null && !headers.isEmpty()) { + headers.forEach(builder::addHeader); + } + + Request request = builder.build(); + okHttpClient.newCall(request).enqueue(callback); + } catch (IOException e) { + callback.onFailure(null, e); + } + } + + 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-model/src/main/java/com/cool/store/dto/wechat/AccessTokenDTO.java b/coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/AccessTokenDTO.java new file mode 100644 index 000000000..2b046715e --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/AccessTokenDTO.java @@ -0,0 +1,17 @@ +package com.cool.store.dto.wechat; + +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/10/10 15:01 + * @Version 1.0 + */ +@Data +public class AccessTokenDTO { + + private String access_token; + + private Integer expires_in; + +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/WechatTemplateMessageDTO.java b/coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/WechatTemplateMessageDTO.java new file mode 100644 index 000000000..33f7a681a --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/WechatTemplateMessageDTO.java @@ -0,0 +1,90 @@ +package com.cool.store.dto.wechat; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.Map; + +/** + * @Author suzhuhong + * @Date 2025/10/10 14:36 + * @Version 1.0 + */ +@Data +public class WechatTemplateMessageDTO { + + /** + * 接收者openid + */ + @JsonProperty("touser") + private String toUser; + + /** + * 模板ID + */ + @JsonProperty("template_id") + private String templateId; + + /** + * 模板跳转链接(非必须) + */ + private String url; + + /** + * 跳小程序所需数据,不需跳小程序可不用传该数据 + */ + private MiniprogramDTO miniprogram; + + /** + * 模板数据 + */ + private Map data; + + /** + * 小程序跳转DTO + */ + @Data + public static class MiniprogramDTO { + + /** + * 所需跳转到的小程序appid + */ + private String appid; + + /** + * 所需跳转到小程序的具体页面路径,支持带参数 + */ + private String pagepath; + } + + /** + * 模板数据项DTO + */ + @Data + public static class TemplateDataItemDTO { + + /** + * 模板内容 + */ + private String value; + + /** + * 模板内容字体颜色,不填默认为黑色 + */ + private String color; + + public TemplateDataItemDTO() { + } + + public TemplateDataItemDTO(String value) { + this.value = value; + this.color = "#333333"; + } + + public TemplateDataItemDTO(String value, String color) { + this.value = value; + this.color = color; + } + } + +} diff --git a/coolstore-partner-service/src/main/java/com/cool/store/builder/TemplateMessageBuilder.java b/coolstore-partner-service/src/main/java/com/cool/store/builder/TemplateMessageBuilder.java new file mode 100644 index 000000000..9881c7cb3 --- /dev/null +++ b/coolstore-partner-service/src/main/java/com/cool/store/builder/TemplateMessageBuilder.java @@ -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 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 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 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 buildTemplateData(Map data) { + Map 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"; // 默认用深灰色 + } + } +} diff --git a/coolstore-partner-service/src/main/java/com/cool/store/config/weixin/WechatMiniappProperties.java b/coolstore-partner-service/src/main/java/com/cool/store/config/weixin/WechatMiniappProperties.java new file mode 100644 index 000000000..b1be5c0f6 --- /dev/null +++ b/coolstore-partner-service/src/main/java/com/cool/store/config/weixin/WechatMiniappProperties.java @@ -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; + +} diff --git a/coolstore-partner-service/src/main/java/com/cool/store/config/weixin/WechatMpProperties.java b/coolstore-partner-service/src/main/java/com/cool/store/config/weixin/WechatMpProperties.java new file mode 100644 index 000000000..10acc5f25 --- /dev/null +++ b/coolstore-partner-service/src/main/java/com/cool/store/config/weixin/WechatMpProperties.java @@ -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"; +} diff --git a/coolstore-partner-service/src/main/java/com/cool/store/service/wechat/WechatTemplateService.java b/coolstore-partner-service/src/main/java/com/cool/store/service/wechat/WechatTemplateService.java new file mode 100644 index 000000000..58912b529 --- /dev/null +++ b/coolstore-partner-service/src/main/java/com/cool/store/service/wechat/WechatTemplateService.java @@ -0,0 +1,155 @@ +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.enums.wechat.WechatTemplateEnum; +import com.cool.store.utils.OkHttpUtil; +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 java.io.IOException; +import java.util.Map; + +/** + * @Author suzhuhong + * @Date 2025/10/10 14:15 + * @Version 1.0 + */ +@Slf4j +@Service +public class WechatTemplateService { + + + @Autowired + private WechatMpProperties wechatMpProperties; + + @Autowired + private TemplateMessageBuilder templateMessageBuilder; + + @Autowired + private OkHttpUtil okHttpUtil; + @Autowired + private ObjectMapper objectMapper; + + + public String getAccessToken() { + 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); + return responseDTO.getAccess_token(); + } + return null; + } catch (IOException e) { + log.error("获取access_token失败", e); + } catch (Exception e) { + log.error("解析access_token响应失败", e); + } + return null; + } + + public boolean sendNormalTemplate(String openId, WechatTemplateEnum template, Map data) { + WechatTemplateMessageDTO messageDTO = templateMessageBuilder.buildNormalTemplate(openId, template, data); + return sendTemplateMessage(messageDTO); + } + + public boolean sendMiniAppTemplate(String openId, WechatTemplateEnum template, + Map data, String miniAppPagePath) { + WechatTemplateMessageDTO messageDTO = templateMessageBuilder.buildMiniappTemplate( + openId, template, data, miniAppPagePath); + return sendTemplateMessage(messageDTO); + } + + public boolean sendMiniAppTemplateWithUrl(String openId, WechatTemplateEnum template, + Map 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); + + try { + String result = okHttpUtil.doPostJson(url, messageDTO); + log.info("发送模板消息响应: {}", result); + + return Boolean.TRUE; + } catch (Exception e) { + log.error("解析模板消息响应失败", e); + return false; + } + } + + public void sendTemplateMessageAsync(WechatTemplateMessageDTO messageDTO, WechatCallback callback) { + String accessToken = getAccessToken(); + if (accessToken == null) { + log.error("获取access_token失败,无法发送模板消息"); + callback.onFailure(new IOException("获取access_token失败")); + return; + } + + String url = String.format("%s?access_token=%s", + wechatMpProperties.getSendTemplateMessageUrl(), accessToken); + + okHttpUtil.doPostAsync(url, messageDTO, new okhttp3.Callback() { + @Override + public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException { + try { + String result = response.body() != null ? response.body().string() : null; + log.info("异步发送模板消息响应: {}", result); + + if (response.isSuccessful() && result != null) { + callback.onSuccess(Boolean.TRUE); + } else { + callback.onFailure(new IOException("请求失败,状态码: " + response.code())); + } + } catch (Exception e) { + callback.onFailure(e); + } + } + + @Override + public void onFailure(okhttp3.Call call, IOException e) { + log.error("异步发送模板消息失败", e); + callback.onFailure(e); + } + }); + } + + public void sendNormalTemplateAsync(String openId, WechatTemplateEnum template, + Map data, WechatCallback callback) { + WechatTemplateMessageDTO messageDTO = templateMessageBuilder.buildNormalTemplate(openId, template, data); + sendTemplateMessageAsync(messageDTO, callback); + } + + public void sendMiniAppTemplateAsync(String openId, WechatTemplateEnum template, + Map data, String miniAppPagePath, WechatCallback callback) { + WechatTemplateMessageDTO messageDTO = templateMessageBuilder.buildMiniappTemplate( + openId, template, data, miniAppPagePath); + sendTemplateMessageAsync(messageDTO, callback); + } + + public interface WechatCallback { + void onSuccess(Boolean successFlag); + void onFailure(Exception e); + } +} diff --git a/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCTestController.java b/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCTestController.java index 64c87bda8..2f6cdc355 100644 --- a/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCTestController.java +++ b/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCTestController.java @@ -8,11 +8,13 @@ import com.cool.store.dto.FoodTokenDTO; import com.cool.store.dto.GetAccessTokenDTO; import com.cool.store.dto.HqtTokenDTO; import com.cool.store.dto.ModifyPasswordDTO; +import com.cool.store.dto.wechat.WechatTemplateMessageDTO; import com.cool.store.entity.*; import com.cool.store.enums.DownSystemTypeEnum; import com.cool.store.enums.MessageEnum; import com.cool.store.enums.SMSMsgEnum; import com.cool.store.enums.point.ShopSubStageStatusEnum; +import com.cool.store.enums.wechat.WechatTemplateEnum; import com.cool.store.job.XxlJobHandler; import com.cool.store.mapper.FranchiseFeeMapper; import com.cool.store.mapper.LineInfoMapper; @@ -36,6 +38,7 @@ import com.cool.store.service.*; import com.cool.store.service.impl.CommonService; import com.cool.store.service.impl.OrderSysInfoServiceImpl; import com.cool.store.service.impl.UserAuthMappingServiceImpl; +import com.cool.store.service.wechat.WechatTemplateService; import com.cool.store.utils.CoolDateUtils; import com.cool.store.utils.RedisConstantUtil; import com.cool.store.utils.RedisUtilPool; @@ -556,4 +559,21 @@ public class PCTestController { return request; } + @Resource + WechatTemplateService wechatTemplateService; + + @ApiOperation("测试小程序模板消息") + @PostMapping("/testMiniAppTemplate") + public ApiResponse testMiniAppTemplate() { + Map data = new HashMap<>(); + data.put("character_string2", "ceshi002"); + data.put("thing10", "测试通知功能"); + data.put("time14", "2025-10-01 12:00:00"); + data.put("thing25", "正新管理有限公司"); + data.put("thing60", "上海市-松江区"); + wechatTemplateService.sendNormalTemplate("o9_unvpJy1SGdnkeun7igRBSLuB0", WechatTemplateEnum.TEST, data); + return ApiResponse.success(true); + } + + } From 89a14766ae7ee7fadd55b71d35975d6354ae4db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E7=AB=B9=E7=BA=A2?= Date: Tue, 14 Oct 2025 15:10:54 +0800 Subject: [PATCH 02/22] =?UTF-8?q?feat:=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolstore-partner-model/pom.xml | 4 + .../store/dto/wechat/CallbackMessageDTO.java | 35 ++++++ .../com/cool/store/handler/WeChatHandler.java | 106 ++++++++++++++++++ coolstore-partner-web/pom.xml | 4 + .../controller/webb/PCTestController.java | 38 +++++++ pom.xml | 6 + 6 files changed, 193 insertions(+) create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/CallbackMessageDTO.java create mode 100644 coolstore-partner-service/src/main/java/com/cool/store/handler/WeChatHandler.java diff --git a/coolstore-partner-model/pom.xml b/coolstore-partner-model/pom.xml index 0a5705685..b24df7cd5 100644 --- a/coolstore-partner-model/pom.xml +++ b/coolstore-partner-model/pom.xml @@ -39,6 +39,10 @@ com.alibaba easyexcel + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + \ No newline at end of file diff --git a/coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/CallbackMessageDTO.java b/coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/CallbackMessageDTO.java new file mode 100644 index 000000000..8ad82c9f3 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/CallbackMessageDTO.java @@ -0,0 +1,35 @@ +package com.cool.store.dto.wechat; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; +import lombok.Data; + +/** + * @Author suzhuhong + * @Date 2025/10/14 14:39 + * @Version 1.0 + */ +@Data +@JacksonXmlRootElement(localName = "xml") +public class CallbackMessageDTO { + + @JacksonXmlProperty(localName = "ToUserName") + private String toUserName; + + @JacksonXmlProperty(localName = "FromUserName") + private String fromUserName; + + @JacksonXmlProperty(localName = "CreateTime") + private Long createTime; + + @JacksonXmlProperty(localName = "MsgType") + private String msgType; + + @JacksonXmlProperty(localName = "Event") + private String event; + + @JacksonXmlProperty(localName = "EventKey") + private String eventKey; + + +} diff --git a/coolstore-partner-service/src/main/java/com/cool/store/handler/WeChatHandler.java b/coolstore-partner-service/src/main/java/com/cool/store/handler/WeChatHandler.java new file mode 100644 index 000000000..33adf8cb6 --- /dev/null +++ b/coolstore-partner-service/src/main/java/com/cool/store/handler/WeChatHandler.java @@ -0,0 +1,106 @@ +package com.cool.store.handler; + +import com.cool.store.dto.wechat.CallbackMessageDTO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * @Author suzhuhong + * @Date 2025/10/14 14:56 + * @Version 1.0 + */ +@Component +@Slf4j +public class WeChatHandler { + + + public String processMessage(CallbackMessageDTO message) { + String msgType = message.getMsgType(); + + switch (msgType) { + case "event": + return handleEvent(message); + +// case "text": +// return handleTextMessage(message); +// +// case "image": +// return handleImageMessage(message); + + default: + // 其他类型的消息直接回复success + return buildSuccessReply(message.getFromUserName(), message.getToUserName()); + } + } + + private String handleEvent(CallbackMessageDTO message) { + String event = message.getEvent(); + + switch (event) { + case "subscribe": + // 关注事件 - 绑定用户 + return handleSubscribeEvent(message); + + case "unsubscribe": + // 取消关注事件 - 解绑用户 + return handleUnsubscribeEvent(message); + + default: + return buildWelcomeReply(message.getFromUserName(), message.getToUserName()); + } + } + + private String handleSubscribeEvent(CallbackMessageDTO message) { + String openId = message.getFromUserName(); + + try { + //userBindingService.bindOfficialAccountUser(openId); + // 立即回复欢迎消息 + return buildWelcomeReply(message.getFromUserName(), message.getToUserName()); + + } catch (Exception e) { + // 即使处理失败也要返回success + return buildWelcomeReply(message.getFromUserName(), message.getToUserName()); + } + } + + /** + * 处理取消关注事件 + */ + private String handleUnsubscribeEvent(CallbackMessageDTO message) { + String openId = message.getFromUserName(); + + // 异步处理用户解绑 + //userBindingService.unbindOfficialAccountUser(openId); + + return "success"; + } + + private String buildSuccessReply(String fromUser, String toUser) { + return String.format( + "" + + "" + + "" + + "%d" + + "" + + "" + + "", + fromUser, toUser, System.currentTimeMillis() / 1000 + ); + } + + + private String buildWelcomeReply(String fromUser, String toUser) { + return String.format( + "" + + "" + + "" + + "%d" + + "" + + "" + + "", + fromUser, toUser, System.currentTimeMillis() / 1000 + ); + } + +} diff --git a/coolstore-partner-web/pom.xml b/coolstore-partner-web/pom.xml index 22e75fcb9..9afd3a4b0 100644 --- a/coolstore-partner-web/pom.xml +++ b/coolstore-partner-web/pom.xml @@ -33,6 +33,10 @@ com.aliyun.oss aliyun-sdk-oss + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + diff --git a/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCTestController.java b/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCTestController.java index 2f6cdc355..a35709410 100644 --- a/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCTestController.java +++ b/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCTestController.java @@ -8,6 +8,7 @@ import com.cool.store.dto.FoodTokenDTO; import com.cool.store.dto.GetAccessTokenDTO; import com.cool.store.dto.HqtTokenDTO; import com.cool.store.dto.ModifyPasswordDTO; +import com.cool.store.dto.wechat.CallbackMessageDTO; import com.cool.store.dto.wechat.WechatTemplateMessageDTO; import com.cool.store.entity.*; import com.cool.store.enums.DownSystemTypeEnum; @@ -15,6 +16,7 @@ import com.cool.store.enums.MessageEnum; import com.cool.store.enums.SMSMsgEnum; import com.cool.store.enums.point.ShopSubStageStatusEnum; import com.cool.store.enums.wechat.WechatTemplateEnum; +import com.cool.store.handler.WeChatHandler; import com.cool.store.job.XxlJobHandler; import com.cool.store.mapper.FranchiseFeeMapper; import com.cool.store.mapper.LineInfoMapper; @@ -43,6 +45,8 @@ import com.cool.store.utils.CoolDateUtils; import com.cool.store.utils.RedisConstantUtil; import com.cool.store.utils.RedisUtilPool; import com.cool.store.utils.poi.StringUtils; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -558,7 +562,41 @@ public class PCTestController { } return request; } + private final XmlMapper xmlMapper = new XmlMapper(); + @Autowired + WeChatHandler weChatHandler; + @RequestMapping(value = "/testWxNotice", produces = "application/xml;charset=UTF-8") + @ApiOperation("testWxNotice") + public String handleWechatMessage( + @RequestParam("signature") String signature, + @RequestParam("timestamp") String timestamp, + @RequestParam("nonce") String nonce, + @RequestParam(value = "echostr", required = false) String echostr, + @RequestBody(required = false) String requestBody) { + + System.out.println("收到微信消息:"); + System.out.println("signature: " + signature); + System.out.println("timestamp: " + timestamp); + System.out.println("nonce: " + nonce); + System.out.println("echostr: " + echostr); + System.out.println("requestBody: " + requestBody); + + // 验证签名 +// if (!verifySignature(signature, timestamp, nonce, TOKEN)) { +// return "signature verification failed"; +// } + if (StringUtils.isNotEmpty(requestBody)) { + try { + CallbackMessageDTO message = xmlMapper.readValue(requestBody, CallbackMessageDTO.class); + return weChatHandler.processMessage(message); + } catch (Exception e) { + log.info("回调处理失败 e:{}",e.getMessage()); + return "success"; + } + } + return nonce; + } @Resource WechatTemplateService wechatTemplateService; diff --git a/pom.xml b/pom.xml index b4d687d85..093ad8d70 100644 --- a/pom.xml +++ b/pom.xml @@ -232,6 +232,12 @@ alibabacloud-dysmsapi20170525 2.0.24 + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.15.2 + From 28e1b860b1ca2819f78b7548a49a8649dda12a72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E7=AB=B9=E7=BA=A2?= Date: Tue, 14 Oct 2025 17:01:52 +0800 Subject: [PATCH 03/22] =?UTF-8?q?feat:=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolstore-partner-model/pom.xml | 4 -- .../store/dto/wechat/CallbackMessageDTO.java | 9 --- .../com/cool/store/handler/WeChatHandler.java | 66 ++++++++++++++----- coolstore-partner-web/pom.xml | 4 -- .../controller/webb/PCTestController.java | 9 +-- pom.xml | 5 -- 6 files changed, 51 insertions(+), 46 deletions(-) diff --git a/coolstore-partner-model/pom.xml b/coolstore-partner-model/pom.xml index b24df7cd5..0a5705685 100644 --- a/coolstore-partner-model/pom.xml +++ b/coolstore-partner-model/pom.xml @@ -39,10 +39,6 @@ com.alibaba easyexcel - - com.fasterxml.jackson.dataformat - jackson-dataformat-xml - \ No newline at end of file diff --git a/coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/CallbackMessageDTO.java b/coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/CallbackMessageDTO.java index 8ad82c9f3..1bb7755dd 100644 --- a/coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/CallbackMessageDTO.java +++ b/coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/CallbackMessageDTO.java @@ -1,7 +1,5 @@ package com.cool.store.dto.wechat; -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import lombok.Data; /** @@ -10,25 +8,18 @@ import lombok.Data; * @Version 1.0 */ @Data -@JacksonXmlRootElement(localName = "xml") public class CallbackMessageDTO { - @JacksonXmlProperty(localName = "ToUserName") private String toUserName; - @JacksonXmlProperty(localName = "FromUserName") private String fromUserName; - @JacksonXmlProperty(localName = "CreateTime") private Long createTime; - @JacksonXmlProperty(localName = "MsgType") private String msgType; - @JacksonXmlProperty(localName = "Event") private String event; - @JacksonXmlProperty(localName = "EventKey") private String eventKey; diff --git a/coolstore-partner-service/src/main/java/com/cool/store/handler/WeChatHandler.java b/coolstore-partner-service/src/main/java/com/cool/store/handler/WeChatHandler.java index 33adf8cb6..df3ba74cf 100644 --- a/coolstore-partner-service/src/main/java/com/cool/store/handler/WeChatHandler.java +++ b/coolstore-partner-service/src/main/java/com/cool/store/handler/WeChatHandler.java @@ -3,6 +3,16 @@ package com.cool.store.handler; import com.cool.store.dto.wechat.CallbackMessageDTO; 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.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.StringReader; +import java.util.HashMap; +import java.util.Map; /** * @Author suzhuhong @@ -14,12 +24,37 @@ import org.springframework.stereotype.Component; public class WeChatHandler { - public String processMessage(CallbackMessageDTO message) { - String msgType = message.getMsgType(); + + public Map parseXmlToMap(String xmlContent) throws Exception { + Map 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 messageMap) { + String msgType = (String) messageMap.get("MsgType"); + String event = (String) messageMap.get("Event"); switch (msgType) { case "event": - return handleEvent(message); + return handleEvent(messageMap); // case "text": // return handleTextMessage(message); @@ -29,46 +64,45 @@ public class WeChatHandler { default: // 其他类型的消息直接回复success - return buildSuccessReply(message.getFromUserName(), message.getToUserName()); + return "success"; } } - private String handleEvent(CallbackMessageDTO message) { - String event = message.getEvent(); + private String handleEvent(Map 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(message); + return handleSubscribeEvent(fromUserName,toUserName); case "unsubscribe": // 取消关注事件 - 解绑用户 - return handleUnsubscribeEvent(message); + return handleUnsubscribeEvent(fromUserName,toUserName); default: - return buildWelcomeReply(message.getFromUserName(), message.getToUserName()); + return buildWelcomeReply(fromUserName, toUserName); } } - private String handleSubscribeEvent(CallbackMessageDTO message) { - String openId = message.getFromUserName(); + private String handleSubscribeEvent(String fromUserName,String toUserName) { try { - //userBindingService.bindOfficialAccountUser(openId); // 立即回复欢迎消息 - return buildWelcomeReply(message.getFromUserName(), message.getToUserName()); + return buildWelcomeReply(fromUserName, toUserName); } catch (Exception e) { // 即使处理失败也要返回success - return buildWelcomeReply(message.getFromUserName(), message.getToUserName()); + return buildWelcomeReply(fromUserName, toUserName); } } /** * 处理取消关注事件 */ - private String handleUnsubscribeEvent(CallbackMessageDTO message) { - String openId = message.getFromUserName(); + private String handleUnsubscribeEvent(String fromUserName,String toUserName) { // 异步处理用户解绑 //userBindingService.unbindOfficialAccountUser(openId); diff --git a/coolstore-partner-web/pom.xml b/coolstore-partner-web/pom.xml index 9afd3a4b0..22e75fcb9 100644 --- a/coolstore-partner-web/pom.xml +++ b/coolstore-partner-web/pom.xml @@ -33,10 +33,6 @@ com.aliyun.oss aliyun-sdk-oss - - com.fasterxml.jackson.dataformat - jackson-dataformat-xml - diff --git a/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCTestController.java b/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCTestController.java index a35709410..f197f089c 100644 --- a/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCTestController.java +++ b/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCTestController.java @@ -46,7 +46,6 @@ import com.cool.store.utils.RedisConstantUtil; import com.cool.store.utils.RedisUtilPool; import com.cool.store.utils.poi.StringUtils; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.dataformat.xml.XmlMapper; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -562,7 +561,6 @@ public class PCTestController { } return request; } - private final XmlMapper xmlMapper = new XmlMapper(); @Autowired WeChatHandler weChatHandler; @@ -582,14 +580,9 @@ public class PCTestController { System.out.println("echostr: " + echostr); System.out.println("requestBody: " + requestBody); - // 验证签名 -// if (!verifySignature(signature, timestamp, nonce, TOKEN)) { -// return "signature verification failed"; -// } if (StringUtils.isNotEmpty(requestBody)) { try { - CallbackMessageDTO message = xmlMapper.readValue(requestBody, CallbackMessageDTO.class); - return weChatHandler.processMessage(message); + return weChatHandler.processMessage(weChatHandler.parseXmlToMap(requestBody)); } catch (Exception e) { log.info("回调处理失败 e:{}",e.getMessage()); return "success"; diff --git a/pom.xml b/pom.xml index 093ad8d70..f3901d5b8 100644 --- a/pom.xml +++ b/pom.xml @@ -233,11 +233,6 @@ 2.0.24 - - com.fasterxml.jackson.dataformat - jackson-dataformat-xml - 2.15.2 - From 211fc19499fe4ff13127f545b4ec3bf731fb3f10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E7=AB=B9=E7=BA=A2?= Date: Tue, 14 Oct 2025 19:02:09 +0800 Subject: [PATCH 04/22] =?UTF-8?q?feat:=E5=BE=AE=E4=BF=A1=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../store/dao/PartnerUserWechatBindDAO.java | 21 +++++++++++++++++++ .../mapper/PartnerUserWechatBindMapper.java | 16 ++++++++++++++ .../mapper/PartnerUserWechatBindMapper.xml | 20 ++++++++++++++++++ .../store/entity/PartnerUserWechatBindDO.java | 4 ++++ .../impl/WechatMiniAppServiceImpl.java | 5 +++++ 5 files changed, 66 insertions(+) diff --git a/coolstore-partner-dao/src/main/java/com/cool/store/dao/PartnerUserWechatBindDAO.java b/coolstore-partner-dao/src/main/java/com/cool/store/dao/PartnerUserWechatBindDAO.java index 388e59e80..9134b3efb 100644 --- a/coolstore-partner-dao/src/main/java/com/cool/store/dao/PartnerUserWechatBindDAO.java +++ b/coolstore-partner-dao/src/main/java/com/cool/store/dao/PartnerUserWechatBindDAO.java @@ -31,6 +31,27 @@ public class PartnerUserWechatBindDAO { return partnerUserWechatBindMapper.insert(partnerUserWechatBindDO); } + /** + * 更新 + * @param partnerUserWechatBindDO + * @return + */ + public Integer update(PartnerUserWechatBindDO partnerUserWechatBindDO) { + if (partnerUserWechatBindDO == null) { + return 0; + } + return partnerUserWechatBindMapper.update(partnerUserWechatBindDO); + } + + /** + * 更新所有的unionId对应的服务号ID + * @param unionId 开发平台的用户ID + * @param serviceAccountOpenId 服务号ID + * @return + */ + public Integer updateByUnionId(String unionId,String serviceAccountOpenId) { + return partnerUserWechatBindMapper.updateByUnionId(unionId,serviceAccountOpenId); + } public PartnerUserWechatBindDO getByOpenIdAndPartnerId(String partnerId, String openId) { if (StringUtil.isEmpty(partnerId)|| StringUtil.isEmpty(openId)){ diff --git a/coolstore-partner-dao/src/main/java/com/cool/store/mapper/PartnerUserWechatBindMapper.java b/coolstore-partner-dao/src/main/java/com/cool/store/mapper/PartnerUserWechatBindMapper.java index a94d56629..d69cb7fba 100644 --- a/coolstore-partner-dao/src/main/java/com/cool/store/mapper/PartnerUserWechatBindMapper.java +++ b/coolstore-partner-dao/src/main/java/com/cool/store/mapper/PartnerUserWechatBindMapper.java @@ -18,6 +18,22 @@ public interface PartnerUserWechatBindMapper { */ Integer insert(PartnerUserWechatBindDO partnerUserWechatBindDO); + /** + * 更新数据 + * @param partnerUserWechatBindDO + * @return + */ + Integer update(PartnerUserWechatBindDO partnerUserWechatBindDO); + + + /** + * 更新所有的unionId对应的服务号ID + * @param unionId 开发平台的用户ID + * @param serviceAccountOpenId 服务号ID + * @return + */ + Integer updateByUnionId(String unionId,String serviceAccountOpenId); + /** * 根据partnerId与openId查询 * @param partnerId diff --git a/coolstore-partner-dao/src/main/resources/mapper/PartnerUserWechatBindMapper.xml b/coolstore-partner-dao/src/main/resources/mapper/PartnerUserWechatBindMapper.xml index c638c9338..a3fa9d4a3 100644 --- a/coolstore-partner-dao/src/main/resources/mapper/PartnerUserWechatBindMapper.xml +++ b/coolstore-partner-dao/src/main/resources/mapper/PartnerUserWechatBindMapper.xml @@ -8,6 +8,8 @@ + + @@ -17,15 +19,33 @@ open_id, bind_time, partner_id, + union_id, create_time ) VALUES ( #{openId, jdbcType=VARCHAR}, #{bindTime, jdbcType=TIMESTAMP}, #{partnerId, jdbcType=VARCHAR}, + #{unionId, jdbcType=VARCHAR}, #{createTime, jdbcType=TIMESTAMP} ) + + UPDATE xfsg_partner_user_wechat_bind + + + union_id = #{unionId, jdbcType=VARCHAR}, + + where id = #{id} + + + + + UPDATE xfsg_partner_user_wechat_bind + set service_account_open_id = #{serviceAccountOpenId} + where union_id = #{unionId} + + diff --git a/coolstore-partner-model/src/main/java/com/cool/store/entity/PartnerUserWechatBindDO.java b/coolstore-partner-model/src/main/java/com/cool/store/entity/PartnerUserWechatBindDO.java index f3496136a..79eabd0c4 100644 --- a/coolstore-partner-model/src/main/java/com/cool/store/entity/PartnerUserWechatBindDO.java +++ b/coolstore-partner-model/src/main/java/com/cool/store/entity/PartnerUserWechatBindDO.java @@ -36,6 +36,10 @@ public class PartnerUserWechatBindDO implements Serializable { */ private String partnerId; + private String unionId; + + private String serviceAccountOpenId; + /** * 创建时间 */ diff --git a/coolstore-partner-service/src/main/java/com/cool/store/service/impl/WechatMiniAppServiceImpl.java b/coolstore-partner-service/src/main/java/com/cool/store/service/impl/WechatMiniAppServiceImpl.java index dcdec32f3..e77939a32 100644 --- a/coolstore-partner-service/src/main/java/com/cool/store/service/impl/WechatMiniAppServiceImpl.java +++ b/coolstore-partner-service/src/main/java/com/cool/store/service/impl/WechatMiniAppServiceImpl.java @@ -110,9 +110,14 @@ 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 + zlPartnerUserBindDO.setUnionId(unionId); + partnerUserWechatBindDAO.update(zlPartnerUserBindDO); } BeanUtil.copyProperties(hyPartnerUserInfoDO, userInfoVO); fillLineInfo(userInfoVO, hyPartnerUserInfoDO.getPartnerId()); From ba6835a8876e856a36e696b3a05846bbdc30d210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E7=AB=B9=E7=BA=A2?= Date: Wed, 15 Oct 2025 10:32:48 +0800 Subject: [PATCH 05/22] =?UTF-8?q?feat:=E5=BE=AE=E4=BF=A1=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../store/dto/wechat/WechatUserInfoDTO.java | 115 ++++++++++++++++++ .../com/cool/store/handler/WeChatHandler.java | 20 ++- .../impl/WechatMiniAppServiceImpl.java | 6 +- .../service/wechat/WechatTemplateService.java | 29 +++++ 4 files changed, 165 insertions(+), 5 deletions(-) create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/WechatUserInfoDTO.java diff --git a/coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/WechatUserInfoDTO.java b/coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/WechatUserInfoDTO.java new file mode 100644 index 000000000..573ef562e --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/WechatUserInfoDTO.java @@ -0,0 +1,115 @@ +package com.cool.store.dto.wechat; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +/** + * @Author suzhuhong + * @Date 2025/10/15 9:56 + * @Version 1.0 + */ +@Data +public class WechatUserInfoDTO { + /** + * 用户是否订阅该公众号标识 + * 0代表未关注,1代表关注 + */ + private Integer subscribe; + + /** + * 用户的标识,对当前公众号唯一 + */ + private String openid; + + /** + * 用户的昵称 + */ + private String nickname; + + /** + * 用户的性别 + * 1为男性,2为女性,0为未知 + */ + private Integer sex; + + /** + * 用户所在城市 + */ + private String city; + + /** + * 用户所在国家 + */ + private String country; + + /** + * 用户所在省份 + */ + private String province; + + /** + * 用户的语言 + * 简体中文为zh_CN + */ + private String language; + + /** + * 用户头像 + */ + private String headimgurl; + + /** + * 用户关注时间,为时间戳 + */ + @JsonProperty("subscribe_time") + private Long subscribeTime; + + /** + * 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段 + */ + private String unionid; + + /** + * 公众号运营者对粉丝的备注 + */ + private String remark; + + /** + * 用户所在的分组ID(兼容旧的用户分组接口) + */ + private Integer groupid; + + /** + * 用户被打上的标签ID列表 + */ + @JsonProperty("tagid_list") + private List tagidList; + + /** + * 返回用户关注的渠道来源 + */ + @JsonProperty("subscribe_scene") + private String subscribeScene; + + /** + * 二维码扫码场景(开发者自定义) + */ + @JsonProperty("qr_scene") + private Long qrScene; + + /** + * 二维码扫码场景描述(开发者自定义) + */ + @JsonProperty("qr_scene_str") + private String qrSceneStr; + + /** + * 是否已关注 + */ + public boolean isSubscribed() { + return subscribe != null && subscribe == 1; + } + +} diff --git a/coolstore-partner-service/src/main/java/com/cool/store/handler/WeChatHandler.java b/coolstore-partner-service/src/main/java/com/cool/store/handler/WeChatHandler.java index df3ba74cf..624189a10 100644 --- a/coolstore-partner-service/src/main/java/com/cool/store/handler/WeChatHandler.java +++ b/coolstore-partner-service/src/main/java/com/cool/store/handler/WeChatHandler.java @@ -1,6 +1,8 @@ package com.cool.store.handler; -import com.cool.store.dto.wechat.CallbackMessageDTO; +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; @@ -8,6 +10,7 @@ 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; @@ -23,7 +26,10 @@ import java.util.Map; @Slf4j public class WeChatHandler { - + @Resource + PartnerUserWechatBindDAO partnerUserWechatBindDAO; + @Resource + WechatTemplateService wechatTemplateService; public Map parseXmlToMap(String xmlContent) throws Exception { Map result = new HashMap<>(); @@ -88,8 +94,16 @@ public class WeChatHandler { } private String handleSubscribeEvent(String fromUserName,String toUserName) { - try { + + //根据openId 获取用户信息 + WechatUserInfoDTO userInfo = wechatTemplateService.getUserInfo(fromUserName, null); + + //根据unionId 更新服务号ID + if (userInfo != null) { + partnerUserWechatBindDAO.updateByUnionId(userInfo.getUnionid(),fromUserName); + } + // 立即回复欢迎消息 return buildWelcomeReply(fromUserName, toUserName); diff --git a/coolstore-partner-service/src/main/java/com/cool/store/service/impl/WechatMiniAppServiceImpl.java b/coolstore-partner-service/src/main/java/com/cool/store/service/impl/WechatMiniAppServiceImpl.java index e77939a32..35fc28372 100644 --- a/coolstore-partner-service/src/main/java/com/cool/store/service/impl/WechatMiniAppServiceImpl.java +++ b/coolstore-partner-service/src/main/java/com/cool/store/service/impl/WechatMiniAppServiceImpl.java @@ -116,8 +116,10 @@ public class WechatMiniAppServiceImpl implements WechatMiniAppService { partnerUserWechatBindDAO.insertSelective(bindDO); }else { //维护unionId 针对老数据没有unionId - zlPartnerUserBindDO.setUnionId(unionId); - partnerUserWechatBindDAO.update(zlPartnerUserBindDO); + if (zlPartnerUserBindDO.getUnionId()==null){ + zlPartnerUserBindDO.setUnionId(unionId); + partnerUserWechatBindDAO.update(zlPartnerUserBindDO); + } } BeanUtil.copyProperties(hyPartnerUserInfoDO, userInfoVO); fillLineInfo(userInfoVO, hyPartnerUserInfoDO.getPartnerId()); diff --git a/coolstore-partner-service/src/main/java/com/cool/store/service/wechat/WechatTemplateService.java b/coolstore-partner-service/src/main/java/com/cool/store/service/wechat/WechatTemplateService.java index 58912b529..bb07d4bce 100644 --- a/coolstore-partner-service/src/main/java/com/cool/store/service/wechat/WechatTemplateService.java +++ b/coolstore-partner-service/src/main/java/com/cool/store/service/wechat/WechatTemplateService.java @@ -4,6 +4,7 @@ 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.poi.StringUtils; @@ -59,6 +60,34 @@ public class WechatTemplateService { 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 data) { WechatTemplateMessageDTO messageDTO = templateMessageBuilder.buildNormalTemplate(openId, template, data); return sendTemplateMessage(messageDTO); From 24b4be799e78cba4c8ee74d52ced89fc36a3b17c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E7=AB=B9=E7=BA=A2?= Date: Wed, 15 Oct 2025 10:47:57 +0800 Subject: [PATCH 06/22] =?UTF-8?q?feat:=E5=BE=AE=E4=BF=A1=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/mapper/PartnerUserWechatBindMapper.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coolstore-partner-dao/src/main/resources/mapper/PartnerUserWechatBindMapper.xml b/coolstore-partner-dao/src/main/resources/mapper/PartnerUserWechatBindMapper.xml index a3fa9d4a3..b77174957 100644 --- a/coolstore-partner-dao/src/main/resources/mapper/PartnerUserWechatBindMapper.xml +++ b/coolstore-partner-dao/src/main/resources/mapper/PartnerUserWechatBindMapper.xml @@ -36,8 +36,8 @@ union_id = #{unionId, jdbcType=VARCHAR}, - where id = #{id} + where id = #{id} From 0545fee340bf6d40b59663fb481eb3a4d16705b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E7=AB=B9=E7=BA=A2?= Date: Wed, 15 Oct 2025 11:12:22 +0800 Subject: [PATCH 07/22] =?UTF-8?q?feat:=E9=80=9A=E7=9F=A5=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/application-ab.properties | 2 ++ .../src/main/resources/application-online.properties | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/coolstore-partner-web/src/main/resources/application-ab.properties b/coolstore-partner-web/src/main/resources/application-ab.properties index 76aff14c4..a4c6e43aa 100644 --- a/coolstore-partner-web/src/main/resources/application-ab.properties +++ b/coolstore-partner-web/src/main/resources/application-ab.properties @@ -133,3 +133,5 @@ hqt.token.grant_type=client_credentials hqt.token.client.id=WrPffdGpcWkcPsbN hqt.token.client.secret=rYe9Cwug5LwQNIBJAiW0a7weF9CAhYCD +wechat.mp.appId=wx4a18ef8bb41aa55c +wechat.mp.appSecret=793904b58f4ecdead3bbe4312c5f5c45 diff --git a/coolstore-partner-web/src/main/resources/application-online.properties b/coolstore-partner-web/src/main/resources/application-online.properties index af4c35790..4701a60c9 100644 --- a/coolstore-partner-web/src/main/resources/application-online.properties +++ b/coolstore-partner-web/src/main/resources/application-online.properties @@ -134,4 +134,7 @@ hqt.token.url=https://tc.cloud.hecom.cn hqt.token.username=18161486722 hqt.token.grant_type=client_credentials hqt.token.client.id=WrPffdGpcWkcPsbN -hqt.token.client.secret=rYe9Cwug5LwQNIBJAiW0a7weF9CAhYCD \ No newline at end of file +hqt.token.client.secret=rYe9Cwug5LwQNIBJAiW0a7weF9CAhYCD + +wechat.mp.appId=wx4a18ef8bb41aa55c +wechat.mp.appSecret=793904b58f4ecdead3bbe4312c5f5c45 \ No newline at end of file From 1a416ce4b45dc0f9b4638e2a26f77dbc345a5234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E7=AB=B9=E7=BA=A2?= Date: Thu, 16 Oct 2025 15:44:09 +0800 Subject: [PATCH 08/22] =?UTF-8?q?feat:=E6=9C=8D=E5=8A=A1=E5=8F=B7=E9=80=9A?= =?UTF-8?q?=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wechat/WechatTemplateDetailEnum.java | 46 ++++++++++++++++ .../enums/wechat/WechatTemplateEnum.java | 22 ++++---- .../cool/store/dao/HyPartnerUserInfoDAO.java | 8 +++ .../store/mapper/HyPartnerUserInfoMapper.java | 3 ++ .../mapper/HyPartnerUserInfoMapper.xml | 19 +++++++ .../dto/wechat/ServiceAccountOpenIdDTO.java | 23 ++++++++ .../impl/MessageTemplateServiceImpl.java | 40 ++++++++++++++ .../service/wechat/WechatTemplateService.java | 54 ------------------- .../controller/webb/PCTestController.java | 2 +- 9 files changed, 153 insertions(+), 64 deletions(-) create mode 100644 coolstore-partner-common/src/main/java/com/cool/store/enums/wechat/WechatTemplateDetailEnum.java create mode 100644 coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/ServiceAccountOpenIdDTO.java diff --git a/coolstore-partner-common/src/main/java/com/cool/store/enums/wechat/WechatTemplateDetailEnum.java b/coolstore-partner-common/src/main/java/com/cool/store/enums/wechat/WechatTemplateDetailEnum.java new file mode 100644 index 000000000..f94d68138 --- /dev/null +++ b/coolstore-partner-common/src/main/java/com/cool/store/enums/wechat/WechatTemplateDetailEnum.java @@ -0,0 +1,46 @@ +package com.cool.store.enums.wechat; + +/** + * @Author suzhuhong + * @Date 2025/10/16 15:19 + * @Version 1.0 + */ +public enum WechatTemplateDetailEnum { + + CHARACTER_STRING2("编号","character_string2"), + THING10("项目名称","thing10"), + TIME14("完成时间","time14"), + THING25("客户名称","thing25"), + THING60("位置","thing60"), + ; + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + private String name; + + private String code; + + WechatTemplateDetailEnum(String name, String code) { + this.name = name; + this.code = code; + } + + + + +} diff --git a/coolstore-partner-common/src/main/java/com/cool/store/enums/wechat/WechatTemplateEnum.java b/coolstore-partner-common/src/main/java/com/cool/store/enums/wechat/WechatTemplateEnum.java index adba1821f..dc4906ebe 100644 --- a/coolstore-partner-common/src/main/java/com/cool/store/enums/wechat/WechatTemplateEnum.java +++ b/coolstore-partner-common/src/main/java/com/cool/store/enums/wechat/WechatTemplateEnum.java @@ -2,6 +2,12 @@ package com.cool.store.enums.wechat; import com.fasterxml.jackson.annotation.JsonValue; +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.List; + +import static com.cool.store.enums.wechat.WechatTemplateDetailEnum.*; + /** * @Author suzhuhong * @Date 2025/10/10 14:39 @@ -9,24 +15,22 @@ import com.fasterxml.jackson.annotation.JsonValue; */ public enum WechatTemplateEnum { - ORDER_PAY_SUCCESS("ORDER_PAY_SUCCESS", "TM00001", "订单支付成功通知", - "您的订单已支付成功\n订单号:{{orderNo.DATA}}\n支付金额:{{amount.DATA}}元\n支付时间:{{payTime.DATA}}\n感谢您的购买!"), + QUESTION_NOTICE("QUESTION_NOTICE", "T3sp5gBItHKD8oCeEiQMjn7JXpngFiz3dDcaArk84xY", "收到工单通知", + Arrays.asList(CHARACTER_STRING2,THING10,TIME14,THING25,THING60)), - TEST("TEST", "T3sp5gBItHKD8oCeEiQMjn7JXpngFiz3dDcaArk84xY", "收到工单通知", - "测试模板"), ; private final String code; private final String templateId; private final String title; - private final String content; + private final List contentList; - WechatTemplateEnum(String code, String templateId, String title, String content) { + WechatTemplateEnum(String code, String templateId, String title, List contentList) { this.code = code; this.templateId = templateId; this.title = title; - this.content = content; + this.contentList = contentList; } @JsonValue @@ -42,8 +46,8 @@ public enum WechatTemplateEnum { return title; } - public String getContent() { - return content; + public List getContentList() { + return contentList; } /** diff --git a/coolstore-partner-dao/src/main/java/com/cool/store/dao/HyPartnerUserInfoDAO.java b/coolstore-partner-dao/src/main/java/com/cool/store/dao/HyPartnerUserInfoDAO.java index f54825b8c..5dd700af4 100644 --- a/coolstore-partner-dao/src/main/java/com/cool/store/dao/HyPartnerUserInfoDAO.java +++ b/coolstore-partner-dao/src/main/java/com/cool/store/dao/HyPartnerUserInfoDAO.java @@ -1,5 +1,6 @@ package com.cool.store.dao; +import com.cool.store.dto.wechat.ServiceAccountOpenIdDTO; import com.cool.store.entity.HyPartnerUserInfoDO; import com.cool.store.mapper.HyPartnerUserInfoMapper; import com.google.common.collect.Lists; @@ -112,4 +113,11 @@ public class HyPartnerUserInfoDAO { return hyPartnerUserInfoMapper.selectPasswordIsNull(); } + + public List selectLastBindRecord(List mobileList){ + if (CollectionUtils.isEmpty(mobileList)){ + return Lists.newArrayList(); + } + return hyPartnerUserInfoMapper.selectLastBindRecord(mobileList); + } } diff --git a/coolstore-partner-dao/src/main/java/com/cool/store/mapper/HyPartnerUserInfoMapper.java b/coolstore-partner-dao/src/main/java/com/cool/store/mapper/HyPartnerUserInfoMapper.java index 4ac2fcb53..c25bedbdb 100644 --- a/coolstore-partner-dao/src/main/java/com/cool/store/mapper/HyPartnerUserInfoMapper.java +++ b/coolstore-partner-dao/src/main/java/com/cool/store/mapper/HyPartnerUserInfoMapper.java @@ -1,5 +1,6 @@ package com.cool.store.mapper; +import com.cool.store.dto.wechat.ServiceAccountOpenIdDTO; import com.cool.store.entity.HyPartnerUserInfoDO; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; @@ -55,4 +56,6 @@ public interface HyPartnerUserInfoMapper extends tk.mybatis.mapper.common.Mappe List selectPasswordIsNull(); + List selectLastBindRecord(@Param("mobileList") List mobileList); + } \ No newline at end of file diff --git a/coolstore-partner-dao/src/main/resources/mapper/HyPartnerUserInfoMapper.xml b/coolstore-partner-dao/src/main/resources/mapper/HyPartnerUserInfoMapper.xml index 7f7b18652..b6dd4cdb2 100644 --- a/coolstore-partner-dao/src/main/resources/mapper/HyPartnerUserInfoMapper.xml +++ b/coolstore-partner-dao/src/main/resources/mapper/HyPartnerUserInfoMapper.xml @@ -195,4 +195,23 @@ + + \ No newline at end of file diff --git a/coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/ServiceAccountOpenIdDTO.java b/coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/ServiceAccountOpenIdDTO.java new file mode 100644 index 000000000..425ba9021 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/dto/wechat/ServiceAccountOpenIdDTO.java @@ -0,0 +1,23 @@ +package com.cool.store.dto.wechat; + +import lombok.Data; + +import java.util.Date; + +/** + * @Author suzhuhong + * @Date 2025/10/16 14:13 + * @Version 1.0 + */ +@Data +public class ServiceAccountOpenIdDTO { + + private String partnerId; + + private String unionId; + + private String serviceAccountOpenId; + + private Date lastUpdateTime; + +} diff --git a/coolstore-partner-service/src/main/java/com/cool/store/service/impl/MessageTemplateServiceImpl.java b/coolstore-partner-service/src/main/java/com/cool/store/service/impl/MessageTemplateServiceImpl.java index 8ed0160bd..850636e67 100644 --- a/coolstore-partner-service/src/main/java/com/cool/store/service/impl/MessageTemplateServiceImpl.java +++ b/coolstore-partner-service/src/main/java/com/cool/store/service/impl/MessageTemplateServiceImpl.java @@ -10,9 +10,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; @@ -20,12 +23,15 @@ 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; @@ -70,9 +76,13 @@ public class MessageTemplateServiceImpl implements MessageTemplateService { @Resource MatterConfigDAO matterConfigDAO; @Resource + WechatTemplateService wechatTemplateService; + @Resource RedisUtilPool redisUtilPool; @Resource TaskExecutor noticeThreadPool; + @Resource + HyPartnerUserInfoDAO hyPartnerUserInfoDAO; @@ -207,6 +217,36 @@ public class MessageTemplateServiceImpl implements MessageTemplateService { JSONObject.toJSONString(request.getStoreInfoList()), JSONObject.toJSONString(request.getUserInfoList()), userId); + + //发送通知 + Set userIds = authUser.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()); + + //分批 查询用户信息 + List openIdList = new ArrayList<>(); + Lists.partition(new ArrayList<>(userIds), 100).forEach(x->{ + List userInfoByUserIds = enterpriseUserDAO.getUserInfoByUserIds(x); + //取出用户的手机号,过滤掉空的手机号 + List mobileList = userInfoByUserIds.stream().filter(user -> StringUtils.isNotBlank(user.getMobile())).map(EnterpriseUserDO::getMobile).collect(Collectors.toList()); + if (CollectionUtils.isNotEmpty(mobileList)){ + List 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 data = new HashMap<>(); + data.put(WechatTemplateDetailEnum.CHARACTER_STRING2.getCode(), "ceshi002"); + data.put(WechatTemplateDetailEnum.THING10.getCode(), messageTemplateDO.getMessageTitle()); + data.put(WechatTemplateDetailEnum.TIME14.getCode(), DateUtils.parseDateToStr(DateUtils.SPECIAL_DATE_START_1, messageTemplateDO.getDeadline())); + data.put(WechatTemplateDetailEnum.THING25.getCode(), "正新管理有限公司"); + data.put(WechatTemplateDetailEnum.THING60.getCode(), "上海市-松江区"); + openIdList.forEach(x->{ + wechatTemplateService.sendMiniAppTemplate(x, WechatTemplateEnum.QUESTION_NOTICE,data,null); + }); } catch (Exception e) { log.info("发布流程异常,已取消发布"); } finally { diff --git a/coolstore-partner-service/src/main/java/com/cool/store/service/wechat/WechatTemplateService.java b/coolstore-partner-service/src/main/java/com/cool/store/service/wechat/WechatTemplateService.java index bb07d4bce..a4768d52d 100644 --- a/coolstore-partner-service/src/main/java/com/cool/store/service/wechat/WechatTemplateService.java +++ b/coolstore-partner-service/src/main/java/com/cool/store/service/wechat/WechatTemplateService.java @@ -127,58 +127,4 @@ public class WechatTemplateService { return false; } } - - public void sendTemplateMessageAsync(WechatTemplateMessageDTO messageDTO, WechatCallback callback) { - String accessToken = getAccessToken(); - if (accessToken == null) { - log.error("获取access_token失败,无法发送模板消息"); - callback.onFailure(new IOException("获取access_token失败")); - return; - } - - String url = String.format("%s?access_token=%s", - wechatMpProperties.getSendTemplateMessageUrl(), accessToken); - - okHttpUtil.doPostAsync(url, messageDTO, new okhttp3.Callback() { - @Override - public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException { - try { - String result = response.body() != null ? response.body().string() : null; - log.info("异步发送模板消息响应: {}", result); - - if (response.isSuccessful() && result != null) { - callback.onSuccess(Boolean.TRUE); - } else { - callback.onFailure(new IOException("请求失败,状态码: " + response.code())); - } - } catch (Exception e) { - callback.onFailure(e); - } - } - - @Override - public void onFailure(okhttp3.Call call, IOException e) { - log.error("异步发送模板消息失败", e); - callback.onFailure(e); - } - }); - } - - public void sendNormalTemplateAsync(String openId, WechatTemplateEnum template, - Map data, WechatCallback callback) { - WechatTemplateMessageDTO messageDTO = templateMessageBuilder.buildNormalTemplate(openId, template, data); - sendTemplateMessageAsync(messageDTO, callback); - } - - public void sendMiniAppTemplateAsync(String openId, WechatTemplateEnum template, - Map data, String miniAppPagePath, WechatCallback callback) { - WechatTemplateMessageDTO messageDTO = templateMessageBuilder.buildMiniappTemplate( - openId, template, data, miniAppPagePath); - sendTemplateMessageAsync(messageDTO, callback); - } - - public interface WechatCallback { - void onSuccess(Boolean successFlag); - void onFailure(Exception e); - } } diff --git a/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCTestController.java b/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCTestController.java index f197f089c..af2d3ddc6 100644 --- a/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCTestController.java +++ b/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCTestController.java @@ -602,7 +602,7 @@ public class PCTestController { data.put("time14", "2025-10-01 12:00:00"); data.put("thing25", "正新管理有限公司"); data.put("thing60", "上海市-松江区"); - wechatTemplateService.sendNormalTemplate("o9_unvpJy1SGdnkeun7igRBSLuB0", WechatTemplateEnum.TEST, data); + wechatTemplateService.sendNormalTemplate("o9_unvpJy1SGdnkeun7igRBSLuB0", WechatTemplateEnum.QUESTION_NOTICE, data); return ApiResponse.success(true); } From 7b88d3bec90f1b2cadf30b2ecf016eb342e4723c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E7=AB=B9=E7=BA=A2?= Date: Thu, 16 Oct 2025 16:31:45 +0800 Subject: [PATCH 09/22] =?UTF-8?q?feat:=E6=9C=8D=E5=8A=A1=E5=8F=B7=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E5=BC=82=E5=B8=B8=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/mapper/HyPartnerUserInfoMapper.xml | 8 ++++---- .../store/service/impl/MessageTemplateServiceImpl.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/coolstore-partner-dao/src/main/resources/mapper/HyPartnerUserInfoMapper.xml b/coolstore-partner-dao/src/main/resources/mapper/HyPartnerUserInfoMapper.xml index b6dd4cdb2..6b4606782 100644 --- a/coolstore-partner-dao/src/main/resources/mapper/HyPartnerUserInfoMapper.xml +++ b/coolstore-partner-dao/src/main/resources/mapper/HyPartnerUserInfoMapper.xml @@ -197,10 +197,10 @@