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 进行参数校验

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

相关推荐
『 时光荏苒 』3 小时前
微信小程序实时日志记录-接口监控
微信小程序·小程序·微信小程序日志·日志抓取
老李不敲代码3 小时前
榕壹云外卖跑腿系统:基于Spring Boot+MySQL+UniApp的智慧生活服务平台
spring boot·mysql·微信小程序·uni-app·软件需求
社会底层无业大学生5 小时前
微信小程序跳
微信小程序·小程序·notepad++
ace_TiAmo7 小时前
React8+taro开发微信小程序,实现lottie动画
微信小程序·小程序·react·taro
老李不敲代码8 小时前
榕壹云在线商城系统:基于THinkPHP+ Mysql+UniApp全端适配、高效部署的电商解决方案
mysql·微信小程序·小程序·uni-app·软件需求
小咕聊编程12 小时前
【含文档+PPT+源码】基于微信小程序的卫生院预约挂号管理系统的设计与实现
微信小程序·小程序
夜猫的兔子14 小时前
微信小程序中使用ECharts 并且动态设置数据
微信小程序·小程序·echarts
2305_7978820919 小时前
美食推荐小程序
大数据·微信小程序·小程序
社会底层无业大学生1 天前
微信小程序跳2
微信小程序·小程序·notepad++