mirror of
https://github.com/silenceper/wechat.git
synced 2026-02-04 21:02:25 +08:00
客服管理功能添加 (#600)
This commit is contained in:
@@ -35,12 +35,14 @@
|
||||
|
||||
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
|
||||
| ---------------- | --------- | -------------------------------------- | ---------- | -------- |
|
||||
| 获取客服基本信息 | GET | /cgi-bin/customservice/getkflist | NO | |
|
||||
| 添加客服帐号 | POST | /customservice/kfaccount/add | NO | |
|
||||
| 邀请绑定客服帐号 | POST | /customservice/kfaccount/inviteworker | NO | |
|
||||
| 设置客服信息 | POST | /customservice/kfaccount/update | NO | |
|
||||
| 上传客服头像 | POST/FORM | /customservice/kfaccount/uploadheadimg | NO | |
|
||||
| 删除客服帐号 | GET | /customservice/kfaccount/del | NO | |
|
||||
| 获取客服基本信息 | GET | /cgi-bin/customservice/getkflist | YES | (csm *Manager) List |
|
||||
| 添加客服帐号 | POST | /customservice/kfaccount/add | YES | (csm *Manager) Add |
|
||||
| 邀请绑定客服帐号 | POST | /customservice/kfaccount/inviteworker | YES | (csm *Manager) InviteBind |
|
||||
| 设置客服信息 | POST | /customservice/kfaccount/update | YES | (csm *Manager) Update |
|
||||
| 上传客服头像 | POST/FORM | /customservice/kfaccount/uploadheadimg | YES | (csm *Manager) UploadHeadImg |
|
||||
| 删除客服帐号 | POST | /customservice/kfaccount/del | YES | (csm *Manager) Delete |
|
||||
| 获取在线客服 | POST | /cgi-bin/customservice/getonlinekflist| YES | (csm *Manager) OnlineList |
|
||||
| 下发客服输入状态 | POST | /cgi-bin/message/custom/typing | YES | (csm *Manager) SendTypingStatus |
|
||||
|
||||
#### 会话控制
|
||||
|
||||
|
||||
253
officialaccount/customerservice/manager.go
Normal file
253
officialaccount/customerservice/manager.go
Normal file
@@ -0,0 +1,253 @@
|
||||
package customerservice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/silenceper/wechat/v2/officialaccount/context"
|
||||
"github.com/silenceper/wechat/v2/util"
|
||||
)
|
||||
|
||||
// TypingStatus 输入状态类型
|
||||
type TypingStatus string
|
||||
|
||||
const (
|
||||
customerServiceListURL = "https://api.weixin.qq.com/cgi-bin/customservice/getkflist"
|
||||
customerServiceOnlineListURL = "https://api.weixin.qq.com/cgi-bin/customservice/getonlinekflist"
|
||||
customerServiceAddURL = "https://api.weixin.qq.com/customservice/kfaccount/add"
|
||||
customerServiceUpdateURL = "https://api.weixin.qq.com/customservice/kfaccount/update"
|
||||
customerServiceDeleteURL = "https://api.weixin.qq.com/customservice/kfaccount/del"
|
||||
customerServiceInviteURL = "https://api.weixin.qq.com/customservice/kfaccount/inviteworker"
|
||||
customerServiceUploadHeadImg = "https://api.weixin.qq.com/customservice/kfaccount/uploadheadimg"
|
||||
customerServiceTypingURL = "https://api.weixin.qq.com/cgi-bin/message/custom/typing"
|
||||
)
|
||||
|
||||
const (
|
||||
// Typing 表示正在输入状态
|
||||
Typing TypingStatus = "Typing"
|
||||
// CancelTyping 表示取消正在输入状态
|
||||
CancelTyping TypingStatus = "CancelTyping"
|
||||
)
|
||||
|
||||
// Manager 客服管理者,可以管理客服
|
||||
type Manager struct {
|
||||
*context.Context
|
||||
}
|
||||
|
||||
// NewCustomerServiceManager 实例化客服管理
|
||||
func NewCustomerServiceManager(ctx *context.Context) *Manager {
|
||||
csm := new(Manager)
|
||||
csm.Context = ctx
|
||||
return csm
|
||||
}
|
||||
|
||||
// KeFuInfo 客服基本信息
|
||||
type KeFuInfo struct {
|
||||
KfAccount string `json:"kf_account"` // 完整客服帐号,格式为:帐号前缀@公众号微信号
|
||||
KfNick string `json:"kf_nick"` // 客服昵称
|
||||
KfID int `json:"kf_id"` // 客服编号
|
||||
KfHeadImgURL string `json:"kf_headimgurl"` // 客服头像
|
||||
KfWX string `json:"kf_wx"` // 如果客服帐号已绑定了客服人员微信号, 则此处显示微信号
|
||||
InviteWX string `json:"invite_wx"` // 如果客服帐号尚未绑定微信号,但是已经发起了一个绑定邀请, 则此处显示绑定邀请的微信号
|
||||
InviteExpTime int `json:"invite_expire_time"` // 如果客服帐号尚未绑定微信号,但是已经发起过一个绑定邀请, 邀请的过期时间,为unix 时间戳
|
||||
InviteStatus string `json:"invite_status"` // 邀请的状态,有等待确认“waiting”,被拒绝“rejected”, 过期“expired”
|
||||
}
|
||||
|
||||
type resKeFuList struct {
|
||||
util.CommonError
|
||||
KfList []*KeFuInfo `json:"kf_list"`
|
||||
}
|
||||
|
||||
// List 获取所有客服基本信息
|
||||
func (csm *Manager) List() (customerServiceList []*KeFuInfo, err error) {
|
||||
var accessToken string
|
||||
accessToken, err = csm.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", customerServiceListURL, accessToken)
|
||||
var response []byte
|
||||
response, err = util.HTTPGet(uri)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var res resKeFuList
|
||||
err = util.DecodeWithError(response, &res, "ListCustomerService")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
customerServiceList = res.KfList
|
||||
return
|
||||
}
|
||||
|
||||
// KeFuOnlineInfo 客服在线信息
|
||||
type KeFuOnlineInfo struct {
|
||||
KfAccount string `json:"kf_account"`
|
||||
Status int `json:"status"`
|
||||
KfID int `json:"kf_id"`
|
||||
AcceptedCase int `json:"accepted_case"`
|
||||
}
|
||||
|
||||
type resKeFuOnlineList struct {
|
||||
util.CommonError
|
||||
KfOnlineList []*KeFuOnlineInfo `json:"kf_online_list"`
|
||||
}
|
||||
|
||||
// OnlineList 获取在线客服列表
|
||||
func (csm *Manager) OnlineList() (customerServiceOnlineList []*KeFuOnlineInfo, err error) {
|
||||
var accessToken string
|
||||
accessToken, err = csm.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", customerServiceOnlineListURL, accessToken)
|
||||
var response []byte
|
||||
response, err = util.HTTPGet(uri)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var res resKeFuOnlineList
|
||||
err = util.DecodeWithError(response, &res, "ListOnlineCustomerService")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
customerServiceOnlineList = res.KfOnlineList
|
||||
return
|
||||
}
|
||||
|
||||
// Add 添加客服账号
|
||||
func (csm *Manager) Add(kfAccount, nickName string) (err error) {
|
||||
// kfAccount:完整客服帐号,格式为:帐号前缀@公众号微信号,帐号前缀最多10个字符,必须是英文、数字字符或者下划线,后缀为公众号微信号,长度不超过30个字符
|
||||
// nickName:客服昵称,最长16个字
|
||||
// 参数此处均不做校验
|
||||
var accessToken string
|
||||
accessToken, err = csm.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", customerServiceAddURL, accessToken)
|
||||
data := struct {
|
||||
KfAccount string `json:"kf_account"`
|
||||
NickName string `json:"nickname"`
|
||||
}{
|
||||
KfAccount: kfAccount,
|
||||
NickName: nickName,
|
||||
}
|
||||
var response []byte
|
||||
response, err = util.PostJSON(uri, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = util.DecodeWithCommonError(response, "AddCustomerService")
|
||||
return
|
||||
}
|
||||
|
||||
// Update 修改客服账号
|
||||
func (csm *Manager) Update(kfAccount, nickName string) (err error) {
|
||||
var accessToken string
|
||||
accessToken, err = csm.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", customerServiceUpdateURL, accessToken)
|
||||
data := struct {
|
||||
KfAccount string `json:"kf_account"`
|
||||
NickName string `json:"nickname"`
|
||||
}{
|
||||
KfAccount: kfAccount,
|
||||
NickName: nickName,
|
||||
}
|
||||
var response []byte
|
||||
response, err = util.PostJSON(uri, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = util.DecodeWithCommonError(response, "UpdateCustomerService")
|
||||
return
|
||||
}
|
||||
|
||||
// Delete 删除客服帐号
|
||||
func (csm *Manager) Delete(kfAccount string) (err error) {
|
||||
var accessToken string
|
||||
accessToken, err = csm.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", customerServiceDeleteURL, accessToken)
|
||||
data := struct {
|
||||
KfAccount string `json:"kf_account"`
|
||||
}{
|
||||
KfAccount: kfAccount,
|
||||
}
|
||||
var response []byte
|
||||
response, err = util.PostJSON(uri, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = util.DecodeWithCommonError(response, "DeleteCustomerService")
|
||||
return
|
||||
}
|
||||
|
||||
// InviteBind 邀请绑定客服帐号和微信号
|
||||
func (csm *Manager) InviteBind(kfAccount, inviteWX string) (err error) {
|
||||
var accessToken string
|
||||
accessToken, err = csm.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", customerServiceInviteURL, accessToken)
|
||||
data := struct {
|
||||
KfAccount string `json:"kf_account"`
|
||||
InviteWX string `json:"invite_wx"`
|
||||
}{
|
||||
KfAccount: kfAccount,
|
||||
InviteWX: inviteWX,
|
||||
}
|
||||
var response []byte
|
||||
response, err = util.PostJSON(uri, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = util.DecodeWithCommonError(response, "InviteBindCustomerService")
|
||||
return
|
||||
}
|
||||
|
||||
// UploadHeadImg 上传客服头像
|
||||
func (csm *Manager) UploadHeadImg(kfAccount, fileName string) (err error) {
|
||||
var accessToken string
|
||||
accessToken, err = csm.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s&kf_account=%s", customerServiceUploadHeadImg, accessToken, kfAccount)
|
||||
var response []byte
|
||||
response, err = util.PostFile("media", fileName, uri)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = util.DecodeWithCommonError(response, "UploadCustomerServiceHeadImg")
|
||||
return
|
||||
}
|
||||
|
||||
//SendTypingStatus 下发客服输入状态给用户
|
||||
func (csm *Manager) SendTypingStatus(openid string, cmd TypingStatus) (err error) {
|
||||
var accessToken string
|
||||
accessToken, err = csm.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", customerServiceTypingURL, accessToken)
|
||||
data := struct {
|
||||
ToUser string `json:"touser"`
|
||||
Command string `json:"command"`
|
||||
}{
|
||||
ToUser: openid,
|
||||
Command: string(cmd),
|
||||
}
|
||||
var response []byte
|
||||
response, err = util.PostJSON(uri, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = util.DecodeWithCommonError(response, "SendTypingStatus")
|
||||
return
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/silenceper/wechat/v2/officialaccount/broadcast"
|
||||
"github.com/silenceper/wechat/v2/officialaccount/config"
|
||||
"github.com/silenceper/wechat/v2/officialaccount/context"
|
||||
"github.com/silenceper/wechat/v2/officialaccount/customerservice"
|
||||
"github.com/silenceper/wechat/v2/officialaccount/device"
|
||||
"github.com/silenceper/wechat/v2/officialaccount/js"
|
||||
"github.com/silenceper/wechat/v2/officialaccount/material"
|
||||
@@ -197,3 +198,8 @@ func (officialAccount *OfficialAccount) GetSubscribe() *message.Subscribe {
|
||||
}
|
||||
return officialAccount.subscribeMsg
|
||||
}
|
||||
|
||||
// GetCustomerServiceManager 客服管理
|
||||
func (officialAccount *OfficialAccount) GetCustomerServiceManager() *customerservice.Manager {
|
||||
return customerservice.NewCustomerServiceManager(officialAccount.ctx)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,15 @@ func (c *CommonError) Error() string {
|
||||
return fmt.Sprintf("%s Error , errcode=%d , errmsg=%s", c.apiName, c.ErrCode, c.ErrMsg)
|
||||
}
|
||||
|
||||
// NewCommonError 新建CommonError错误,对于无errcode和errmsg的返回也可以返回该通用错误
|
||||
func NewCommonError(apiName string, code int64, msg string) *CommonError {
|
||||
return &CommonError{
|
||||
apiName: apiName,
|
||||
ErrCode: code,
|
||||
ErrMsg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeWithCommonError 将返回值按照CommonError解析
|
||||
func DecodeWithCommonError(response []byte, apiName string) (err error) {
|
||||
var commError CommonError
|
||||
|
||||
Reference in New Issue
Block a user