springboot对接微信小程序,发送订阅消息

在Spring Boot中对接微信小程序发送订阅消息,需要通过微信服务端API实现。以下是完整的实现步骤和代码示例:

1. 准备工作

  1. 在小程序后台申请订阅消息模板,获取模板ID
  2. 确保小程序已获取用户订阅授权(需用户主动触发)
  3. 获取小程序的AppID和AppSecret

2. 配置参数(application.yml)

perl 复制代码
wechat:
  miniapp:
    appid: 你的小程序AppID
    secret: 你的小程序AppSecret
    subscribe-msg-url: https://api.weixin.qq.com/cgi-bin/message/subscribe/send
    access-token-url: https://api.weixin.qq.com/cgi-bin/token

3. 配置类

less 复制代码
@Data
@Configuration
@ConfigurationProperties(prefix = "wechat.miniapp")
public class WechatMiniConfig {
    private String appid;
    private String secret;
    private String subscribeMsgUrl;
    private String accessTokenUrl;
}

4. AccessToken管理(带缓存)

arduino 复制代码
@Component
public class WechatTokenManager {
    private static final Logger logger = LoggerFactory.getLogger(WechatTokenManager.class);
    
    @Autowired
    private WechatMiniConfig config;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private static final String ACCESS_TOKEN_KEY = "wechat:miniapp: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 = config.getAccessTokenUrl() + "?grant_type=client_credential" +
                "&appid=" + config.getAppid() +
                "&secret=" + config.getSecret();

        try {
            RestTemplate restTemplate = new RestTemplate();
            String response = restTemplate.getForObject(url, String.class);
            JsonObject json = JsonParser.parseString(response).getAsJsonObject();
            
            if (json.has("errcode")) {
                logger.error("获取AccessToken失败:{}", response);
                throw new RuntimeException("微信接口调用失败");
            }
            
            String newToken = json.get("access_token").getAsString();
            int expiresIn = json.get("expires_in").getAsInt();
            
            redisTemplate.opsForValue().set(
                ACCESS_TOKEN_KEY, 
                newToken, 
                expiresIn - 200,  // 提前200秒过期
                TimeUnit.SECONDS
            );
            
            return newToken;
        } catch (Exception e) {
            logger.error("刷新AccessToken异常", e);
            throw new RuntimeException("微信服务不可用");
        }
    }
}

5. 订阅消息发送服务

typescript 复制代码
@Service
public class SubscribeMessageService {
    
    @Autowired
    private WechatTokenManager tokenManager;
    @Autowired
    private WechatMiniConfig config;

    public void sendSubscribeMessage(String openid, String templateId, 
                                    Map<String, Object> data) {
        String accessToken = tokenManager.getAccessToken();
        String url = config.getSubscribeMsgUrl() + "?access_token=" + accessToken;

        Map<String, Object> params = new HashMap<>();
        params.put("touser", openid);
        params.put("template_id", templateId);
        params.put("data", buildMessageData(data));
        params.put("page", "pages/index/index"); // 可选跳转页面

        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 Map<String, Map<String, String>> buildMessageData(Map<String, Object> data) {
        Map<String, Map<String, String>> result = new HashMap<>();
        data.forEach((key, value) -> {
            result.put(key, Collections.singletonMap("value", value.toString()));
        });
        return result;
    }

    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);
        }
    }
}

6. 控制器示例

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

    @Autowired
    private SubscribeMessageService messageService;

    @PostMapping("/send")
    public ResponseEntity<?> sendMessage(@RequestBody MessageRequest request) {
        Map<String, Object> data = new HashMap<>();
        data.put("thing1", new HashMap<String, String>(){{ put("value", "订单状态更新"); }});
        data.put("time2", new HashMap<String, String>(){{ put("value", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME)); }});
        
        messageService.sendSubscribeMessage(
            request.getOpenid(),
            "你的模板ID",
            data
        );
        
        return ResponseEntity.ok().build();
    }
}

7. 消息请求DTO

kotlin 复制代码
@Data
public class MessageRequest {
    @NotBlank
    private String openid;
    // 其他业务参数...
}

8. 注意事项

  1. 模板参数格式

    • 必须严格按照模板要求的参数格式发送
    • 每个参数值需要用 {"value": "内容"} 格式包裹
    • 参数值长度需符合模板限制
  2. AccessToken管理

    • 必须缓存access_token,每天获取次数有限(2000次/天)
    • 推荐使用Redis缓存,设置合理过期时间
  3. 用户授权

    • 需要用户主动订阅消息(前端需调用wx.requestSubscribeMessage)
    • 每个模板的授权是独立的
  4. 错误处理

    • 40001:access_token无效,需要重新获取
    • 43101:用户拒绝接受消息
    • 47003:模板参数不准确
  5. 性能优化

    • 使用连接池(推荐使用OkHttp或Apache HttpClient)
    • 异步发送非关键性消息

9. 完整调用流程

  1. 小程序端获取用户openid
  2. 用户触发订阅授权(前端调用wx.requestSubscribeMessage)
  3. 服务端接收发送请求
  4. 构造符合模板规范的消息内容
  5. 调用微信消息接口发送

10. 最佳实践建议

  1. 消息去重:对相同内容的消息添加业务去重机制
  2. 发送频率控制:避免对用户造成骚扰
  3. 模板版本管理:当模板修改时做好版本兼容
  4. 日志记录:记录消息发送日志以便排查问题
  5. 监控报警:对发送失败的情况建立监控机制

完整实现时建议配合使用:

  • Spring Boot Actuator 进行健康监控
  • Swagger 生成API文档
  • Spring Cache 优化Token缓存
  • Hibernate Validator 进行参数校验

以上方案已包含微信订阅消息的核心实现逻辑,可根据具体业务需求扩展消息模板管理、发送记录追踪等功能。

相关推荐
gaojianqiao12343 小时前
uniapp引入七鱼客服微信小程序SDK
微信小程序·uni-app
天上掉下来个程小白5 小时前
添加购物车-02.代码开发
java·服务器·前端·后端·spring·微信小程序·苍穹外卖
沙尘暴炒饭1 天前
用uniapp在微信小程序实现画板(电子签名)功能,使用canvas实现功能
微信小程序·小程序·uni-app
爱分享的程序员2 天前
小程序多线程实战
微信小程序
AALoveTouch2 天前
霸王茶姬微信小程序自动化签到系统完整实现&解析
微信小程序·自动化·notepad++
GalenWu3 天前
对象转换为 JSON 字符串(或反向解析)
前端·javascript·微信小程序·json
ᥬ 小月亮3 天前
Uniapp编写微信小程序,使用canvas进行绘图
微信小程序·uni-app·c#
BXCQ_xuan3 天前
uniapp小程序轮播图高度自适应优化详解
微信小程序·小程序·uni-app
艾路菲尔3 天前
微信小程序地图缩放scale隐性bug
微信小程序
前端开发小吴3 天前
微信小程序预览文件 兼容性苹果
微信小程序·小程序