对接微信小程序订阅消息

微信小程序订阅消息升级,以前的消息模板不适合使用了
官方文档 微信官方文档

需求

使用小程序开直播宣讲会,学生可以预约,已经预约的学生发送开播前提醒以及开播提醒

整体流程

1、由产品在微信小程序中设计好模板,并且把模板id及内容给到前端及后端
2、前端在小程序页面发起消息订阅授权,用户同意授权后,把微信返回的templateIds传给后端
3、后端保存templateIds及组装好相应的内容,保存到数据库
4、使用定时器去查询待发送的数据,发送给用户(开播前提醒)
5、用户播时,发送开播提醒

后端关键设计及代码

定义模板

public interface ConstantTemplate {
    /**
     * 提前提醒
     */
    String CLOUD_PREACH_TEMPLATE_ID = "xxxxxxxxxxxxxx";

    String CLOUD_PREACH_TEMPLATE_DATA = "{\"time1\":{\"value\":\"%s\"},\"thing2\":{\"value\":\"%s\"},\"thing3\":{\"value\":\"%s\"}}";
    /**
     * 开播提醒
     */
    String INTERVIEW_NOTICE_TEMPLATE_ID = "xxxxxxxxxxxxxxxx";

    String INTERVIEW_NOTICE_TEMPLATE_DATA ="{\"thing1\":{\"value\":\"%s\"},\"thing2\":{\"value\":\"%s\"},\time3\":{\"value\":\"%s\"},\"thing4\":{\"value\":\"%s\"},\"thing5\":{\"value\":\"%s\"}}";
}
@Getter
public enum WxMiniMsgTemplateEnum {
    /**
     * 微信小程序消息模板
     */
    CLOUD_PREACH_TEMPLATE(CLOUD_PREACH_TEMPLATE_ID, CLOUD_PREACH_TEMPLATE_DATA),
    INTERVIEW_NOTICE_TEMPLATE(INTERVIEW_NOTICE_TEMPLATE_ID, INTERVIEW_NOTICE_TEMPLATE_DATA);

    private final String id;
    private final String data;

    WxMiniMsgTemplateEnum(String id, String data) {
        this.id = id;
        this.data = data;
    }

    public static String getDataById(String id) {
        return Arrays.stream(WxMiniMsgTemplateEnum.values()).filter(r -> r.getId().equals(id)).findFirst().map(WxMiniMsgTemplateEnum::getData).orElse(null);

    }
}

保存消息

for (String templateId : dto.getTemplateIds()) {
      //发送小程序订阅消息
    XyWxMiniMsgDTO wxMsgDTO = new XyWxMiniMsgDTO();
    wxMsgDTO.setPage(dto.getPage());
    wxMsgDTO.setEndpoint(com.xyedu.sims.common.enums.SourceTypeEnum.STUDENT_MINI.getCode());
    wxMsgDTO.setLang("zh_CN");
    wxMsgDTO.setMiniprogramState(ConstantUtil.MiniprogramState);
    wxMsgDTO.setTouser(social.getOpenId());
    LocalDateTime sendTime = topicInfo.getStartTime();
    LocalDateTime pastTime = topicInfo.getEndTime();
    //提前20分钟发送通知
    wxMsgDTO.setSendTime(sendTime.minusMinutes(20));
    wxMsgDTO.setPastTime(pastTime);
    String date = DateUtil.getDefaultFormatDate(DateUtil.localDateTimeToDate(topicInfo.getStartTime()));
    //组装模板数据
    String data = WxMiniMsgTemplateEnum.getDataById(templateId);
    if (ToolUtil.isNotEmpty(data)) {
        data = String.format(data, date, StrUtil.subWithLength(topicInfo.getTitle(), 0, 20), userCache.getRealName() + "同学,云宣讲即将开始~");
        wxMsgDTO.setData(JSONUtil.toJsonStr(data));
    }
    wxMsgDTO.setTemplateId(templateId);
    XyWxMiniMsg miniMsg = BeanUtil.copy(wxMsgDTO, XyWxMiniMsg.class);
    //保存至数据库
    remoteXyWxMiniMsgService.add(miniMsg);
}

封装发送工具

@Slf4j
public class WxMiniMsgUtil {

    //官方文档: https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html

    private static final String WX_MINI_MSG_URL = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=";

    public static Object send(String accessToken, XyWxMiniMsgDTO dto) {
        String url = WX_MINI_MSG_URL + accessToken;       
        Map<String, Object> reqBody = new HashMap<>();
        reqBody.put("touser", dto.getTouser());
        reqBody.put("template_id", dto.getTemplateId());
        reqBody.put("miniprogram_state", dto.getMiniprogramState());
        reqBody.put("lang", dto.getLang());
        reqBody.put("page", dto.getPage());
        reqBody.put("data", JSONUtil.parseObj(dto.getData()));
        try {
            return HttpRequest.post(url)
                    .body(JSONUtil.toJsonStr(reqBody))
                    .timeout(5000)
                    .execute()
                    .body();
        } catch (Exception e) {
            log.error("send wx mini msg occur an exception,e={}", e.getMessage());
            return null;
        }
    }
}

使用定时器发送,定时器每3分钟执行一次查询,查询未发送及发送失败的消息,超过时间的,则会设置发送状态为过期

@Getter
public enum WxMiniMsgSendStatusEnum {

    /**
     *
     */
    SENDING(0, "未发送"),
    SEND_SUCCESS(1, "发送成功"),
    SEND_FAIL(2, "发送失败"),
    OVERDUE(3,"过期");

    private Integer code;
    private String msg;

    WxMiniMsgSendStatusEnum(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public static String getMsg(Object code) {
        if (code == null) {
            return null;
        }
        return Arrays.stream(values()).filter(e -> e.getCode().equals(code)).findFirst().map(e -> e.getMsg()).orElse("");
    }
}
	@Override
    public ReturnT<String> execute(String param) throws Exception {
        log.debug("[send wx mini msg]-start");
        XyWxMiniMsgQueryDTO dto = new XyWxMiniMsgQueryDTO();
        dto.setEndpoint(SourceTypeEnum.STUDENT_MINI.getCode());
        //从数据库中获取应发送的数据列表
        List<XyWxMiniMsg> list = remoteXyWxMiniMsgService.list(dto);
        if (CollectionUtil.isNotEmpty(list)) {
            log.debug("[send wx mini msg]-query list,list.size={}", list.size());
            final WxMaService wxService = WxMaConfiguration.getMaService();
            String accessToken = wxService.getAccessToken();
            for (XyWxMiniMsg msg : list) {
                XyWxMiniMsgDTO msgDTO = BeanUtil.copy(msg, XyWxMiniMsgDTO.class);
                //通知微信发送订阅消息
                Object response = WxMiniMsgUtil.send(accessToken, msgDTO);
                if (ToolUtil.isNotEmpty(response)) {
                    msg.setResponse(response.toString());
                    JSONObject object = JSONUtil.parseObj(response);
                    //处理微信响应信息
                    if (object.containsKey("errcode")
                            && NumberUtil.isNumber(object.get("errcode").toString())
                            && Integer.valueOf(object.get("errcode").toString()) == 0) {
                        msg.setSendStatus(WxMiniMsgSendStatusEnum.SEND_SUCCESS.getCode());
                    } else if (object.containsKey("errcode")
                            && NumberUtil.isNumber(object.get("errcode").toString())
                            && Integer.valueOf(object.get("errcode").toString()) == 42001) {
                        log.error("[send wx mini msg]-response fail,refresh accessToken={}", response);
                        msg.setSendStatus(WxMiniMsgSendStatusEnum.SEND_FAIL.getCode());
                        WxMaConfiguration.getMaService().getAccessToken(true);
                    } else {
                        msg.setSendStatus(WxMiniMsgSendStatusEnum.SEND_FAIL.getCode());
                        log.error("[send wx mini msg]-response fail,response={}", response);
                    }
                } else {
                    msg.setSendStatus(WxMiniMsgSendStatusEnum.SEND_FAIL.getCode());
                    log.error("[send wx mini msg]-response empty,may be catch exception,params[accessToken={}\n,msgDTO={}]", accessToken, msgDTO);
                }
            }
            remoteXyWxMiniMsgService.updateBatch(list);
        }
        log.debug("[send wx mini msg]-finish");
        return ReturnT.SUCCESS;
    }

需求第5点未完成,思路大概是在组装提前提醒模板时,同时保存开播提醒消息至数据库,并使用templateId+宣讲会id作为唯一标识,在开播时查询数据库对应的待发送消息,最后通过工具类进行发送即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值