Skip to content

Commit

Permalink
🆕 #2309 【企业微信】新增微信客服-接待人员管理、会话分配与消息收发、基础信息获取等相关接口
Browse files Browse the repository at this point in the history
  • Loading branch information
borisbao authored and binarywang committed Feb 8, 2022
1 parent 37fe7d2 commit 438d583
Show file tree
Hide file tree
Showing 22 changed files with 1,096 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package me.chanjar.weixin.cp.api;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import lombok.NonNull;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.WxCpBaseResp;
import me.chanjar.weixin.cp.bean.external.*;
Expand Down Expand Up @@ -921,5 +925,35 @@ WxCpGetMomentComments getMomentComments(String momentId, String userId)
*/
WxCpProductAlbumResult getProductAlbum(String productId) throws WxErrorException;

/**
* <pre>
* 上传附件资源
* https://open.work.weixin.qq.com/api/doc/90001/90143/95178
* </pre>
* @param mediaType
* @param fileType
* @param attachmentType
* @param inputStream
* @return
* @throws WxErrorException
* @throws IOException
*/
WxMediaUploadResult uploadAttachment(String mediaType, String fileType, Integer attachmentType,
InputStream inputStream) throws WxErrorException, IOException;

/**
* <pre>
* 上传附件资源
* https://open.work.weixin.qq.com/api/doc/90001/90143/95178
* </pre>
* @param mediaType
* @param attachmentType
* @param file
* @return
* @throws WxErrorException
*/
WxMediaUploadResult uploadAttachment(String mediaType, Integer attachmentType, File file)
throws WxErrorException;


}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package me.chanjar.weixin.cp.api;

import java.util.List;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.WxCpBaseResp;
import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountAdd;
Expand All @@ -9,6 +10,14 @@
import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountLinkResp;
import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountListResp;
import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountUpd;
import me.chanjar.weixin.cp.bean.kf.WxCpKfCustomerBatchGetResp;
import me.chanjar.weixin.cp.bean.kf.WxCpKfMsgListResp;
import me.chanjar.weixin.cp.bean.kf.WxCpKfMsgSendRequest;
import me.chanjar.weixin.cp.bean.kf.WxCpKfMsgSendResp;
import me.chanjar.weixin.cp.bean.kf.WxCpKfServiceStateResp;
import me.chanjar.weixin.cp.bean.kf.WxCpKfServiceStateTransResp;
import me.chanjar.weixin.cp.bean.kf.WxCpKfServicerListResp;
import me.chanjar.weixin.cp.bean.kf.WxCpKfServicerOpResp;

/**
* 微信客服接口
Expand Down Expand Up @@ -67,4 +76,116 @@ public interface WxCpKfService {
*/
WxCpKfAccountLinkResp getAccountLink(WxCpKfAccountLink link) throws WxErrorException;

/**
* 接待人员管理
* 添加指定客服帐号的接待人员,每个客服帐号目前最多可添加500个接待人员。
* @param openKfid 客服帐号ID
* @param userIdList 接待人员userid列表。第三方应用填密文userid,即open_userid
* 可填充个数:1 ~ 100。超过100个需分批调用。
* @return 添加客服账号结果
* @throws WxErrorException 异常
*/
WxCpKfServicerOpResp addServicer(String openKfid, List<String> userIdList) throws WxErrorException;

/**
* 接待人员管理
* 从客服帐号删除接待人员
* @param openKfid 客服帐号ID
* @param userIdList 接待人员userid列表。第三方应用填密文userid,即open_userid
* 可填充个数:1 ~ 100。超过100个需分批调用。
* @return 删除客服账号结果
* @throws WxErrorException 异常
*/
WxCpKfServicerOpResp delServicer(String openKfid, List<String> userIdList) throws WxErrorException;

/**
* 接待人员管理
* 获取某个客服帐号的接待人员列表
* @param openKfid 客服帐号ID
* @return 接待人员列表
* @throws WxErrorException 异常
*/
WxCpKfServicerListResp listServicer(String openKfid) throws WxErrorException;

/**
* 分配客服会话
* 获取会话状态
* @param openKfid 客服帐号ID
* @param externalUserId 微信客户的external_userid
* @return
* @throws WxErrorException
*/
WxCpKfServiceStateResp getServiceState(String openKfid, String externalUserId)
throws WxErrorException;

/**
* 分配客服会话
* 变更会话状态
* @param openKfid 客服帐号ID
* @param externalUserId 微信客户的external_userid
* @param serviceState 变更的目标状态,状态定义和所允许的变更可参考概述中的流程图和表格
* @param servicerUserId 接待人员的userid。第三方应用填密文userid,即open_userid。当state=3时要求必填,接待人员须处于“正在接待”中。
* @return 部分状态返回回复语code
* @throws WxErrorException
*/
WxCpKfServiceStateTransResp transServiceState(String openKfid, String externalUserId,
Integer serviceState, String servicerUserId) throws WxErrorException;

/**
* 读取消息
* 微信客户发送的消息、接待人员在企业微信回复的消息、发送消息接口发送失败事件(如被用户拒收)、客户点击菜单消息的回复消息,
* 可以通过该接口获取具体的消息内容和事件。不支持读取通过发送消息接口发送的消息。
* 支持的消息类型:文本、图片、语音、视频、文件、位置、链接、名片、小程序、菜单、事件。
* @param cursor 上一次调用时返回的next_cursor,第一次拉取可以不填。不多于64字节
* @param token 回调事件返回的token字段,10分钟内有效;可不填,如果不填接口有严格的频率限制。不多于128字节
* @param limit 期望请求的数据量,默认值和最大值都为1000。
* 注意:可能会出现返回条数少于limit的情况,需结合返回的has_more字段判断是否继续请求。
* @param voiceFormat 语音消息类型,0-Amr 1-Silk,默认0。可通过该参数控制返回的语音格式
* @return 微信消息
* @throws WxErrorException 异常
*/
WxCpKfMsgListResp syncMsg(String cursor, String token, Integer limit, Integer voiceFormat)
throws WxErrorException;

/**
* 发送消息
* 当微信客户处于“新接入待处理”或“由智能助手接待”状态下,可调用该接口给用户发送消息。
* 注意仅当微信客户在主动发送消息给客服后的48小时内,企业可发送消息给客户,最多可发送5条消息;若用户继续发送消息,企业可再次下发消息。
* 支持发送消息类型:文本、图片、语音、视频、文件、图文、小程序、菜单消息、地理位置。
* @param request 发送信息
* @return 发送结果
* @throws WxErrorException 异常
*/
WxCpKfMsgSendResp sendMsg(WxCpKfMsgSendRequest request) throws WxErrorException;

/**
* 发送欢迎语等事件响应消息
* 当特定的事件回调消息包含code字段,或通过接口变更到特定的会话状态,会返回code字段。
* 开发者可以此code为凭证,调用该接口给用户发送相应事件场景下的消息,如客服欢迎语、客服提示语和会话结束语等。
* 除"用户进入会话事件"以外,响应消息仅支持会话处于获取该code的会话状态时发送,如将会话转入待接入池时获得的code仅能在会话状态为”待接入池排队中“时发送。
*
* 目前支持的事件场景和相关约束如下:
*
* 事件场景 允许下发条数 code有效期 支持的消息类型 获取code途径
* 用户进入会话,用于发送客服欢迎语 1条 20秒 文本、菜单 事件回调
* 进入接待池,用于发送排队提示语等 1条 48小时 文本 转接会话接口
* 从接待池接入会话,用于发送非工作
* 时间的提示语或超时未回复的提示语
* 等 1条 48小时 文本 事件回调、转接会话接口
* 结束会话,用于发送结束会话提示语
* 或满意度评价等 1条 20秒 文本、菜单 事件回调、转接会话接口
* @param request
* @return
* @throws WxErrorException
*/
WxCpKfMsgSendResp sendMsgOnEvent(WxCpKfMsgSendRequest request) throws WxErrorException;

/**
* 获取客户基础信息
* @param externalUserIdList
* @return
* @throws WxErrorException
*/
WxCpKfCustomerBatchGetResp customerBatchGet(List<String> externalUserIdList)
throws WxErrorException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxCpErrorMsgEnum;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.error.WxRuntimeException;
import me.chanjar.weixin.common.util.BeanUtils;
import me.chanjar.weixin.common.util.fs.FileUtils;
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor;
import me.chanjar.weixin.common.util.json.GsonParser;
import me.chanjar.weixin.cp.api.WxCpExternalContactService;
import me.chanjar.weixin.cp.api.WxCpService;
Expand Down Expand Up @@ -814,4 +821,19 @@ public WxCpProductAlbumResult getProductAlbum(String productId) throws WxErrorEx
return WxCpProductAlbumResult.fromJson(result);
}

@Override
public WxMediaUploadResult uploadAttachment(String mediaType, String fileType, Integer attachmentType,
InputStream inputStream) throws WxErrorException, IOException {
return uploadAttachment(mediaType, attachmentType, FileUtils.createTmpFile(inputStream,
UUID.randomUUID().toString(), fileType));
}

@Override
public WxMediaUploadResult uploadAttachment(String mediaType, Integer attachmentType, File file)
throws WxErrorException {
String params = "?media_type=" + mediaType + "&attachment_type=" + attachmentType;
final String url = this.mainService.getWxCpConfigStorage().getApiUrl(UPLOAD_ATTACHMENT + params);
return this.mainService.execute(MediaUploadRequestExecutor.create(
this.mainService.getRequestHttp()), url, file);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package me.chanjar.weixin.cp.api.impl;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import java.util.List;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.WxCpKfService;
Expand All @@ -12,6 +17,14 @@
import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountLinkResp;
import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountListResp;
import me.chanjar.weixin.cp.bean.kf.WxCpKfAccountUpd;
import me.chanjar.weixin.cp.bean.kf.WxCpKfCustomerBatchGetResp;
import me.chanjar.weixin.cp.bean.kf.WxCpKfMsgListResp;
import me.chanjar.weixin.cp.bean.kf.WxCpKfMsgSendRequest;
import me.chanjar.weixin.cp.bean.kf.WxCpKfMsgSendResp;
import me.chanjar.weixin.cp.bean.kf.WxCpKfServiceStateResp;
import me.chanjar.weixin.cp.bean.kf.WxCpKfServiceStateTransResp;
import me.chanjar.weixin.cp.bean.kf.WxCpKfServicerListResp;
import me.chanjar.weixin.cp.bean.kf.WxCpKfServicerOpResp;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;

import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Kf.*;
Expand All @@ -25,6 +38,7 @@
@RequiredArgsConstructor
public class WxCpKfServiceImpl implements WxCpKfService {
private final WxCpService cpService;
private static final Gson GSON = new GsonBuilder().create();

@Override
public WxCpKfAccountAddResp addAccount(WxCpKfAccountAdd add) throws WxErrorException {
Expand Down Expand Up @@ -61,4 +75,117 @@ public WxCpKfAccountLinkResp getAccountLink(WxCpKfAccountLink link) throws WxErr
return WxCpKfAccountLinkResp.fromJson(responseContent);
}

@Override
public WxCpKfServicerOpResp addServicer(String openKfid, List<String> userIdList) throws WxErrorException {
return servicerOp(openKfid, userIdList, SERVICER_ADD);
}

@Override
public WxCpKfServicerOpResp delServicer(String openKfid, List<String> userIdList) throws WxErrorException {
return servicerOp(openKfid, userIdList, SERVICER_DEL);
}

private WxCpKfServicerOpResp servicerOp(String openKfid, List<String> userIdList, String uri) throws WxErrorException {
String url = cpService.getWxCpConfigStorage().getApiUrl(uri);

JsonObject json = new JsonObject();
json.addProperty("open_kfid", openKfid);
JsonArray userIdArray = new JsonArray();
userIdList.forEach(userIdArray::add);
json.add("userid_list", userIdArray);

String responseContent = cpService.post(url, json.toString());
return WxCpKfServicerOpResp.fromJson(responseContent);
}

@Override
public WxCpKfServicerListResp listServicer(String openKfid) throws WxErrorException {
String url = cpService.getWxCpConfigStorage().getApiUrl(SERVICER_LIST + openKfid);
String responseContent = cpService.get(url, null);
return WxCpKfServicerListResp.fromJson(responseContent);
}

@Override
public WxCpKfServiceStateResp getServiceState(String openKfid, String externalUserId)
throws WxErrorException {
String url = cpService.getWxCpConfigStorage().getApiUrl(SERVICE_STATE_GET);

JsonObject json = new JsonObject();
json.addProperty("open_kfid", openKfid);
json.addProperty("external_userid", externalUserId);

String responseContent = cpService.post(url, json.toString());
return WxCpKfServiceStateResp.fromJson(responseContent);
}

@Override
public WxCpKfServiceStateTransResp transServiceState(String openKfid, String externalUserId,
Integer serviceState, String servicerUserId) throws WxErrorException {
String url = cpService.getWxCpConfigStorage().getApiUrl(SERVICE_STATE_TRANS);

JsonObject json = new JsonObject();
json.addProperty("open_kfid", openKfid);
json.addProperty("external_userid", externalUserId);
json.addProperty("service_state", serviceState);
json.addProperty("servicer_userid", servicerUserId);

String responseContent = cpService.post(url, json.toString());
return WxCpKfServiceStateTransResp.fromJson(responseContent);
}

@Override
public WxCpKfMsgListResp syncMsg(String cursor, String token, Integer limit, Integer voiceFormat)
throws WxErrorException {
String url = cpService.getWxCpConfigStorage().getApiUrl(SYNC_MSG);

JsonObject json = new JsonObject();
if (cursor!=null) {
json.addProperty("cursor", cursor);
}
if (token!=null) {
json.addProperty("token", token);
}
if (limit!=null) {
json.addProperty("limit", limit);
}
if (voiceFormat!=null) {
json.addProperty("voice_format", voiceFormat);
}

String responseContent = cpService.post(url, json);
return WxCpKfMsgListResp.fromJson(responseContent);
}

@Override
public WxCpKfMsgSendResp sendMsg(WxCpKfMsgSendRequest request) throws WxErrorException {
String url = cpService.getWxCpConfigStorage().getApiUrl(SEND_MSG);

String responseContent = cpService.post(url, GSON.toJson(request));

return WxCpKfMsgSendResp.fromJson(responseContent);
}

@Override
public WxCpKfMsgSendResp sendMsgOnEvent(WxCpKfMsgSendRequest request) throws WxErrorException {
String url = cpService.getWxCpConfigStorage().getApiUrl(SEND_MSG_ON_EVENT);

String responseContent = cpService.post(url, GSON.toJson(request));

return WxCpKfMsgSendResp.fromJson(responseContent);
}

@Override
public WxCpKfCustomerBatchGetResp customerBatchGet(List<String> externalUserIdList)
throws WxErrorException {
String url = cpService.getWxCpConfigStorage().getApiUrl(CUSTOMER_BATCH_GET);

JsonArray array = new JsonArray();

externalUserIdList.forEach(array::add);
JsonObject json = new JsonObject();
json.add("external_userid_list", array);
String responseContent = cpService.post(url, json.toString());
return WxCpKfCustomerBatchGetResp.fromJson(responseContent);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ public class ExternalContact implements Serializable {
@SerializedName("name")
private String name;

@SerializedName("nickname")
private String nickname;

@SerializedName("avatar")
private String avatar;

Expand Down
Loading

0 comments on commit 438d583

Please sign in to comment.