一、开发前准备清单

1. 微信侧配置

  • 注册小程序(微信公众平台

  • 获取小程序 AppID 和 AppSecret(开发管理->开发设置)

  • 申请订阅消息模板(功能->订阅消息)

二、Spring Boot服务搭建

步骤1:初始化项目

通过 Spring Initializr 创建项目,选择:

  • Web

  • Redis

  • Lombok

步骤2:添加关键依赖

<!-- pom.xml -->
<dependencies>
    <!-- 基础依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Redis缓存 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <!-- JSON处理 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.83</version>
    </dependency>
</dependencies>

 步骤3:配置文件

# application.yml
wx:
  mini:
    appid: wx1234567890abcdef  # 替换你的AppID
    secret: your_app_secret_here
    template-id: TPL_001  # 订阅模板ID

spring:
  redis:
    host: localhost
    port: 6379

三、核心代码实现

1. 微信配置自动加载

@Data
@Configuration
@ConfigurationProperties(prefix = "wx.mini")
public class WxMiniConfig {
    private String appid;
    private String secret;
    private String templateId;
}

2. AccessToken管理器(自动刷新)

@Service
public class WxTokenService {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private WxMiniConfig wxConfig;

    private static final String REDIS_KEY = "wx:token";

    public String getValidToken() {
        // 1. 从Redis查询
        String token = redisTemplate.opsForValue().get(REDIS_KEY);
        if (StringUtils.isNotBlank(token)) return token;

        // 2. 调用微信API获取
        String url = String.format(
            "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
            wxConfig.getAppid(), wxConfig.getSecret()
        );
        
        JSONObject response = restTemplate.getForObject(url, JSONObject.class);
        token = response.getString("access_token");
        int expires = response.getIntValue("expires_in");

        // 3. 缓存Token(提前200秒过期)
        redisTemplate.opsForValue().set(
            REDIS_KEY, 
            token, 
            expires - 200, 
            TimeUnit.SECONDS
        );
        
        return token;
    }
}

3. 消息推送服务

@Service
public class MsgPushService {

    @Autowired
    private WxTokenService tokenService;
    @Autowired
    private WxMiniConfig wxConfig;

    public boolean pushMessage(String openid, Map<String,String> content) {
        String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" 
                   + tokenService.getValidToken();

        JSONObject params = new JSONObject();
        params.put("touser", openid);
        params.put("template_id", wxConfig.getTemplateId());
        params.put("page", "pages/index/index"); // 跳转路径

        JSONObject data = new JSONObject();
        content.forEach((key, val) -> {
            data.put(key, new JSONObject().fluentPut("value", val));
        });
        params.put("data", data);

        // 发送请求
        JSONObject result = restTemplate.postForObject(url, params, JSONObject.class);
        return result.getIntValue("errcode") == 0;
    }
}

4. 对外接口

@RestController
@RequestMapping("/api/msg")
public class MsgController {

    @Autowired
    private MsgPushService msgService;

    @PostMapping("/push")
    public ResponseEntity<String> push(@RequestParam String openid) {
        // 构建消息内容(需与模板参数对应)
        Map<String, String> data = new HashMap<>();
        data.put("thing1", "您的订单已发货");
        data.put("time2", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));

        boolean success = msgService.pushMessage(openid, data);
        return success ? 
            ResponseEntity.ok("推送成功") : 
            ResponseEntity.status(500).body("推送失败");
    }
}

四、小程序端对接

1. 触发订阅弹窗

// pages/order/order.js
Page({
  onSubscribe() {
    wx.requestSubscribeMessage({
      tmplIds: ['TPL_001'], // 替换你的模板ID
      success: (res) => {
        if (res['TPL_001'] === 'accept') {
          this.sendPushRequest();
        }
      }
    })
  },

  sendPushRequest() {
    wx.request({
      url: 'https://xxxx.com/api/msg/push',
      method: 'POST',
      data: {
        openid: getApp().globalData.userInfo.openid // 需提前获取openid
      },
      success: () => wx.showToast({ title: '消息已发送' })
    })
  }
})

2. 获取OpenID流程

// app.js
App({
  onLaunch() {
    wx.login({
      success: res => {
        wx.request({
          url: 'https://your.domain.com/api/wx/login',
          data: { code: res.code },
          success: (res) => {
            this.globalData.openid = res.data.openid;
          }
        })
      }
    })
  }
})

五、避坑指南

高频问题排查

错误码原因解决方案
40001AccessToken失效检查Redis缓存是否正常
43101用户未授权订阅确保已弹出订阅弹窗并点击同意
47003模板参数错误检查data字段与模板参数是否匹配
Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐