Spring + Fastweixin 微信開發


  這篇文章轉自《http://www.qtdebug.com/spring-weixin/》

微信有兩種模式,編輯模式和開發者模式,有些功能是互斥的,不可以同時使用,微信開發需要在開發者模式下進行(開發者模式下仍然可以去微信的網頁上群發消息)。下面介紹的功能能滿足大部分的需求,響應文本消息,圖文消息,創建菜單,響應菜單消息等。

我們給微信提供服務有兩種消息模式,被動和主動

  • 被動: 例如用戶輸入文本,點擊菜單,微信服務器會訪問我們的 Web 服務對應的 URL,我們返回對應的消息給微信服務器
  • 主動: 例如創建菜單,群發消息,這種模式需要我們主動去觸發,給微信服務器發送消息,可以是執行某個定時任務觸發,或者我們訪問某個 URL 然后在其響應的代碼里觸發
  • 幾個關鍵點

    • 微信服務器和我們的服務器綁定驗證時使用 GET 發送一個帶有 echostr 參數的請求
    • 其他消息使用的是 POST,常用的消息有
      1. 事件消息 event: subscribe, unsubscribe, location 等
      2. 文本消息 text: 可以回復文本,鏈接,圖文
      3. 點擊菜單回復的 click
      4. 點擊菜單跳轉為 view
    • 微信服務器訪問我們的服務器的 URL 只有一個,就是在配置頁中配置的 URL
    • 使用 app_id + app_secret 從微信服務器獲取 access_token,有效時間是 7200 秒
    • 微信的接口:用於我們主動的從微信服務器獲取信息或者主動的向微信服務器寫入信息

    准備外網能訪問的域名

    • 使用 ngrok.cc 讓本地服務能在外網訪問,這樣微信服務器才能訪問到我們的 Web 服務器

    准備公眾號

    1. 訪問 https://mp.weixin.qq.com 注冊開發者賬號
    2. 登陸后到最下面 開發 > 基本配置 中啟用開發者模式,並點擊 修改配置 配置我們自己給微信提供服務的 Web 地址、Token 等,然后使用 appID和 appsecret 就可以進行開發了
      • 如果我們申請的是 個人訂閱號,很多功能接口都沒有,例如自定義菜單都沒有,為了使用所有的功能進行開發練習,可以使用微信提供的 公眾平台測試帳號開發 > 開發者工具 > 公眾平台測試帳號 > 進入,就可以看到 appID 和 appsecret,也需要在這里配置給微信提供服務的 Web 地址,Token 等,然后在頁面中部找到 請用微信掃描關注測試公眾號,掃描關注即可
      • 查看公眾號接口權限說明開發 > 開發者工具 > 開發者文檔 > 進入 > 公眾號接口權限說明
      • 個人訂閱號 是不能進行認證的
      • Gradle 依賴

        1
        compile( "com.github.sd4324530:fastweixin:1.3.11")

        Fastweixin 的 git: https://git.oschina.net/pyinjava/fastweixin

         

      • 給微信提供服務的 Controller

        1
        public class WeixinController extends WeixinControllerSupport

        消息響應

        在 WeixinController 中重載下面幾個函數

        • 響應訂閱消息

          1
          protected BaseMsg handleSubscribe(BaseEvent event)
        • 響應文本消息

          1
          protected BaseMsg handleTextMsg(TextReqMsg msg)
        • 響應點擊菜單消息

          1
          protected BaseMsg handleMenuClickEvent(MenuEvent event)

        創建消息

        • 創建文本消息

          1
          new TextMsg("你好: <a href=\"http://www.baidu.com\">百度</a>");
        • 創建圖文消息(單圖文,多圖文都可以)

          1
          2
          3
          4
          String picUrl = "http://image.17car.com.cn/image/20120810/20120810092133_13289.jpg"; // 消息中顯示的圖片
          String url = "http://news.17car.com.cn/saishi/20120810/336283.html"; // 點擊消息后跳轉的網頁的地址
          String description = "700 馬力道路賽車 DDMWorks 打造最強 Atom";
          NewsMsg msg = new NewsMsg(Arrays.asList(new Article("Atom", description, picUrl, url), new Article("Atom", description, picUrl, url)));

        創建菜單

      • 創建菜單

        • 微信不會訪問這個URL,需要我們自己訪問創建菜單的 URL,然后才能向微信發送創建菜單信息。
        • 微信只能保證菜單 24 小時之類生效,想要馬上看到菜單效果,先取消關注,然后再次關注就可以了
        • CLICK 類型的菜單會發送消息到我們的服務器,handleMenuClickEvent() 進行響應,根據 key 來判斷是哪個菜單被點擊
        • VIEW 類型的菜單不回發送消息到我們的服務器,而是直接跳轉到對應的 URL
        • 菜單分一級菜單和二級菜單
          • 最多有 3 個一級菜單,5 個二級菜單
          • 一級菜單最多 4 個漢字,二級菜單最多 7 個漢字
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        @GetMapping("/create-menu")
        @ResponseBody
        public String createMenu() {
        // 准備一級主菜單
        MenuButton main1 = new MenuButton();
        main1.setType(MenuType.CLICK); // 可點擊的菜單
        main1.setKey( "main1");
        main1.setName( "主菜單一");
         
        MenuButton main2 = new MenuButton();
        main2.setType(MenuType.VIEW); // 鏈接的菜單,點擊后跳轉到對應的 URL
        main2.setName( "主菜單二");
        main2.setUrl( "http://www.baidu.com");
         
        MenuButton main3 = new MenuButton();
        main3.setType(MenuType.CLICK);
        main3.setName( "真題");
         
        // 帶有子菜單
        MenuButton sub1 = new MenuButton();
        sub1.setType(MenuType.CLICK); // 帶有子菜單
        sub1.setName( "2016 語文");
        sub1.setKey( "sub1");
         
        MenuButton sub2 = new MenuButton();
        sub2.setType(MenuType.CLICK);
        sub2.setName( "2016 數學");
        sub2.setKey( "sub2");
        main3.setSubButton(Arrays.asList(sub1, sub2));
         
        Menu menu = new Menu();
        menu.setButton(Arrays.asList(main1, main2, main3));
         
        //創建菜單
        ApiConfig config = new ApiConfig(APP_ID, APP_SECRET);
        MenuAPI menuAPI = new MenuAPI(config);
        ResultType resultType = menuAPI.createMenu(menu);
        return resultType.toString();
        }

        使用 Json 字符串創建菜單

        上面的程序創建菜單太麻煩了,可以使用 Json 字符串,然后反序列化為菜單對象,下面使用 Jackson 來實現。

        Jackson 依賴: compile("com.fasterxml.jackson.core:jackson-databind:2.7.4")

        菜單的 Json 字符串可以放在文件,數據庫中等,方便修改,而且比使用對象的方式更直觀,例如下面這樣:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        {
        "button": [{
        "type": "CLICK",
        "name": "主菜單一",
        "key": "main1"
        }, {
        "type": "VIEW",
        "name": "主菜單二",
        "url": "http://www.baidu.com"
        }, {
        "type": "CLICK",
        "name": "真題",
        "subButton": [{
        "type": "CLICK",
        "name": "2016 語文",
        "key": "sub1"
        }, {
        "type": "CLICK",
        "name": "2016 數學",
        "key": "sub2"
        }]
        }]
        }
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        @GetMapping("/create-menu")
        @ResponseBody
        public String createMenu() throws Exception {
        String json = "{\n" +
        " \"button\": [{\n" +
        " \"type\": \"CLICK\",\n" +
        " \"name\": \"今日歌曲\",\n" +
        " \"key\": \"V1001_TODAY_MUSIC\"\n" +
        " }, {\n" +
        " \"name\": \"菜單\",\n" +
        " \"subButton\": [{\n" +
        " \"type\": \"VIEW\",\n" +
        " \"name\": \"搜索\",\n" +
        " \"url\": \"http://www.soso.com/\"\n" +
        " }, {\n" +
        " \"type\": \"VIEW\",\n" +
        " \"name\": \"視頻\",\n" +
        " \"url\": \"http://v.qq.com/\"\n" +
        " }, {\n" +
        " \"type\": \"CLICK\",\n" +
        " \"name\": \"贊一下我們\",\n" +
        " \"key\": \"V1001_GOOD\"\n" +
        " }]\n" +
        " }]\n" +
        "}";
         
        ObjectMapper mapper = new ObjectMapper();
        Menu menu = mapper.readValue(json, Menu.class);
         
        //創建菜單
        ApiConfig config = new ApiConfig(APP_ID, APP_SECRET);
        MenuAPI menuAPI = new MenuAPI(config);
        ResultType resultType = menuAPI.createMenu(menu);
        return resultType.toString();
        }

        示例程序

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        62
        63
        64
        65
        66
        67
        68
        69
        70
        71
        72
        73
        74
        75
        76
        77
        78
        79
        80
        81
        82
        83
        84
        85
        86
        87
        88
        89
        90
        91
        92
        93
        94
        95
        96
        97
        98
        99
        100
        101
        102
        103
        104
        105
        106
        107
        108
        109
        110
        111
        112
        113
        114
        115
        116
        117
        118
        119
        120
        121
        122
        package com.xtuer.controller;
         
        import com.github.sd4324530.fastweixin.api.MenuAPI;
        import com.github.sd4324530.fastweixin.api.config.ApiConfig;
        import com.github.sd4324530.fastweixin.api.entity.Menu;
        import com.github.sd4324530.fastweixin.api.entity.MenuButton;
        import com.github.sd4324530.fastweixin.api.enums.MenuType;
        import com.github.sd4324530.fastweixin.api.enums.ResultType;
        import com.github.sd4324530.fastweixin.message.Article;
        import com.github.sd4324530.fastweixin.message.BaseMsg;
        import com.github.sd4324530.fastweixin.message.NewsMsg;
        import com.github.sd4324530.fastweixin.message.TextMsg;
        import com.github.sd4324530.fastweixin.message.req.MenuEvent;
        import com.github.sd4324530.fastweixin.message.req.TextReqMsg;
        import com.github.sd4324530.fastweixin.servlet.WeixinControllerSupport;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
        import org.springframework.web.bind.annotation.GetMapping;
        import org.springframework.web.bind.annotation.RequestMapping;
        import org.springframework.web.bind.annotation.ResponseBody;
        import org.springframework.web.bind.annotation.RestController;
         
        import java.util.Arrays;
         
        @RestController
        @RequestMapping("/weixin")
        public class WeixinController extends WeixinControllerSupport {
        private static final Logger LOG = LoggerFactory.getLogger(WeixinController.class);
        private static final String TOKEN = "xxxxxx"; // 你的 token
        private static final String APP_ID = "yyyyyy"; // 你的 appID
        private static final String APP_SECRET = "zzzzzz"; // 你的 appsecret
         
        // 重寫父類方法,處理對應的微信消息
        @Override
        protected BaseMsg handleTextMsg(TextReqMsg msg) {
        String content = msg.getContent(); // content 是用戶輸入的信息
         
        switch (content.toUpperCase()) {
        case "URL":
        // 消息中有鏈接
        return new TextMsg("你好: <a href=\"http://www.baidu.com\">百度</a>");
        case "ATOM":
        // 圖文消息
        String picUrl = "http://image.17car.com.cn/image/20120810/20120810092133_13289.jpg"; // 消息中顯示的圖片
        String url = "http://news.17car.com.cn/saishi/20120810/336283.html"; // 點擊消息后跳轉的網頁的地址
        String description = "700 馬力道路賽車 DDMWorks 打造最強 Atom";
        return new NewsMsg(Arrays.asList(new Article("Atom", description, picUrl, url), new Article("Atom", description, picUrl, url)));
        default:
        return new TextMsg("不識別的命令, 您輸入的內容是: " + content);
        }
        }
         
        @Override
        protected BaseMsg handleMenuClickEvent(MenuEvent event) {
        String key = event.getEventKey();
        switch (key.toUpperCase()) {
        case "MAIN1":
        return new TextMsg("點擊按鈕");
        case "SUB1":
        return new TextMsg("2016 語文");
        case "SUB2":
        return new TextMsg("2016 數學");
        default:
        return new TextMsg("不識別的菜單命令");
        }
        }
         
        // 設置 TOKEN,用於綁定微信服務器
        @Override
        protected String getToken() {
        return TOKEN;
        }
         
        // 獲取 access token: http://localhost:8080/weixin/access-token
        @GetMapping("/access-token")
        @ResponseBody
        public String getAccessToken() {
        ApiConfig config = new ApiConfig(APP_ID, APP_SECRET);
        return config.getAccessToken();
        }
         
        // 創建菜單, 訪問 http://localhost:8080/weixincreate-menu 就會把菜單信息發送給微信服務器
        @GetMapping("/create-menu")
        @ResponseBody
        public String createMenu() {
        // 准備一級主菜單
        MenuButton main1 = new MenuButton();
        main1.setType(MenuType.CLICK); // 可點擊的菜單
        main1.setKey( "main1");
        main1.setName( "主菜單一");
         
        MenuButton main2 = new MenuButton();
        main2.setType(MenuType.VIEW); // 鏈接的菜單,點擊后跳轉到對應的 URL
        main2.setName( "主菜單二");
        main2.setUrl( "http://www.baidu.com");
         
        MenuButton main3 = new MenuButton();
        main3.setType(MenuType.CLICK); // 帶有子菜單
        main3.setName( "真題");
         
        // 帶有子菜單
        MenuButton sub1 = new MenuButton();
        sub1.setType(MenuType.CLICK);
        sub1.setName( "2016 語文");
        sub1.setKey( "sub1");
         
        MenuButton sub2 = new MenuButton();
        sub2.setType(MenuType.CLICK);
        sub2.setName( "2016 數學");
        sub2.setKey( "sub2");
        main3.setSubButton(Arrays.asList(sub1, sub2));
         
        Menu menu = new Menu();
        menu.setButton(Arrays.asList(main1, main2, main3));
         
        //創建菜單
        ApiConfig config = new ApiConfig(APP_ID, APP_SECRET);
        MenuAPI menuAPI = new MenuAPI(config);
        ResultType resultType = menuAPI.createMenu(menu);
        return resultType.toString();
        }
        }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM