Springboot对接微信公众号,发送模板消息,具体详细步骤

以下是使用 Spring Boot 对接微信公众号发送模板消息的详细步骤和完整实现方案:


1. 准备工作

  1. 公众号申请

    • 拥有认证的服务号(订阅号不支持模板消息)
    • 在公众号后台开通「模板消息」功能
  2. 获取模板ID

    • 在公众号后台 -> 功能 -> 模板消息 -> 添加模板
    • 选择行业模板或自定义模板内容
    • 记录生成的模板ID(如:XyZ1234...
  3. 获取凭证

    • AppIDAppSecret(公众号后台 -> 开发 -> 基本配置)

2. 项目配置

2.1 application.yml 配置

python 复制代码
# 微信配置
wechat:
  public:
    app-id: wx1234567890abcdef
    app-secret: 0123456789abcdef0123456789abcdef
    template-id: XyZ1234567890abcdefg  # 模板消息ID
    access-token-url: https://api.weixin.qq.com/cgi-bin/token
    template-msg-url: https://api.weixin.qq.com/cgi-bin/message/template/send

2.2 配置类

less 复制代码
@Data
@Configuration
@ConfigurationProperties(prefix = "wechat.public")
public class WechatPublicConfig {
    private String appId;
    private String appSecret;
    private String templateId;
    private String accessTokenUrl;
    private String templateMsgUrl;
}

3. AccessToken 管理

3.1 Redis 缓存实现

arduino 复制代码
@Component
public class WechatAccessTokenManager {
    
    @Autowired
    private WechatPublicConfig config;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    private static final String ACCESS_TOKEN_KEY = "wechat:public:access_token";

    public String getAccessToken() {
        String token = redisTemplate.opsForValue().get(ACCESS_TOKEN_KEY);
        if (StringUtils.isNotBlank(token)) {
            return token;
        }
        return refreshAccessToken();
    }

    private synchronized String refreshAccessToken() {
        String url = String.format("%s?grant_type=client_credential&appid=%s&secret=%s",
                config.getAccessTokenUrl(), config.getAppId(), config.getAppSecret());
        
        try {
            RestTemplate restTemplate = new RestTemplate();
            String response = restTemplate.getForObject(url, String.class);
            JsonObject json = JsonParser.parseString(response).getAsJsonObject();
            
            if (json.has("errcode")) {
                throw new RuntimeException("获取Token失败: " + response);
            }
            
            String newToken = json.get("access_token").getAsString();
            int expiresIn = json.get("expires_in").getAsInt();
            
            redisTemplate.opsForValue().set(
                ACCESS_TOKEN_KEY, 
                newToken, 
                expiresIn - 300,  // 提前5分钟过期
                TimeUnit.SECONDS
            );
            
            return newToken;
        } catch (Exception e) {
            throw new RuntimeException("刷新AccessToken失败", e);
        }
    }
}

4. 模板消息发送服务

4.1 消息发送核心类

typescript 复制代码
@Service
public class TemplateMessageService {

    @Autowired
    private WechatAccessTokenManager tokenManager;
    @Autowired
    private WechatPublicConfig config;

    public void sendTemplateMessage(String openId, Map<String, TemplateData> data) {
        String accessToken = tokenManager.getAccessToken();
        String url = config.getTemplateMsgUrl() + "?access_token=" + accessToken;

        Map<String, Object> params = new HashMap<>();
        params.put("touser", openId);
        params.put("template_id", config.getTemplateId());
        params.put("url", "https://yourdomain.com/detail"); // 跳转链接(可选)
        params.put("data", data);

        try {
            RestTemplate restTemplate = new RestTemplate();
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            
            HttpEntity<Map<String, Object>> request = new HttpEntity<>(params, headers);
            ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);
            
            handleResponse(response.getBody());
        } catch (Exception e) {
            throw new RuntimeException("发送模板消息失败", e);
        }
    }

    private void handleResponse(String responseBody) {
        JsonObject json = JsonParser.parseString(responseBody).getAsJsonObject();
        int errcode = json.get("errcode").getAsInt();
        if (errcode != 0) {
            String errmsg = json.get("errmsg").getAsString();
            throw new RuntimeException("微信接口错误[" + errcode + "]: " + errmsg);
        }
    }
}

// 模板数据封装类
@Data
@AllArgsConstructor
public class TemplateData {
    private String value;
    private String color; // 可选颜色值,如"#173177"
}

5. 控制器调用示例

less 复制代码
@RestController
@RequestMapping("/wechat/msg")
public class MessageController {

    @Autowired
    private TemplateMessageService messageService;

    @PostMapping("/send")
    public ResponseEntity<String> sendMsg(@RequestParam String openId) {
        // 构造模板参数(需与模板内容匹配)
        Map<String, TemplateData> data = new HashMap<>();
        data.put("first", new TemplateData("订单支付成功通知", "#173177"));
        data.put("keyword1", new TemplateData("2023123456789", null));
        data.put("keyword2", new TemplateData("¥199.00", "#FF0000"));
        data.put("remark", new TemplateData("感谢您的支持!", null));
        
        messageService.sendTemplateMessage(openId, data);
        return ResponseEntity.ok("消息已发送");
    }
}

6. 模板消息参数规范

假设模板内容为:

复制代码
{{first.DATA}}
订单编号:{{keyword1.DATA}}
订单金额:{{keyword2.DATA}}
{{remark.DATA}}

发送时 data 参数必须包含:

json 复制代码
{
  "data": {
    "first": {"value": "..."},
    "keyword1": {"value": "..."},
    "keyword2": {"value": "..."},
    "remark": {"value": "..."}
  }
}

7. 注意事项

  1. 模板匹配

    • 参数名称必须与模板中的 {{keywordX.DATA}} 完全一致
    • 参数个数必须匹配模板定义
  2. 颜色值

    • 可选参数,默认黑色
    • 使用十六进制格式(如 #173177
  3. 用户授权

    • 用户必须关注公众号
    • 模板消息需要用户授权(不同场景授权方式不同)
  4. 频率限制

    • 单个用户每日接收上限为 5 条
    • 同一内容 30 秒内不可重复发送
  5. 错误处理

    • 40001:AccessToken 失效,需刷新后重试
    • 41028:form_id 不正确(适用于一次性订阅场景)
    • 45009:API 调用太频繁

8. 完整调用流程

  1. 用户关注公众号,获取用户 openid
  2. 用户触发业务事件(如支付成功)
  3. 服务端构造模板参数
  4. 获取有效的 access_token
  5. 调用微信模板消息接口发送
  6. 处理发送结果(成功/失败)

9. 高级优化建议

  1. 异步发送 :使用 @Async 异步处理非实时消息
  2. 消息队列:通过 RabbitMQ/Kafka 解耦消息发送
  3. 模板管理:将模板ID存入数据库,实现动态配置
  4. 日志追踪:记录消息发送日志用于审计和排查问题
  5. HTTPS 证书:确保回调地址使用有效的 HTTPS 证书

通过以上步骤即可实现微信公众号模板消息的发送功能,建议结合具体业务需求进行扩展。

相关推荐
WindHunter61516 小时前
越是非标项目,越要先签“需求确认书”
经验分享·微信·制造·微信公众平台
羊群智妍2 天前
跨境、合规、垂类全覆盖 2026 GEO五强服务商适配指南
百度·微信·微信公众平台·新浪微博·segmentfault
sheji34162 天前
【开题答辩全过程】以 微信小程网上书店为例,包含答辩的问题和答案
微信
kyh10033811203 天前
第二个微信小游戏《汉字碰碰消》上线啦!
微信·微信小程序·微信小游戏·去水印微信小程序·养了个羊
kyh10033811203 天前
汉字消除微信小游戏实现教程
微信·微信小游戏·小游戏源码·消除小游戏
陈思杰系统思考Jason4 天前
系统思考:飞轮效应与系统结构
百度·微信·微信公众平台·新浪微博·微信开放平台
YJlio4 天前
WinObj 学习笔记(15.7):看懂内核对象管理器与命名空间的“地图”
linux·服务器·网络·windows·笔记·学习·微信
陈思杰系统思考Jason4 天前
系统思考:复盘第一性原理
百度·微信·微信公众平台·新浪微博·微信开放平台
孙严Pay5 天前
快捷支付:高效安全的在线支付新选择
笔记·科技·计算机网络·其他·微信
开开心心_Every5 天前
免费进销存管理软件:云端本地双部署
java·游戏·微信·eclipse·pdf·excel·语音识别