package wxapi import ( "bytes" "encoding/json" "errors" "fmt" "io/ioutil" "net/http" "strings" "sync" "time" ) const ( GetTokenUrl = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET" GetServiceUrl = "https://qyapi.weixin.qq.com/cgi-bin/kf/account/list?access_token=ACCESS_TOKEN" GetServicerUrl = "https://qyapi.weixin.qq.com/cgi-bin/kf/servicer/list?access_token=ACCESS_TOKEN&open_kfid=OPEN_KFID" GetStaffUrl = "https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&userid=USERID" GetCustomerUrl = "https://qyapi.weixin.qq.com/cgi-bin/kf/customer/batchget?access_token=ACCESS_TOKEN" GetMsgUrl = "https://qyapi.weixin.qq.com/cgi-bin/kf/sync_msg?access_token=ACCESS_TOKEN" GetSessionStateUrl = "https://qyapi.weixin.qq.com/cgi-bin/kf/service_state/get?access_token=ACCESS_TOKEN" UpdateSessionStateUrl = "https://qyapi.weixin.qq.com/cgi-bin/kf/service_state/trans?access_token=ACCESS_TOKEN" ) // 自定义封装企业微信的API接口 type WxApi struct { lock sync.Mutex corpid string corpsecret string accessToken string tokenExpiresTime int64 expiresIn int64 } func NewWxApi(corpid, corpsecret string) *WxApi { return &WxApi{corpid: corpid, corpsecret: corpsecret} } // 刷新token func (a *WxApi) refreshAccessToken() error { if a.accessToken == "" || (a.tokenExpiresTime-time.Now().Unix()) < (a.expiresIn/10) { a.lock.Lock() defer a.lock.Unlock() if !((a.tokenExpiresTime - time.Now().Unix()) < (a.expiresIn / 10)) { return nil } url := GetTokenUrl url = strings.ReplaceAll(url, "ID", a.corpid) url = strings.ReplaceAll(url, "SECRET", a.corpsecret) resp, err := http.Get(url) if err != nil { return err } defer resp.Body.Close() bts, err := ioutil.ReadAll(resp.Body) if err != nil { return err } var data TokenResp err = json.Unmarshal(bts, &data) if err != nil { return err } if data.Errcode != 0 { return errors.New(fmt.Sprintf("errcode: %v,errmsg: %v", data.Errcode, data.Errmsg)) } a.accessToken = data.AccessToken a.expiresIn = data.ExpiresIn a.tokenExpiresTime = time.Now().Add(time.Duration(data.ExpiresIn) * time.Second).Unix() } return nil } // GetServiceList 获取客服账号列表 func (a *WxApi) GetServiceList() (err error, list []ServiceAccount) { err = a.refreshAccessToken() if err != nil { return } url := GetServiceUrl url = strings.ReplaceAll(url, "ACCESS_TOKEN", a.accessToken) res, err := http.Post(url, "application/json", strings.NewReader(`{"offset":0,"limit":999}`)) if err != nil { return } defer res.Body.Close() bodyBts, err := ioutil.ReadAll(res.Body) if err != nil { return } var data ServiceListResp err = json.Unmarshal(bodyBts, &data) if err != nil { return } if data.Errcode != 0 { return errors.New(fmt.Sprintf("errcode: %v,errmsg: %v", data.Errcode, data.Errmsg)), nil } list = data.AccountList return } // GetServicerList 获取接待账号列表 func (a *WxApi) GetServicerList(openKfid string) (err error, list []ServicerAccount) { err = a.refreshAccessToken() if err != nil { return } url := GetServicerUrl url = strings.ReplaceAll(url, "ACCESS_TOKEN", a.accessToken) url = strings.ReplaceAll(url, "OPEN_KFID", openKfid) res, err := http.Get(url) if err != nil { return } defer res.Body.Close() bodyBts, err := ioutil.ReadAll(res.Body) if err != nil { return } var data ServicerListResp err = json.Unmarshal(bodyBts, &data) if err != nil { return } if data.Errcode != 0 { return errors.New(fmt.Sprintf("errcode: %v,errmsg: %v", data.Errcode, data.Errmsg)), nil } list = data.ServicerList return } // 读取成员 func (a *WxApi) GetStaffList(userid string) (err error, d Staff) { err = a.refreshAccessToken() if err != nil { return } url := GetStaffUrl url = strings.ReplaceAll(url, "ACCESS_TOKEN", a.accessToken) url = strings.ReplaceAll(url, "USERID", userid) res, err := http.Get(url) if err != nil { return } defer res.Body.Close() bodyBts, err := ioutil.ReadAll(res.Body) if err != nil { return } var data StaffResp err = json.Unmarshal(bodyBts, &data) if err != nil { return } if data.Errcode != 0 { return errors.New(fmt.Sprintf("errcode: %v,errmsg: %v", data.Errcode, data.Errmsg)), Staff{} } d = data.Staff return } // GetCustomerList 获取客户基础信息列表 func (a *WxApi) GetCustomerList(userIds []string) (err error, list []Customer) { err = a.refreshAccessToken() if err != nil { return } url := GetCustomerUrl url = strings.ReplaceAll(url, "ACCESS_TOKEN", a.accessToken) body := CustomerParam{ ExternalUseridList: userIds, NeedEnterSessionContext: 0, } bts, _ := json.Marshal(body) res, err := http.Post(url, "application/json", bytes.NewReader(bts)) if err != nil { return } defer res.Body.Close() bodyBts, err := ioutil.ReadAll(res.Body) if err != nil { return } var data CustomerListResp err = json.Unmarshal(bodyBts, &data) if err != nil { return } if data.Errcode != 0 { return errors.New(fmt.Sprintf("errcode: %v,errmsg: %v", data.Errcode, data.Errmsg)), nil } list = data.CustomerList return } // GetMsgList 读取消息 func (a *WxApi) GetMsgList(cursor, token, openKfid string) (err error, hasMore int, nextCursor string, list []Msg) { err = a.refreshAccessToken() if err != nil { return } url := GetMsgUrl url = strings.ReplaceAll(url, "ACCESS_TOKEN", a.accessToken) body := MsgParam{ Cursor: cursor, Token: token, Limit: 1000, VoiceFormat: 0, OpenKfid: openKfid, } bts, _ := json.Marshal(body) res, err := http.Post(url, "application/json", bytes.NewReader(bts)) if err != nil { return } defer res.Body.Close() bodyBts, err := ioutil.ReadAll(res.Body) if err != nil { return } var data MsgListResp err = json.Unmarshal(bodyBts, &data) if err != nil { return } if data.Errcode != 0 { err = errors.New(fmt.Sprintf("errcode: %v,errmsg: %v", data.Errcode, data.Errmsg)) return } hasMore = data.HasMore nextCursor = data.NextCursor for _, msg := range data.MsgList { var m Msg m.Msgid, _ = msg["msgid"].(string) m.OpenKfid, _ = msg["open_kfid"].(string) m.ExternalUserid, _ = msg["external_userid"].(string) m.SendTime, _ = msg["send_time"].(float64) m.Origin, _ = msg["origin"].(float64) m.ServicerUserid, _ = msg["servicer_userid"].(string) m.Msgtype, _ = msg["msgtype"].(string) if m.Msgtype != "" { content, _ := msg[m.Msgtype] eventBts, _ := json.Marshal(content) m.DataInfo = string(eventBts) if m.Msgtype == "event" { var event = make(map[string]interface{}) _ = json.Unmarshal(eventBts, &event) if _, b := event["event_type"].(string); b { m.OpenKfid, _ = event["open_kfid"].(string) m.ExternalUserid, _ = event["external_userid"].(string) } } } list = append(list, m) } return } // UpdateSessionState 变更会话状态 func (a *WxApi) UpdateSessionState(openKfid, externalUserid, servicerUserid string, serviceState int) (err error) { err = a.refreshAccessToken() if err != nil { return } url := UpdateSessionStateUrl url = strings.ReplaceAll(url, "ACCESS_TOKEN", a.accessToken) body := UpdateSessionStateParam{ OpenKfid: openKfid, ExternalUserid: externalUserid, ServiceState: serviceState, ServicerUserid: servicerUserid, } bts, _ := json.Marshal(body) res, err := http.Post(url, "application/json", bytes.NewReader(bts)) if err != nil { return } defer res.Body.Close() bodyBts, err := ioutil.ReadAll(res.Body) if err != nil { return } var data UpdateSessionStateResp err = json.Unmarshal(bodyBts, &data) if err != nil { return } if data.Errcode != 0 { return errors.New(fmt.Sprintf("errcode: %v,errmsg: %v", data.Errcode, data.Errmsg)) } return } // 获取会话状态 func (a *WxApi) GetSessionState(openKfid, externalUserid string) (err error, d GetSessionStateResp) { err = a.refreshAccessToken() if err != nil { return } url := GetSessionStateUrl url = strings.ReplaceAll(url, "ACCESS_TOKEN", a.accessToken) body := GetSessionStateParam{ OpenKfid: openKfid, ExternalUserid: externalUserid, } bts, _ := json.Marshal(body) res, err := http.Post(url, "application/json", bytes.NewReader(bts)) if err != nil { return } defer res.Body.Close() bodyBts, err := ioutil.ReadAll(res.Body) if err != nil { return } var data GetSessionStateResp err = json.Unmarshal(bodyBts, &data) if err != nil { return } if data.Errcode != 0 { err = errors.New(fmt.Sprintf("errcode: %v,errmsg: %v", data.Errcode, data.Errmsg)) return } d = data return }