View Javadoc
1   package com.foxinmy.weixin4j.mp.api;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   
6   import com.alibaba.fastjson.JSON;
7   import com.alibaba.fastjson.JSONObject;
8   import com.alibaba.fastjson.TypeReference;
9   import com.foxinmy.weixin4j.exception.WeixinException;
10  import com.foxinmy.weixin4j.http.weixin.ApiResult;
11  import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
12  import com.foxinmy.weixin4j.model.Token;
13  import com.foxinmy.weixin4j.mp.model.Following;
14  import com.foxinmy.weixin4j.mp.model.User;
15  import com.foxinmy.weixin4j.mp.model.ChangeOpenidResult;
16  import com.foxinmy.weixin4j.mp.type.Lang;
17  import com.foxinmy.weixin4j.token.TokenManager;
18  
19  /**
20   * 用户相关API
21   * 
22   * @className UserApi
23   * @author jinyu(foxinmy@gmail.com)
24   * @date 2014年9月25日
25   * @since JDK 1.6
26   * @see com.foxinmy.weixin4j.mp.model.User
27   */
28  public class UserApi extends MpApi {
29  
30  	private final TokenManager tokenManager;
31  
32  	public UserApi(TokenManager tokenManager) {
33  		this.tokenManager = tokenManager;
34  	}
35  
36  	/**
37  	 * 获取用户信息
38  	 * 
39  	 * @param openId
40  	 *            用户对应的ID
41  	 * @return 用户对象
42  	 * @throws WeixinException
43  	 * @see {@link #getUser(String,Lang)}
44  	 */
45  	public User getUser(String openId) throws WeixinException {
46  		return getUser(openId, Lang.zh_CN);
47  	}
48  
49  	/**
50  	 * 获取用户信息
51  	 * <p>
52  	 * 在关注者与公众号产生消息交互后,公众号可获得关注者的OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的,对于不同公众号,
53  	 * 同一用户的openid不同),公众号可通过本接口来根据OpenID获取用户基本信息,包括昵称、头像、性别、所在城市、语言和关注时间
54  	 * </p>
55  	 * 
56  	 * @param openId
57  	 *            用户对应的ID
58  	 * @param lang
59  	 *            国家地区语言版本
60  	 * @return 用户对象
61  	 * @throws WeixinException
62  	 * @see <a href=
63  	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140839&token=&lang=zh_CN">
64  	 *      获取用户信息</a>
65  	 * @see com.foxinmy.weixin4j.mp.type.Lang
66  	 * @see com.foxinmy.weixin4j.mp.model.User
67  	 */
68  	public User getUser(String openId, Lang lang) throws WeixinException {
69  		String user_info_uri = getRequestUri("api_user_info_uri");
70  		Token token = tokenManager.getCache();
71  		WeixinResponse response = weixinExecutor
72  				.get(String.format(user_info_uri, token.getAccessToken(), openId, lang.name()));
73  
74  		return response.getAsObject(new TypeReference<User>() {
75  		});
76  	}
77  
78  	/**
79  	 * 批量获取用户信息
80  	 * 
81  	 * @param openIds
82  	 *            用户ID
83  	 * @return 用户列表
84  	 * @see <a href=
85  	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140839&token=&lang=zh_CN">
86  	 *      获取用户信息</a>
87  	 * @see com.foxinmy.weixin4j.mp.model.User
88  	 * @throws WeixinException
89  	 * @see {@link #getUsers(Lang,String[])}
90  	 */
91  	public List<User> getUsers(String... openIds) throws WeixinException {
92  		return getUsers(Lang.zh_CN, openIds);
93  	}
94  
95  	/**
96  	 * 批量获取用户信息
97  	 * 
98  	 * @param lang
99  	 *            国家地区语言版本
100 	 * @param openIds
101 	 *            用户ID 最多100个
102 	 * @return 用户列表
103 	 * @see <a href=
104 	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140840&token=&lang=zh_CN">
105 	 *      获取用户信息</a>
106 	 * @see com.foxinmy.weixin4j.mp.type.Lang
107 	 * @see com.foxinmy.weixin4j.mp.model.User
108 	 * @throws WeixinException
109 	 */
110 	public List<User> getUsers(Lang lang, String... openIds) throws WeixinException {
111 		String api_users_info_uri = getRequestUri("api_users_info_uri");
112 		StringBuilder parameter = new StringBuilder();
113 		parameter.append("{\"user_list\": [");
114 		for (String openId : openIds) {
115 			parameter.append("{\"openid\": \"").append(openId).append("\"");
116 			parameter.append(",\"lang\": \"").append(lang.name()).append("\"").append("},");
117 		}
118 		parameter.delete(parameter.length() - 1, parameter.length());
119 		parameter.append("]}");
120 		Token token = tokenManager.getCache();
121 		WeixinResponse response = weixinExecutor.post(String.format(api_users_info_uri, token.getAccessToken()),
122 				parameter.toString());
123 
124 		return JSON.parseArray(response.getAsJson().getString("user_info_list"), User.class);
125 	}
126 
127 	/**
128 	 * 获取公众号一定数量(10000)的关注者列表 <font corlor="red">请慎重使用</font>
129 	 * 
130 	 * @param nextOpenId
131 	 *            下一次拉取数据的openid 不填写则默认从头开始拉取
132 	 * @return 关注者信息 <font color="red">包含用户的详细信息</font>
133 	 * @throws WeixinException
134 	 * @see <a href=
135 	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140839&token=&lang=zh_CN">
136 	 *      获取关注者列表</a>
137 	 * @see <a href=
138 	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140840&token=&lang=zh_CN">
139 	 *      批量获取用户信息</a>
140 	 * @see com.foxinmy.weixin4j.mp.model.Following
141 	 * @see com.foxinmy.weixin4j.mp.model.User
142 	 */
143 	public Following getFollowing(String nextOpenId) throws WeixinException {
144 		Following following = getFollowingOpenIds(nextOpenId);
145 		if (following.getCount() > 0) {
146 			List<User> users = new ArrayList<User>(following.getCount());
147 			for (int i = 1; i <= (int) Math.ceil(following.getCount() / 100d); i++) {
148 				users.addAll(getUsers(following.getOpenIds()
149 						.subList((i - 1) * 100, Math.min(i * 100, following.getCount())).toArray(new String[] {})));
150 			}
151 			following.setUserList(users);
152 		}
153 		return following;
154 	}
155 
156 	/**
157 	 * 获取公众号一定数量(10000)的关注者列表
158 	 * 
159 	 * @param nextOpenId
160 	 *            下一次拉取数据的openid 不填写则默认从头开始拉取
161 	 * @return 关注者信息 <font color="red">不包含用户的详细信息</font>
162 	 * @throws WeixinException
163 	 * @see <a href=
164 	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140840&token=&lang=zh_CN">
165 	 *      获取关注者列表</a>
166 	 * @see com.foxinmy.weixin4j.mp.model.Following
167 	 */
168 	public Following getFollowingOpenIds(String nextOpenId) throws WeixinException {
169 		String following_uri = getRequestUri("following_uri");
170 		Token token = tokenManager.getCache();
171 		WeixinResponse response = weixinExecutor
172 				.get(String.format(following_uri, token.getAccessToken(), nextOpenId == null ? "" : nextOpenId));
173 
174 		JSONObject result = response.getAsJson();
175 		Following following = JSON.toJavaObject(result, Following.class);
176 
177 		if (following.getCount() > 0) {
178 			following.setOpenIds(JSON.parseArray(result.getJSONObject("data").getString("openid"), String.class));
179 		}
180 		return following;
181 	}
182 
183 	/**
184 	 * 获取公众号全部的关注者列表 <font corlor="red">请慎重使用</font>
185 	 * <p>
186 	 * 当公众号关注者数量超过10000时,可通过填写next_openid的值,从而多次拉取列表的方式来满足需求,
187 	 * 将上一次调用得到的返回中的next_openid值,作为下一次调用中的next_openid值
188 	 * </p>
189 	 * 
190 	 * @return 用户对象集合
191 	 * @throws WeixinException
192 	 * @see <a href=
193 	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140840&token=&lang=zh_CN">
194 	 *      获取关注者列表</a>
195 	 * @see <a href=
196 	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140839&token=&lang=zh_CN">
197 	 *      批量获取用户信息</a>
198 	 * @see com.foxinmy.weixin4j.mp.model.User
199 	 * @see com.foxinmy.weixin4j.mp.model.Following
200 	 * @see #getFollowing(String)
201 	 */
202 	public List<User> getAllFollowing() throws WeixinException {
203 		List<User> userList = new ArrayList<User>();
204 		String nextOpenId = null;
205 		Following f = null;
206 		for (;;) {
207 			f = getFollowing(nextOpenId);
208 			if (f.hasContent()) {
209 				userList.addAll(f.getUserList());
210 				nextOpenId = f.getNextOpenId();
211 				continue;
212 			}
213 			break;
214 		}
215 		return userList;
216 	}
217 
218 	/**
219 	 * 获取公众号全部的关注者列表 <font corlor="red">请慎重使用</font>
220 	 * <p>
221 	 * 当公众号关注者数量超过10000时,可通过填写next_openid的值,从而多次拉取列表的方式来满足需求,
222 	 * 将上一次调用得到的返回中的next_openid值,作为下一次调用中的next_openid值
223 	 * </p>
224 	 * 
225 	 * @return 用户openid集合
226 	 * @throws WeixinException
227 	 * @see <a href=
228 	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140840&token=&lang=zh_CN">
229 	 *      获取关注者列表</a>
230 	 * @see #getFollowingOpenIds(String)
231 	 */
232 	public List<String> getAllFollowingOpenIds() throws WeixinException {
233 		List<String> openIds = new ArrayList<String>();
234 		String nextOpenId = null;
235 		Following f = null;
236 		for (;;) {
237 			f = getFollowingOpenIds(nextOpenId);
238 			if (f.hasContent()) {
239 				openIds.addAll(f.getOpenIds());
240 				nextOpenId = f.getNextOpenId();
241 				continue;
242 			}
243 			break;
244 		}
245 		return openIds;
246 	}
247 
248 	/**
249 	 * 设置用户备注名
250 	 * 
251 	 * @param openId
252 	 *            用户ID
253 	 * @param remark
254 	 *            备注名
255 	 * @throws WeixinException
256 	 * @see <a href=
257 	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140838&token=&lang=zh_CN">
258 	 *      设置用户备注名</a>
259 	 */
260 	public ApiResult remarkUserName(String openId, String remark) throws WeixinException {
261 		String username_remark_uri = getRequestUri("username_remark_uri");
262 		Token token = tokenManager.getCache();
263 		JSONObject obj = new JSONObject();
264 		obj.put("openid", openId);
265 		obj.put("remark", remark);
266 		WeixinResponse response = weixinExecutor.post(String.format(username_remark_uri, token.getAccessToken()),
267 				obj.toJSONString());
268 
269 		return response.getAsResult();
270 	}
271 
272 
273 	/**
274 	 * 批量转换openid
275 	 *
276 	 * @param fromAppid 原账号ID
277 	 * @param openIds 原账号openid列表,最多不能超过100个
278 	 * @return 转换后的openid
279 	 * @throws WeixinException
280 	 * @see <a href="https://kf.qq.com/faq/1901177NrqMr190117nqYJze.html">openid转换</a>
281 	 * @see com.foxinmy.weixin4j.mp.model.ChangeOpenidResult
282 	 */
283 	public List<ChangeOpenidResult> batchChangeOpenid(String fromAppid, List<String> openIds) throws WeixinException {
284 		String change_openid_uri = getRequestUri("change_openid_uri");
285 		StringBuilder parameter = new StringBuilder();
286 		parameter.append("{\"from_appid\":\"").append(fromAppid).append("\"");
287 		parameter.append(",\"openid_list\": [");
288 		for (String openId : openIds) {
289 			parameter.append("\"").append(openId).append("\",");
290 		}
291 		parameter.delete(parameter.length() - 1, parameter.length());
292 		parameter.append("]}");
293 		Token token = tokenManager.getCache();
294 		WeixinResponse response = weixinExecutor.post(String.format(change_openid_uri, token.getAccessToken()),
295 				parameter.toString());
296 
297 		return JSON.parseArray(response.getAsJson().getString("result_list"), ChangeOpenidResult.class);
298 	}
299 
300 	/**
301 	 * 转换所有openid
302 	 *
303 	 * @param fromAppid 原账号ID
304 	 * @return 转换后的openid
305 	 * @throws WeixinException
306 	 * @see <a href="https://kf.qq.com/faq/1901177NrqMr190117nqYJze.html">openid转换</a>
307 	 * @see com.foxinmy.weixin4j.mp.model.ChangeOpenidResult
308 	 */
309 	public List<ChangeOpenidResult> changeAllOpenid(String fromAppid) throws WeixinException {
310 		List<String> openIds = null;
311 		String nextOpenId = null;
312 		Following following = null;
313 		int batchSize = 100;
314 		List<ChangeOpenidResult> results = new ArrayList<ChangeOpenidResult>();
315 		for (;;) {
316 			following = getFollowingOpenIds(nextOpenId);
317 			if (following.hasContent()) {
318 				openIds = following.getOpenIds();
319 				int split = (int) Math.ceil(1.0 * openIds.size() / batchSize);
320             	for (int i = 0; i < split; i++) {
321                 	List<String> batch = openIds.subList((i * batchSize), Math.min(openIds.size(), (i + 1) * batchSize));
322                 	results.addAll(batchChangeOpenid(fromAppid,batch));
323             	}
324 				nextOpenId = following.getNextOpenId();
325 				continue;
326 			}
327 			break;
328 		}
329 		return results;
330 	}
331 }