feat:微信通知
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<String, String> 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<String, String> 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<String, String> 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<String, String> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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<String, TemplateDataItemDTO> 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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"; // 默认用深灰色
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
@@ -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<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);
|
||||
|
||||
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<String, Object> data, WechatCallback callback) {
|
||||
WechatTemplateMessageDTO messageDTO = templateMessageBuilder.buildNormalTemplate(openId, template, data);
|
||||
sendTemplateMessageAsync(messageDTO, callback);
|
||||
}
|
||||
|
||||
public void sendMiniAppTemplateAsync(String openId, WechatTemplateEnum template,
|
||||
Map<String, Object> 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);
|
||||
}
|
||||
}
|
||||
@@ -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<Boolean> testMiniAppTemplate() {
|
||||
Map<String, Object> 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user