uniapp 微信小程序实现定时消息订阅提醒(前后端)

背景

消息订阅是解决小程序对用户主动发起通知的问题。

通知示例如下:

微信官方文档-消息

消息订阅实现有三种:

由于本小程序不属于线下公共服务,也无微信支付功能,因此选用一次性订阅消息(弹窗订阅)。(注意:一次性订阅,是指用户调用了wx.requestSubscribeMessage一次,小程序可发送一次通知,不能多次发送,若再次发送通知,只能用户再调用wx.requestSubscribeMessage一次)

消息订阅全流程:

前端实现

  • 步骤一:获取模板 ID

公共模版库选用一个符合的模版,记录模版ID

  • 步骤二:获取下发权限

前端只能通过点击事件触发wx.requestSubscribeMessage。

typescript 复制代码
// 做了防多次点击处理。
const subscribe = (item?: any) => {
      if (item.disabled) {
        return
      }

      if (isSubscribing) {
        uni.showToast({
          title: '正在订阅中,请稍后',
          icon: 'none',
        })
        return
      }

      isSubscribing = true

      uni.requestSubscribeMessage({
        tmplIds: [TEMPLATE_ID],// TEMPLATE_ID替换为选用的模版id
        success(res: any) {
          uni.hideLoading()
          if (res[TEMPLATE_ID] === 'accept') {
            setSubscribeStatus(true, '已订阅')
            uni.showToast({
              title: '订阅成功',
              icon: 'success',
            })
          }
        },
        fail() {
          uni.showToast({
            title: '订阅失败',
            icon: 'error',
          })
        },
        complete: () => {
          isSubscribing = false
        },
      })
    }

微信官方文档-小程序订阅消息(用户通过弹窗订阅)开发指南

后端实现

技术栈:springboot 本小程序业务需要,需要每天执行一次通知请求。因此先实现了一个定时任务类。

实现定时任务

1.在入口类,开启定时任务:@EnableScheduling

less 复制代码
@EnableScheduling // 开启定时任务
@SpringBootApplication
public class BirthdayReminderApplication {
    public static void main(String[] args) {
        SpringApplication.run(BirthdayReminderApplication.class, args);
    }

}
  1. 定时任务。每天定时发送通知
ini 复制代码
public class SubscriptionScheduler {

    @Autowired
    BirthdayService birthdayService;

    @Autowired
    WxSubscribeService wxSubscribeService;

    @Scheduled(cron = "0 30 11 * * ?") // 每天上午11:30点执行
    public void sendDailySubscription() {
        // 1. 查询需要发送订阅消息的用户列表
        List<Birthday> allBirthdayList = birthdayService.allList();
        List<Birthday> remindBirthdayList = CommonUtil.getReminderDate(allBirthdayList);

        String token = wxSubscribeService.getToken();

        for (Birthday birthday : remindBirthdayList) {
            wxSubscribeService.sendMsg(token, birthday);
        }
    }
}

订阅实现

流程:

需调用两个接口:先获取access_token 微信官方文档-获取token api-> 发送订阅消息请求微信官方文档-发送订阅api

获取access_token api参数需要用户小程序appid 以及secret

订阅消息请求 api参数需要 发送的用户openid,模版id,模版字段

注意:

  • 有些模版的字段长度可能有限制,而模版并未注明,不满足会导致发送通知请求失败。如本小程序选择的模版,字段name1 限制为3个字符,若超过,通知并未成功发送。
ini 复制代码
@Service
public class WxSubscribeServiceImpl implements WxSubscribeService {
    @Autowired
    private WeChatProperties weChatProperties;

    @Override
    public String getToken() {
        String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token";
        Map<String, String> map = new HashMap<>();
        map.put("appId", weChatProperties.getAppid());
        map.put("secret", weChatProperties.getSecret());
        map.put("grant_type", "client_credential");
        String result = HttpClientUtil.doGet(tokenUrl, map);
        JSONObject jsonObject = JSON.parseObject(result);
        String token = jsonObject.getString("access_token");
        System.out.println(result + token);
        return token;
    }

    @Override
    public void sendMsg(String token, Birthday birthday) {
        String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + token;
        HashMap<String, Object> map = new HashMap<>();
        map.put("touser", birthday.getCreator());
        map.put("template_id", weChatProperties.getTemplateId());
        map.put("page", "pages/index/index");

        try {
            HashMap<String, Object> data = new HashMap<>();

            String name = birthday.getName().length() > 3 ? birthday.getName().substring(0, 3) : birthday.getName();
            data.put("name1", formatParam(name));

            String solarBirthdayInThisYear = birthdayInThisYear(birthday.getBirthday());
            if (birthday.getBirthdayType().equals(BirthdayType.LUNAR)) {
                solarBirthdayInThisYear = CommonUtil.lunar2Solar(solarBirthdayInThisYear);
            }

            long daysDiff = ChronoUnit.DAYS.between(parseDate(solarBirthdayInThisYear), LocalDate.now());
            String dayDiffStr = daysDiff <= 0 ? "今天" : (daysDiff + "天后");
            data.put("thing2", formatParam(dayDiffStr));
            String dateStr = birthday.getBirthdayType().equals(BirthdayType.LUNAR) ?
                    CommonUtil.lunarBirthdayToText(birthday.getBirthday()) :
                    CommonUtil.getMonthDay(birthday.getBirthday());
            data.put("thing6", formatParam(dateStr));
            data.put("thing5", formatParam("别忘了送上生日祝福哦"));
            map.put("data", data);

            String jsonObject = JSON.toJSONString(map);
            HttpUtil.post(url, jsonObject);
        } catch (Exception e) {
            throw new RuntimeException("消息发送失败", e);
        }
    }
}

效果:

小程序端:发起一次性消息的订阅:(示例图) (可理解为用户授权允许消息通知)

服务器端发送消息通知:

相关推荐
Uyker2 小时前
微信小程序动态效果实战指南:从悬浮云朵到丝滑列表加载
前端·微信小程序·小程序
Uyker15 小时前
从零开始制作小程序简单概述
前端·微信小程序·小程序
打小就很皮...1 天前
HBuilder 发行Android(apk包)全流程指南
前端·javascript·微信小程序
前端缘梦1 天前
微信小程序登录方案实践-从账号体系到用户信息存储
前端·微信小程序
coding随想2 天前
2025年小程序开发全解析:技术储备、行业趋势与实战案例
微信小程序
Nueuis2 天前
微信小程序前端面经
前端·微信小程序·小程序
轩1152 天前
实现仿中国婚博会微信小程序
微信小程序·小程序
知否技术2 天前
2025微信小程序开发实战教程(一)
前端·微信小程序
喝牛奶的小蜜蜂2 天前
个人小程序:不懂后台,如何做数据交互
前端·微信小程序·小程序·云开发