模板消息二次封装
Reid 2019-10-20
Java
微信小程序
呀,好久没写博客了,今天开始要重新抓起来。
# 需求
如果大家跟我一样做的业务是基于微信公众号的,那一定会接触到发送模板消息 (opens new window)的开发需求。
而实际开发中我们很少会自己手写原生代码去调用微信的 API,更多是借用第三方的 SDK,也等于是第一次封装,为什么说是第一次呢,因为仅仅第一次的封装还是会让咱们的代码很臃肿,比如说这样( 这是之前的一个 Node 项目):
...
sendTemplate(user.orderOpenId, this.config.Wechat.TemplateIds.Submitted, url, {
first: {
value: '您已成功下单,订单将在48小时内审核并发货,请耐心等候',
color: '#404040'
},
keyword1: {
value: order.id,
color: '#173177'
},
keyword2: {
value: brand.name,
color: '#173177'
},
keyword3: {
value: (order.totalPrice / 100).toFixed(2) + '元',
color: '#173177'
},
keyword4: {
value: moment(order.createdAt).format('LLL'),
color: '#173177'
},
remark: {
value: '请核对信息。',
color: '#404040'
}
})
...
# 重构
作为一段逻辑的绿叶逻辑,这样的代码很明显是不优雅的。让我们用 Java 重构一下(因为最近项目在用 Java 对以前的项目进行重构):
private void sendWxTemplate(String orderId) throws WxErrorException {
BrandPO brand = brandDao.selectById(body.getBrandId());
UserPO user = userDao.selectById(body.getUserId());
PreOrderWXTO preOrderTemplate = new PreOrderWXTO();
preOrderTemplate.setOrderId(orderId);
preOrderTemplate.setBrandName(brand.getName());
preOrderTemplate.setTotalPrice(BigDecimalUtils.div(body.getTotalPrice(), 100) + "");
preOrderTemplate.setOrderCreatedAt(DateUtils.simpleFormat(now));
weChatService.sendTemplateMsg(user.getOrderOpenId(), preOrderTemplate);
}
上面这段代码中我们隐藏了模板 id 与模板的配置,让开发人员只需关注需要传递的参数,而具体的封装代码如下:
下单成功模板消息体,因为 templateId 是 final 装饰的,所以并不会暴露出 setter。如果你对Lombok (opens new window)不熟悉,可以先去了解一下。
@Data
public class PreOrderWXTO {
private final String templateId = "WXTM000001";
private String orderId;
private String brandName;
private String totalPrice;
private String orderCreatedAt;
}
调用微信 API 发送模板消息,这里我用到的是WxJava (opens new window)这 SDK。
public void sendTemplateMsg(String openId, Object templateBundle) throws WxErrorException {
Map templateParams = MapUtils.objectToMap(templateBundle);
String templateId = (String) templateParams.get("templateId");
WechatMpConfig.MpConfig mpConfig = wechatMpConfig.getConfigs().get(0);
WxMpService wxMpService = WechatMpServiceConfig.getMpServices().get(mpConfig.getAppId());
// 根据模板id从数据库查出具体的配置
WxTemplateMessagePO templateMsg = wxTemplateMessageDao.selectById(templateId);
templateMsg = templateMsg.deserialize();
WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
.toUser(openId)
.templateId(templateMsg.getTemplateId())
.url(templateMsg.getUrl())
.build();
// 对#{}中的参数进行替换
for (WxTemplateMessagePO.MsgData data : templateMsg.getDatas()) {
String value = data.getValue();
Pattern pattern = Pattern.compile("#\\{\\w+}");
Matcher matcher = pattern.matcher(value);
List<String> paramKeys = new ArrayList<>();
while (matcher.find()) {
matcher.group(0);
String group = matcher.group(0);
paramKeys.add(group.replaceAll("[^\\w+]", ""));
}
for (String paramKey : paramKeys) {
String paramValue = (String) templateParams.get(paramKey);
value = value.replace("#{" + paramKey + "}", paramValue);
}
templateMessage.addData(new WxMpTemplateData(data.getKey(), value, data.getColor()));
}
wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);
}
微信模板配置表。
CREATE TABLE `wx_template_message` (
`code` char(10) NOT NULL,
`template_id` char(50) NOT NULL COMMENT '模板id',
`url` varchar(100) DEFAULT NULL COMMENT '转发地址',
`datas` text NOT NULL COMMENT '具体消息内容',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`code`),
UNIQUE KEY `UK_template_id` (`template_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='微信模板消息';
具体的配置,对应上面 SQL 中的 data 字段,#{}是自己约定的一种替换格式。
// 具体的配置,对应上面SQL中的data字段
[
{
"key": "first",
"value": "您已成功下单,订单将在48小时内审核并发货,请耐心等候",
"color": "#404040"
},
{
"key": "keyword1",
"value": "#{orderId}",
"color": "#173177"
},
{
"key": "keyword2",
"value": "#{brandName}",
"color": "#173177"
},
{
"key": "keyword3",
"value": "#{totalPrice}",
"color": "#173177"
},
{
"key": "keyword4",
"value": "#{orderCreatedAt}",
"color": "#173177"
},
{
"key": "remark",
"value": "请核对信息。",
"color": "#404040"
}
]
微信模板配置实体
@Data
@TableName("wx_template_message")
public class WxTemplateMessagePO {
@TableId
private String code;
private String templateId;
private String url;
@TableField("datas")
private String datasText;
@TableField(exist = false)
private List<MsgData> datas;
public WxTemplateMessagePO deserialize() {
this.datas = new Gson().fromJson(datasText, new TypeToken<ArrayList<MsgData>>(){}.getType());
return this;
}
@Data
public static class MsgData {
private String key;
private String value;
private String color;
}
}
# 总结
上面的 Java 代码没有对模板消息的 url 进行相关处理,这是因为我们项目最近还在重构中,加上以前很少需要配置 url,但基本做法都一样,大家也可以参考上面的代码配置 url 中的动态参数。
咳咳,整段代码下来,是不是感觉比原来的版本多了很多东西,虽然编写和设计这些东西会花费咱们一点时间,但这时间肯定是值得的,通过二次封装,就会让一个东西更简单易用。