微信公众号开发_微信公众号开发和微信公众平台 - CSDN
精华内容
参与话题
  • 手把手教你如何微信公众号开发

    万次阅读 多人点赞 2018-10-27 14:30:21
    最近的话,发现微信开发其实也有很多挺有意思的地方,比如最近很火的一款游戏“跳一跳”,也让我如此着迷。。但是,今天我所要讲的并不是对于小程序的开发,而是要说...而我主要讲解一下关于微信公众号开发中,一...

         最近的话,发现微信开发其实也有很多挺有意思的地方,比如最近很火的一款游戏“跳一跳”,也让我如此着迷。。但是,今天我所要讲的并不是对于小程序的开发,而是要说一下,关于微信开发的另外一个内容,那就是微信公众号。。

        关于,什么是微信公众号,微信公众号怎么申请,这个我就不多说,这些基本的概念不在这里进行讲解,自己可以直接百度就可以找到很多的资源。而我主要讲解一下关于微信公众号开发中,一些比较重要和常见的知识点,所以,这个也并不是基础篇的文章哦~!好歹也要对微信公众号有一点了解才行。~!

    一:实现外网域名访问本地服务项目

    描述:我们在刚开始接触微信公众号开发的时候,我想,一般情况下,很多人都是没有服务器的,简单点说,就是没有服务器IP,或者是公网访问的域名。然而,要想能够实现微信公众能能够与我们后台代码进行交互,那么肯定就需要有一个公网能够访问的地址,那么这怎么办好呢?

    解法一:很简单呀,直接去新浪云,阿里云,华为云,买一个服务器呗,而且是学生的话,还有优惠,多好。。但是,这个又比较繁琐,里面又有配置内容啥的,一堆(比如,tomcat,mysql,jdk等等)。那么,看看第二种方法。。。。(但是,请记住,这个是想做服务端开发必须会的,如果你连部署项目都不会,你觉得公司对你的感觉如何?多一个技能就是一个优势)

    解法二:反向代理。。如果,你是第一次听说这个名词,那么就赶紧恶补一下,这个名词的含义。我简单点说,就是通过反向代理的模式,来代理你本地的ip,以便能够在公网地址能够访问到本地的项目。。具体,请看下面,如何进行~!~!

    步骤:(1)下载ogrok客户端---------也就是反向代理的客户端,当然还有很多类似的,我这里就使用这个而已。

    这个可以通过该链接进行下载ogrok下载链接

    (2)解压刚下载好的客户端文件到自己的一个目录下

    (3)通过cmd命令,进入DOS,并且进入到刚刚解压的ogrok目录下

    (4)执行 ngrok -config=ngrok.cfg -subdomain xxx 8080 //(xxx 是你自定义的域名前缀)

    比如,我这里就是xxx就是用myjavttest

    结果:

    (5)这样的话,我们就可以通过上面的地址进行访问本地的项目了。(原来都是用的localhost:8080/login.jsp或者127.0.0.1:8080/login.jsp),当然,前提是本地有一个开启的项目,这样才可以,别本地都没有项目开启,就用公网去访问,你觉得,它能够访问么?所以,请别忘记这一点。

    (二)微信公众号客户端与后台进行验证身份

    首先,我们通过第一步,我们就能够得到一个以公网地址访问的一个IP(域名),那么既然要使得微信公众号能够与后台进行关联,那么肯定需要配置微信公众号的具体对应的服务器地址了。

    步骤:(1)首先,进入微信公众号开发官网,并且进行登陆

    (2)

    (3)

    (4)

    OK,配置到这个的话,就简单的,将基本的配置进行设置好了。。那么,下面才是关键,进入真正的开发阶段。。

    如果微信端,要与后台进行关联,那么当用户进行与后台交互的时候,后台就需要采取,身份验证,而这个是通过GET方式的请求,而只有通过的才可以进行后续的处理。所以,如何进行,那么就看下面的代码:这里讲解两种形式~~~~

    第一种:(采取Servlet)

    @WebServlet(name = "ConnectWeChatServlet")
    public class ConnectWeChatServlet extends HttpServlet {
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        }
    
        /**
         * 进行验证是否身份匹配
         * @param request
         * @param response
         * @throws ServletException
         * @throws IOException
         */
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            String signature = request.getParameter("signature");
            String timestamp = request.getParameter("timestamp");
            String nonce = request.getParameter("nonce");
            String echostr = request.getParameter("echostr");
            System.out.println(""+signature +"@"+timestamp +"$"+nonce +"^"+echostr);
            PrintWriter out = response.getWriter();
            if(CheckConnectUtils.checkConncetWithWeChat(signature,timestamp,nonce)){
                out.print(echostr);
            }
        }

    验证的代码:

    package com.hnu.scw.utils;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.Arrays;
    /**
     * @author scw
     * @create 2018-01-17 9:28
     * @desc 检查微信和服务器是否链接成功
     **/
    public class CheckConnectUtils {
        private static final String token = "wechat"; //这个就是微信公众号之前配置的token,必须保持一致
        /**
         * 判断是否链接匹配
         * @param signature
         * @param timestamp
         * @param nonce
         * @return
         */
        public static boolean checkConncetWithWeChat(String signature,String timestamp,String nonce){
            String[] arr = new String[]{token,timestamp,nonce};
            //排序
            Arrays.sort(arr);
            //生成字符串
            StringBuilder stringBuilder = new StringBuilder();
            for (String str:arr) {
                stringBuilder.append(str);
            }
            //进行SHA1加密
            String encodeString = passSha1Encode(stringBuilder.toString());
            if(signature.equals(encodeString)){
                return true;
            }else{
                return false;
            }
        }
    
        /**
         * 字符串进行SHA1加密
         * @param str
         * @return
         */
        public static String passSha1Encode(String str){
            if(str == null || str.length() == 0){
                return null;
            }
            char hexDigits[] = {'0','1','2','3','4','5','6','7','8','9'
            ,'a','b','c','d','e','f'};
            try{
                MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
                mdTemp.update(str.getBytes());
                byte[] md = mdTemp.digest();
                int j = md.length;
                char[] buf = new char[j*2];
                int k = 0;
                for(int i=0 ; i <j ; i++){
                    byte byte0 = md[i];
                    buf[k++] = hexDigits[byte0 >>>4 & 0xf];
                    buf[k++] = hexDigits[byte0 & 0xf];
                }
                return new String(buf);
            } catch (NoSuchAlgorithmException e) {
               return null;
            }
        }
    }

    第二种:(采取框架,比如SpringMVC+Spring+Hibernate)

    /**
     * @author scw
     * @create 2018-01-18 11:38
     * @desc 微信前端连接的主要控制类
     **/
    @Controller
    public class WeChatDogPrimaryController {
        /**
         * 进行微信用户验证,只能是Get方法
         * @param request
         * @param response
         */
        @RequestMapping(value = "/wechat" ,method = RequestMethod.GET)
        public void connectValidate(HttpServletRequest request , HttpServletResponse response) throws IOException {
            String signature = request.getParameter("signature");
            String timestamp = request.getParameter("timestamp");
            String nonce = request.getParameter("nonce");
            String echostr = request.getParameter("echostr");
            System.out.println(""+signature +"@"+timestamp +"$"+nonce +"^"+echostr);
            PrintWriter out = response.getWriter();
            if(CheckConnectUtils.checkConncetWithWeChat(signature,timestamp,nonce)){
                out.print(echostr);
            }
        }
    
    
        /**
         * 客户端进行的消息处理
         * @param request
         * @param response
         */
        @RequestMapping(value = "/wechat" ,method = RequestMethod.POST)
        public void disposeClientMessage(HttpServletRequest request , HttpServletResponse response ) throws IOException {
          
        }

    (三)微信客户端与后台进行消息交互(比如,文本,图片,视频,音频)

    消息的实体类:

    package com.hnu.scw.model;
    
    /**
     * @author scw
     * @create 2018-01-17 13:37
     * @desc 对于所有消息的基本父类
     **/
    public class BaseMessage {
        private String ToUserName;
        private String FromUserName;
        private String CreateTime;
        private String MsgType;
    
        public String getToUserName() {
            return ToUserName;
        }
    
        public void setToUserName(String toUserName) {
            ToUserName = toUserName;
        }
    
        public String getFromUserName() {
            return FromUserName;
        }
    
        public void setFromUserName(String fromUserName) {
            FromUserName = fromUserName;
        }
    
        public String getCreateTime() {
            return CreateTime;
        }
    
        public void setCreateTime(String createTime) {
            CreateTime = createTime;
        }
    
        public String getMsgType() {
            return MsgType;
        }
    
        public void setMsgType(String msgType) {
            MsgType = msgType;
        }
    }
    
    package com.hnu.scw.model;
    
    /**
     * @author SCW
     * @create 2018-01-17 15:08
     * @desc 图片的基本类
     **/
    public class ImageBase {
        private String MediaId;
    
        public String getMediaId() {
            return MediaId;
        }
    
        public void setMediaId(String mediaId) {
            MediaId = mediaId;
        }
    }
    
    package com.hnu.scw.model;
    
    /**
     * @author scw
     * @create 2018-01-17 15:09
     * @desc 图片消息
     **/
    public class ImageMessage extends BaseMessage {
        private ImageBase Image ;
    
        public ImageBase getImageBase() {
            return Image;
        }
    
        public void setImageBase(ImageBase Image) {
            this.Image = Image;
        }
    }
    
    package com.hnu.scw.model;
    
    /**
     * @author Administrator
     * @create 2018-01-17 16:45
     * @desc 音乐类型的基本类
     **/
    public class MusicBase {
        private String Title;
        private String Description;
        private String MusicUrl;
        private String HQMusicUrl;
        private String ThumbMediaId;
    
        public String getTitle() {
            return Title;
        }
    
        public void setTitle(String title) {
            Title = title;
        }
    
        public String getDescription() {
            return Description;
        }
    
        public void setDescription(String description) {
            Description = description;
        }
    
        public String getMusicUrl() {
            return MusicUrl;
        }
    
        public void setMusicUrl(String musicUrl) {
            MusicUrl = musicUrl;
        }
    
        public String getHQMusicUrl() {
            return HQMusicUrl;
        }
    
        public void setHQMusicUrl(String HQMusicUrl) {
            this.HQMusicUrl = HQMusicUrl;
        }
    
        public String getThumbMediaId() {
            return ThumbMediaId;
        }
    
        public void setThumbMediaId(String thumbMediaId) {
            ThumbMediaId = thumbMediaId;
        }
    }
    
    package com.hnu.scw.model;
    
    /**
     * @author Administrator
     * @create 2018-01-17 16:46
     * @desc 用于包装音乐的实体
     **/
    public class MusicMessage extends BaseMessage {
        private MusicBase Music;
    
        public MusicBase getMusic() {
            return Music;
        }
    
        public void setMusic(MusicBase music) {
            Music = music;
        }
    }
    
    package com.hnu.scw.model;
    
    /**
     * @author scw
     * @create 2018-01-17 10:03
     * @desc 文本消息的内容
     **/
    public class MyTestMessage  extends BaseMessage{
        private String Content;
        private String  MsgId;
    
        public String getContent() {
            return Content;
        }
    
        public void setContent(String content) {
            Content = content;
        }
    
        public String getMsgId() {
            return MsgId;
        }
    
        public void setMsgId(String msgId) {
            MsgId = msgId;
        }
    }
    
    package com.hnu.scw.model;
    
    /**
     * @author scw
     * @create 2018-01-17 13:38
     * @desc 对于图文消息最内层结构的实体类
     **/
    public class NewsBase {
        private String Title;
        private String Description;
        private String PicUrl;
        private String Url;
    
        public String getTitle() {
            return Title;
        }
    
        public void setTitle(String title) {
            Title = title;
        }
    
        public String getDescription() {
            return Description;
        }
    
        public void setDescription(String description) {
            Description = description;
        }
    
        public String getPicUrl() {
            return PicUrl;
        }
    
        public void setPicUrl(String picUrl) {
            PicUrl = picUrl;
        }
    
        public String getUrl() {
            return Url;
        }
    
        public void setUrl(String url) {
            Url = url;
        }
    }
    
    package com.hnu.scw.model;
    
    import java.util.List;
    
    /**
     * @author scw
     * @create 2018-01-17 13:35
     * @desc 对于图文消息的实体类
     **/
    public class NewsMessage extends BaseMessage{
        private int ArticleCount;
        private List<NewsBase> Articles;
    
        public int getArticleCount() {
            return ArticleCount;
        }
    
        public void setArticleCount(int articleCount) {
            ArticleCount = articleCount;
        }
    
        public List<NewsBase> getArticles() {
            return Articles;
        }
    
        public void setArticles(List<NewsBase> articles) {
            Articles = articles;
        }
    }

    消息封装工具类:

    package com.hnu.scw.utils;
    import com.hnu.scw.model.*;
    import com.thoughtworks.xstream.XStream;
    import org.dom4j.Document;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    import javax.servlet.http.HttpServletRequest;
    import java.io.InputStream;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    /**
     * @author scw
     * @create 2018-01-17 9:52
     * @desc 用户转换消息的格式
     **/
    public class MessageUtils {
        /**
         * 定义多种消息类型
         */
        public static final String MESSAGE_TEXT = "text";
        public static final String MESSAGE_IMAGE = "image";
        public static final String MESSAGE_VOICE = "voice";
        public static final String MESSAGE_MUSIC = "music";
        public static final String MESSAGE_VIDEO = "video";
        public static final String MESSAGE_LINK = "link";
        public static final String MESSAGE_LOCATION = "location";
        public static final String MESSAGE_EVENT = "event";
        public static final String MESSAGE_SUBSCRIBE = "subscribe";
        public static final String MESSAGE_UNSUBSCRIBE = "unsubscribe";
        public static final String MESSAGE_CLICK = "CLICK";
        public static final String MESSAGE_VIEW = "VIEW";
        //扫码事件
        public static final String MESSAGE_SCANCODE = "scancode_push";
    
        /**
         * XML格式转为map格式
         * @param request
         * @return
         */
        public static Map<String , String> xmlToMap(HttpServletRequest request){
            Map<String ,String> map = new HashMap<String , String>();
            try {
                InputStream inputStream =null;
                inputStream = request.getInputStream();
                SAXReader reader = new SAXReader();
                Document doc = reader.read(inputStream);
                Element rootElement = doc.getRootElement();
                List<Element> elements = rootElement.elements();
                for (Element el:elements) {
                    map.put(el.getName() , el.getText());
                }
                inputStream.close();
                return map ;
            } catch (Exception e) {
                e.printStackTrace();
                return null ;
            }
        }
        /**
         * 文本消息对象转为xml格式
         * @param myTestMessage
         * @return
         */
        public static String textMessage2Xml(MyTestMessage myTestMessage){
            XStream xStream = new XStream();
            xStream.alias("xml" , myTestMessage.getClass());
            return xStream.toXML(myTestMessage);
        }
        /**
         * 将图文消息对象转化为图文格式的XML
         * @return
         */
        public static String newsMessage2XML(NewsMessage newsMessage){
            XStream xStream = new XStream();
            //将需要修改的一些标签进行替换
            xStream.alias("xml" , newsMessage.getClass());
            xStream.alias("item" , new NewsBase().getClass());
            return xStream.toXML(newsMessage);
        }
        /**
         * 设置需要返回的图文信息
         * @param fromUserName
         * @param toUserName
         * @return
         */
        public static String initNewSMessage(String fromUserName , String toUserName ){
            String message = null;
            List<NewsBase> newList = new ArrayList<NewsBase>();
            NewsMessage newsMessage = new NewsMessage();
            NewsBase  newsBase = new NewsBase();
            newsBase.setTitle("我是图文消息");
            newsBase.setDescription("测试测试测试测试测试测试测试测试测试");
        newsBase.setPicUrl("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=677717294,4155848424&fm=27&gp=0.jpg")
            newsBase.setUrl("www.baidu.com");
            //添加图文消息的内容
            newList.add(newsBase);
    
            //注意接受消息和发送消息的顺序要反过来,因为现在是从服务器进行发送了,而客户端是接收端了
            newsMessage.setFromUserName(toUserName);
            newsMessage.setToUserName(fromUserName);
            newsMessage.setCreateTime(String.valueOf(System.currentTimeMillis()));
            newsMessage.setMsgType("news");
            newsMessage.setArticleCount(newList.size());
            newsMessage.setArticles(newList);
            //调用转为图文的XML
            return MessageUtils.newsMessage2XML(newsMessage);
        }
        /**
         * 设置需要返回的文本信息
         * @param fromUserName
         * @param toUserName
         * @param content
         * @return
         */
        public static String initText(String fromUserName , String toUserName , String content){
            MyTestMessage text = new MyTestMessage();
            //注意接受消息和发送消息的顺序要烦过来
            text.setFromUserName(toUserName);
            text.setToUserName(fromUserName);
            text.setMsgType(MessageUtils.MESSAGE_TEXT);
            long time = System.currentTimeMillis();
            text.setCreateTime(String.valueOf(time));
            text.setContent(content);
            return MessageUtils.textMessage2Xml(text);
        }
        /**
         * 设置订阅时,返回菜单的内容
         * @return
         */
        public static String menuText(){
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("菜单1:回复数字1,有惊喜\n");
            stringBuilder.append("菜单2:回复数字2,有惊喜\n");
            stringBuilder.append("菜单3:回复数字3,有惊喜\n");
            stringBuilder.append("菜单4:回复数字3,有惊喜\n");
            stringBuilder.append("菜单5:回复数字3,有惊喜\n");
            stringBuilder.append("菜单6:回复数字未知的东东,也还有有惊喜哦\n");
            return stringBuilder.toString();
        }
        /**
         * 回复关键字"1"的时候的内容
         * @return
         */
        public static String inputNumber1(){
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("我是惊喜111,哈哈,惊喜不惊喜!");
            return stringBuilder.toString();
        }
        /**
         * 回复关键字"2"的时候的内容
         * @return
         */
        public static String inputNumber2(){
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("我是惊喜2222,哈哈,惊喜不惊喜!");
            return stringBuilder.toString();
        }
        /**
         * 返回图片消息(对于视频和音频都是一样的方式,只需要更改类型即可,即将Image修改为video,voice)
         * @param fromUserName
         * @param toUserName
         * @return
         */
        public static String initImageMessage(String fromUserName , String toUserName ){
            ImageBase imageBase = new ImageBase();
            //这个media_Id是在之前执行过上传图片返回得到的信息
            imageBase.setMediaId("HK17wQmCupESK4B9u14PqI4w3gtteXhUtGgriJW6G5c8O-Y0OsjGbYfQYhGDbYDx");
            ImageMessage imageMessage = new ImageMessage();
            imageMessage.setFromUserName(toUserName);
            imageMessage.setToUserName(fromUserName);
            imageMessage.setMsgType(MessageUtils.MESSAGE_IMAGE);
            long time = System.currentTimeMillis();
            imageMessage.setCreateTime(String.valueOf(time));
            imageMessage.setImageBase(imageBase);
            return imageMessage2XML(imageMessage);
        }
        /**
         * 将图片消息对象转化为图片格式的XML
         * @return
         */
        public static String imageMessage2XML(ImageMessage imageMessage){
            XStream xStream = new XStream();
            //将需要修改的一些标签进行替换
            xStream.alias("xml" , imageMessage.getClass());
            xStream.alias("Image" , new ImageBase().getClass());
            return xStream.toXML(imageMessage);
        }
        /**
         * 将音乐消息对象转化为图片格式的XML
         * @return
         */
        public static String musicMessage2XML(MusicMessage musicMessage){
            XStream xStream = new XStream();
            //将需要修改的一些标签进行替换
            xStream.alias("xml" , musicMessage.getClass());
            xStream.alias("Music" , new MusicBase().getClass());
            return xStream.toXML(musicMessage);
        }
        /**
         * 返回音乐消息
         * @param fromUserName
         * @param toUserName
         * @return
         */
        public static String initMusicMessage(String fromUserName , String toUserName ){
            MusicBase musicBase = new MusicBase();
            //这个ThumbMediaId是在之前执行过上传缩略图返回得到的信息(这个和上传图片的方法是一样的,都是调用pictureUtils中的上传方法)
            musicBase.setThumbMediaId("vJOi5E4_U91onQnsayPdkzxted6ZctEAEzcoLd3BJ8a00gJLuhEmTckF6S2XkS5R");
            musicBase.setTitle("来一首音乐");
            musicBase.setDescription("静静的听首歌吧!");
            //设置高质量音质的歌曲路径,可以和一般音质音乐的路径一样
            musicBase.setHQMusicUrl("http://myjava.ngrok.xiaomiqiu.cn/resource/mymusic.mp3");
            //设置音乐的路径
            musicBase.setMusicUrl("http://myjava.ngrok.xiaomiqiu.cn/resource/mymusic.mp3");
            MusicMessage musicMessage = new MusicMessage();
            musicMessage.setFromUserName(toUserName);
            musicMessage.setToUserName(fromUserName);
            //设置类型为音乐
            musicMessage.setMsgType(MessageUtils.MESSAGE_MUSIC);
            long time = System.currentTimeMillis();
            musicMessage.setCreateTime(String.valueOf(time));
            musicMessage.setMusic(musicBase);
            return musicMessage2XML(musicMessage);
        }
    }

    交互主类:

    package com.hnu.scw.controller;
    import com.hnu.scw.utils.CheckConnectUtils;
    import com.hnu.scw.utils.MessageUtils;
    import com.hnu.scw.utils.WeiXinUserInfoUtiols;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.Map;
    /**
     * @author scw
     * @create 2018-01-18 11:38
     * @desc 微信前端连接的主要控制类
     **/
    @Controller
    public class WeChatDogPrimaryController {
        /**
         * 进行微信用户验证,只能是Get方法
         * @param request
         * @param response
         */
        @RequestMapping(value = "/wechat" ,method = RequestMethod.GET)
        public void connectValidate(HttpServletRequest request , HttpServletResponse response) throws IOException {
            String signature = request.getParameter("signature");
            String timestamp = request.getParameter("timestamp");
            String nonce = request.getParameter("nonce");
            String echostr = request.getParameter("echostr");
            System.out.println(""+signature +"@"+timestamp +"$"+nonce +"^"+echostr);
            PrintWriter out = response.getWriter();
            if(CheckConnectUtils.checkConncetWithWeChat(signature,timestamp,nonce)){
                out.print(echostr);
            }
        }
        /**
         * 客户端进行的消息处理
         * @param request
         * @param response
         */
        @RequestMapping(value = "/wechat" ,method = RequestMethod.POST)
        public void disposeClientMessage(HttpServletRequest request , HttpServletResponse response ) throws IOException {
            backTestFunction(request , response );
        }
        /**
         * 文字回复功能开发
         * @param request
         * @param response
         * @throws ServletException
         * @throws IOException
         */
        public void backTestFunction(HttpServletRequest request , HttpServletResponse response ) throws IOException {
            //防止进行post提交和响应的消息乱码
            request.setCharacterEncoding("UTF-8");
            response.setHeader("Content-type", "text/html;charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            PrintWriter out = response.getWriter();
            try{
                //将发送过来的消息XML形式转为map内容
                Map<String , String> map = MessageUtils.xmlToMap(request);
                String fromUserName = map.get("FromUserName");
                String toUserName = map.get("ToUserName");
                String msgType = map.get("MsgType");
                String content = map.get("Content");
                String message = null ;
                if(MessageUtils.MESSAGE_TEXT.equals(msgType)){
                    //进行关键字回复功能
                    if("1".equals(content)){
                        message = MessageUtils.initText(fromUserName,toUserName,MessageUtils.inputNumber1());
                    }else if("2".equals(content)){
                        message = MessageUtils.initText(fromUserName,toUserName,MessageUtils.inputNumber2());
                    }
                    else if("3".equals(content)){
                        //客户端输入“3”,返回一条图文消息
                        message = MessageUtils.initNewSMessage(fromUserName,toUserName);
                    }else if("4".equals(content)){
                        //客户端输入“4”,返回一条图片消息
                        message = MessageUtils.initImageMessage(fromUserName,toUserName);
                    }else if("5".equals(content)){
                        //客户端输入“5”,返回一首音乐消息
                        message = MessageUtils.initMusicMessage(fromUserName,toUserName);
                    }else if("6".equals(content)){
                        //测试是否能够获取用户的信息
                        message = MessageUtils.initText(fromUserName,toUserName, WeiXinUserInfoUtiols.getUserInfo(fromUserName));
                    }else if(content.startsWith("翻译")){
                        //客户端输入“以翻译开头”,返回对应的翻译信息
                        /*String translateResult = TranslationUtils.translate(content.substring(2,content.length()));
                        message = MessageUtils.initText(fromUserName,toUserName , translateResult);*/
                    }else {
                        message = MessageUtils.initText(fromUserName,toUserName,"你发送的消息是:" + content);
                    }
                }else if(MessageUtils.MESSAGE_EVENT.equals(msgType)){
                    String eventType = map.get("Event");
                    //完成订阅时候返回的内容
                    if(MessageUtils.MESSAGE_SUBSCRIBE .equals(eventType)){
                        message = MessageUtils.initText(fromUserName,toUserName , MessageUtils.menuText());
                    }else if(MessageUtils.MESSAGE_CLICK .equals(eventType)){
                        //进行的是click按钮的点击事件(这里就推送一下主菜单内容)
                        message = MessageUtils.initText(fromUserName,toUserName , MessageUtils.menuText());
                    }else if(MessageUtils.MESSAGE_VIEW .equals(eventType)){
                        //进行的是view按钮的点击事件(这里就推送一下主菜单内容)
                        String viewUrl = map.get("EventKey");
                        message = MessageUtils.initText(fromUserName,toUserName , viewUrl);
                    }else if(MessageUtils.MESSAGE_SCANCODE .equals(eventType)) {
                        //进行的是扫码事件
                        String key = map.get("EventKey");
                        message = MessageUtils.initText(fromUserName,toUserName , key);
                    }
                }else if(MessageUtils.MESSAGE_LOCATION .equals(msgType)) {
                    //进行的是地理位置信息
                    String label = map.get("Label");
                    message = MessageUtils.initText(fromUserName,toUserName , label);
                }
                //打印输出的xml格式内容,方便进行调试
                System.out.println(message);
                out.print(message);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                out.close();
            }
        }
    }

    (四)获取Access_Token

    Access_Token是一个全局的票据,调用任何的高级接口,都需要这个,所以这个非常非常的重要

    具体代码:

    package com.hnu.scw.utils;
    import com.hnu.scw.menu.BaseButton;
    import com.hnu.scw.menu.ClickButton;
    import com.hnu.scw.menu.CustomeMenu;
    import com.hnu.scw.menu.ViewButton;
    import com.hnu.scw.model.AccessToken;
    import com.hnu.scw.projectconst.ProjectConst;
    import net.sf.json.JSONObject;
    import org.apache.http.HttpEntity;
    import org.apache.http.HttpResponse;
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.DefaultHttpClient;
    import org.apache.http.util.EntityUtils;
    import org.springframework.web.context.ContextLoader;
    import org.springframework.web.context.WebApplicationContext;
    
    import javax.servlet.ServletContext;
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    
    /**
     * @author scw
     * @create 2018-01-17 14:13
     * @desc 用户获取access_token,众号调用各接口时都需使用access_token
     **/
    public class WeiXinUtils {
        /**
         * 微信公众号的APPID和Appsecret,这个是每个微信公众号都唯一的,以后配置不同的公众号配置这里即可
         */
        private static final String APPID = "自己公众号对应的内容";
        private static final String APPSECRET = "自己公众号对应的内容";
        //获取access_token的URL
        private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
    
        //进行创建菜单的接口URL
        private static final String CREATE_MENU_URL ="https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
    
        //菜单查询的接口URL
        private static final String QUERY_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN";
    
        //菜单删除的接口URL
        private static final String DELETE_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN";
    
        /**
         * Get请求,方便到一个url接口来获取结果
         * @param url
         * @return
         */
        public static JSONObject doGetStr(String url){
            DefaultHttpClient defaultHttpClient = new DefaultHttpClient();
            HttpGet httpGet = new HttpGet(url);
            JSONObject jsonObject = null;
            try{
                HttpResponse response = defaultHttpClient.execute(httpGet);
                HttpEntity entity = response.getEntity();
                if(entity != null){
                    String result = EntityUtils.toString(entity, "UTF-8");
                    jsonObject = JSONObject.fromObject(result);
                }
            } catch (ClientProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return jsonObject;
        }
    
        /**
         * 带参数的post请求,方便到一个url接口来获取结果
         * @param url
         * @param outStr
         * @return
         */
        public static JSONObject doPostStr(String url , String outStr)  {
            DefaultHttpClient defaultHttpClient = new DefaultHttpClient();
            HttpPost httpPost = new HttpPost(url);
            JSONObject jsonObject = null;
            try {
                httpPost.setEntity(new StringEntity(outStr , "UTF-8"));
                HttpResponse response = defaultHttpClient.execute(httpPost);
                HttpEntity entity = response.getEntity();
                if(entity != null){
                    String result = EntityUtils.toString(entity, "UTF-8");
                    jsonObject = JSONObject.fromObject(result);
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (ClientProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return jsonObject;
        }
    
        /**
         * 获取access_token
         * @return
         */
        public static AccessToken getAccessToken(){
            AccessToken accessToken = new AccessToken();
            String url = ACCESS_TOKEN_URL.replace("APPID" ,APPID).replace("APPSECRET",APPSECRET);
            JSONObject jsonObject = doGetStr(url);
            if(jsonObject !=null){
                accessToken.setToken(jsonObject.getString("access_token"));
                accessToken.setExpireIn(jsonObject.getLong("expires_in"));
            }
            return accessToken;
        }

    测试是否成功获取:

    package com.hnu.scw.test;
    import com.hnu.scw.model.AccessToken;
    import com.hnu.scw.utils.WeiXinAccessTokenKeepAlive;
    import com.hnu.scw.utils.WeiXinUtils;
    import org.junit.Test;
    /**
     * @author scw
     * @create 2018-01-18 15:21
     * @desc 用于对Access_token内容相关的测试
     **/
    public class AccessTokenTest {
        /**
         * 获取到Access_Token,这个对于要想使用其他的微信接口,就必须要有这个进行验证
         */
        @Test
        public void getAccssTokenTest(){
            AccessToken accessToken = WeiXinUtils.getAccessToken();
            System.out.println("token:" +accessToken.getToken());
            System.out.println("有效时间:" +accessToken.getExpireIn());
        }
    }

    (五)自定义菜单

    下面的代码,就接着(四)中的类写就可以了,因为都属于微信开发的工具类

    步骤:

    (1)创建菜单按钮的实体对象类

    BaseButton:

    package com.hnu.scw.menu;
    /**
     * @author scw
     * @create 2018-01-17 17:20
     * @desc 最基础的Button
     **/
    public class BaseButton {
        private String type;
        private String name;
        //子按钮(也可以理解为二级菜单)
        private BaseButton[] sub_button;
    
        public String getType() {
            return type;
        }
    
        public void setType(String type) {
            this.type = type;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public BaseButton[] getSub_button() {
            return sub_button;
        }
    
        public void setSub_button(BaseButton[] sub_button) {
            this.sub_button = sub_button;
        }
    }
    

    clickButton:

    package com.hnu.scw.menu;
    /**
     * @author Administrator
     * @create 2018-01-17 17:21
     * @desc Click类型的Button实体
     **/
    public class ClickButton extends BaseButton {
        private String key;
    
        public String getKey() {
            return key;
        }
    
        public void setKey(String key) {
            this.key = key;
        }
    }

    viewButton:

    package com.hnu.scw.menu;
    /**
     * @author scw
     * @create 2018-01-17 17:22
     * @desc 类型是View的按钮实体
     **/
    public class ViewButton extends BaseButton{
        private String url;
    
        public String getUrl() {
            return url;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    }
    

    CustomerMenu菜单定义:

    package com.hnu.scw.menu;
    /**
     * @author scw
     * @create 2018-01-17 17:23
     * @desc 自定义菜单的实体
     **/
    public class CustomeMenu {
        //对菜单按钮进行封装
        private BaseButton[] button;
    
        public BaseButton[] getButton() {
            return button;
        }
    
        public void setButton(BaseButton[] button) {
            this.button = button;
        }
    }

    (2)调用接口,进行菜单的创建

     /**
         * 设置菜单的形式
         * @return
         */
        public static CustomeMenu initMenu(){
            CustomeMenu customeMenu = new CustomeMenu();
            ClickButton clickButton = new ClickButton();
            clickButton.setName("click菜单");
            clickButton.setType("click");
            clickButton.setKey("01");
    
            ViewButton viewButton = new ViewButton();
            viewButton.setName("view菜单");
            viewButton.setType("view");
            viewButton.setUrl("需要访问的地址");
    
            ClickButton clickButton2 = new ClickButton();
            clickButton2.setName("扫码事件的click菜单");
            clickButton2.setType("scancode_push");
            clickButton2.setKey("02");
    
            ClickButton clickButton3 = new ClickButton();
            clickButton3.setName("地理位置的click菜单");
            clickButton3.setType("location_select");
            clickButton3.setKey("03");
    
            BaseButton baseButton = new BaseButton();
            baseButton.setName("菜单");
            //将clickButton2,clickButton3作为一个子菜单中的按钮
            baseButton.setSub_button(new BaseButton[]{clickButton2,clickButton3});
            //把按钮分别放入到菜单中
            customeMenu.setButton(new BaseButton[]{clickButton,viewButton,baseButton});
    
            return customeMenu;
        }
    
        /**
         * 创建自定义菜单
         * @param token
         * @param menu
         * @return
         */
        public static int createMenu(String token , String menu){
            int result = 0;
            String url = CREATE_MENU_URL.replace("ACCESS_TOKEN" ,token);
            JSONObject jsonObject = doPostStr(url, menu);
            if(jsonObject != null){
                //接受返回回来的参数,如果是0,就是创建成功
                result = jsonObject.getInt("errcode");
            }
            return result;
        }
    
        /**
         * 对菜单进行查询
         * @param token
         * @return
         */
        public static JSONObject queryMenu(String token){
            String url = QUERY_MENU_URL.replace("ACCESS_TOKEN" ,token);
            JSONObject jsonObject = doGetStr(url);
            return jsonObject;
        }
    
        /**
         * 对菜单进行删除
         * @param token
         * @return
         */
        public static JSONObject deleteMenu(String token){
            String url = DELETE_MENU_URL.replace("ACCESS_TOKEN" ,token);
            JSONObject jsonObject = doGetStr(url);
            return jsonObject;
        }

    (3)生成菜单

    package com.hnu.scw.test;
    import com.hnu.scw.model.AccessToken;
    import com.hnu.scw.utils.WeiXinUtils;
    import net.sf.json.JSONObject;
    import org.junit.Test;
    /**
     * @author scw
     * @create 2018-01-18 15:21
     * @desc 菜单相关的测试
     **/
    public class MenuTest {
        /**
         * 创建菜单
         */
        @Test
        public void creatMenuTest(){
            //获取到access_token
            AccessToken accessToken = WeiXinUtils.getAccessToken();
            //获取到自定义菜单的格式(JSONObject将对象转为json,然后再需要转为字符串型)
            String menu = JSONObject.fromObject(WeiXinUtils.initMenu()).toString();
            //调用创建菜单
            int result = WeiXinUtils.createMenu(accessToken.getToken(), menu);
            if(result == 0){
                //如果调用方法之后,返回的是0,那么就表示创建成功。
                System.out.println("创建成功");
            }else{
                System.out.println("创建失败");
            }
        }
        /**
         * 查询菜单
         */
        @Test
        public void queryMenuTest(){
            //获取到access_token
            AccessToken accessToken = WeiXinUtils.getAccessToken();
            //调用菜单查询的方法,返回是的一个Json格式
            JSONObject jsonObject = WeiXinUtils.queryMenu(accessToken.getToken());
            System.out.println(jsonObject);
        }
        /**
         * 删除菜单
         */
        @Test
        public void deleteMenuTest(){
            //获取到access_token
            AccessToken accessToken = WeiXinUtils.getAccessToken();
            //调用菜单查询的方法,返回是的一个Json格式
            JSONObject jsonObject = WeiXinUtils.deleteMenu(accessToken.getToken());
            if(jsonObject.getInt("errcode") == 0){
                //返回0,表示的是删除成功
                System.out.println("删除成功");
            }else{
                System.out.println("删除失败");
            }
    
        }
    }

           上面就是一些基本的微信公众号的交互了,刚刚接触可能不是很熟,慢慢的就了解了之后,其实也很简单的,当然,对于其中的一些高级接口的使用,可以看看我其他的文章,都有提到。。如果有问题,不明白的地方,欢迎进行交流和留言~!另外,推荐了比较好的学习资源,就是慕课网,这里面对于比较基础的微信公众号开发还是讲解的比较好,再结合我的文章,肯定是没有任何问题的。。。。

    Github的地址:

    https:https://github.com/qq496616246/WeChatCode.git

    git地址:git@github.com:qq496616246/WeChatCode.git

    展开全文
  • 微信公众号开发详细教程

    千次阅读 2019-05-24 17:53:17
    微信公众号开发之配置开发服务器 微信公众号开发之获取access_token 微信公众号开发之关键词回复 微信公众号开发之模板消息 微信公众号开发之授权回调 ...
    展开全文
  • 微信公众号开发——项目搭建

    万次阅读 2018-09-11 15:23:25
    前往微信公众平台(https://mp.weixin.qq.com/)获取开发权限和开发账号,公众号分为好几种,小程序,订阅号,服务号,企业号,个人只能用订阅号,权限比较少(api接口权限); 二 想要自定义模块功能或者是回复...

    一 

    前往微信公众平台(https://mp.weixin.qq.com/)获取开发权限和开发账号,公众号分为好几种,小程序,订阅号,服务号,企业号,个人只能用订阅号,权限比较少(api接口权限);

    想要自定义模块功能或者是回复信息啥的,就得有一个自己的后台来写接口,所以要配置自己的服务器

    基本配置:服务器配置(首页-开发-基本配置-服务器配置)

    进入修改配置,这里的服务器地址必须是外网(没有服务器的小伙伴如果这里分不清。。外网特征就是别人可以通过你的本机ip直接访问到,在简单点就是,你如果使用WiFi连接的网络那就肯定是内网或者说是局域网,查看方式,window键加R键,输入cmd回车,然后输入ipconfig 查看你当前的ip,然后百度本机ip,不一样的话就不是外网,解决办法是使用natapp进行内网穿透或者是用路由器进行端口映射,将服务器的某个端口映射出去,在最后会有详细步骤)而且是80端口,格式为ip/域名+wx(接口名 ,固定,但是前面可以加路由比如ip/home/wx)Token随便写个;

    然后是后台不多说先上代码:

    /// <summary>
    /// 配合微信服务器验证域名或者是ip的可用行或者是获取消息的推送--pxj
    /// </summary>
    [AtSkipAuthorize]
    public void wx()
    {
    	//获取消息推送,或者是事件推送,类型是xml文件流
    	if (Request.RequestType == "POST")
    	{
    
    	}
    	else
    	{
    		#region 验证请求来源是否是微信
    		string signature = Request["signature"].ToString();
    		string timestamp = Request["timestamp"].ToString();
    
    		string nonce = Request["nonce"].ToString();
    		string echostr = Request["echostr"].ToString();
    		string token = "hellowb";
    		List<string> list = new List<string>() { token, timestamp, nonce };
    		list.Sort();
    		string data = string.Join("", list);
    		byte[] temp1 = Encoding.UTF8.GetBytes(data);
    		SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider();
    		byte[] temp2 = sha.ComputeHash(temp1);
    
    		var hashCode = BitConverter.ToString(temp2);
    		hashCode = hashCode.Replace("-", "").ToLower();
    
    		if (hashCode == signature)
    		{
    			Response.Write(echostr);
    			Response.End();
    		}
    		LogHelper.WriteLog("signature:" + signature + " timestamp:" + timestamp + " nonce:" + nonce + " echostr:" + echostr);
    		#endregion
    	}
    }

    写好后台配置好服务器就可以在微信公众号网站也就是刚才的那个页面点击提交了,点击提交后微信的服务器会向你所配置的服务器80端口的这个接口发送GET请求,验证接口是否正确,如果提交成功,恭喜你可以进入下一个坑了;

    当你在写微信公众号的页面的时候,你有时候会用到上传图片或者打开摄像机等要求,这时候要接入JS_SDK,配置签名等等,这个签名应该算是配置里最难受的一块了。。。

    签名的获取有几个步骤,先是要通过appid和appSecret来获取access_token,然后用access_token来获取jsapi_ticket,代码如下

     //获取access_token
    string url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", appId, appSecret);
    var wc = new WebClient();
    string access_tokenRet = wc.DownloadString(url);
    string access_token = JsonHelper.JsonToObj<AccessTokenModel>(access_tokenRet).Access_Token;
    accesstokenstatic = access_token;
    
    //获取jsapi_ticket
    string Jsapi_TicketUrl = string.Format("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={0}&type=jsapi", access_token);
    var jsapiRet = wc.DownloadString(Jsapi_TicketUrl);
    string ticket = JsonHelper.JsonToObj<TickModel>(jsapiRet).ticket;
    ticketstatic = ticket;

    然后获取十位的时间戳和自己写的随机字符串和页面传来的url,将jsapi_ticket,url,timestamp(时间戳),nocestr(随机字符串随便写一个就行)sort方法排序,然后根据这几个的顺序进行赋值并用sha1加密如下

    string signaturestring = "jsapi_ticket=" + ticket + "&noncestr=" + nocestr + "&timestamp=" + timestamp + "&url=" + url2;
    byte[] temp1 = Encoding.UTF8.GetBytes(signaturestring);
    
    SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider();
    byte[] temp2 = sha.ComputeHash(temp1);
    var hashCode = BitConverter.ToString(temp2);
    hashCode = hashCode.Replace("-", "").ToLower();

     如此一来全部的都有了,返回这些就行了,如果报错了说url不合法就要取配置以下域名,二级域名也能用;

    四 ,带参二维码的扫描和事件的推送

    当你的公众号涉及到分享二维码给其他人,每一张二维码上还得要有你的信息的时候,需要在公众号的二维码上添加上一个场景值,比如你的用户id,当客户或者是其他用户通过你分享的二维码进入公众号的时候,微信服务器会推送给你一个xml包,其中一个子节点上就存着那个场景值;

    直接上代码--获取部分,

     public void GetQrCode()
     {
    	 string accesstoken = accesstokenstatic;
    	 if (string.IsNullOrEmpty(accesstoken))
    	 {
    		 //先主动取获取以下所需要的变量
    		 getaccesstoken(null, null);
    		 accesstoken = accesstokenstatic;
    		 Timer();
    	 }
    	//使用匿名对象作为post的参数然后再序列化
    	 var datap = new
    	 {
    		 //二维码的有效期(10天)
    		 expire_seconds = 864000,
    		 //类型动态的二维码
    		 action_name = "QR_SCENE",
    		 //信息
    		 action_info = new
    		 {
    			 scene = new {
    				 //自定义参数数值型为scene_id ,字符为scene_str
    				 scene_id = 15552300793
    			 }
    		 }
    	 };
    	 //使用流发送post带参请求
    	 string url = string.Format("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={0}",accesstoken);
    	 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
    	 request.Method = "POST";
    	 request.ContentType = "application/json";
    	 byte[] data = Encoding.UTF8.GetBytes(JsonHelper.ModelToJson(datap));
    	 request.ContentLength = data.Length;
    	 using (Stream strea = request.GetRequestStream())
    	 {
    		 strea.Write(data,0,data.Length);
    		 strea.Close();
    	 }
    	 HttpWebResponse respose = (HttpWebResponse)request.GetResponse();
    	 Stream stream = respose.GetResponseStream();
    	 string stringRespose = "";
    	 using (StreamReader sr = new StreamReader(stream, Encoding.UTF8))
    	 {
    		 stringRespose = sr.ReadToEnd();
    	 }
    	 if (string.IsNullOrEmpty(stringRespose))
    	 {
    		 Response.Write("sorry");
    	 }
    	 else { 
    		 //此时已经获取到二维码所使用的ticket
    		 string qrticket = JsonHelper.JsonToObj<QrCoderTicektModel>(stringRespose).ticket; 
    		 //二维码解析后的地址(可以自己转化为二维码)
    		 string qrurl =  JsonHelper.JsonToObj<QrCoderTicektModel>(stringRespose).url; 
    		 Response.Write("hhhhhh");
    		 LogHelper.WriteLog("getsometing:" + stringRespose);
    	 }
     }

    现在是获得了二维码的解析后的url,和获取二维码的ticket;有两种方式可以得到二维码,一种是直接将这个地址url返给前端,前端自己生成,二是后台获取到这个二维码后保存到服务器,前端自己去取;

    获取微信的推送,如刚才所说,扫码的时候会发送一个xml包;

    代码如下

    public void wx()
    {
    	//获取消息推送,或者是事件推送,类型是xml文件流
    	if (Request.RequestType == "POST")
    	{
    		#region 推送
    		//加载xml包,获取推送的内容
    		StreamReader sr = new StreamReader(Request.InputStream);
    		string xmlData = sr.ReadToEnd();
    		LogHelper.WriteLog(xmlData);
    		var doc = new XmlDocument();
    		doc.LoadXml(xmlData);
    		//获取xml包里的消息类型和事件类型,分类处理
    		var msgTypeNode = doc.SelectSingleNode("xml/MsgType");
    		var evenTypeNode = doc.SelectSingleNode("xml/Event");
    		string msgTypeString = msgTypeNode.InnerText;
    		if (evenTypeNode == null)
    		{
    			//此时为普通的消息推送
    		}
    		else
    		{
    			//此时为事件推送
    			string evenTypeString = evenTypeNode.InnerText;
    			//扫描带参的二维码的时候和关注和取关的推送
    			if (evenTypeString == "SCAN" || evenTypeString == "subscribe")
    			{
    				string SaoMaRenOpenId = doc.SelectSingleNode("xml/FromUserName").InnerText;
    				var BeiSaoRenCanShuNode = doc.SelectSingleNode("xml/EventKey");
    				if (BeiSaoRenCanShuNode != null)
    				{
    					string BeiSaoRenCanShuSting = BeiSaoRenCanShuNode.InnerText.Split('_')[1];
    					//已经获取扫码人和被扫人的个人id,下面就是存到数据库做关联就ok了,用到的时候取
    				}
    			}
    
    			//地理位置推送
    			if (evenTypeString == "LOCATION")
    			{
    
    			}
    
    			//点击自定义菜单拉去信息的时候的推送
    			if (evenTypeString == "CLICK")
    			{
    
    			}
    		}
    		#endregion
    	}

    五,

    natapp内网穿透:

    https://natapp.cn/注册,登录,获取自己的隧道,下载客户端,解压,cmd进入客户端的文件夹,natapp.exe -authtoken = "你的autotoken",

    如下则成功了

     

     

    展开全文
  • 微信公众号开发(三)前端界面

    万次阅读 热门讨论 2020-06-26 18:20:09
    由于该公众号主要的用途是用于公司售后服务,经沟通确定相关需求. 第一: 了解相关的UI框架,最终选定了Jquery-WeUI框架. 第二: 制作简单案例,在移动端进行测试,要达到移动端的自适应. 目前完成三个界面如下: ...

    由于该公众号主要的用途是用于公司售后服务,经沟通确定相关需求.

    第一: 了解相关的UI框架,最终选定了Jquery-WeUI框架.

    第二: 制作简单案例,在移动端进行测试,要达到移动端的自适应.

    目前完成三个界面如下:

    上面的就是 目前的界面以及手机适应的效果,后期需要修改.

    下面的是第一个界面的代码:

     

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0" />
            
            <!-- Jquery  -->
            <script src="/WeChatDemo/jquery-weui/dist/lib/jquery-2.1.4.js"></script>
            <!--weui css-->
            <link rel="stylesheet" href="https://cdn.bootcss.com/weui/1.1.0/style/weui.min.css" />
            <!--jQuery weui-->
            <link rel="stylesheet" href="https://cdn.bootcss.com/jquery-weui/0.8.0/css/jquery-weui.min.css">
            <!--切换样式-->
            <style type="text/css">
                .weui_navbar_item.weui_bar_item_on {
                    color: #2196F3;
                    cursor: pointer;
                }
            </style>
            <title>售后测试	</title>
        </head>
    <body ontouchstart>
    <header class="ui-header">
    			<h1>**电气售后服务公众号</h1></header>
    			
    			
    <div class="weui-cells weui-cells_form">
    <!-- 车型 -->
      <div class="weui-cell">
        <div class="weui-cell__hd"><label class="weui-label ">车型</label></div>
        <div class="weui-cell__bd">
        <!-- 这个地方的pattern 需要进行修改 -->
        <!-- 此处输入车型信息 添加 车型class  -->
          <input class="weui-input chexing" type="text"   placeholder="点击选择车型">
        </div>
      </div>
    <!-- 车型 -->
    <!-- 车号 -->
      <div class="weui-cell weui-cell_vcode">
        <div class="weui-cell__hd">
          <label class="weui-label">车号</label>
        </div>
        <div class="weui-cell__bd">
        <!-- 此处添加车号信息  添加车号 class -->
          <input class="weui-input chehao" type="text" placeholder="请手动输入机车号">
        </div>
      </div>
    <!-- 车号 -->
    <!-- 日期 -->
      <div class="weui-cell">
        <div class="weui-cell__hd"><label for="" class="weui-label">日期</label></div>
        <div class="weui-cell__bd">
        <!-- 此处添加 日期信息 添加日期 class -->
          <!-- <input class="weui-input faultdate" type="date" value=""/> -->
          <input class="weui-input faultdate" type="text" data-toggle='date'/>
        </div>
      </div>
    <!-- 日期 -->
    <!-- 故障发生地点 -->
    <!-- 暂时有些问题 先去掉 -->
     <!--  <div class="weui-cell">
        <div class="weui-cell__hd"><label for="" class="weui-label">地点</label></div>
        <div class="weui-cell__bd">
          <input class="weui-input faultarea" type="text"  data-toggle="city-picker" value="浙江 杭州 拱墅区" />
        </div>
      </div> -->
    
    <!-- 故障发生地点 -->
    <!-- 时间 -->
    <!-- 这个部分暂时省略,等待后期需求需要在改回去 -->
      <!-- <div class="weui-cell">
        <div class="weui-cell__hd"><label for="" class="weui-label">时间</label></div>
        <div class="weui-cell__bd">
        此处添加 时间信息 添加时间 class
          <input class="weui-input faulttime" type="datetime-local" value="" placeholder="">
        </div>
      </div> -->
    <!-- 时间 -->
    <!-- 机车故障信息 -->
    <div class="weui-cells__title">机车故障填报区</div>
    <div class="weui-cells weui-cells_form">
      <div class="weui-cell">
        <div class="weui-cell__bd">
        <!-- 故障信息 添加故障class faultinfo -->
          <textarea class="weui-textarea faultinfo" placeholder="请在此处填写机车故障信息!" rows="3"></textarea>
          <div class="weui-textarea-counter"><span>0</span>/200</div>
        </div>
      </div>
    </div>
    <!-- 机车故障信息 -->
    </div>
    
    <!-- 提交成功提示界面 -->
    <a  class="weui-btn weui-btn_primary">提交</a>
    <a  class="weui-btn weui-btn_warn">取消</a>
    <!-- 提交成功提示界面 -->
    <!-- 底部foot -->
    <div class="weui-footer weui-footer_fixed-bottom">
      <p class="weui-footer__links">
        <a href="javascript:void(0);" class="weui-footer__link ">**电气售后</a>
      </p>
      <p class="weui-footer__text ">Copyright © 2010-2018 **dq.com</p>
    </div>
    <!-- 底部foot -->
    
    
    
    
    <script type="text/javascript">
    $(document).ready(function(){
    	$("a.weui-btn.weui-btn_primary").click(function(){
    	 alert("已经获取点击事件"); 
    	 		
    	 	var chexing = $(".weui-input.chexing").val();
    	 	var chehao = $(".weui-input.chehao").val();
    	 	var faultdate = $(".weui-input.faultdate").val();
    	 	var faulttime = $(".weui-input.faulttime").val();
    	 	var faultinfo = $(".weui-textarea.faultinfo").val();
    	 	alert("车型是:" + chexing + "车号是:" + chehao + "故障填报时间是:" + faultdate
    	 			+"故障时间是:" + faulttime + "故障信息是:" + faultinfo); 
    	 	
    	/*  	 $.ajax({
    	        //直接"post"或者"get",不需要"doPost","doGet",该函数到后端接收缓冲区会自动匹配
    	        type : "post",      
    	        //servlet文件名为Calculator,需要提前在web.xml里面注册
    	        url : "FaultServlet", 
    	       dataType :  "json",  //数据类型,可以为json,xml等等,
    	        data :
    	        {
    	             "chexing" : chexing,//车型
    	             "chehao":chehao,//车号
    	             "faultdate":faultdate,//故障时间
    	             "faultinfo":faultinfo //故障信息
    	        },
    	        success : function(response)
    	        {
    	               //处理后端传递过来的 JSON 数据.
    	               var success = response.success;
    	               alert("后端处理完毕");
    	               if(success == "SUCCESS"){
    	            	   //使用jquery 代码进行跳转.
    	               $(location).attr('href', 'http://inut5w.natappfree.cc//WeChatDemo/submitsuccess.html');
    	               }
    	        },
    	        error : function(xhr, status, errMsg,response)
    	        {	
    	        	//服务器错误处理
    	        	var v1 = xhr;
    	        	var v2 = status;
    	        	var v3 = errMsg;
    	            alert("数据传输失败!");
    	        }
    	    });  */
    	 	
    //	 	 $(location).attr('href', 'http://inut5w.natappfree.cc//WeChatDemo/submitsuccess.html');
    	 	 $(location).attr('href', 'http://localhost:20000//WeChatDemo/submitsuccess.html');
    	  });
    	  <!--这个是机车车型的Picker-->
    	  $(".weui-input.chexing").picker({
    		 // input: '.weui-input.chexing',//这两个属性加上之后没有效果.有待测试.
    		 // container: '#container',
    		  title: "选择您的故障车型",
    		  cols: [
    		    {
    		      textAlign: 'center',
    		      values: ['HXD1', 'HXD3', 'HXN5', 'HXN5B', 'HXN3', 'HXN3B', 'HXD3D']
    		    }
    		  ]
    		});
    	  <!--这个是机车车型的Picker-->
    	  <!--这个是机车型号的Picker-->
    	 /*  $(".weui-input.chehao").picker({
    		 // input: '.weui-input.chexing',//这两个属性加上之后没有效果.有待测试.
    		 // container: '#container',
    		  title: "选择您的故障车号",
    		  cols: [
    		    {
    		      textAlign: 'center',
    		      values: ['0001', '0002', '0003', '0004', '0005', '0006', '0007']
    		    }
    		  ]
    		}); */
    		//车号的picker 暂时禁用 由于车号估计数量比较多 
    		//所以 为了便于用户的使用 决定使用 手动输入的方式.
    	  <!--这个是机车型号的Picker-->
    	  <!--这个是日期的Picker-->
    	/*   $(".weui-input.faultdate").calendar(); */ //这个只是日期的简单的日期 不包括时间的格式.
    	//下面的这个是包括的日期还有时间
    	  $(".weui-input.faultdate").calendar();
    	  <!--这个是日期的Picker-->
    	  <!--故障填报区这写字体增加样式-->
    	  $(".weui-cells__title").css({"color":"red","font-size":"12px"});
    	  /* #18b4ed  蓝色的背景    不正常的绿色  #33cc00 */
    	  $(".ui-header").css({"color":"#fff","font-size":"15px","background-color":"#1AAD19","text-align": "center"});
    	  /*  $(".ui-header h1").css({"color":"#fff","font-size":"20px",}); */
    	  
    	  <!--故障填报区这写字体增加样式-->
    	  <!--故障地点-->
    	  //$(".weui-input.faultarea").cityPicker();//目前有问题 暂时先去掉
    	  <!--故障地点-->
    	  
    	});
    </script>
    
    
            <!--页面切换-->
            <script src="https://res.wx.qq.com/open/libs/zepto/1.1.6/zepto.js"></script>
            <script src="https://cdn.bootcss.com/jquery/1.11.0/jquery.min.js"></script>
            <script src="https://cdn.bootcss.com/jquery-weui/0.8.2/js/jquery-weui.min.js"></script>
        </body>
    </html>


    上面引入文件的时候注意这个地方:

     

     

     <!--weui css-->
            <link rel="stylesheet" href="https://cdn.bootcss.com/weui/1.1.0/style/weui.min.css" />
            <!--jQuery weui-->
            <link rel="stylesheet" href="https://cdn.bootcss.com/jquery-weui/0.8.0/css/jquery-weui.min.css">


    本地文件也有如下图:

     

    但是引入本地的文件,在电脑测试的时候是可以出现效果的,可是在手机端测试的时候无法适应移动端设备.

    改为代码中的引入方式就可以做到适应,不清楚是版本的问题,还是文件的问题.

    这里是Jquery-WeUI官网首页:JQueryWeUI官网.

    我们可以参照相关Demo,根据自身需求进行前端界面的设计以及代码的编写.

    展开全文
  • 微信公众平台开发入门

    万人学习 2019-12-30 15:18:17
    通过本课程的学习,学员能够入门微信公众平台开发,能够胜任企业级的订阅号、服务号、企业号的应用开发工作。 通过本课程的学习,学员能够对微信公众平台有一个清晰的、系统性的认识。例如,公众号是什么,它有...
  • 微信公众号开发技术要点

    万次阅读 多人点赞 2019-03-08 09:29:26
    微信公众号开发技术要点 微信公众号开发技术要点 微信公众号及其接口功能介绍 基本概念 公众号开发者模式 代码验证及图示 Open ID与Union ID 基本概念 使用说明 Access_token 基本介绍 注意事项 获取流程 ...
  • 微信公众号开发小记(一)开篇

    万次阅读 2018-08-10 12:33:33
    接下来的时间里,我会以连载的形式分享微信公众号开发的相关文章,其中也会说一下我遇到的问题以及解决的思路,以及在开发过程中的一些感悟!因为是一个连载系列,所以会以一个初学者,也就是接触过编程但是没有接触...
  • 微信公众号开发基本流程

    万次阅读 多人点赞 2019-04-26 09:40:21
    过年前后做了个微信公众号项目,已经过去一段时间了,抽空回忆总结下基本流程吧,不然很快估计自己就忘了。。 微信公众平台官网:https://mp.weixin.qq.com 文章目录一、注册公众号二、了解公众号管理页面三、必备...
  • 微信公众号开发教程(一) 验证接入

    万次阅读 多人点赞 2018-06-22 14:22:35
    微信公众号开发教程(一)验证接入本篇文章主要介绍了微信公众号开发接入详细流程,希望对刚接触公众号开发的同学有所帮助,有兴趣的同学可多多关注叩丁狼公众号,后续会更新不同的公众号小案例。公众号的分类我们平常...
  • 微信公众号主要有以下几个步骤 微信公众号的通讯机制 微信公众号简介 1.注册微信公众号 2.注册测试公众号 3.搭建微信本地调试环境 1)下载客户端natapp: 2)安装natapp: 4.微信公众号接入(校验签名) 第1步中...
  • 如何在本地进行微信公众号开发和调试
  • 微信公众号开发之获取code

    万次阅读 2018-12-31 17:12:26
    微信公众号开发过程中要获取用户信息,需要通过OAuth2.0机制获得code,然后用code换取openID。 1、先确定自己的回调地址,下面拼接地址时需要用到,这里我用REDIRECT_URI代替。 2、拼接地址,如下,细心点看: ...
  • 微信开发中,一个页面如何跳转到公众号首页? 相当于在页面实现一个退出登陆的效果,跳转到刚进入公众号首页的样子
  • 在工作中经常遇到公众号开发,其中公众号的缓存问题是比较恶心人的。 解决办法: 找到 文件传输助手,发送 debugtbs.qq.com ,打开链接,清楚TBS内核,success ...
  • 微信公众号开发中关于cookie的问题

    万次阅读 2016-03-11 10:23:19
    最近在做的一个项目中使用到了微信公众号,在公众号中打开的页面中需要使用到cookie,这样方便两个页面之间的传值,但是发现页面跳转时候cookie已经过期。百思不得其解,浏览器中测试正常的,到了微信中就不行了?。...
  • 微信公众号开发(二、消息推送)

    万次阅读 2016-08-05 14:53:10
    微信公众号的配置完了,消息推送就比较简单了。本节主要介绍一下推送的代码。  一、首先需要在公众号平台中新建一个模板(用于推送消息)。下面是测试和正式微信公众号的模板。正式的模板可以设置标题、行业、内容...
  • 下载微信开发者工具 https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html 下载好后界面如下: 二.在命令行运行项目,npm run dev 三.在上张图片的红框内输入项目URL地址,如:...
  • android跳转微信指定公众号界面

    万次阅读 2016-12-01 14:41:03
    android跳转微信指定公众号界面注:跳转到微信公众号,首先需要到微信开发平台,绑定对应的公众号String appId = "你的ID";//开发者平台ID IWXAPI api = WXAPIFactory.createWXAPI(getActivity(), appId, false); ...
  • 微信公众号开发测试帐号

    万次阅读 2018-01-10 10:33:01
    扫描关注后登录 ...填写JS接口安全域名 ,设置JS接口安全域后,通过关注该测试号,开发者即可在该域名下调用微信开放的JS接口,请阅读微信JSSDK开发文档。 注意:不知道啥原因,用自己的帐号申请测试
  • 微信公众号使用教程

    万人学习 2018-11-27 16:29:08
    微信公众号使用,是一套基础课程,为后面的《PHP微信公众号开发》做铺垫。微信几乎是智能手机上必装的软件,很多的商家在微信上开通公众号,想要把公众号的价值发挥大,必须学会公众号的使用,开发和营销。这一套...
1 2 3 4 5 ... 20
收藏数 123,654
精华内容 49,461
关键字:

微信公众号开发