View Javadoc
1   package com.foxinmy.weixin4j.mp.api;
2   
3   import java.io.InputStream;
4   import java.util.ArrayList;
5   import java.util.Date;
6   import java.util.List;
7   
8   import com.alibaba.fastjson.JSON;
9   import com.alibaba.fastjson.JSONObject;
10  import com.alibaba.fastjson.TypeReference;
11  import com.foxinmy.weixin4j.exception.WeixinException;
12  import com.foxinmy.weixin4j.http.MimeType;
13  import com.foxinmy.weixin4j.http.apache.content.InputStreamBody;
14  import com.foxinmy.weixin4j.http.apache.mime.FormBodyPart;
15  import com.foxinmy.weixin4j.http.weixin.ApiResult;
16  import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
17  import com.foxinmy.weixin4j.model.Token;
18  import com.foxinmy.weixin4j.mp.model.KfAccount;
19  import com.foxinmy.weixin4j.mp.model.KfChatRecord;
20  import com.foxinmy.weixin4j.mp.model.KfOnlineAccount;
21  import com.foxinmy.weixin4j.mp.model.KfSession;
22  import com.foxinmy.weixin4j.mp.model.KfSession.KfSessionCounter;
23  import com.foxinmy.weixin4j.token.TokenManager;
24  import com.foxinmy.weixin4j.util.FileUtil;
25  import com.foxinmy.weixin4j.util.ObjectId;
26  import com.foxinmy.weixin4j.util.StringUtil;
27  
28  /**
29   * 多客服API
30   *
31   * @className CustomApi
32   * @author jinyu(foxinmy@gmail.com)
33   * @date 2014年11月16日
34   * @since JDK 1.6
35   * @see <a href="http://dkf.qq.com">多客服说明</a>
36   */
37  public class CustomApi extends MpApi {
38  
39  	private final TokenManager tokenManager;
40  
41  	public CustomApi(TokenManager tokenManager) {
42  		this.tokenManager = tokenManager;
43  	}
44  
45  	/**
46  	 * 客服聊天记录
47  	 *
48  	 * @param startTime
49  	 *            查询开始时间
50  	 * @param endTime
51  	 *            查询结束时间 每次查询不能跨日查询
52  	 * @param number
53  	 *            最多10000条
54  	 * @throws WeixinException
55  	 * @see com.foxinmy.weixin4j.mp.model.KfChatRecord
56  	 * @see <a href="http://dkf.qq.com/document-1_1.html">查询客服聊天记录</a>
57  	 * @see <a href=
58  	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044854&token=&lang=zh_CN">
59  	 *      查询客服聊天记录</a>
60  	 */
61  	public List<KfChatRecord> getKfChatRecord(Date startTime, Date endTime,
62  			int number) throws WeixinException {
63  		List<KfChatRecord> records = new ArrayList<KfChatRecord>();
64  		String kf_chatrecord_uri = getRequestUri("kf_chatrecord_uri");
65  		Token token = tokenManager.getCache();
66  		JSONObject obj = new JSONObject();
67  		obj.put("starttime", startTime.getTime() / 1000);
68  		obj.put("endtime", endTime.getTime() / 1000);
69  		obj.put("msgid", "1");
70  		obj.put("number", Math.min(10000, number));
71  		JSONObject result = null;
72  		do {
73  			WeixinResponse response = weixinExecutor.post(
74  					String.format(kf_chatrecord_uri, token.getAccessToken()),
75  					obj.toJSONString());
76  			result = response.getAsJson();
77  			String text = result.getString("recordlist");
78  			if (StringUtil.isBlank(text) || "[]".equals(text)) {
79  				break;
80  			}
81  			records.addAll(JSON.parseArray(text, KfChatRecord.class));
82  			obj.put("msgid", result.getString("msgid"));
83  		} while (obj.getIntValue("number") == result.getIntValue("number"));
84  		return records;
85  	}
86  
87  	/**
88  	 * 获取公众号中所设置的客服基本信息,包括客服工号、客服昵称、客服登录账号
89  	 *
90  	 * @return 多客服信息列表
91  	 * @see com.foxinmy.weixin4j.mp.model.KfAccount
92  	 * @see <a href=
93  	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">
94  	 *      获取客服基本信息</a>
95  	 * @throws WeixinException
96  	 */
97  	public List<KfAccount> listKfAccount() throws WeixinException {
98  		Token token = tokenManager.getCache();
99  		String kf_list_uri = getRequestUri("kf_list_uri");
100 		WeixinResponse response = weixinExecutor.get(String.format(kf_list_uri,
101 				token.getAccessToken()));
102 		String text = response.getAsJson().getString("kf_list");
103 		return JSON.parseArray(text, KfAccount.class);
104 	}
105 
106 	/**
107 	 * 获取在线客服在线状态(手机在线、PC客户端在线、手机和PC客户端全都在线)、客服自动接入最大值、 客服当前接待客户数
108 	 *
109 	 * @return 多客服在线信息列表
110 	 * @see com.foxinmy.weixin4j.mp.model.KfOnlineAccount
111 	 * @see <a href=
112 	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">
113 	 *      获取客服在线信息</a>
114 	 * @throws WeixinException
115 	 */
116 	public List<KfOnlineAccount> listOnlineKfAccount() throws WeixinException {
117 		Token token = tokenManager.getCache();
118 		String kf_onlinelist_uri = getRequestUri("kf_onlinelist_uri");
119 		WeixinResponse response = weixinExecutor.get(String.format(
120 				kf_onlinelist_uri, token.getAccessToken()));
121 		String text = response.getAsJson().getString("kf_online_list");
122 		return JSON.parseArray(text, KfOnlineAccount.class);
123 	}
124 
125 	/**
126 	 * 新增客服账号
127 	 *
128 	 * @param id
129 	 *            完整客服账号,格式为:账号前缀@公众号微信号,账号前缀最多10个字符,必须是英文或者数字字符。如果没有公众号微信号,
130 	 *            请前往微信公众平台设置。
131 	 * @param name
132 	 *            客服昵称,最长6个汉字或12个英文字符
133 	 * @return 处理结果
134 	 * @throws WeixinException
135 	 * @see <a href=
136 	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">
137 	 *      新增客服账号</a>
138 	 */
139 	public ApiResult createKfAccount(String id, String name)
140 			throws WeixinException {
141 		JSONObject obj = new JSONObject();
142 		obj.put("kf_account", id);
143 		obj.put("nickname", name);
144 		String kf_create_uri = getRequestUri("kf_create_uri");
145 		Token token = tokenManager.getCache();
146 		WeixinResponse response = weixinExecutor.post(
147 				String.format(kf_create_uri, token.getAccessToken()),
148 				obj.toJSONString());
149 		return response.getAsResult();
150 	}
151 
152 	/**
153 	 * 更新客服账号
154 	 *
155 	 * @param id
156 	 *            完整客服账号,格式为:账号前缀@公众号微信号,账号前缀最多10个字符,必须是英文或者数字字符。如果没有公众号微信号,
157 	 *            请前往微信公众平台设置。
158 	 * @param name
159 	 *            客服昵称,最长6个汉字或12个英文字符
160 	 * @return 处理结果
161 	 * @throws WeixinException
162 	 * @see <a href=
163 	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">
164 	 *      新增客服账号</a>
165 	 */
166 	public ApiResult updateKfAccount(String id, String name)
167 			throws WeixinException {
168 		JSONObject obj = new JSONObject();
169 		obj.put("kf_account", id);
170 		obj.put("nickname", name);
171 		String kf_update_uri = getRequestUri("kf_update_uri");
172 		Token token = tokenManager.getCache();
173 		WeixinResponse response = weixinExecutor.post(
174 				String.format(kf_update_uri, token.getAccessToken()),
175 				obj.toJSONString());
176 		return response.getAsResult();
177 	}
178 
179 	/**
180 	 * 邀请绑定客服帐号
181 	 * 新添加的客服帐号是不能直接使用的,只有客服人员用微信号绑定了客服账号后,方可登录Web客服进行操作。此接口发起一个绑定邀请到客服人员微信号
182 	 * ,客服人员需要在微信客户端上用该微信号确认后帐号才可用。尚未绑定微信号的帐号可以进行绑定邀请操作,邀请未失效时不能对该帐号进行再次绑定微信号邀请。
183 	 *
184 	 * @param kfAccount
185 	 *            完整客服帐号,格式为:帐号前缀@公众号微信号
186 	 * @param inviteAccount
187 	 *            接收绑定邀请的客服微信号
188 	 * @return 处理结果
189 	 * @see <a href=
190 	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN"
191 	 *      >邀请绑定客服帐号<a/>
192 	 * @throws WeixinException
193 	 */
194 	public ApiResult inviteKfAccount(String kfAccount, String inviteAccount)
195 			throws WeixinException {
196 		JSONObject obj = new JSONObject();
197 		obj.put("kf_account", kfAccount);
198 		obj.put("invite_wx", inviteAccount);
199 		String kf_invite_uri = getRequestUri("kf_invite_uri");
200 		Token token = tokenManager.getCache();
201 		WeixinResponse response = weixinExecutor.post(
202 				String.format(kf_invite_uri, token.getAccessToken()),
203 				obj.toJSONString());
204 		return response.getAsResult();
205 	}
206 
207 	/**
208 	 * 上传客服头像
209 	 *
210 	 * @param accountId
211 	 *            完整客服账号,格式为:账号前缀@公众号微信号
212 	 * @param is
213 	 *            头像图片文件必须是jpg格式,推荐使用640*640大小的图片以达到最佳效果
214 	 * @param fileName
215 	 *            文件名 为空时将自动生成
216 	 * @return 处理结果
217 	 * @throws WeixinException
218 	 * @see <a href=
219 	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">
220 	 *      上传客服头像</a>
221 	 */
222 	public ApiResult uploadKfAvatar(String accountId, InputStream is,
223 			String fileName) throws WeixinException {
224 		if (StringUtil.isBlank(fileName)) {
225 			fileName = ObjectId.get().toHexString();
226 		}
227 		if (StringUtil.isBlank(FileUtil.getFileExtension(fileName))) {
228 			fileName = String.format("%s.jpg", fileName);
229 		}
230 		MimeType mimeType = new MimeType("image",
231 				FileUtil.getFileExtension(fileName));
232 		Token token = tokenManager.getCache();
233 		String kf_avatar_uri = getRequestUri("kf_avatar_uri");
234 		WeixinResponse response = weixinExecutor
235 				.post(String.format(kf_avatar_uri, token.getAccessToken(),
236 						accountId), new FormBodyPart("media",
237 						new InputStreamBody(is, mimeType.toString(), fileName)));
238 
239 		return response.getAsResult();
240 	}
241 
242 	/**
243 	 * 删除客服账号
244 	 *
245 	 * @param id
246 	 *            完整客服账号,格式为:账号前缀@公众号微信号
247 	 * @return 处理结果
248 	 * @throws WeixinException
249 	 * @see <a href=
250 	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">
251 	 *      删除客服账号</a>
252 	 */
253 	public ApiResult deleteKfAccount(String id) throws WeixinException {
254 		Token token = tokenManager.getCache();
255 		String kf_delete_uri = getRequestUri("kf_delete_uri");
256 		WeixinResponse response = weixinExecutor.get(String.format(
257 				kf_delete_uri, token.getAccessToken(), id));
258 
259 		return response.getAsResult();
260 	}
261 
262 	/**
263 	 * 创建会话
264 	 * <p>
265 	 * 开发者可以使用本接口,为多客服的客服工号创建会话,将某个客户直接指定给客服工号接待,需要注意此接口不会受客服自动接入数以及自动接入开关限制。
266 	 * 只能为在线的客服(PC客户端在线,或者已绑定多客服助手)创建会话。
267 	 * </p>
268 	 *
269 	 * @param userOpenId
270 	 *            用户的userOpenId
271 	 * @param kfAccount
272 	 *            完整客服账号,格式为:账号前缀@公众号微信号
273 	 * @param text
274 	 *            附加信息,文本会展示在客服人员的多客服客户端
275 	 * @return 处理结果
276 	 * @throws WeixinException
277 	 * @see <a href=
278 	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN">
279 	 *      创建会话</a>
280 	 */
281 	public ApiResult createKfSession(String userOpenId, String kfAccount,
282 			String text) throws WeixinException {
283 		Token token = tokenManager.getCache();
284 		String kfsession_create_uri = getRequestUri("kfsession_create_uri");
285 		JSONObject obj = new JSONObject();
286 		obj.put("openid", userOpenId);
287 		obj.put("kf_account", kfAccount);
288 		obj.put("text", text);
289 		WeixinResponse response = weixinExecutor.post(
290 				String.format(kfsession_create_uri, token.getAccessToken()),
291 				obj.toJSONString());
292 
293 		return response.getAsResult();
294 	}
295 
296 	/**
297 	 * 关闭会话
298 	 *
299 	 * @param userOpenId
300 	 *            用户的userOpenId
301 	 * @param kfAccount
302 	 *            完整客服账号,格式为:账号前缀@公众号微信号
303 	 * @param text
304 	 *            附加信息,文本会展示在客服人员的多客服客户端
305 	 * @return 处理结果
306 	 * @throws WeixinException
307 	 * @see <a href=
308 	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN">
309 	 *      关闭会话</a>
310 	 */
311 	public ApiResult closeKfSession(String userOpenId, String kfAccount,
312 			String text) throws WeixinException {
313 		Token token = tokenManager.getCache();
314 		String kfsession_close_uri = getRequestUri("kfsession_close_uri");
315 		JSONObject obj = new JSONObject();
316 		obj.put("openid", userOpenId);
317 		obj.put("kf_account", kfAccount);
318 		obj.put("text", text);
319 		WeixinResponse response = weixinExecutor.post(
320 				String.format(kfsession_close_uri, token.getAccessToken()),
321 				obj.toJSONString());
322 
323 		return response.getAsResult();
324 	}
325 
326 	/**
327 	 * 获取客户的会话状态:获取客户当前的会话状态。
328 	 *
329 	 * @param userOpenId
330 	 *            用户的openid
331 	 * @return 会话对象
332 	 * @throws WeixinException
333 	 * @see com.foxinmy.weixin4j.mp.model.KfSession
334 	 * @see <a href=
335 	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN">
336 	 *      获取会话状态</a>
337 	 */
338 	public KfSession getKfSession(String userOpenId) throws WeixinException {
339 		Token token = tokenManager.getCache();
340 		String kfsession_get_uri = getRequestUri("kfsession_get_uri");
341 		WeixinResponse response = weixinExecutor.get(String.format(
342 				kfsession_get_uri, token.getAccessToken(), userOpenId));
343 
344 		KfSession session = response
345 				.getAsObject(new TypeReference<KfSession>() {
346 				});
347 		session.setUserOpenId(userOpenId);
348 		return session;
349 	}
350 
351 	/**
352 	 * 获取客服的会话列表:获取某个客服正在接待的会话列表。
353 	 *
354 	 * @param kfAccount
355 	 *            完整客服账号,格式为:账号前缀@公众号微信号,账号前缀最多10个字符,必须是英文或者数字字符。
356 	 * @return 会话列表
357 	 * @throws WeixinException
358 	 * @see com.foxinmy.weixin4j.mp.model.KfSession
359 	 * @see <a href=
360 	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN">
361 	 *      获取客服的会话列表</a>
362 	 */
363 	public List<KfSession> listKfSession(String kfAccount)
364 			throws WeixinException {
365 		Token token = tokenManager.getCache();
366 		String kfsession_list_uri = getRequestUri("kfsession_list_uri");
367 		WeixinResponse response = weixinExecutor.get(String.format(
368 				kfsession_list_uri, token.getAccessToken(), kfAccount));
369 
370 		List<KfSession> sessionList = JSON.parseArray(response.getAsJson()
371 				.getString("sessionlist"), KfSession.class);
372 		return sessionList;
373 	}
374 
375 	/**
376 	 * 获取未接入会话列表:获取当前正在等待队列中的会话列表,此接口最多返回最早进入队列的100个未接入会话。</br> <font
377 	 * color="red">缺陷:没有count字段</font>
378 	 *
379 	 * @return 会话列表
380 	 * @throws WeixinException
381 	 * @see com.foxinmy.weixin4j.mp.model.KfSession
382 	 * @see com.foxinmy.weixin4j.mp.model.KfSession.KfSessionCounter
383 	 * @see <a href=
384 	 *      "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN">
385 	 *      获取客服的会话列表</a>
386 	 */
387 	public KfSessionCounter listKfWaitSession() throws WeixinException {
388 		Token token = tokenManager.getCache();
389 		String kfsession_wait_uri = getRequestUri("kfsession_wait_uri");
390 		WeixinResponse response = weixinExecutor.get(String.format(
391 				kfsession_wait_uri, token.getAccessToken()));
392 
393 		return response.getAsObject(new TypeReference<KfSessionCounter>() {
394 		});
395 	}
396 }