总体思路:运营后台处理某个任务时,推送相关消息到绑定了小程序的微信账户的服务通知里。需要用到用户的openid和用户在小程序中有交互动作产生的formId。
后台表结构设计:

小程序中的代码(taro-ui):
<View className="orderInfo"> <View className="infoItem" onClick={this.navigateTo.bind(this, '/pages/order/unHandleOrder')} > <AtForm className="tb-submitForm" onSubmit={this.onSubmit.bind(this)} reportSubmit > <AtButton formType='submit' className="num tb-submitBtn">{orderCount}</AtButton> </AtForm> <View className="infoTip">处理订单</View> </View> <View className="vLine" /> <View className="infoItem" onClick={this.navigateTo.bind(this, '/pages/me/msgCenter')}> <AtForm className="tb-submitForm" onSubmit={this.onSubmit.bind(this)} reportSubmit > <AtButton formType='submit' className="num tb-submitBtn">{msgCount}</AtButton> </AtForm> <View className="infoTip">未读消息</View> </View> </View>
//表单提交事件,用于采集用户form_id onSubmit (event) { this.props.dispatch(action('app/updateUserFormId', event.detail.formId)); }app.js:
*updateUserFormId({ payload }, { select, call, put }) { try { const wechatId = yield select(state => state.app.openid); const formId = payload; console.log('updateUserFormId print formId : ' + formId); console.log('updateUserFormId print wechatId : ' + wechatId); const data = { urlParms: '/sys/api/v1/user/updateUserFormId', bodys: { wechatId, formId }, }; const res = yield call(request, data); if (res.code == 0) { // nothing to do } else { Taro.showToast({ title: res.message, icon: 'none', duration: 2000, }); } } catch (e) { console.log(e); } },后台代码:
1)保存formId
/** * 更新用户的formId * @author xxx * @date 2019-03-05 17:31:57 * @param formId * @param wechatId * @return */@RequestMapping("/updateUserFormId")@ResponseBody@HttpSecurityAnnotation(typeValue = MethodType.USER_SERVICE,openLog=true)public String updateUserFormId(@NotBlank(message="formId不能为空") @Bodys(value="formId")String formId, @NotBlank(message="wechatId不能为空") @Bodys(value="wechatId")String wechatId){try{userService.updateUserFormId(wechatId,formId);}catch(Exception e){log.error("更新用户的formId失败!",e);return JSONUtil.wrapResponse(JSONUtil.ResponseCode.FAILURE,e.getMessage());}return JSONUtil.wrapResponse(JSONUtil.ResponseCode.SUCCESS,"更新用户的formId成功");}1)发送模板消息:
if(addOperation){//新增对账单,发送消息到买方微信(拥有对账权限的买方账号)。String open = env.getProperty("wechat.uniformMessage.open","off");if("off".equals(open)){//发送统一服务消息开关已关闭return;}//小程序跳转页面String page = env.getProperty("wechat.duizhangdan.page","pages/me/index");//7天 = 7 * 24 = 168小时,这里去掉1小时。//当前时间往前推167个小时,如果用户的的formId最后更新时间大于这个时间,则说明formId有效。//formId目前有效期微信给出的是7天。Date newDate = DateUtils.addHours(new Date(),-167);List<PersonVO> personList = this.userService.getMbrCellphones(ECAuthConstant.BLANACE_ACC, blanace.getBuyerId());if(!personList.isEmpty()){JSONObject data = new JSONObject();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Map<String,Object> keyMap1 = new HashMap<String,Object>();keyMap1.put("value",blanace.getBuyerName());//添加客户名称data.put("keyword1",keyMap1);Map<String,Object> keyMap2 = new HashMap<String,Object>();keyMap2.put("value",blanace.getSellerName());//添加卖方名称data.put("keyword2",keyMap2);Map<String,Object> keyMap3 = new HashMap<String,Object>();keyMap3.put("value",blanace.getMonth());//添加对账月份data.put("keyword3",keyMap3);Map<String,Object> keyMap4 = new HashMap<String,Object>();keyMap4.put("value",sdf.format(blanace.getCreateTime()));//添加创建时间data.put("keyword4",keyMap4);for(PersonVO personVO : personList){if(StringUtils.isEmpty(personVO.getWechatFormId())){ logger.warn("用户的微信formId为空,不发送消息!用户名[" + personVO.getPersonName() + "]");continue; } if(personVO.getWechatFormIdModifyTime().before(newDate)){ //formId认为无效了,则不用发送了。 logger.warn("用户的微信formId时间已失效,不发送消息!用户名[" + personVO.getPersonName() + "]"); continue;}final UniformMsg uniformMsg = new UniformMsg();uniformMsg.setTouser(personVO.getWechatId());//uniformMsg.setTouser("oZaHr4iznKdHEjGremZZfvPS3X3k");uniformMsg.setForm_id(personVO.getWechatFormId());//uniformMsg.setForm_id("7821c2ceb9e848f1a95a2af8ce5f5a1c");uniformMsg.setTemplate_id(env.getProperty("wechat.duizhangdan.template_id"));uniformMsg.setPage(page); //小程序跳转页面uniformMsg.setData(data);this.taskExecutor.execute(new Runnable() {public void run() {wechatService.sendUniformMessage(uniformMsg);}});}}}IWechatService.java:
@Autowired private Environment env; @Autowired private RedisTemplate redisTemplate; private volatile Boolean init = false; private String appid; private String secret; //获取微信accessToken接口地址 private String accessTokenIp; //获取微信发送统一模板消息接口地址 private String uniformMessageIp; private void init(){ init = true; appid = env.getProperty("wechat.appid"); secret = env.getProperty("wechat.secret"); accessTokenIp = env.getProperty("wechat.accesstoken.ip"); uniformMessageIp = env.getProperty("wechat.uniformMessage.ip"); } @Override public String getAccessToken() throws Exception{ if(!init){ init(); } NameValuePair[] nameValuePairs = new NameValuePair[3]; NameValuePair nameValuePair = new NameValuePair(); nameValuePair.setName("appid"); nameValuePair.setValue(this.appid); NameValuePair nameValuePair2 = new NameValuePair(); nameValuePair2.setName("secret"); nameValuePair2.setValue(this.secret); NameValuePair nameValuePair3 = new NameValuePair(); nameValuePair3.setName("grant_type"); nameValuePair3.setValue("client_credential"); nameValuePairs[0] = nameValuePair; nameValuePairs[1] = nameValuePair2; nameValuePairs[2] = nameValuePair3; HttpClient client = new HttpClient(); GetMethod method = new GetMethod(this.accessTokenIp); method.addRequestHeader("Content-Type", "application/x-www-form-urlencoded"); method.setQueryString(nameValuePairs); client.executeMethod(method); String retValue = method.getResponseBodyAsString(); method.releaseConnection(); JSONObject jsonObject = JSONObject.parseObject(retValue); if(jsonObject.get("errcode") != null){ logger.warn("微信小程序接口,获取accessToken失败,原因:{}",jsonObject.get("errmsg")); return null; } logger.info("打印accessToken jsonObject:{}",jsonObject.toJSONString()); //将微信accessToken放入redis中 redisTemplate.opsForValue().set("wechatAccessToken",jsonObject.get("access_token")); return (String)jsonObject.get("access_token"); } @Override public void sendUniformMessage(UniformMsg uniformMsg) { if(StringUtils.isEmpty(uniformMessageIp)){ init(); } Double randomNum = Math.random(); String redisAccesssToken = (String) redisTemplate.opsForValue().get("wechatAccessToken"); if(StringUtils.isEmpty(redisAccesssToken)){ try { this.getAccessToken(); } catch (Exception e) { logger.error("发送统一模板消息,获取accessToken异常!",e); throw new RuntimeException("发送统一模板消息,获取accessToken异常!"); } } redisAccesssToken = (String) redisTemplate.opsForValue().get("wechatAccessToken"); uniformMsg.getWeapp_template_msg().put("template_id",uniformMsg.getTemplate_id()); uniformMsg.getWeapp_template_msg().put("page",uniformMsg.getPage()); uniformMsg.getWeapp_template_msg().put("form_id",uniformMsg.getForm_id()); uniformMsg.getWeapp_template_msg().put("data",uniformMsg.getData()); uniformMsg.getWeapp_template_msg().put("emphasis_keyword",""); Map<String,Object> submitMap = Maps.newHashMap(); submitMap.put("touser",uniformMsg.getTouser()); submitMap.put("weapp_template_msg",uniformMsg.getWeapp_template_msg()); //请求url String postUrl = this.uniformMessageIp + "?access_token=" + redisAccesssToken; //执行请求发送 logger.info("[" + randomNum + "]submitMap:{}", JSON.toJSONString(submitMap)); String retValue = ""; try{ retValue = this.httpsRequest(postUrl,"POST",JSON.toJSONString(submitMap)); }catch(Exception e){ logger.error(e.getMessage(),e); return; } logger.info("[" + randomNum + "]打印retValue:{}",retValue); logger.info("[" + randomNum + "]打印accessToken:{}",redisAccesssToken); //得到返回结果 JSONObject jsonObject = JSONObject.parseObject(retValue); if(jsonObject.get("errcode") != null && (int)jsonObject.get("errcode") != 0){ logger.warn("[" + randomNum + "]微信小程序接口,发送统一模板消息失败,原因:{}",jsonObject.get("errmsg")); if(DJECConstants.WechatUniformMsgErrcodeEnum.ACCESS_TOKEN_INVALID.getCode() == (int)jsonObject.get("errcode")){ logger.info("[" + randomNum + "]accessToken失效,重新获取token,再调一次。"); try { redisAccesssToken = this.getAccessToken(); } catch (Exception e) { logger.error(e.getMessage(),e); return; } postUrl = this.uniformMessageIp + "?access_token=" + redisAccesssToken; try{ //retValue = this.doPost(postUrl,JSON.toJSONString(submitMap)); retValue = this.httpsRequest(postUrl,"POST",JSON.toJSONString(submitMap)); }catch(Exception e){ logger.error(e.getMessage(),e); return; } logger.info("[" + randomNum + "]打印retValue(第二次):{}",retValue); //得到返回结果 jsonObject = JSONObject.parseObject(retValue); if(jsonObject.get("errcode") != null && (int)jsonObject.get("errcode") !=0) { logger.warn("[" + randomNum + "]微信小程序接口,发送统一模板消息(第二次)失败,原因:{}", jsonObject.get("errmsg")); return; } logger.info("[" + randomNum + "]发送服务消息成功(第二次)。"); } return; } logger.info("[" + randomNum + "]发送服务消息成功。"); } //通用方法,执行请求连接。 public String httpsRequest(String requestUrl, String requestMethod, String outputStr){ try { URL url = new URL(requestUrl); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); // 设置请求方式(GET/POST) conn.setRequestMethod(requestMethod); conn.setRequestProperty("content-type", "application/x-www-form-urlencoded"); // 当outputStr不为null时向输出流写数据 if (null != outputStr) { OutputStream outputStream = conn.getOutputStream(); // 注意编码格式 outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } // 从输入流读取返回内容 InputStream inputStream = conn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; StringBuffer buffer = new StringBuffer(); while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } // 释放资源 bufferedReader.close(); inputStreamReader.close(); inputStream.close(); conn.disconnect(); return buffer.toString(); } catch (ConnectException ce) { logger.error("连接超时:{}",ce); } catch (Exception e) { logger.error("https请求异常:{}",e); } return null; }方法httpsRequest参考了:https://blog.csdn.net/wave_zf/article/details/82463715
UniformMsg.java:
import com.alibaba.fastjson.JSONObject;/** * 微信小程序统一发送模板消息对象 * @author XXXX * @date 2019年3月1日 18:14:15 */public class UniformMsg { /** * 微信小程序模板ID */ private String template_id; /** * 微信用户绑定的小程序openid */ private String touser; /** * 跳转至小程序的页面路径 */ private String page; /** * 表单提交场景下,为 submit 事件带上的 formId */ private String form_id; /** * 模板需要放大的关键词,不填则默认无放大 */ private String emphasis_keyword; private JSONObject weapp_template_msg = new JSONObject(); private JSONObject data = new JSONObject(); public String getTemplate_id() { return template_id; } public void setTemplate_id(String template_id) { this.template_id = template_id; } public String getTouser() { return touser; } public void setTouser(String touser) { this.touser = touser; } public String getPage() { return page; } public void setPage(String page) { this.page = page; } public String getForm_id() { return form_id; } public void setForm_id(String form_id) { this.form_id = form_id; } public String getEmphasis_keyword() { return emphasis_keyword; } public void setEmphasis_keyword(String emphasis_keyword) { this.emphasis_keyword = emphasis_keyword; } public JSONObject getData() { return data; } public void setData(JSONObject data) { this.data = data; } public JSONObject getWeapp_template_msg() { return weapp_template_msg; } public void setWeapp_template_msg(JSONObject weapp_template_msg) { this.weapp_template_msg = weapp_template_msg; }}restful工具测试:

request body json结构:
{ "touser" : "oZaHr4iznKdHEjGremZZfvPS3X3k", "weapp_template_msg" : { "data":{ "keyword1":{ "value":"娄底建材工程有限公司" }, "keyword2":{ "value":"2019-02" } }, "template_id":"mfFyTYNfYJhEOKxbW5hPSYOvlhcztM3qoclq6SHJ04s", "form_id" : "70aff62c35a64ece84a281797cad095f", "page" : "pages/order/statementAccount" }}
微信接收效果:















