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

效果:

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

服务器端发送消息通知:

相关推荐
stark张宇25 分钟前
超越 Hello World:深入小程序 Hybrid 初衷、安全配置与上线全链路
nginx·微信小程序·php
菜鸟una2 小时前
【瀑布流大全】分析原理及实现方式(微信小程序和网页都适用)
前端·css·vue.js·微信小程序·小程序·typescript
知识分享小能手7 小时前
uni-app 入门学习教程,从入门到精通, uni-app常用API的详细语法知识点(上)(5)
前端·javascript·vue.js·学习·微信小程序·小程序·uni-app
Strawberry967 小时前
chooseVideo传视频无法取到缩略图
微信小程序
江城开朗的豌豆14 小时前
小程序与H5的“握手言和”:无缝嵌入与双向通信实战
前端·javascript·微信小程序
江城开朗的豌豆14 小时前
小程序静默更新?用户却无感?一招教你“强提醒”
前端·javascript·微信小程序
菜鸟una16 小时前
【微信小程序 + map组件】自定义地图气泡?原生气泡?如何抉择?
前端·vue.js·程序人生·微信小程序·小程序·typescript
狂团商城小师妹1 天前
XYlease租赁商城小程序
微信·微信小程序·小程序
顾青1 天前
微信小程序 VisionKit 实战(二):静态图片人脸检测与人像区域提取
前端·微信小程序
顾青1 天前
微信小程序实现身份证识别与裁剪(基于 VisionKit)
前端·微信小程序