From 477de7d4f2b68c887d74f91cd5a392baabbc73a7 Mon Sep 17 00:00:00 2001 From: Afeyer Date: Wed, 4 Aug 2021 18:40:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81SDK=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E7=9B=91=E6=8E=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- account.go | 25 ++++++++++++---- cache/cache.go | 1 + cache/redis.go | 4 +++ client.go | 12 +++++++- customer.go | 5 +++- media.go | 10 +++++-- monitor.go | 79 +++++++++++++++++++++++++++++++++++++++++++++++++ sendmsg.go | 5 +++- servicer.go | 15 ++++++++-- servicestate.go | 10 +++++-- syncmsg.go | 5 +++- token.go | 5 +++- upgrade.go | 20 ++++++++++--- util/util.go | 10 +++++++ 14 files changed, 185 insertions(+), 21 deletions(-) create mode 100644 monitor.go diff --git a/account.go b/account.go index 8c904d0..85fa385 100644 --- a/account.go +++ b/account.go @@ -33,7 +33,10 @@ type AccountAddSchema struct { // AccountAdd 添加客服账号 func (r *Client) AccountAdd(options AccountAddOptions) (info AccountAddSchema, err error) { - data, err := util.HttpPost(fmt.Sprintf(accountAddAddr, r.accessToken), options) + target := fmt.Sprintf(accountAddAddr, r.accessToken) + r.recordUpdate(target) + + data, err := util.HttpPost(target, options) if err != nil { return info, err } @@ -51,7 +54,10 @@ type AccountDelOptions struct { // AccountDel 删除客服账号 func (r *Client) AccountDel(options AccountDelOptions) (info BaseModel, err error) { - data, err := util.HttpPost(fmt.Sprintf(accountDelAddr, r.accessToken), options) + target := fmt.Sprintf(accountDelAddr, r.accessToken) + r.recordUpdate(target) + + data, err := util.HttpPost(target, options) if err != nil { return info, err } @@ -71,7 +77,10 @@ type AccountUpdateOptions struct { // AccountUpdate 修复客服账号 func (r *Client) AccountUpdate(options AccountUpdateOptions) (info BaseModel, err error) { - data, err := util.HttpPost(fmt.Sprintf(accountUpdateAddr, r.accessToken), options) + target := fmt.Sprintf(accountUpdateAddr, r.accessToken) + r.recordUpdate(target) + + data, err := util.HttpPost(target, options) if err != nil { return info, err } @@ -97,7 +106,10 @@ type AccountListSchema struct { // AccountList 获取客服账号列表 func (r *Client) AccountList() (info AccountListSchema, err error) { - data, err := util.HttpGet(fmt.Sprintf(accountListAddr, r.accessToken)) + target := fmt.Sprintf(accountListAddr, r.accessToken) + r.recordUpdate(target) + + data, err := util.HttpGet(target) if err != nil { return info, err } @@ -122,7 +134,10 @@ type AddContactWaySchema struct { // AddContactWay 获取客服账号链接 func (r *Client) AddContactWay(options AddContactWayOptions) (info AddContactWaySchema, err error) { - data, err := util.HttpPost(fmt.Sprintf(addContactWayAddr, r.accessToken), options) + target := fmt.Sprintf(addContactWayAddr, r.accessToken) + r.recordUpdate(target) + + data, err := util.HttpPost(target, options) if err != nil { return info, err } diff --git a/cache/cache.go b/cache/cache.go index eae8a15..a7cd872 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -5,4 +5,5 @@ import "time" type Cache interface { Set(k, v string, expires time.Duration) error Get(k string) (string, error) + Scan(cursor uint64, match string, count int64) (keys []string, newCursor uint64, err error) } diff --git a/cache/redis.go b/cache/redis.go index d5081e4..9cad680 100644 --- a/cache/redis.go +++ b/cache/redis.go @@ -122,4 +122,8 @@ func (r *Redis) Get(k string) (string, error) { return "", err } return con, nil +} + +func (r *Redis) Scan(cursor uint64, match string, count int64) (keys []string, newCursor uint64, err error) { + return r.Point.Scan(context.TODO(), cursor, match, count).Result() } \ No newline at end of file diff --git a/client.go b/client.go index c0c8721..1e6a644 100644 --- a/client.go +++ b/client.go @@ -21,6 +21,8 @@ type Options struct { Cache cache.Cache // 数据缓存 ExpireTime time.Duration // 令牌过期时间 IsCloseCache bool // 是否关闭自动缓存AccessToken, 默认缓存 + MonitorOpen bool // 是否开启监控 + MonitorLogExpireTime time.Duration // 监控日志过期时间,默认三天 } // Client 微信客服实例 @@ -35,6 +37,8 @@ type Client struct { mutex sync.Mutex accessToken string // 用户访问凭证 isCloseCache bool // 是否自动缓存AccessToken, 默认缓存 + monitorOpen bool // 是否开启监控 + monitorLogExpireTime time.Duration // 监控日志过期时间,默认三天 } // New 初始化微信客服实例 @@ -44,7 +48,11 @@ func New(options Options) (client *Client, err error) { } if options.ExpireTime == 0 { - options.ExpireTime = 6000 + options.ExpireTime = 6000 * time.Second + } + + if options.MonitorOpen && options.MonitorLogExpireTime == 0 { + options.MonitorLogExpireTime = 3 * 24 * 3600 } client = &Client{ @@ -57,6 +65,8 @@ func New(options Options) (client *Client, err error) { eventQueue: sync.Map{}, mutex: sync.Mutex{}, isCloseCache: options.IsCloseCache, + monitorOpen: options.MonitorOpen, + monitorLogExpireTime: options.MonitorLogExpireTime, } if err = client.initAccessToken(); err != nil { diff --git a/customer.go b/customer.go index ac7b9f9..722bf8a 100644 --- a/customer.go +++ b/customer.go @@ -33,7 +33,10 @@ type CustomerBatchGetSchema struct { // CustomerBatchGet 客户基本信息获取 func (r *Client) CustomerBatchGet(options CustomerBatchGetOptions) (info CustomerBatchGetSchema, err error) { - data, err := util.HttpPost(fmt.Sprintf(customerBatchGetAddr, r.accessToken), options) + target := fmt.Sprintf(customerBatchGetAddr, r.accessToken) + r.recordUpdate(target) + + data, err := util.HttpPost(target, options) if err != nil { return info, err } diff --git a/media.go b/media.go index 7b85f52..db32d52 100644 --- a/media.go +++ b/media.go @@ -47,7 +47,10 @@ func (r *Client) MediaUpload(options MediaUploadOptions) (info MediaUploadSchema FileSize: options.FileSize, File: options.File, } - data, err := util.HttpPostFile(fmt.Sprintf(mediaUploadAddr, r.accessToken, options.Type), fileOptions) + target := fmt.Sprintf(mediaUploadAddr, r.accessToken, options.Type) + r.recordUpdate(target) + + data, err := util.HttpPostFile(target, fileOptions) if err != nil { return info, err } @@ -67,7 +70,10 @@ func (r *Client) MediaUpload(options MediaUploadOptions) (info MediaUploadSchema //视频(video) :10MB,支持MP4格式 //普通文件(file):20MB func (r *Client) MediaOriginUpload(fileName, fileType string, size int, body []byte) (info MediaUploadSchema, err error) { - data, err := util.HttpPostOriginFile(fmt.Sprintf(mediaUploadAddr, r.accessToken, fileType), fileName, size, body) + target := fmt.Sprintf(mediaUploadAddr, r.accessToken, fileType) + r.recordUpdate(target) + + data, err := util.HttpPostOriginFile(target, fileName, size, body) if err != nil { return info, err } diff --git a/monitor.go b/monitor.go new file mode 100644 index 0000000..b34f492 --- /dev/null +++ b/monitor.go @@ -0,0 +1,79 @@ +package WeChatCustomerServiceSDK + +import ( + "github.com/NICEXAI/WeChatCustomerServiceSDK/util" + "strconv" + "strings" + "time" +) + +// MonitorSchema SDK监控数据 +type MonitorSchema map[string]map[string]int + +// Monitor 获取当前服务SDK调用统计数据 +func (r *Client) Monitor() MonitorSchema { + keyList := make([]string, 0) + cursor := uint64(0) + hasMore := true + + for hasMore { + keys, newCursor, err := r.cache.Scan(cursor, "wechat:log:" + r.corpID + "*", 1000) + if err != nil { + break + } + if newCursor == 0 || len(keys) == 0 { + hasMore = false + } + cursor = newCursor + keyList = append(keyList, keys...) + } + + monitorInfo := MonitorSchema{} + for _, key := range keyList { + options := strings.Split(key, ":") + if len(options) != 5 { + continue + } + con, err := r.cache.Get(key) + if err != nil { + continue + } + timeDic := monitorInfo[options[3]] + if timeDic == nil { + monitorInfo[options[3]] = map[string]int{} + } + count, err := strconv.Atoi(con) + if err != nil { + continue + } + monitorInfo[options[3]][options[4]] = count + } + return monitorInfo +} + +//记录SDK调用信息 +func (r *Client) recordUpdate(path string) { + if !r.monitorOpen { + return + } + path = util.ParseRoute(path) + if path == "" { + return + } + recordKey := "wechat:log:" + r.corpID + ":" + time.Now().Format("2006010215") + ":" + path[1:] + con, err := r.cache.Get(recordKey) + if err != nil { + return + } + if con == "" { + if err = r.cache.Set(recordKey, strconv.Itoa(1), r.monitorLogExpireTime); err != nil { + return + } + return + } + count, err := strconv.Atoi(con) + if err != nil { + return + } + _ = r.cache.Set(recordKey, strconv.Itoa(count + 1), r.monitorLogExpireTime) +} \ No newline at end of file diff --git a/sendmsg.go b/sendmsg.go index 006b4bd..eac9ac7 100644 --- a/sendmsg.go +++ b/sendmsg.go @@ -19,7 +19,10 @@ type SendMsgSchema struct { // SendMsg 获取消息 func (r *Client) SendMsg(options interface{}) (info SendMsgSchema, err error) { - data, err := util.HttpPost(fmt.Sprintf(sendMsgAddr, r.accessToken), options) + target := fmt.Sprintf(sendMsgAddr, r.accessToken) + r.recordUpdate(target) + + data, err := util.HttpPost(target, options) if err != nil { return info, err } diff --git a/servicer.go b/servicer.go index 12d8416..b473f64 100644 --- a/servicer.go +++ b/servicer.go @@ -32,7 +32,10 @@ type ReceptionistSchema struct { // ReceptionistAdd 添加接待人员 func (r *Client) ReceptionistAdd(options ReceptionistOptions) (info ReceptionistSchema, err error) { - data, err := util.HttpPost(fmt.Sprintf(receptionistAddAddr, r.accessToken), options) + target := fmt.Sprintf(receptionistAddAddr, r.accessToken) + r.recordUpdate(target) + + data, err := util.HttpPost(target, options) if err != nil { return info, err } @@ -45,7 +48,10 @@ func (r *Client) ReceptionistAdd(options ReceptionistOptions) (info Receptionist // ReceptionistDel 删除接待人员 func (r *Client) ReceptionistDel(options ReceptionistOptions) (info ReceptionistSchema, err error) { - data, err := util.HttpPost(fmt.Sprintf(receptionistDelAddr, r.accessToken), options) + target := fmt.Sprintf(receptionistDelAddr, r.accessToken) + r.recordUpdate(target) + + data, err := util.HttpPost(target, options) if err != nil { return info, err } @@ -67,7 +73,10 @@ type ReceptionistListSchema struct { // ReceptionistList 获取接待人员列表 func (r *Client) ReceptionistList(kfID string) (info ReceptionistListSchema, err error) { - data, err := util.HttpGet(fmt.Sprintf(receptionistListAddr, r.accessToken, kfID)) + target := fmt.Sprintf(receptionistListAddr, r.accessToken, kfID) + r.recordUpdate(target) + + data, err := util.HttpGet(target) if err != nil { return info, err } diff --git a/servicestate.go b/servicestate.go index 25adc8e..c2402f8 100644 --- a/servicestate.go +++ b/servicestate.go @@ -33,7 +33,10 @@ type ServiceStateGetSchema struct { //3 由人工接待 人工接待中。可选择结束会话 //4 已结束 会话已经结束。不允许变更会话状态,等待用户重新发起咨询 func (r *Client) ServiceStateGet(options ServiceStateGetOptions) (info ServiceStateGetSchema, err error) { - data, err := util.HttpPost(fmt.Sprintf(serviceStateGetAddr, r.accessToken), options) + target := fmt.Sprintf(serviceStateGetAddr, r.accessToken) + r.recordUpdate(target) + + data, err := util.HttpPost(target, options) if err != nil { return info, err } @@ -54,7 +57,10 @@ type ServiceStateTransOptions struct { // ServiceStateTrans 变更会话状态 func (r *Client) ServiceStateTrans(options ServiceStateTransOptions) (info BaseModel, err error) { - data, err := util.HttpPost(fmt.Sprintf(serviceStateTransAddr, r.accessToken), options) + target := fmt.Sprintf(serviceStateTransAddr, r.accessToken) + r.recordUpdate(target) + + data, err := util.HttpPost(target, options) if err != nil { return info, err } diff --git a/syncmsg.go b/syncmsg.go index 75a1fd6..9073ae0 100644 --- a/syncmsg.go +++ b/syncmsg.go @@ -40,7 +40,10 @@ type SyncMsgSchema struct { // SyncMsg 获取消息 func (r *Client) SyncMsg(options SyncMsgOptions) (info SyncMsgSchema, err error) { - data, err := util.HttpPost(fmt.Sprintf(syncMsgAddr, r.accessToken), options) + target := fmt.Sprintf(syncMsgAddr, r.accessToken) + r.recordUpdate(target) + + data, err := util.HttpPost(target, options) if err != nil { return info, err } diff --git a/token.go b/token.go index 6cccf9b..0d19e57 100644 --- a/token.go +++ b/token.go @@ -20,7 +20,10 @@ type AccessTokenSchema struct { // GetAccessToken 获取调用凭证access_token func (r *Client) GetAccessToken() (info AccessTokenSchema, err error) { - data, err := util.HttpGet(fmt.Sprintf(getTokenAddr, r.corpID, r.secret)) + target := fmt.Sprintf(getTokenAddr, r.corpID, r.secret) + r.recordUpdate(target) + + data, err := util.HttpGet(target) if err != nil { return info, err } diff --git a/upgrade.go b/upgrade.go index 69383ae..ef578f0 100644 --- a/upgrade.go +++ b/upgrade.go @@ -57,7 +57,10 @@ type UpgradeServiceOptions struct { // UpgradeService 为客户升级为专员或客户群服务 func (r *Client) UpgradeService(options UpgradeServiceOptions) (info BaseModel, err error) { - data, err := util.HttpPost(fmt.Sprintf(upgradeService, r.accessToken), options) + target := fmt.Sprintf(upgradeService, r.accessToken) + r.recordUpdate(target) + + data, err := util.HttpPost(target, options) if err != nil { return info, err } @@ -81,7 +84,10 @@ type UpgradeMemberServiceOptions struct { // UpgradeMemberService 为客户升级为专员服务 func (r *Client) UpgradeMemberService(options UpgradeMemberServiceOptions) (info BaseModel, err error) { - data, err := util.HttpPost(fmt.Sprintf(upgradeService, r.accessToken), options) + target := fmt.Sprintf(upgradeService, r.accessToken) + r.recordUpdate(target) + + data, err := util.HttpPost(target, options) if err != nil { return info, err } @@ -105,7 +111,10 @@ type UpgradeServiceGroupChatOptions struct { // UpgradeGroupChatService 为客户升级为客户群服务 func (r *Client) UpgradeGroupChatService(options UpgradeServiceGroupChatOptions) (info BaseModel, err error) { - data, err := util.HttpPost(fmt.Sprintf(upgradeService, r.accessToken), options) + target := fmt.Sprintf(upgradeService, r.accessToken) + r.recordUpdate(target) + + data, err := util.HttpPost(target, options) if err != nil { return info, err } @@ -124,7 +133,10 @@ type UpgradeServiceCancelOptions struct { // UpgradeServiceCancel 为客户取消推荐 func (r *Client) UpgradeServiceCancel(options UpgradeServiceCancelOptions) (info BaseModel, err error) { - data, err := util.HttpPost(fmt.Sprintf(upgradeServiceCancel, r.accessToken), options) + target := fmt.Sprintf(upgradeServiceCancel, r.accessToken) + r.recordUpdate(target) + + data, err := util.HttpPost(target, options) if err != nil { return info, err } diff --git a/util/util.go b/util/util.go index 1b4991c..cc6e300 100644 --- a/util/util.go +++ b/util/util.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "mime/multipart" "net/http" + "net/url" "strconv" ) @@ -112,3 +113,12 @@ func HttpPostOriginFile(path, fileName string, size int, body []byte) ([]byte, e } return ioutil.ReadAll(resp.Body) } + +// ParseRoute 解析路由 +func ParseRoute(path string) string { + route, err := url.ParseRequestURI(path) + if err != nil { + return "" + } + return route.Path +} \ No newline at end of file