精华内容
下载资源
问答
  • 微信模板消息开发

    2018-04-24 14:16:29
    微信、小程序、微信接口、模板消息微信模板消息, 微信、小程序、微信接口、模板消息微信模板消息
  • 微信模板消息只能发给一个人,如果要群发,需要通过php循环,依次发送。 注意,如果模板消息发信息时有时无,不稳定,可能你的access_token令牌更新缓存不及时,过期了.可以根据日志文件查看.建议300秒更新一下.否则会很...
  • 在公司微信项目开发中,我主要负责消息中心的模板消息接口设计实现。主要是将微信公众号的推送模板消息功能放到公司的消息中心系统中,微信后台项目通过RMI调用接口,实现推送功能。在这里记录总结下当时的设计实现...

    说明

          在公司微信项目开发中,我主要负责消息中心的模板消息接口设计实现。主要是将微信公众号的推送模板消息功能放到公司的消息中心系统中,微信后台项目通过RMI调用接口,实现推送功能。在这里记录总结下当时的设计实现思路。
          公司使用Hessian实现RMI,所以接口设计为一个Hessian接口。

    正文

    接口的设计要先确定接口的名称,参数,返回值。这里由于考虑到了今后的扩展,可能会运营不止一个公众号,所以在接口参数的设计上,比较具体。

    public interface IWechatMessageService {
    
        public int sendTempMsg(String spid, Integer templateId, String data);
    }

    通过阅读微信开发文档,我们知道了在推送模板消息时,必须要有Access_Token,templateid,openId和要发送的数据,这些数据组成一个json串发送给微信服务器,微信服务器返回一个json数据包包含对应的信息。

    消息推送的设计思路:
          通过微信后台项目远程调用接口,公众号的id,模板的id(id均为在表中的主键),还有推送的数据作为参数传递,消息中心接受到参数后,首先会根据公众号id判断是否存在该公众号信息,再根据模板id判断是否存在模板信息,若都不存在问题,则会将这些参数数据组装成一个DTO,放入消息队列,此时接口方法执行完毕,返回状态码。放入消息队列后,有队列订阅者对消息进行处理(即发送数据到微信服务器),至此推送模板消息的流程结束。

    在以上的思路中有几个问题:

    1. 对于公众号和模板信息的校验,这些信息都存储在数据库中,每次调用接口校验时都是否都需要从数据库查询数据?
    2. 对于推送的数据,这个参数是多用户数据,也就是一次传递一批用户的数据,每个用户推送的内容不同,所以数据格式为JsonArray,在往消息队列中投递的时候,是一次投递一批,一个DTO,还是将每个用户的数据查分组成DTO,进行投递?
    3. 消息数据进入队列后,如何消费,该怎样向微信服务器发送数据?
    4. 在推送时,需要Access_Token,这个token与微信后台项目是共用的,而且token是有有效期的,在过期时导致消息推送失败,该如何处理?

    接下来针对每个问题记录下当时的解决方法:

    1. 公众号、模板标识的校验

          在发送模板消息时,需要Access_Token和对应的模板id,Access_Token的获取需要公众号的appid和secret,定制模板需要向微信官方申请,而且是人工审核,十分严格。当运营不止一个公众号时就需要通过表记录公众号的信息,而且推送的内容不同使用的模板不同,也需要表记录使用的模板信息。所以传递的参数都应该在表中有记录,为了避免每次校验时查询数据库,这里采用了将公众号信息和模板信息加载到内存的方式。具体是使用一个监听器,在项目启动时,从数据库查询数据并存储到内存中,这里使用HashMap作为存储的数据结构,key为主键 value为对应的实体类对象。

    //加载模板信息
    private static Map<Integer,Template> ALL_TEMPLATE = new HashMap<Integer, Template>();
    List<Template> templateList = templateService.getAll();
    if (templateList != null && templateList.size() > 0) {
        for (Template template : templateList) {
            nteger templateid = template.getTemplateid();
            ALL_TEMPLATE.put(templateid,template);
        }
    }

    2.关于多用户数据的投递

    在开始设计时,我采用的是直接将数据组装成一个DTO,发送到队列中,消费时是接受一批的用户数据,在发送时对数据做处理,拆分出每个用户的数据,逐个发送。不过在审查的时候,主管说要提前拆分,将单个用户的DTO用list存储,调用方法以list形式发送,消费时以多线程的方式进行发送。为什么要这么做?主管告诉我这样可以防止数据的大批量丢失,如果一次以单个DTO放到队列,当队列出现问题时,会丢很多数据。公司使用的是RabbitMQ,并且封装了管理类RabbitmqManager,通过阅读源码发现,当以list形式发送时,在方法内部还是循环一个个发送到队列中。

    //将一批消息中的所有用户消息以list放入队列
            List<WechatTempMsgDTO> dataList = new ArrayList<WechatTempMsgDTO>();
            for(int i = 0; i < dataArray.size(); i++){
                JSONObject dataOfUser = dataArray.getJSONObject(i);
                WechatTempMsgDTO wechatTempMsgDTO = new WechatTempMsgDTO(accessToken,templateId,dataOfUser.toJSONString(),batchid,0,spid);
                dataList.add(wechatTempMsgDTO);
            }
    
            int status = queueManager.sendMessageObject(GlobalVars.WECHAT_QUEUE_NAME,dataList);

    发送到队列的源码

        public <T> int sendMessageObject(String queueName, List<T> messgaes) {
            try {
                MessageProperties properties = messgaes != null && messgaes.size() > 0 ? this.getTypeProperties(messgaes.get(0).getClass()) : null;
                Iterator it = messgaes.iterator();
    
                while(it.hasNext()) {
                    T expectedBody = it.next();
                    this.mqTemplate.convertAndSend(queueName, new Message(JSON.toJSONString(expectedBody).getBytes(Charsets.UTF_8), properties));
                }
    
                return 1;
            } catch (Exception var6) {
                return -1;
            }
        }

    3.队列信息消费

    这里使用了消息队列的发布订阅模式,通过一个监听器,在项目启动时订阅队列,等待从队列中获取消息,进行处理。这里采用了多线程的方式进行推送,通过阅读源码,发现多线程的实现方式是一个订阅者subscribe有一个线程池,每个线程代表一个消费者consumer

    订阅部分的实现源码

    queueManager.subscribeQueue(GlobalVars.WECHAT_QUEUE_NAME, new QueueSubscriber<WechatTempMsgDTO>() {
                @Override
                public void onGetMessage(QueueContext<WechatTempMsgDTO> queueContext) {
                    WechatTempMsgDTO wechatTempMsgDTO = queueContext.getMessage();
                    //此时接收到的是单个用户信息的DTO
                    sendMessgae(wechatTempMsgDTO);
                }
            },GlobalVars.WECHAT_CONCURRENT_THREAD_NUM,1);

    订阅多线程消费的实现源码

    Connection conn = this.factory.newConnection();
    ExecutorService threadPool = Executors.newFixedThreadPool(threadCount);
    ((Map)subToThread).put(subscriber, threadPool);
    
    for(int i = 0; i < threadCount; ++i) {
         RabbitConsumer<T> consumer = new RabbitConsumer(conn, queueName, subscriber, maxCount);
         consumer.init();
         threadPool.submit(consumer);
    }

    4.关于Access_Token的使用

    阅读微信开发文档,了解到Access_Token只有两个小时的有效期,且重复获将导致上次获取的token失效。由于公司中的消息系统与微信项目是两个不同的项目,这就导致了token的共享问题及token失效时的获取问题。
    这里使用了redis缓存,将token存入到redis中并设置有效期,解决共享问题。
    在失效获取时,问题在队列消费推送时可能会出现消息没有推送完,token过期剩余所有消息推送失败的问题,此时就需要消息系统去主动获取token,而此时如果微信项目也同时请求,就会有竞争造成一方失效。为解决此问题使用了分布式锁,用redis实现分布式锁,思路是当token失效时,先从redis缓存中获取,如果没有则设置锁,使用了redis的setnx命令,若成功设置获得锁,则先设置锁的有效期避免死锁,再进行请求token,请求成功后将token存入缓存设置有效期,释放锁返回token;若获取锁失败,则循环获取,仍先从redis中获取。这里要避免死循环,如果是相关参数错误,则会一直获取不到造成死循环。在编写代码时,我觉得这部分锁的设计仍然有缺陷,锁的可重入性并没有得到保证,这里需不需要可重入性?如果是利用redis设计实现一个锁工具,就应该保证可重入性。希望大家不吝赐教
    参考资料:分布式锁的作用及实现(Redis)

       //使用redis实现分布式锁 获取Access_Token
        private String getAccessToken(final String spid, String appid, String secret){
            String res = "";
            final String url = GlobalVars.GET_ACCESS_TOKEN_URL.replace("APPID",appid).replace("APPSECRET",secret);
            while("".equals(res)){
                res = tempmanager.getString(GlobalVars.WECHAT_ACCESS_TOKEN_CACHE_PREFIX + spid);
                if(Validator.isNotNull(res)){
                   return res;
                }
                res = tempmanager.execute(new IRedisCacheManager.RedisExecutor<String>() {
                    @Override
                    public String execute(Jedis jedis) throws Exception {
                        String access_token = "";
                        String currentTimeMillis = String.valueOf(System.currentTimeMillis());
                        //加锁
                        Long result = jedis.setnx(GlobalVars.ACCESS_TOKEN_DISTRIBUTE_LOCK_KEY + spid,currentTimeMillis);
                        //加锁成功 进行请求 否则直接返回 循环获取
                        if(result == 1){
                            // 加锁成功后,必须设置锁的有效期,避免死锁
                            jedis.expire(GlobalVars.ACCESS_TOKEN_DISTRIBUTE_LOCK_KEY + spid,2);
                            String strResult = HttpUtil.getRequestURL(url,null);
                            JSONObject jsonResult = JSONObject.parseObject(strResult);
                            access_token = jsonResult.getString("access_token");
                            if(Validator.isNull(access_token)){
                                logger.error("请求Access_Token失败: " + jsonResult.toString());
                                return null;  //当请求微信服务器出错,返回错误的数据包 返回null跳出循环,避免死循环
                            }
                            jedis.set(GlobalVars.WECHAT_ACCESS_TOKEN_CACHE_PREFIX + spid,access_token);
                            //设置access_token缓存的有效期
                            jedis.expire(GlobalVars.WECHAT_ACCESS_TOKEN_CACHE_PREFIX + spid,GlobalVars.WECHAT_ACCESS_TOKEN_CACHE_EXPIRE);
                            //释放锁
                            jedis.del(GlobalVars.ACCESS_TOKEN_DISTRIBUTE_LOCK_KEY + spid);
                        }
    
                        return access_token;
                    }
                });
            }
            logger.info("获取Access_Token为: " + res);
    
            return res;
        }

    对于推送失败后,这里采用了重新放回对列进行重试的机制,重试次数限制为3次。当发送成功时会记录到send_success表中,最后一次重试发送失败则会记录到send_failed表中并记录错误信息

    if(Integer.valueOf(code) == 0){
        //将发送成功的信息记录到成功表
        saveDataToSuccess(templateId,toUser,batchid,param,tryCount);
    }else{
       //当错误代码代表access_token失效时,需要重新获取
       if(Integer.valueOf(code) == GlobalVars.WECHAT_ACCESS_TOKEN_INVALID){
           accessToken = accessTokenManager.getAccessToken(spid);
       }
       String msg = jsonResp.toString();
       //判断重复次数 若已超过限定次数 则记录到失败表,否则重新放入消息队列
       if(tryCount < GlobalVars.TRY_COUNT){
            sendToQueue(accessToken,templateId,batchid,dataOfUser.toJSONString(),tryCount + 1);
       }else{
            saveDataToFaild(templateId,toUser,batchid,param,tryCount,msg);
       }
    }

    最后感谢主管和同事们对我完成这次任务的帮助,让我学到了很多

    其他优秀博文:
    分布式锁的几种实现方式
    分布式之消息队列复习精讲

    展开全文
  • 微信公众号开发-接收消息+模板消息。包含所有源代码、工具类、JAR包,MyEclipse开发的,其他软件开发的可以参考着修改。
  • 最近在自己公司公众号里边加的微信模板消息推送,可以加到自己的后台里边,而且是群发,这样每次企业有什么活动可以群发给所有关注公众号的会员,是一个比较好的营销手段,亲测可用,不懂得可以问我。
  • 微信模板消息代码

    2018-11-21 18:27:12
    Java版本微信模板消息推送后台代码。适合初学者。小程序模板消息推送开发
  • springboot实现微信模板消息推送,包含微信端代码。简单封装,方便二次开发
  • 微信模板消息源码.txt

    2019-09-17 13:37:56
    微信模板消息 并且附有使用说明。把里面需要替换的appid secret template_id修改成你自己的配置信息。注意一下你使用的模板的结构哦 适当的增删keyword 即可哦。
  • wechat_pusher:Go 实现的微信模板消息推送服务
  • 主要介绍了微信公众平台 发送模板消息(Java接口开发),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 微信模板消息发送php

    2016-05-05 10:57:29
    在wxsend.php文件里,填入appid,secrect,openid即可使用。模板id默认使用了一个充值模板。2016.05.05为止测试通过。
  • 微信公众号开发之发送模板消息

    万次阅读 多人点赞 2018-11-10 11:08:37
    在我们做微信公众号开发时,发送模板消息往往是必不可少的功能。今天我们就来说说吧! 1、申请模板消息 首先我们应该知道,模板消息是需要申请的。这个申请就其本身来说是很easy的(我前一天晚上申请的,显示需要2...

    在我们做微信公众号开发时,发送模板消息往往是必不可少的功能。今天我们就来说说吧!

    1、申请模板消息

    首先我们应该知道,模板消息是需要申请的。这个申请就其本身来说是很easy的(我前一天晚上申请的,显示需要2--3个工作日,结果第二天早上就发现已经开通了,所以说腾讯官方还是比较给力的哈)。

    但是我们在申请时还是有一些东西要注意,这个在官方的文档有非常详细的说明。

    这个我建议你好好看看。选择行业的时候可要谨慎些,因为这个一个月只可以修改一次。

    那么,我们来看看在哪里申请?

    这里我已经申请过了。

    申请之后就耐心等待,审核通过之后再功能这一栏里就会出现模板消息的菜单。你可以看看我上面的截图,就在第三项。

    2、添加模板消息

    审核通过之后,我们就可以添加模板消息,进行开发了。

    这个很简单:

    我们点击模板消息进入后,直接在模板库中选择你需要的消息模板添加就可以了,添加之后就会在我的模板中。会有一个模板id,这个模板id在我们发送消息的时候会用到。

    3、消息发送功能开发

    接下来我们就看看如何发送模板消息:

    这个是官方文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277

    我呢,也来说说我的实现吧。为了更方便,我会直接将相关代码贴出来。

    文档中我们可以看到接口地址如下:

    http请求方式: POST
    https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN

    这里我们首先需要的就是access_token了,这个在这里就不多说了。通过你的appid和secret就可以获取。

    【获取access_token : https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183

    关于相关参数,我直接就将官方文档贴来了(文档写的很清楚):

    POST数据示例如下:

          {
               "touser":"OPENID",
               "template_id":"ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY",
               "url":"http://weixin.qq.com/download",  
               "miniprogram":{
                 "appid":"xiaochengxuappid12345",
                 "pagepath":"index?foo=bar"
               },          
               "data":{
                       "first": {
                           "value":"恭喜你购买成功!",
                           "color":"#173177"
                       },
                       "keyword1":{
                           "value":"巧克力",
                           "color":"#173177"
                       },
                       "keyword2": {
                           "value":"39.8元",
                           "color":"#173177"
                       },
                       "keyword3": {
                           "value":"2014年9月22日",
                           "color":"#173177"
                       },
                       "remark":{
                           "value":"欢迎再次购买!",
                           "color":"#173177"
                       }
               }
           }
    

    参数说明

    参数是否必填说明
    touser接收者openid
    template_id模板ID
    url模板跳转链接(海外帐号没有跳转能力)
    miniprogram跳小程序所需数据,不需跳小程序可不用传该数据
    appid所需跳转到的小程序appid(该小程序appid必须与发模板消息的公众号是绑定关联关系,暂不支持小游戏)
    pagepath所需跳转到小程序的具体页面路径,支持带参数,(示例index?foo=bar),暂不支持小游戏
    data模板数据
    color模板内容字体颜色,不填默认为黑色

    注:url和miniprogram都是非必填字段,若都不传则模板无跳转;若都传,会优先跳转至小程序。开发者可根据实际需要选择其中一种跳转方式即可。当用户的微信客户端版本不支持跳小程序时,将会跳转至url。

    返回码说明

    在调用模板消息接口后,会返回JSON数据包。正常时的返回JSON数据包示例:

        {
               "errcode":0,
               "errmsg":"ok",
               "msgid":200228332
           }

    相信看完以上文档,基本上没有什么问题了。

    以下是我的部分代码:

    // 获取token
            String token = saveAndFlushAccessTokenUtil.getToken();
    
            String postUrl = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + token;
    
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("touser", "发送到用户的openid");   // openid
            jsonObject.put("template_id", "你的模板id");
            jsonObject.put("url", "http://www.baidu.com");
    
            JSONObject data = new JSONObject();
            JSONObject first = new JSONObject();
            first.put("value", "hello");
            first.put("color", "#173177");
            JSONObject keyword1 = new JSONObject();
            keyword1.put("value", "hello");
            keyword1.put("color", "#173177");
            JSONObject keyword2 = new JSONObject();
            keyword2.put("value", "hello");
            keyword2.put("color", "#173177");
            JSONObject keyword3 = new JSONObject();
            keyword3.put("value", "hello");
            keyword3.put("color", "#173177");
            JSONObject remark = new JSONObject();
            remark.put("value", "hello");
            remark.put("color", "#173177");
            
            data.put("first",first);
            data.put("keyword1",keyword1);
            data.put("keyword2",keyword2);
            data.put("keyword3",keyword3);
            data.put("remark",remark);
    
            jsonObject.put("data", data);
    
            String string = HttpClientUtils.sendPostJsonStr(postUrl, jsonObject.toJSONString());
            JSONObject result = JSON.parseObject(string);
            int errcode = result.getIntValue("errcode");
            if(errcode == 0){
                // 发送成功
                System.out.println("发送成功");
            } else {
                // 发送失败
                System.out.println("发送失败");
            }

    下面是http请求工具类:

    package car.repair.common.util;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.http.HttpEntity;
    import org.apache.http.ParseException;
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.entity.ContentType;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.util.EntityUtils;
    
    import java.io.IOException;
    
    /**
     * @author zhuzhe
     * @date 2017/12/11
     * HttpClient工具类
     */
    @Slf4j
    public class HttpClientUtils {
    
        /**
         * 以jsonString形式发送HttpPost的Json请求,String形式返回响应结果
         *
         * @param url
         * @param jsonString
         * @return
         */
        public static String sendPostJsonStr(String url, String jsonString) throws IOException {
            if (jsonString == null || jsonString.isEmpty()) {
                return sendPost(url);
            }
            String resp = "";
            StringEntity entityStr = new StringEntity(jsonString,
                    ContentType.create("text/plain", "UTF-8"));
            CloseableHttpClient httpClient = HttpClients.createDefault();
            HttpPost httpPost = new HttpPost(url);
            httpPost.setEntity(entityStr);
            CloseableHttpResponse response = null;
            try {
                response = httpClient.execute(httpPost);
                HttpEntity entity = response.getEntity();
                resp = EntityUtils.toString(entity, "UTF-8");
                EntityUtils.consume(entity);
            } catch (ClientProtocolException e) {
                log.error(e.getMessage());
            } catch (IOException e) {
                log.error(e.getMessage());
            } finally {
                if (response != null) {
                    try {
                        response.close();
                    } catch (IOException e) {
                        log.error(e.getMessage());
                    }
                }
            }
            if (resp == null || resp.equals("")) {
                return "";
            }
            return resp;
        }
    
        /**
         * 发送不带参数的HttpPost请求
         *
         * @param url
         * @return
         */
        public static String sendPost(String url) throws IOException {
            // 1.获得一个httpclient对象
            CloseableHttpClient httpclient = HttpClients.createDefault();
            // 2.生成一个post请求
            HttpPost httppost = new HttpPost(url);
            CloseableHttpResponse response = null;
            try {
                // 3.执行get请求并返回结果
                response = httpclient.execute(httppost);
            } catch (IOException e) {
                log.error(e.getMessage());
            }
            // 4.处理结果,这里将结果返回为字符串
            HttpEntity entity = response.getEntity();
            String result = null;
            try {
                result = EntityUtils.toString(entity);
            } catch (ParseException | IOException e) {
                log.error(e.getMessage());
            }
            return result;
        }
    }
    
    

     

    收到消息,我就不自己弄图了。这里附上官方图片一张:

     

    转载请务必保留此出处(原作者):https://blog.csdn.net/zhuzhezhuzhe1/article/details/83927016

     

    版权声明:本文为原创文章,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。

    https://blog.csdn.net/zhuzhezhuzhe1

     

    展开全文
  • 1、什么是模板消息?微信为防止服务号对用户进行恶意骚扰和营销,而服务号在某些场景又必须给用户发送消息时(如购物成功、支付成功),这时候就可以应用微信提供的...3、微信模板一些注意事项1、只有认证服务号才...

    1、什么是模板消息?

    微信为防止服务号对用户进行恶意骚扰和营销,而服务号在某些场景又必须给用户发送消息时(如购物成功、支付成功),这时候就可以应用微信提供的模板消息来给用户进行提醒。

    2、模板消息能做什么?

    模板消息可以让公众号主动发消息给用户 而且不限制时间。 目的是模板消息仅用于公众号向用户发送重要的服务通知(说是发重要通知,谁知道营销内容算不算呢,呵呵)。

    3、微信模板一些注意事项

    1、只有认证服务号才可以使用,在功能->添加功能插件处看到申请模板消息功能的入口。;
    2、需要选择公众账号服务所处的2个行业,每月可更改1次所选行业;
    3、选择行业只是为了给你提供与你行业相关的模板,在所选择行业的模板库中选用已有的模板进行调用;
    4、每个账号可以同时使用25个模板。
    5、当前每个账号的模板消息的日调用上限为10万次。
    因为我没有认证的服务号,我会拿我的测试公众号来讲解,与实际的服务号发送模板消息的原理是一样的。

    4、如何使用模板消息发信息 代码下面解析代码以及如何使用。

    <?php
      //获取微信access_token
     function getaccess_token(){
        //appid与appsecret改成你自己的
        $appid = '自己的appid';
        $appsecret = '自己的appsecret';
        $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$appid}&secret={$appsecret}";
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL,$url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,0);
        $data = curl_exec($ch);
        curl_close($ch);
        $data = json_decode($data,true);
        return $data['access_token'];
     }
      //设置与发送模板信息
     function set_msg(){
        //获取access_token
        $access_token = getaccess_token();
        //这里是在模板里修改相应的变量
        $formwork = '{
               "touser":"发送用户的openid",
               "template_id":"微信公众号模板ID",
               "url":"http://www.wangwenxiao.com",            
               "data":{
                       "title": {
                           "value":"这里是自己定义的标题",
                           "color":"#173177"
                       },
                       "content":{
                           "value":"这里是自定义内容,啦啦啦",
                           "color":"#173177"
                       },
                       "time": {
                           "value":"这里填写时间",
                           "color":"#173177"
                       }
               }
           }';
        $url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={$access_token}";
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL,$url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,0);
        curl_setopt($ch, CURLOPT_POST,1);
        curl_setopt($ch, CURLOPT_POSTFIELDS,$formwork);
        $data = curl_exec($ch);
        curl_close($ch);
        return $data;
    }
    echo set_msg();

    返回 errmsg:ok  即为已发送

    5、代码解析

    开头的函数getaccess_token是获取自己公众号的access_token。
    22-40行是定义要发送的消息的信息
    23行是指明消息发给那个openid
    24行是指明要用哪个模板。
    25行是指点开消息后打开那个地址。
    26行data里的值要与模板中的内容相对应。比如上图箭头4.我建立的模板内容中有{{title.DATA}}{{content.DATA}}{{time.DATA}}。data里面就要设置相应的内容来替换掉前面的三个变量。
    42-49行把$forwork函数POST到41行的地址中。信息就发送出去了。



    展开全文
  • 不知道怎么封装实体类等。本来不是搞微信的, 被老板搞得着着急急弄微信。求好心人帮忙。。其他的消息自动回复、自定义菜单都做了 。急急急啊。
  • Java发送微信模板消息

    2019-01-16 20:54:19
    Java发送微信模板消息 微信模板消息如果在小程序实现是比较简单的,但是有些业务必须后台实现。所以只要拿到prepay_id 或者fromId就可以了 1. 添加模板 进入微信公众平台小程序 添加自定义模板 ...

    Java发送微信模板消息

    微信模板消息如果在小程序实现是比较简单的,但是有些业务必须后台实现。所以只要拿到prepay_id 或者fromId就可以了

    1. 添加模板

    进入微信公众平台小程序 添加自定义模板
    https://mp.weixin.qq.com/wxopen/authprofile在这里插入图片描述

    在这里插入图片描述

    查看官方文档
    https://developers.weixin.qq.com/miniprogram/dev/api/sendTemplateMessage.html
    在这里插入图片描述
    注意这里的form_id,表单提交场景下,为 submit 事件带上的 formId;支付场景下,为本次支付的 prepay_id,由于业务中prepay_id比较好拿就是用prepay_id了,注意一个坑就是接受模板消息者必须使用他的prepay_id,而且只能用三次。

    2. 编程

    • 为了方便创建了一张表来保存prepay_id。此步骤可以省略,直接代码写死也是可以的

    在这里插入图片描述

    CREATE TABLE `wx_user_prepayid` (
      `id` int(20) NOT NULL AUTO_INCREMENT,
      `userId` varchar(50) DEFAULT NULL COMMENT '用户id',
      `prepayId` varchar(50) DEFAULT NULL COMMENT '支付id',
      `fromId` varchar(50) DEFAULT NULL COMMENT 'fromId提交表单id',
      `openId` varchar(50) DEFAULT NULL COMMENT 'openId',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=695 DEFAULT CHARSET=utf8
    
    • 代码
      由于比较懒就直接把业务中的代码贴出来了,有需要自己改下 ,这里用的是mybatisPlus
      如果省略的创建数据库步骤直接把下面打注释的拿来用就好了。
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.dlc.lzsharebed.entity.WxUserPrepayid;
    import com.dlc.lzsharebed.mapper.WxUserPrepayidMapper;
    import com.dlc.lzsharebed.service.IWxUserPrepayidService;
    import com.dlc.lzsharebed.utils.SendTemplateMessage;
    import com.github.pagehelper.util.StringUtil;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Service;
    
    /**
     * @author jijl
     * @since 2018-12-05
     */
    @Service
    @Slf4j
    public class WxUserPrepayidServiceImpl extends ServiceImpl<WxUserPrepayidMapper, WxUserPrepayid> implements IWxUserPrepayidService {
        @Override
        public void send(String userId,String endDate) {
            log.info("---发送消息 userId:{}",userId);
            log.info("--发送消息 endDate:{}",userId);
            QueryWrapper queryWrapper = new QueryWrapper();
            queryWrapper.eq("userId", userId);
            queryWrapper.orderByDesc("id");
            WxUserPrepayid wxUserPrepayid = selectOne(queryWrapper);
            if(wxUserPrepayid!=null){
                String fromId = wxUserPrepayid.getPrepayId();
                String optionId = wxUserPrepayid.getOpenId();
                //template_id 根据实际来 TODO
                String template_id = "template_id ";
                String keyword2 = endDate;
                //AppId根据实际来  TODO
                String wx_SpAppId= "AppId";
                //Secrect根据实际来  TODO
                String wx_SpSecrect= "Secrect";
                //发送微信模板消息
                SendTemplateMessage.sendMessage(fromId, optionId, template_id,keyword2,wx_SpAppId,wx_SpSecrect);
            }
            //发送微信模板消息测试
    //        String fromId = "wx05143135971766cee0cb40da0499151483";
    //        String optionId = "oBBqp5baIEPSoqisRVGNjrXRJaEM";
    //        String template_id = "template_id ";
    //        String keyword2 = "2018年9月30日16:33:44";
    //        String wx_SpAppId= "wx_SpAppId";
    //        String wx_SpSecrect= "wx_SpSecrect";
    //        SendTemplateMessage.sendMessage(fromId, optionId, template_id,keyword2,wx_SpAppId,wx_SpSecrect);
        }
    }
    

    这个类写的是不规范的,但是功能可以实现的有强迫症的同学可以改下

    
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import lombok.Data;
    import lombok.extern.slf4j.Slf4j;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.net.*;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @auther: jijl
     * @Date: Create in 2018/11/12
     * @Description: 发送小程序模板消息
     **/
    @Data
    @Slf4j
    public class SendTemplateMessage {
        /**
         * 接收者(用户)的 openid
         */
        private String touser;
        /**
         * 所需下发的模板消息的id
         */
        private String template_id;
        /**
         * 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转。
         */
        private String page;
        /**
         * 表单提交场景下,为 submit 事件带上的 formId;支付场景下,为本次支付的 prepay_id
         */
        private String form_id;
        /**
         * 模板内容,不填则下发空模板
         */
        private Map<String, Object> data;
        /**
         * 模板需要放大的关键词,不填则默认无放大
         */
        private String emphasis_keyword;
    
        /**
         * 发送模板消息sendTemplateMessage
         * 小程序模板消息,发送服务通知
         *
         * @param touser      接收者(用户)的 openid
         * @param template_id 所需下发的模板消息的id
         * @param page        点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转。
         * @param formid      表单提交场景下,为 submit 事件带上的 formId;支付场景下,为本次支付的 prepay_id
         * @return
         */
        public static JSONObject sendTemplateMessage(String touser, String template_id, String page, String formid, Map<String, Object> map, String accessToken) {
            SendTemplateMessage sendTemplateMessage = new SendTemplateMessage();
            //拼接数据
            sendTemplateMessage.setTouser(touser);
            sendTemplateMessage.setTemplate_id(template_id);
            sendTemplateMessage.setPage(page);
            sendTemplateMessage.setForm_id(formid);
            sendTemplateMessage.setData(map);
            sendTemplateMessage.setEmphasis_keyword("");
            String json = JSONObject.toJSONString(sendTemplateMessage);
            log.info("##模版发送JSON数据:  " + json);
            String ret = SendTemplateMessage.sendPost("https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=" + accessToken, json);
            return JSON.parseObject(ret);
        }
    
        /**
         * 发送post请求 json格式
         *
         * @param url
         * @param param
         * @return
         */
        public static String sendPost(String url, String param) {
            PrintWriter out = null;
            BufferedReader in = null;
            String result = "";
            try {
                URL realUrl = new URL(url);
                // 打开和URL之间的连接
                URLConnection conn = realUrl.openConnection();
                // 设置通用的请求属性
                conn.setRequestProperty("Accept", "application/json");
                conn.setRequestProperty("Content-Type", "application/json");
                // 发送POST请求必须设置如下两行
                conn.setDoOutput(true);
                conn.setDoInput(true);
                // 获取URLConnection对象对应的输出流
                out = new PrintWriter(conn.getOutputStream());
                // 发送请求参数
                out.print(param);
                // flush输出流的缓冲
                out.flush();
                // 定义BufferedReader输入流来读取URL的响应
                in = new BufferedReader(
                        new InputStreamReader(conn.getInputStream()));
                String line;
                while ((line = in.readLine()) != null) {
                    result += line;
                }
            } catch (Exception e) {
                System.out.println("发送 POST 请求出现异常!" + e);
                e.printStackTrace();
            }
            //使用finally块来关闭输出流、输入流
            finally {
                try {
                    if (out != null) {
                        out.close();
                    }
                    if (in != null) {
                        in.close();
                    }
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
            return result;
        }
    
        public static void sendMessage(String fromId, String optionId, String template_id,String keyword2,String wx_SpAppId,String wx_SpSecrect) {
            Map<String, Object> stringObjectMap = new HashMap<>();
            Map<String, Object> map1 = new HashMap<>();
            map1.put("value", "尊敬的用户,您的订单即将超时,请及时关锁!");
            Map<String, Object> map2 = new HashMap<>();
            map2.put("value", keyword2);
            stringObjectMap.put("keyword1", map1);
            stringObjectMap.put("keyword2", map2);
            System.out.println(JSONObject.toJSONString(stringObjectMap));
            JSONObject js = sendTemplateMessage(optionId, template_id, "pages/index/main", fromId, stringObjectMap, accessToken( wx_SpAppId, wx_SpSecrect));
            log.info("发送微信消息结果:{}", js);
        }
    
    
        public static String accessToken(String wx_SpAppId,String wx_SpSecrect) {
            String urlValue = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" +wx_SpAppId+ "&secret="+wx_SpSecrect;
            log.info("请求accessToken地址:{}", urlValue);
            StringBuffer result = new StringBuffer();
            try {
                URL url = new URL(urlValue);
                HttpURLConnection con = (HttpURLConnection) url.openConnection();
                con.setDoOutput(true);
                con.setRequestMethod("GET");
                con.setRequestProperty("Content-Type", "text/xml; charset=UTF-8");
                con.connect();
                log.info("HTTP状态码={}", con.getResponseCode());
                BufferedReader inn = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
                String value = inn.readLine().trim();
                while (value != null) {
                    if (!"".equals(value)) {
                        result.append(value.trim() + "\n");
                    }
                    value = inn.readLine();
                }
                inn.close();
            } catch (ProtocolException e) {
                e.printStackTrace();
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            Map respStr = JSONObject.parseObject(result.toString());
            log.info("请求accessToken结果:{}", respStr);
            return String.valueOf(respStr.get("access_token"));
        }
    
    }
    
     
    
    展开全文
  • 微信公众号模板消息推送代码,亲测可行。其中有一个工具类,注释啥的都有。需要的可以看看
  • 现在,我们也要做这样一件事,不过我的业务场景是这样的:现在有一个公众号,还有一个小程序,我需要给指定的已关注我公众号的用户推送一条模板消息,当用户点击模板消息后可以调到我小程序中的指...
  • 微信模版消息发送实例,需要先配置微信模版,其他参数自己获取!
  • 主要介绍了详解微信小程序开发之formId使用(模板消息),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • VB源代码开发微信公众号模板消息、客户消息,需要的童鞋下载来看,共同进步~共同进步~共同进步~
  • 主要介绍了java实现微信公众平台发送模板消息的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • c#发送微信模板消息

    千次阅读 2019-01-02 13:56:51
    直接上代码,微信模板消息api在 微信公众号中,https://developers.weixin.qq.com/miniprogram/dev/api/,如下图 C#代码如下:  string revMsg = "\"data\":{\"keyword1\":{\"...
  • 微信模版消息开发

    千次阅读 热门讨论 2016-09-30 18:18:36
    在项目中,用到了微信的模版消息。搜索了一下,发现了很多好玩的东西,这里跟大家分享一下。 一,微信公众平台简介  微信公众平台,可以分为三类。 (1)订阅号:主要偏重于为用户传达资讯,类似于 报纸杂志,...
  • 微信模板2.封装后数据库中存储3.接口设计总结 前言 原有模板消息推送针对调用方来说,消息内容有点纷繁杂乱,现针对这种情况进行二次开发。 一、设计思路 将微信模板进行再封装,在运营平台可以进行编辑模板,这个...
  • 微信模板消息推送(java实现)

    千次阅读 2020-04-01 12:48:19
    2、模板消息接口-微信开发文档 首先需要在微信公众号后台模板消息中申请模板id,申请好以后可以在模板消息–>我的模板–>模板详情中查看到具体模板id和模板样式 例如: 下面就可以开始写代码了(就是封装好具体...
  • thinkphp 5.0 对接微信模板消息

    千次阅读 2019-01-26 15:14:02
    thinkphp 5.0 通过公众号推送消息 微信公众平台获取到AppID,AppSecret 登录到微信公众平台,找到 开发—基本设置,如图1,图2: ...在微信公众平台首页,上面部分找到 模板消息,会看到自己添加的模板消息列表,如...
  • 微信模板消息中换行

    千次阅读 2018-10-28 19:59:53
    使用换行符:\n。 就这么简单,什么\\n,&lt;br&gt;都没用的。
  • 微信发送模板消息接口文档...温馨提示:请各位开发者使用微信模板消息功能前,仔细阅读上述发送模板消息接口文档,少走弯路; 一、微信公众号开发框架 <!-- 微信框架 参考:https://github.com/Wechat-Gr...
  • 一、实现步骤 1、在测试公众号添加模板 ...//模板消息接口 public function sendTemplateMsg(){ //1、获取access_token $access_token = $this -&gt; getWxAccessToken(); $url = "https://api.weix...
  • 微信模板消息批量推送

    万次阅读 2018-01-27 17:17:18
    由于模板消息发送不需要等待微信的结果,所以利用php的fsockopen()函数可以达到快速发送的效果。代码如下: $data = array( 'touser' => '推送人(粉丝)的openid', 'template_id' => '微信

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 51,070
精华内容 20,428
关键字:

微信模板消息开发