我是靠谱客的博主 正直荔枝,这篇文章主要介绍公众号开发(二)--菜单管理,现在分享给大家,希望可以做个参考。

公众号开发(二)--菜单管理

开发说明

以下是官网的说明

复制代码
1
2
3
4
1、自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单。 2、一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”代替。 3、创建自定义菜单后,菜单的刷新策略是,在用户进入公众号会话页或公众号profile页时,如果发现上一次拉取菜单的请求在5分钟以前,就会拉取一下菜单,如果菜单有更新,就会刷新客户端的菜单。测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果。

菜单类型

复制代码
1
2
3
4
5
6
7
8
9
10
11
1、click:点击推事件用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互; 2、view:跳转URL用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的网页URL,可与网页授权获取用户基本信息接口结合,获得用户基本信息。 3、scancode_push:扫码推事件用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操作后显示扫描结果(如果是URL,将进入URL),且会将扫码的结果传给开发者,开发者可以下发消息。 4、scancode_waitmsg:扫码推事件且弹出“消息接收中”提示框用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操作后,将扫码的结果传给开发者,同时收起扫一扫工具,然后弹出“消息接收中”提示框,随后可能会收到开发者下发的消息。 5、pic_sysphoto:弹出系统拍照发图用户点击按钮后,微信客户端将调起系统相机,完成拍照操作后,会将拍摄的相片发送给开发者,并推送事件给开发者,同时收起系统相机,随后可能会收到开发者下发的消息。 6、pic_photo_or_album:弹出拍照或者相册发图用户点击按钮后,微信客户端将弹出选择器供用户选择“拍照”或者“从手机相册选择”。用户选择后即走其他两种流程。 7、pic_weixin:弹出微信相册发图器用户点击按钮后,微信客户端将调起微信相册,完成选择操作后,将选择的相片发送给开发者的服务器,并推送事件给开发者,同时收起相册,随后可能会收到开发者下发的消息。 8、location_select:弹出地理位置选择器用户点击按钮后,微信客户端将调起地理位置选择工具,完成选择操作后,将选择的地理位置发送给开发者的服务器,同时收起位置选择工具,随后可能会收到开发者下发的消息。 9、media_id:下发消息(除文本消息)用户点击media_id类型按钮后,微信服务器会将开发者填写的永久素材id对应的素材下发给用户,永久素材类型可以是图片、音频、视频、图文消息。请注意:永久素材id必须是在“素材管理/新增永久素材”接口上传后获得的合法id。 10、view_limited:跳转图文消息URL用户点击view_limited类型按钮后,微信客户端将打开开发者在按钮中填写的永久素材id对应的图文消息URL,永久素材类型只支持图文消息。请注意:永久素材id必须是在“素材管理/新增永久素材”接口上传后获得的合法id。

菜单说明

需要通过post方式提交json格式的数据

复制代码
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
{ "button":[ { "type":"click", "name":"今日歌曲", "key":"V1001_TODAY_MUSIC" }, { "name":"菜单", "sub_button":[ { "type":"view", "name":"搜索", "url":"http://www.soso.com/" }, { "type":"miniprogram", "name":"wxa", "url":"http://mp.weixin.qq.com", "appid":"wx286b93c14bbf93aa", "pagepath":"pages/lunar/index" }, { "type":"click", "name":"赞一下我们", "key":"V1001_GOOD" }] }] }

这个json对象,button是一级菜单书组,sub_button是二级菜单列表,每个菜单都有自己的类型。如果存在二级菜单那么一级菜单没有类型只有名称。

下面是其他类型的数据结构

复制代码
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
{ "button": [ { "name": "扫码", "sub_button": [ { "type": "scancode_waitmsg", "name": "扫码带提示", "key": "rselfmenu_0_0", "sub_button": [ ] }, { "type": "scancode_push", "name": "扫码推事件", "key": "rselfmenu_0_1", "sub_button": [ ] } ] }, { "name": "发图", "sub_button": [ { "type": "pic_sysphoto", "name": "系统拍照发图", "key": "rselfmenu_1_0", "sub_button": [ ] }, { "type": "pic_photo_or_album", "name": "拍照或者相册发图", "key": "rselfmenu_1_1", "sub_button": [ ] }, { "type": "pic_weixin", "name": "微信相册发图", "key": "rselfmenu_1_2", "sub_button": [ ] } ] }, { "name": "发送位置", "type": "location_select", "key": "rselfmenu_2_0" }, { "type": "media_id", "name": "图片", "media_id": "MEDIA_ID1" }, { "type": "view_limited", "name": "图文消息", "media_id": "MEDIA_ID2" } ] }
参数是否必须说明
button一级菜单数组,个数应为1~3个
sub_button二级菜单数组,个数应为1~5个
type菜单的响应动作类型,view表示网页类型,click表示点击类型,miniprogram表示小程序类型
name菜单标题,不超过16个字节,子菜单不超过60个字节
keyclick等点击类型必须菜单KEY值,用于消息接口推送,不超过128字节
urlview、miniprogram类型必须网页 链接,用户点击菜单可打开链接,不超过1024字节。 type为miniprogram时,不支持小程序的老版本客户端将打开本url。
media_idmedia_id类型和view_limited类型必须调用新增永久素材接口返回的合法media_id
appidminiprogram类型必须小程序的appid(仅认证公众号可配置)
pagepathminiprogram类型必须小程序的页面路径

以上是摘自官网的说明

代码设计

可以看出来目前有10种菜单类型,不过大多数类型都有相似的属性,个别菜单有自己的属性。

我决定先做一个基础菜单的类型,然后再通过继承的方式去实现所有菜单类型。

基础菜单类型:Menu

  • 作为菜单的基础类型必须要拥有name和type两个属性
  • 还要包含button和sub_button两个菜单组的类型
  • 需要对外提供方法可以添加主菜单和子菜单
  • 还要添加一些校验功能

根据以上的设计思路,下面是我实现的Menu类型

复制代码
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
package wang.dicc.weixin.core.menu; import java.util.ArrayList; import java.util.List; /** * 菜单基础组件 * @author dylan *包含菜单的基础属性是其他菜单父类 *也是创建菜单的根基 */ public class Menu { public Menu() {} /** * 菜单构造函数 * @param name */ public Menu(String name) { super(); this.name = name; } public String menuid; public String type; public String name; private List<Menu> button; private List<Menu> sub_button; public List<Menu> getButton() { return button; } public void setButton(List<Menu> button) { this.button = button; } public List<Menu> getSub_button() { return sub_button; } public void setSub_button(List<Menu> sub_button) { this.sub_button = sub_button; } /** * 添加一个主菜单 * @param m * @return */ public Menu addButton(Menu m) { if(button==null) { button=new ArrayList<Menu>(); } button.add(m); return m; } /** * 添加一个子菜单 * @param m * @return */ public Menu addSubButton(Menu m) { if(sub_button==null) { sub_button=new ArrayList<Menu>(); } sub_button.add(m); return this; } }

可以看到我在添加主菜单和添加子菜单的时候返回的值不一样,这样设计的目的是为了实现下面这种方式添加菜单。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
Menu m=new Menu(); m.addButton(new Menu("男神专属")) .addSubButton(new MenuClick("点击事件", "m1")) .addSubButton(new MenuView("跳转网页", "http://www.dicc.wang/")) .addSubButton(new MenuPic("发送图片", "m2")) .addSubButton(new MenuPicSys("发送系统图片", "m3")) .addSubButton(new MenuPicWeixin("发送微信图片", "m4")); m.addButton(new Menu("女神专属")) .addSubButton(new MenuLocation("获取定位", "m5")) .addSubButton(new MenuScancodePush("扫码推送", "m7")) .addSubButton(new MenuScancodeWaitmsg("扫码等待", "m8"));

添加主菜单的时候可以返回添加的主菜单,所以可以继续添加子菜单。

由于微信公众号只支持二级菜单所以增加子菜单是不需要再考虑下一级菜单,所以在增加子菜单的时候直接返回主菜单,方便向主菜单中继续添加子菜单。

上面这个只是所有菜单的一个基本类型,所以接下来还要实现每个菜单类型。

设计思路

  • 每个菜单类型有自己的有参构造函数,方便创建
  • 每个菜单类型只包含自己需要的属性

接下来还要给大家介绍一个字典类:Dictionaries。这个字典类将会保存常用字典值。

目前暂时只保存了菜单类型。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package wang.dicc.weixin.core.common; /** * 字典类 * @author dylan * */ public class Dictionaries { public static final String menuType_click="click"; public static final String menuType_view="view"; public static final String menuType_scancode_push="scancode_push"; public static final String menuType_scancode_waitmsg="scancode_waitmsg"; public static final String menuType_pic_sysphoto="pic_sysphoto"; public static final String menuType_pic_photo_or_album="pic_photo_or_album"; public static final String menuType_pic_weixin="pic_weixin"; public static final String menuType_location_select="location_select"; public static final String menuType_media_id="media_id"; public static final String menuType_view_limited="view_limited"; public static final String menuType_miniprogram="miniprogram"; }

接下来是每个菜单类型的实现,代码有点多。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package wang.dicc.weixin.core.menu; import wang.dicc.weixin.core.common.Dictionaries; /** * 点击事件菜单 * @author dylan * */ public class MenuClick extends Menu{ public MenuClick(String name, String key) { this.name=name; this.key=key; this.type=Dictionaries.menuType_click; } private String key; }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package wang.dicc.weixin.core.menu; import wang.dicc.weixin.core.common.Dictionaries; /** * 跳转URL * @author dylan * */ public class MenuView extends Menu{ public MenuView(String name,String url) { this.name=name; this.url=url; this.type=Dictionaries.menuType_view; } private String url; }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package wang.dicc.weixin.core.menu; import wang.dicc.weixin.core.common.Dictionaries; /** * 扫码推事件 * @author dylan * */ public class MenuScancodePush extends Menu{ public MenuScancodePush(String name,String key) { this.key = key; this.name=name; this.type=Dictionaries.menuType_scancode_push; } private String key; }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package wang.dicc.weixin.core.menu; import wang.dicc.weixin.core.common.Dictionaries; /** * 扫码推事件(提示等待) * @author dylan * */ public class MenuScancodeWaitmsg extends Menu{ public MenuScancodeWaitmsg(String name,String key) { super(); this.key = key; this.name=name; this.type=Dictionaries.menuType_scancode_waitmsg; } private String key; }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package wang.dicc.weixin.core.menu; import wang.dicc.weixin.core.common.Dictionaries; /** * 系统拍照发图 * @author dylan * */ public class MenuPicSys extends Menu{ public MenuPicSys(String name,String key) { this.name=name; this.key=key; this.type=Dictionaries.menuType_pic_sysphoto; } private String key; }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package wang.dicc.weixin.core.menu; import wang.dicc.weixin.core.common.Dictionaries; /** * 微信相册发图 * @author dylan * */ public class MenuPicWeixin extends Menu{ public MenuPicWeixin(String name,String key) { this.name=name; this.key=key; this.type=Dictionaries.menuType_pic_weixin; } private String key; }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package wang.dicc.weixin.core.menu; import wang.dicc.weixin.core.common.Dictionaries; /** * 获取定位菜单按钮 * @author dylan * */ public class MenuLocation extends Menu{ public MenuLocation(String name,String key) { this.name=name; this.key=key; this.type=Dictionaries.menuType_location_select; } private String key; }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package wang.dicc.weixin.core.menu; import wang.dicc.weixin.core.common.Dictionaries; /** * 下发消息(除文本消息) * @author dylan * */ public class MenuMedia extends Menu{ public MenuMedia(String name,String media_id) { this.name=name; this.media_id=media_id; this.type=Dictionaries.menuType_media_id; } private String media_id; }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package wang.dicc.weixin.core.menu; import wang.dicc.weixin.core.common.Dictionaries; /** * 图文消息 * @author dylan * */ public class MenuViewLimited extends Menu{ public MenuViewLimited(String name,String media_id) { this.name=name; this.media_id=media_id; this.type=Dictionaries.menuType_media_id; } private String media_id; }
复制代码
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
package wang.dicc.weixin.core.menu; import wang.dicc.weixin.core.common.Dictionaries; /** * 小程序 * @author dylan * */ public class MenuMiniprogram extends Menu{ public MenuMiniprogram(String name, String url, String appid, String pagepath) { this.name=name; this.url = url; this.appid = appid; this.pagepath = pagepath; this.type=Dictionaries.menuType_miniprogram; } private String url; /** * 小程序ID */ private String appid; /** * 小程序页面地址 */ private String pagepath; }

所有类型完成之后就要面对一个很重要的问题。

发送菜单管理的请求时我们需要把菜单类型转换成json类型

首先想到的是每个类型添加toJson方法。不过这种类型太麻烦了要写十几个不同的方法,最后还要整合再一起。

还有一个方法就是JSONObject.fromObject方法。它可以将任何类型转换为json字符串,尝试之后发现一个问题就是类型中的空值也会被转换

我们看官方给的示例中空值是没有被显示出来的,所以用JSONObject.fromObject方法可能会出现问题(会不会出问题我也没试主要还是想尽可能的完美一点)

经过一番思考之后我决定写一个方法将类型转换为json,并且不转换空值。

复制代码
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
package wang.dicc.weixin.core.util; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import wang.dicc.weixin.core.menu.Matchrule; /** * json工具组件 * @author dylan * */ public class JsonUtil { /** * 根据传入对象生成json对象 * @param obj * @return */ public static JSONObject fromObject(Object obj) { JSONObject j=new JSONObject(); try { List<Field> fs=new ArrayList<Field>(); Class<?> temp=obj.getClass(); //遍历父类获取所有属性 while(temp!=null) { fs.addAll(Arrays.asList(temp.getDeclaredFields())); temp=temp.getSuperclass(); } for(Field f:fs) { f.setAccessible(true); Object val=f.get(obj); if(val!=null&&!val.toString().isEmpty()) { if(val instanceof ArrayList) { JSONArray js=new JSONArray(); for(Object o:(ArrayList<?>)val) { js.add(fromObject(o)); } j.put(f.getName(), js); }else if(val instanceof Matchrule) { j.put(f.getName(), fromObject(val)); }else { j.put(f.getName(), val); } }else { //如果值为空提取父类的值 } } } catch (IllegalArgumentException | IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } return j; } }

通过上面的方法获取JSONObject类型,直接用toStrong()方法就可以获取json字符串。

个性化菜单

菜单类型管理暂时高一段落,具体请求方式等下说明。现在讲一下个性化菜单。

自定义菜单可以根据开发者的定义,用户在进入公众号的时候会看见不一样的菜单,定义的条件有以下几项。

复制代码
1
2
3
4
5
6
1、用户标签(开发者的业务需求可以借助用户标签来完成) 2、性别 3、手机操作系统 4、地区(用户在微信客户端设置的地区) 5、语言(用户在微信客户端设置的语言)

创建个性化菜单不过就是在普通菜单中添加过滤条件,格式如下

复制代码
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
{ "button": [ { "type": "click", "name": "今日歌曲", "key": "V1001_TODAY_MUSIC" }, { "name": "菜单", "sub_button": [ { "type": "view", "name": "搜索", "url": "http://www.soso.com/" }, { "type": "miniprogram", "name": "wxa", "url": "http://mp.weixin.qq.com", "appid": "wx286b93c14bbf93aa", "pagepath": "pages/lunar/index" }, { "type": "click", "name": "赞一下我们", "key": "V1001_GOOD" } ] } ], "matchrule": { "tag_id": "2", "sex": "1", "country": "中国", "province": "广东", "city": "广州", "client_platform_type": "2", "language": "zh_CN" } }

就是matchrule这段json,所以我们需要再添加一个matchrule类型

复制代码
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
package wang.dicc.weixin.core.menu; /** * 个性化菜单过滤条件 * @author dylan *过滤条件可以为空但不可以全空 */ public class Matchrule { /** * 用户标签的id,可通过用户标签管理接口获取 */ private String tag_id; /** * 性别:男(1)女(2),不填则不做匹配 */ private String sex; /** * 客户端版本,当前只具体到系统型号:IOS(1), Android(2),Others(3),不填则不做匹配 */ private String client_platform_type; /** * 国家信息,是用户在微信中设置的地区,具体请参考地区信息表 */ private String country; /** * 省份信息,是用户在微信中设置的地区,具体请参考地区信息表 */ private String province; /** * 城市信息,是用户在微信中设置的地区,具体请参考地区信息表 */ private String city; /** * 语言信息,是用户在微信中设置的语言,具体请参考语言表: * 1、简体中文 "zh_CN" 2 * 、繁体中文TW "zh_TW" * 3、繁体中文HK "zh_HK" * 4、英文 "en" * 5、印尼 "id" * 6、马来 "ms" * 7、西班牙 "es" * 8、韩国 "ko" * 9、意大利 "it" * 10、日本 "ja" * 11、波兰 "pl" * 12、葡萄牙 "pt" * 13、俄国 "ru" * 14、泰文 "th" * 15、越南 "vi" * 16、阿拉伯语 "ar" * 17、北印度 "hi" * 18、希伯来 "he" * 19、土耳其 "tr" * 20、德语 "de" * 21、法语 "fr" */ private String language; public String getTag_id() { return tag_id; } public void setTag_id(String tag_id) { this.tag_id = tag_id; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getClient_platform_type() { return client_platform_type; } public void setClient_platform_type(String client_platform_type) { this.client_platform_type = client_platform_type; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getLanguage() { return language; } public void setLanguage(String language) { this.language = language; } }

然后在Menu类型中添加Matchrule属性和get&set方法

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
/** * 个性化菜单过滤条件 */ private Matchrule matchrule; public Matchrule getMatchrule() { return matchrule; } public void setMatchrule(Matchrule matchrule) { this.matchrule = matchrule; }

功能实现

菜单管理的准备工作现在都已完成接下来实现菜单管理功能

菜单管理的功能包括

  • 自定义菜单创建
  • 自定义菜单查询
  • 自定义菜单删除
  • 个性化菜单创建
  • 个性化菜单删除
  • 测试个性化菜单
  • 获取自定义菜单配置

添加菜单管理功能的接口添加到RequestUrl中

复制代码
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
/** * 创建菜单 * post */ String menu_creat="https://api.weixin.qq.com/cgi-bin/menu/create?access_token="; /** * 获取菜单 * get */ String menu_get="https://api.weixin.qq.com/cgi-bin/menu/get?access_token="; /** * 删除菜单 * get */ String menu_delete="https://api.weixin.qq.com/cgi-bin/menu/delete?access_token="; /** * 创建个性化菜单 * post */ String menu_create_cond="https://api.weixin.qq.com/cgi-bin/menu/addconditional?access_token="; /** * 删除个性化菜单 * post */ String menu_delete_cond="https://api.weixin.qq.com/cgi-bin/menu/delconditional?access_token="; /** * 测试个性化菜单匹配结果 * post */ String menu_test_match="https://api.weixin.qq.com/cgi-bin/menu/trymatch?access_token="; /** * 获取个性化菜单配置 * get */ String menu_get_curr="https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=";

在HttpClientUtil中添加相关方法,下面给出一个目前完整的内容

复制代码
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
package wang.dicc.common.util.network; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.http.Consts; import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URIBuilder; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.entity.mime.content.FileBody; import org.apache.http.entity.mime.content.StringBody; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.CharsetUtils; import org.apache.http.util.EntityUtils; import net.sf.json.JSONObject; public class HttpClientUtil { /** * 默认编码格式 */ public static final String DefeatCharset="UTF-8"; /** * 发起get请求 * @param url:请求路径(String) * @return */ public static String doGet(String url) { return doGet(url, null,DefeatCharset); } /** * 发起get请求 * @param url:请求路径(String) * @param param:请求参数(Map) * @return */ public static String doGet(String url, Map<String, String> param) { return doGet(url, param,DefeatCharset); } /** * 发起get请求 * @param url:请求路径(String) * @param param:请求参数(Map) * @param charset:编码格式(String) * @return */ public static String doGet(String url, Map<String, String> param, String charset) { CloseableHttpClient httpclient = HttpClients.createDefault(); String resultString = ""; CloseableHttpResponse response = null; try { URIBuilder builder = new URIBuilder(url); if (param != null) { for (String key : param.keySet()) { builder.addParameter(key, param.get(key)); } } URI uri = builder.build(); HttpGet httpGet = new HttpGet(uri); response = httpclient.execute(httpGet); if (response.getStatusLine().getStatusCode() == 200) { resultString = EntityUtils.toString(response.getEntity(), charset); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (response != null) { response.close(); } httpclient.close(); } catch (IOException e) { e.printStackTrace(); } } return resultString; } public static String doPost(String url) { return doPost(url, null,DefeatCharset); } public static String doPost(String url, Map<String, String> param) { return doPost(url, param, DefeatCharset); } public static String doPost(String url, Map<String, String> param, String charset) { CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; try { HttpPost httpPost = new HttpPost(url); if (param != null) { List<NameValuePair> paramList = new ArrayList<>(); for (String key : param.keySet()) { paramList.add(new BasicNameValuePair(key, param.get(key))); } UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList); httpPost.setEntity(entity); } response = httpClient.execute(httpPost); resultString = EntityUtils.toString(response.getEntity(), charset); } catch (Exception e) { e.printStackTrace(); } finally { try { response.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return resultString; } public static String doPostJson(String url, JSONObject json) { return doPostJson(url, json.toString()); } public static String doPostJson(String url, String json) { CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; try { HttpPost httpPost = new HttpPost(url); StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); httpPost.setEntity(entity); response = httpClient.execute(httpPost); resultString = EntityUtils.toString(response.getEntity(), "utf-8"); } catch (Exception e) { e.printStackTrace(); } finally { try { response.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return resultString; } public static void downloadByNIO2(String url, String saveDir, String fileName) { try (InputStream ins = new URL(url).openStream()) { Path target = Paths.get(saveDir, fileName); Files.createDirectories(target.getParent()); Files.copy(ins, target, StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { e.printStackTrace(); } } public static String doPostMuli(String url,String filePath) { CloseableHttpClient httpClient=HttpClients.createDefault(); HttpPost httpPost=new HttpPost(url); File file =new File(filePath); FileBody bin =new FileBody(file); StringBody uploadFileName =new StringBody( "media", ContentType.create("text/plain", Consts.UTF_8)); String resultString=""; HttpEntity reqEntity; try { reqEntity = MultipartEntityBuilder.create().setMode(HttpMultipartMode.BROWSER_COMPATIBLE) .addPart("media", bin)//uploadFile对应服务端类的同名属性<File类型> .addPart("media", uploadFileName)//uploadFileName对应服务端类的同名属性<String类型> .setCharset(CharsetUtils.get("UTF-8")).build(); httpPost.setEntity(reqEntity); CloseableHttpResponse response = httpClient.execute(httpPost); resultString = EntityUtils.toString(response.getEntity(), "utf-8"); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClientProtocolException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return resultString; } }

创建菜单管理实现类MenuOP

复制代码
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
package wang.dicc.weixin.core.menu; import net.sf.json.JSONObject; import wang.dicc.common.util.network.HttpClientUtil; import wang.dicc.weixin.core.common.AuthenticationInfo; import wang.dicc.weixin.core.common.RequestUrl; import wang.dicc.weixin.core.util.JsonUtil; /** * 菜单基础操作组件 * @author dylan *包含菜单的基本操作 */ public class MenuOP { /** * 创建菜单 * @param m * @return */ public String create(Menu m) { return HttpClientUtil.doPostJson(RequestUrl.menu_creat+AuthenticationInfo.access_token, JsonUtil.fromObject(m)); } /** * 查询菜单 * @return */ public String query() { return HttpClientUtil.doGet(RequestUrl.menu_get+AuthenticationInfo.access_token); } /** * 删除菜单 * @return */ public String delete() { return HttpClientUtil.doGet(RequestUrl.menu_delete+AuthenticationInfo.access_token); } /** * 创建个性化菜单 * @param m * @return menuid */ public String createCodn(Menu m) { return HttpClientUtil.doPostJson(RequestUrl.menu_create_cond+AuthenticationInfo.access_token, JsonUtil.fromObject(m)); } /** * 删除个性化菜单 * @param m * @return */ public String deleteCodn(String menuid) { return HttpClientUtil.doPostJson(RequestUrl.menu_delete_cond+AuthenticationInfo.access_token, "{"menuid":""+menuid+""}"); } /** * 测试个性化菜单匹配结果 * @param userid * @return */ public String testMatch(Matchrule m) { return HttpClientUtil.doPostJson(RequestUrl.menu_test_match+AuthenticationInfo.access_token, JsonUtil.fromObject(m).toString()); } /** * 获取个性化菜单配置 * get */ public String getCurr() { return HttpClientUtil.doGet(RequestUrl.menu_get_curr+AuthenticationInfo.access_token); } public static void main(String[] args) { Menu m=new Menu(); m.addButton(new Menu("男神专属")) .addSubButton(new MenuClick("点击事件", "m1")) .addSubButton(new MenuView("跳转网页", "http://www.dicc.wang/")) .addSubButton(new MenuPic("发送图片", "m2")) .addSubButton(new MenuPicSys("发送系统图片", "m3")) .addSubButton(new MenuPicWeixin("发送微信图片", "m4")); m.addButton(new Menu("女神专属")) .addSubButton(new MenuLocation("获取定位", "m5")) // .addSubButton(new MenuMedia("获取消息", "werwe")) .addSubButton(new MenuScancodePush("扫码推送", "m7")) .addSubButton(new MenuScancodeWaitmsg("扫码等待", "m8")); // .addSubButton(new MenuViewLimited("图文消息", "asdf")); JSONObject js=JsonUtil.fromObject(m); System.out.println(js.toString()); MenuOP mp=new MenuOP(); AuthenticationInfo.getAccessToken(); String result=mp.create(m); System.out.println(result); } }

最后给出了一个添加菜单的测试方法,被注释的两个菜单由于还没有多媒体功能暂时无法实现。

这里面没有关于菜单更新的方法,以后可以写一个先删除再创建的方法实现更新。

删除功能是删除所有菜单,所以删除之前一定要仔细确认。

菜单管理功能中还有很多想法没有实现,比如添加菜单的时候控制菜单数量。暂时还没有想好该如何实现,以后会添加。

JsonUtil没有放到dicc-common中而是放在weixin-core中,这个以后可能会有调整。

欢迎到我的首页评论留言

转载于:https://my.oschina.net/flnqs/blog/3027058

最后

以上就是正直荔枝最近收集整理的关于公众号开发(二)--菜单管理的全部内容,更多相关公众号开发(二)--菜单管理内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(62)

评论列表共有 0 条评论

立即
投稿
返回
顶部