weixin4j-qy:新增客服消息
This commit is contained in:
parent
4a194c0f6c
commit
3b1abd1a27
@ -491,4 +491,8 @@
|
||||
|
||||
+ weixin4j-[mp|qy]:version upgrade to 1.6.3
|
||||
|
||||
+ weixin4j-server:version upgrade to 1.1.3
|
||||
+ weixin4j-server:version upgrade to 1.1.3
|
||||
|
||||
* 2015-11-20
|
||||
|
||||
+ weixin4j-qy:新增客服消息
|
||||
@ -638,6 +638,10 @@
|
||||
<desc>too many group now, no need to add new</desc>
|
||||
<text>组数量过多</text>
|
||||
</error>
|
||||
<error>
|
||||
<code>45022</code>
|
||||
<text>应用名字长度不合法,合法长度为2-16个字</text>
|
||||
</error>
|
||||
<error>
|
||||
<code>45024</code>
|
||||
<text>账号数量超过上限</text>
|
||||
@ -1058,6 +1062,70 @@
|
||||
<code>86222</code>
|
||||
<text>不允许消息服务访问的API</text>
|
||||
</error>
|
||||
<error>
|
||||
<code>86304</code>
|
||||
<text>不合法的消息类型</text>
|
||||
</error>
|
||||
<error>
|
||||
<code>86305</code>
|
||||
<text>客服服务未启用</text>
|
||||
</error>
|
||||
<error>
|
||||
<code>86306</code>
|
||||
<text>缺少发送人</text>
|
||||
</error>
|
||||
<error>
|
||||
<code>86307</code>
|
||||
<text>缺少发送人类型</text>
|
||||
</error>
|
||||
<error>
|
||||
<code>86308</code>
|
||||
<text>缺少发送人id</text>
|
||||
</error>
|
||||
<error>
|
||||
<code>86309</code>
|
||||
<text>缺少接收人</text>
|
||||
</error>
|
||||
<error>
|
||||
<code>86310</code>
|
||||
<text>缺少接收人类型</text>
|
||||
</error>
|
||||
<error>
|
||||
<code>86311</code>
|
||||
<text>缺少接收人id</text>
|
||||
</error>
|
||||
<error>
|
||||
<code>86312</code>
|
||||
<text>缺少消息类型</text>
|
||||
</error>
|
||||
<error>
|
||||
<code>86313</code>
|
||||
<text>缺少客服,发送人或接收人类型,必须有一个为kf</text>
|
||||
</error>
|
||||
<error>
|
||||
<code>86314</code>
|
||||
<text>客服不唯一,发送人或接收人类型,必须只有一个为kf</text>
|
||||
</error>
|
||||
<error>
|
||||
<code>86315</code>
|
||||
<text>不合法的发送人类型</text>
|
||||
</error>
|
||||
<error>
|
||||
<code>86316</code>
|
||||
<text>不合法的发送人id。Userid不存在、openid不存在、kf不存在</text>
|
||||
</error>
|
||||
<error>
|
||||
<code>86317</code>
|
||||
<text>不合法的接收人类型</text>
|
||||
</error>
|
||||
<error>
|
||||
<code>86318</code>
|
||||
<text>不合法的接收人id。Userid不存在、openid不存在、kf不存在</text>
|
||||
</error>
|
||||
<error>
|
||||
<code>86319</code>
|
||||
<text>不合法的客服,kf不在客服列表中</text>
|
||||
</error>
|
||||
<error>
|
||||
<code>90001</code>
|
||||
<text>未开通摇一摇周边</text>
|
||||
|
||||
@ -34,7 +34,7 @@ public class Order extends ApiResult {
|
||||
/**
|
||||
* 交易状态
|
||||
*
|
||||
* @see com.foxinmy.weixin4j.mp.type.TradeState
|
||||
* @see com.foxinmy.weixin4j.type.TradeState
|
||||
*/
|
||||
@XmlElement(name = "trade_state")
|
||||
@JSONField(name = "trade_state")
|
||||
@ -54,7 +54,7 @@ public class Order extends ApiResult {
|
||||
/**
|
||||
* 交易类型
|
||||
*
|
||||
* @see com.foxinmy.weixin4j.mp.type.TradeType
|
||||
* @see com.foxinmy.weixin4j.type.TradeType
|
||||
*/
|
||||
@XmlElement(name = "trade_type")
|
||||
@JSONField(name = "trade_type")
|
||||
|
||||
@ -34,20 +34,12 @@ public class MenuTest extends TokenTest {
|
||||
@Test
|
||||
public void create() throws WeixinException {
|
||||
btnList = new ArrayList<Button>();
|
||||
String domain = "http://dianzhang.canyi.net";
|
||||
btnList.add(new Button("立即下单", domain, ButtonType.view));
|
||||
|
||||
Button b1 = new Button("会员中心", "", ButtonType.click);
|
||||
b1.pushSub(new Button("我的信息", "U:INFO", ButtonType.click));
|
||||
b1.pushSub(new Button("修改信息", "U:UP:INFO", ButtonType.click));
|
||||
btnList.add(b1);
|
||||
btnList.add(new Button("个人中心", domain + "/user", ButtonType.view));
|
||||
|
||||
Button b2 = new Button("最新兼职", "PART:NEWEST", ButtonType.click);
|
||||
btnList.add(b2);
|
||||
|
||||
Button b3 = new Button("功能", "", ButtonType.click);
|
||||
b3.pushSub(new Button("附近兼职", "PART:NEAR", ButtonType.click));
|
||||
b3.pushSub(new Button("搜索兼职", "PART:SO", ButtonType.click));
|
||||
b3.pushSub(new Button("公交查询", "BUS:SO", ButtonType.click));
|
||||
btnList.add(b3);
|
||||
btnList.add(new Button("小哥介绍", domain, ButtonType.view));
|
||||
|
||||
JsonResult result = menuApi.createMenu(btnList);
|
||||
Assert.assertEquals(0, result.getCode());
|
||||
@ -75,6 +67,14 @@ public class MenuTest extends TokenTest {
|
||||
System.out.println(btn);
|
||||
}
|
||||
Assert.assertEquals(3, btnList.size());
|
||||
// Button [name=我的门店, type=view,
|
||||
// content=http://dianzhang.canyi.net/setting/index, subs=[]]
|
||||
// Button [name=每日签到, type=click, content=CHECKIN, subs=[]]
|
||||
// Button [name=今日订单, type=null, content=null, subs=[Button [name=今日订单,
|
||||
// type=view, content=http://dianzhang.canyi.net/order/index, subs=[]],
|
||||
// Button [name=营业统计, type=view,
|
||||
// content=http://dianzhang.canyi.net/stats/index, subs=[]]]]
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -98,35 +98,4 @@ public class XmlstreamTest {
|
||||
sb.toString(),
|
||||
com.foxinmy.weixin4j.payment.mch.RefundRecord.class));
|
||||
}
|
||||
|
||||
/*
|
||||
* public static String errorXml() { StringBuffer xml = new StringBuffer();
|
||||
* String url =
|
||||
* "http://qydev.weixin.qq.com/wiki/index.php?title=%E5%85%A8%E5%B1%80%E8%BF%94%E5%9B%9E%E7%A0%81%E8%AF%B4%E6%98%8E"
|
||||
* ; try { Document doc = Jsoup.parse(new URL(url), 5000); Elements eles =
|
||||
* doc.getElementsByTag("tr"); for (Element ele : eles) {
|
||||
* xml.append("<error>"); xml.append("<code>").append(ele.child(0).text())
|
||||
* .append("</code>"); xml.append("<text>").append(ele.child(1).text())
|
||||
* .append("</text>"); xml.append("</error>"); } } catch (Exception e) {
|
||||
* e.printStackTrace(); } return xml.toString(); }
|
||||
*/
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// map2xml();
|
||||
// xml2map();
|
||||
// xml2order();
|
||||
// System.err.println(xml2refundRecordV2());
|
||||
// xml2refundRecordV3();
|
||||
// object2xmlWithoutRootElement();
|
||||
/*
|
||||
* RefundRecord refundRecord = xml2refundRecordV2();
|
||||
* System.err.println(refundRecord); String sign =
|
||||
* refundRecord.getSign(); refundRecord.setSign(null); String validSign
|
||||
* = PayUtil.paysignMd5(refundRecord, "paysignkey");
|
||||
* System.err.println("sign=" + sign + ",validSign=" + validSign);
|
||||
* System.err.println(ListsuffixResultSerializer
|
||||
* .serializeToXML(refundRecord));
|
||||
*/
|
||||
// System.out.println(errorXml());
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,4 +120,8 @@
|
||||
|
||||
* 2015-11-09
|
||||
|
||||
+ version upgrade to 1.6.3
|
||||
+ version upgrade to 1.6.3
|
||||
|
||||
* 2015-11-20
|
||||
|
||||
+ 新增客服消息
|
||||
@ -26,6 +26,7 @@ import com.foxinmy.weixin4j.qy.api.QyApi;
|
||||
import com.foxinmy.weixin4j.qy.api.TagApi;
|
||||
import com.foxinmy.weixin4j.qy.api.UserApi;
|
||||
import com.foxinmy.weixin4j.qy.message.ChatMessage;
|
||||
import com.foxinmy.weixin4j.qy.message.CustomeMessage;
|
||||
import com.foxinmy.weixin4j.qy.message.NotifyMessage;
|
||||
import com.foxinmy.weixin4j.qy.model.AgentInfo;
|
||||
import com.foxinmy.weixin4j.qy.model.AgentOverview;
|
||||
@ -173,6 +174,28 @@ public class WeixinProxy {
|
||||
return notifyApi.sendNotifyMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送客服消息
|
||||
*
|
||||
* @param message
|
||||
* 客服消息对象
|
||||
* @return 发送结果
|
||||
* @see <a
|
||||
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E4%BC%81%E4%B8%9A%E5%AE%A2%E6%9C%8D%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E">客服接口说明</a>
|
||||
* @see com.foxinmy.weixin4j.qy.api.NotifyApi
|
||||
* @see com.foxinmy.weixin4j.tuple.Text
|
||||
* @see com.foxinmy.weixin4j.tuple.Image
|
||||
* @see com.foxinmy.weixin4j.tuple.Voice
|
||||
* @see com.foxinmy.weixin4j.tuple.Video
|
||||
* @see com.foxinmy.weixin4j.tuple.File
|
||||
* @see com.foxinmy.weixin4j.qy.message.CustomeMessage
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public JsonResult sendCustomeMessage(CustomeMessage message)
|
||||
throws WeixinException {
|
||||
return notifyApi.sendCustomeMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义菜单(管理员须拥有应用的管理权限 并且应用必须设置在回调模式)
|
||||
*
|
||||
|
||||
@ -5,8 +5,10 @@ import java.util.Map;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.http.weixin.JsonResult;
|
||||
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
|
||||
import com.foxinmy.weixin4j.model.Token;
|
||||
import com.foxinmy.weixin4j.qy.message.CustomeMessage;
|
||||
import com.foxinmy.weixin4j.qy.message.NotifyMessage;
|
||||
import com.foxinmy.weixin4j.token.TokenHolder;
|
||||
import com.foxinmy.weixin4j.tuple.NotifyTuple;
|
||||
@ -47,7 +49,7 @@ public class NotifyApi extends QyApi {
|
||||
* </p>
|
||||
*
|
||||
* @param message
|
||||
* 客服消息对象
|
||||
* 普通消息对象
|
||||
* @return 如果无权限,则本次发送失败;如果收件人不存在或未关注,发送仍然执行。两种情况下均返回无效的部分</br> { "errcode":
|
||||
* 0, "errmsg": "ok", "invaliduser": "UserID1",
|
||||
* "invalidparty":"PartyID1", "invalidtag":"TagID1" }
|
||||
@ -86,4 +88,35 @@ public class NotifyApi extends QyApi {
|
||||
|
||||
return response.getAsJson();
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送客服消息
|
||||
*
|
||||
* @param message
|
||||
* 客服消息对象
|
||||
* @return 发送结果
|
||||
* @see <a
|
||||
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E4%BC%81%E4%B8%9A%E5%AE%A2%E6%9C%8D%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E">客服接口说明</a>
|
||||
* @see com.foxinmy.weixin4j.tuple.Text
|
||||
* @see com.foxinmy.weixin4j.tuple.Image
|
||||
* @see com.foxinmy.weixin4j.tuple.Voice
|
||||
* @see com.foxinmy.weixin4j.tuple.Video
|
||||
* @see com.foxinmy.weixin4j.tuple.File
|
||||
* @see com.foxinmy.weixin4j.qy.message.CustomeMessage
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public JsonResult sendCustomeMessage(CustomeMessage message)
|
||||
throws WeixinException {
|
||||
NotifyTuple tuple = message.getTuple();
|
||||
String msgtype = tuple.getMessageType();
|
||||
JSONObject obj = (JSONObject) JSON.toJSON(message);
|
||||
obj.put("msgtype", msgtype);
|
||||
obj.put(msgtype, tuple);
|
||||
String message_kf_send_uri = getRequestUri("message_kf_send_uri");
|
||||
Token token = tokenHolder.getToken();
|
||||
WeixinResponse response = weixinExecutor.post(
|
||||
String.format(message_kf_send_uri, token.getAccessToken()),
|
||||
obj.toJSONString());
|
||||
return response.getAsJsonResult();
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,8 +57,10 @@ menu_create_uri={api_base_url}/menu/create?access_token=%s&agentid=%d
|
||||
menu_delete_uri={api_base_url}/menu/delete?access_token=%s&agentid=%d
|
||||
# \u67e5\u8be2\u83dc\u5355
|
||||
menu_get_uri={api_base_url}/menu/get?access_token=%s&agentid=%d
|
||||
# \u53d1\u9001\u6d88\u606f
|
||||
# \u53d1\u9001\u666e\u901a\u6d88\u606f
|
||||
message_send_uri={api_base_url}/message/send?access_token=%s
|
||||
# \u53d1\u9001\u5ba2\u670d\u6d88\u606f
|
||||
message_kf_send_uri={api_base_url}/kf/send?access_token=%s
|
||||
# \u83b7\u53d6\u5fae\u4fe1IP\u5730\u5740
|
||||
getcallbackip_uri={api_base_url}/getcallbackip?access_token=%s
|
||||
# \u83b7\u53d6\u4f01\u4e1a\u53f7\u5e94\u7528\u4fe1\u606f
|
||||
|
||||
@ -0,0 +1,113 @@
|
||||
package com.foxinmy.weixin4j.qy.message;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.foxinmy.weixin4j.tuple.NotifyTuple;
|
||||
|
||||
/**
|
||||
* 客服消息对象
|
||||
*
|
||||
* @className CustomeMessage
|
||||
* @author jy
|
||||
* @date 2015年11月20日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.tuple.Text
|
||||
* @see com.foxinmy.weixin4j.tuple.Image
|
||||
* @see com.foxinmy.weixin4j.tuple.Voice
|
||||
* @see com.foxinmy.weixin4j.tuple.Video
|
||||
* @see com.foxinmy.weixin4j.tuple.File
|
||||
* @see <a
|
||||
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E4%BC%81%E4%B8%9A%E5%AE%A2%E6%9C%8D%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E">客服消息</a>
|
||||
*/
|
||||
public class CustomeMessage implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -3620361273175868681L;
|
||||
|
||||
private CustomeTarget sender;
|
||||
private CustomeTarget receiver;
|
||||
/**
|
||||
* 消息对象
|
||||
*/
|
||||
@JSONField(serialize = false)
|
||||
private NotifyTuple tuple;
|
||||
|
||||
public CustomeMessage(CustomeTarget sender, CustomeTarget receiver,
|
||||
NotifyTuple tuple) {
|
||||
this.sender = sender;
|
||||
this.receiver = receiver;
|
||||
this.tuple = tuple;
|
||||
}
|
||||
|
||||
public CustomeTarget getSender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
public CustomeTarget getReceiver() {
|
||||
return receiver;
|
||||
}
|
||||
|
||||
public NotifyTuple getTuple() {
|
||||
return tuple;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户类型
|
||||
*
|
||||
* @className CustomeIdType
|
||||
* @author jy
|
||||
* @date 2015年11月20日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public enum CustomeIdType {
|
||||
/**
|
||||
* 客服
|
||||
*/
|
||||
kf,
|
||||
/**
|
||||
* 企业员工userid
|
||||
*/
|
||||
userid,
|
||||
/**
|
||||
* 公众号openid
|
||||
*/
|
||||
openid
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息对象
|
||||
*
|
||||
* @className CustomeTarget
|
||||
* @author jy
|
||||
* @date 2015年11月20日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public static class CustomeTarget implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private CustomeIdType type;
|
||||
private String id;
|
||||
|
||||
public CustomeTarget(CustomeIdType type, String id) {
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public CustomeIdType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CustomeMessage [sender=" + sender + ", receiver=" + receiver
|
||||
+ ", tuple=" + tuple + "]";
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,7 @@ import com.foxinmy.weixin4j.qy.model.IdParameter;
|
||||
import com.foxinmy.weixin4j.tuple.NotifyTuple;
|
||||
|
||||
/**
|
||||
* 客服消息对象
|
||||
* 消息提醒对象
|
||||
*
|
||||
* @className NotifyMessage
|
||||
* @author jy
|
||||
|
||||
@ -23,7 +23,7 @@ public class ChatReceiver implements Serializable {
|
||||
@XmlElement(name = "id")
|
||||
private String targetId;
|
||||
/**
|
||||
* 群聊|单聊
|
||||
* 群聊|单聊|userid|openid
|
||||
*/
|
||||
@XmlElement(name = "type")
|
||||
private String chatType;
|
||||
|
||||
@ -159,16 +159,16 @@ public final class WeixinServerBootstrap {
|
||||
* 默认端口启动服务
|
||||
*
|
||||
*/
|
||||
public void startup() throws WeixinException {
|
||||
startup(DEFAULT_SERVERPORT);
|
||||
public Channel startup() throws WeixinException {
|
||||
return startup(DEFAULT_SERVERPORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定端口启动服务
|
||||
*
|
||||
*/
|
||||
public void startup(int serverPort) throws WeixinException {
|
||||
startup(DEFAULT_BOSSTHREADS, DEFAULT_WORKERTHREADS, serverPort);
|
||||
public Channel startup(int serverPort) throws WeixinException {
|
||||
return startup(DEFAULT_BOSSTHREADS, DEFAULT_WORKERTHREADS, serverPort);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -180,9 +180,10 @@ public final class WeixinServerBootstrap {
|
||||
* worker线程数
|
||||
* @param serverPort
|
||||
* 服务启动端口
|
||||
* @return
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public void startup(int bossThreads, int workerThreads, int serverPort)
|
||||
public Channel startup(int bossThreads, int workerThreads, int serverPort)
|
||||
throws WeixinException {
|
||||
messageDispatcher.setMessageHandlerList(messageHandlerList);
|
||||
messageDispatcher.setMessageInterceptorList(messageInterceptorList);
|
||||
@ -200,7 +201,7 @@ public final class WeixinServerBootstrap {
|
||||
messageDispatcher));
|
||||
Channel ch = b.bind(serverPort).sync().channel();
|
||||
logger.info("weixin4j server startup OK:{}", serverPort);
|
||||
ch.closeFuture().sync();
|
||||
return ch.closeFuture().channel();
|
||||
} catch (WeixinException e) {
|
||||
throw e;
|
||||
} catch (InterruptedException e) {
|
||||
|
||||
@ -13,5 +13,14 @@ public enum AgentType {
|
||||
/**
|
||||
* 聊天应用
|
||||
*/
|
||||
chat
|
||||
chat,
|
||||
// 企业客服回调
|
||||
/**
|
||||
* 企业号内部客服,客户为企业号通讯录成员
|
||||
*/
|
||||
kf_internal,
|
||||
/**
|
||||
* 企业号外部客服,客户为服务号openid
|
||||
*/
|
||||
kf_external
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user