From 9f0e014981c7e7d1d6890d0636dd3a84f01c7ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E5=AE=97=E6=A5=A0?= Date: Sun, 29 Jan 2023 14:42:40 +0800 Subject: [PATCH 1/5] 1 --- .../enterprise_wechat/enterprise_test.go | 33 +++++++-- platformapi/enterprise_wechat/wechat_model.go | 1 + platformapi/enterprise_wechat/wechat_msg.go | 73 +++++++++++++++++++ 3 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 platformapi/enterprise_wechat/wechat_msg.go diff --git a/platformapi/enterprise_wechat/enterprise_test.go b/platformapi/enterprise_wechat/enterprise_test.go index fc1410a5..e3dc6a05 100644 --- a/platformapi/enterprise_wechat/enterprise_test.go +++ b/platformapi/enterprise_wechat/enterprise_test.go @@ -3,6 +3,7 @@ package enterprise_wechat import ( "fmt" "git.rosy.net.cn/baseapi" + "git.rosy.net.cn/jx-callback/globals" "go.uber.org/zap" "strconv" "testing" @@ -26,7 +27,6 @@ func init() { } func TestGetToken(t *testing.T) { - api = New("ww9a156bfa070e1857", "JQsEmSTltHhNgdPIT320YJFphiYmRs-YZa-rCBwplss") token, err := api.GetAccessToken() if err != nil { fmt.Println("err=", err) @@ -60,7 +60,6 @@ func TestGetAllDepartmentList(t *testing.T) { // 创建员工 func TestCreateStaff(t *testing.T) { - api = New("ww9a156bfa070e1857", "0jBdCjSmoFiOoHIXyeCK9VbGQ82fVNJZ8uMl6JNN7X4") // 通讯录 err := api.CreateBoss2JxStaff(&CreateBoos2JXStaffReq{ Userid: "jx2022524_6685111", Name: "门店老板", @@ -94,7 +93,6 @@ func TestGetUserByMobile(t *testing.T) { // 创建会话群聊 func TestCreateSession(t *testing.T) { - api = New("ww9a156bfa070e1857", "JQsEmSTltHhNgdPIT320YJFphiYmRs-YZa-rCBwplss") // 小程序 err := api.CreateAppChat(&CreateAppChatParamReq{ UserList: []string{"WangXiao", "LiuLei"}, Name: "刘磊的企业微信群1", @@ -104,7 +102,30 @@ func TestCreateSession(t *testing.T) { fmt.Println("err======", err) } -func TestGetSuiteTicket(t *testing.T) { - api = New("ww9a156bfa070e1857", "JQsEmSTltHhNgdPIT320YJFphiYmRs-YZa-rCBwplss") // 小程序 - +// 发送消息到企业微信 +func TestSendMsg(t *testing.T) { + msg := &EnterpriseSendMsgReq{ + Touser: "LiuLei", + Toparty: "", + Totag: "", + Msgtype: "textcard", + Agentid: 1000005, + Textcard: TextCardObject{ + Title: "异步任务", + Description: "刚刚的同步任务下载链接", + Url: "https://www.baidu.com", + Btntxt: "详情", + }, + EnableIdTrans: 0, + EnableDuplicateCheck: 0, + DuplicateCheckInterval: 0, + } + err := api.SendMsgToUser(msg) + fmt.Println("err === ", err) +} + +// 获取员工信息 +func TestGetUserId(t *testing.T) { + phone, err := api.GetUserIdByMobile("18981810340") + globals.SugarLogger.Debugf("phone := %s,err :=%s", phone, err) } diff --git a/platformapi/enterprise_wechat/wechat_model.go b/platformapi/enterprise_wechat/wechat_model.go index 7cc8c238..27ff1945 100644 --- a/platformapi/enterprise_wechat/wechat_model.go +++ b/platformapi/enterprise_wechat/wechat_model.go @@ -16,6 +16,7 @@ const ( CreateBoosToJxStaff = "cgi-bin/user/create" // 将京西老板创建为企业员工 GetDepartmentUserDetail = "cgi-bin/user/list" // 获取部门用户详细情况 GetUserByMobileUrl = "cgi-bin/user/getuserid" // 通过手机号获取用户id + SendMsgToUser = "cgi-bin/message/send" // 给用户发送消息 EnterpriseTicketInfo = "/suite/receive" // 企业微信服务器会定时(每十分钟)推送ticket ) diff --git a/platformapi/enterprise_wechat/wechat_msg.go b/platformapi/enterprise_wechat/wechat_msg.go new file mode 100644 index 00000000..7da0698e --- /dev/null +++ b/platformapi/enterprise_wechat/wechat_msg.go @@ -0,0 +1,73 @@ +package enterprise_wechat + +import ( + "errors" + "git.rosy.net.cn/baseapi/utils" + "net/http" +) + +const ( + MsgTypeText = "text" // 文本消息 + MsgTypeImg = "image" // 图片消息 + MsgTypeVoice = "voice" // 语音消息 + MsgTypeVideo = "video" // 视频消息 + MsgTypeFile = "file" // 文件消息 + MsgTypeTextCard = "textcard" // 文本卡片消息 + MsgTypeNews = "news" // 图文消息 + MsgTypeMpNews = "mpnews" // 图文消息 + MsgTypeMarkdown = "markdown" // markdown消息 + MsgTypeMiniprogramNotice = "miniprogram_notice" // 小程序通知消息 + MsgTypeTemplateCard = "template_card" // 模板卡片消息(文本型/图文型/按钮型/投票型/多项选择型) +) + +// EnterpriseSendMsgReq 发送企业微信文本卡片消息请求参数 +type EnterpriseSendMsgReq struct { + Touser string `json:"touser"` // 成员ID列表(消息接收者,多个接收者用‘|’分隔,最多支持1000个) + Toparty string `json:"toparty"` // 部门ID列表,多个接收者用‘|’分隔,最多支持100个 + Totag string `json:"totag"` // 标签ID列表,多个接收者用‘|’分隔,最多支持100个 + Msgtype string `json:"msgtype"` // 消息类型,此时固定为:textcard + Agentid int `json:"agentid"` // 企业应用的id + Textcard TextCardObject `json:"textcard"` + EnableIdTrans int `json:"enable_id_trans"` // 表示是否开启id转译,0表示否,1表示是,默认0 + EnableDuplicateCheck int `json:"enable_duplicate_check"` // 表示是否开启重复消息检查,0表示否,1表示是, + DuplicateCheckInterval int `json:"duplicate_check_interval"` // 表示是否重复消息检查的时间间隔,默认1800s,最大不超过4小时 +} + +type TextCardObject struct { + Title string `json:"title"` // 标题,不超过128个字节 + Description string `json:"description"` // 描述,不超过512个字节 + Url string `json:"url"` // 点击后跳转的链接 + Btntxt string `json:"btntxt"` // 按钮文字 +} + +// EnterpriseSendMsgRes 返回参数 +type EnterpriseSendMsgRes struct { + Errcode int `json:"errcode"` // 返回码 + Errmsg string `json:"errmsg"` // 对返回码的文本描述内容 + Invaliduser string `json:"invaliduser"` // 不合法的userid,不区分大小写,统一转为小写 + Invalidparty string `json:"invalidparty"` // 不合法的partyid + Invalidtag string `json:"invalidtag"` // 不合法的标签id + Unlicenseduser string `json:"unlicenseduser"` // 没有基础接口许可(包含已过期)的userid + Msgid string `json:"msgid"` // 消息id,用于撤回应用消息 + ResponseCode string `json:"response_code"` // 仅消息类型为“按钮交互型”,“投票选择型”和“多项选择型”的模板卡片消息返回,应用可使用response_code调用更新模版卡片消息接口,72小时内有效,且只能使用一次 +} + +// SendMsgToUser 将消息发送给企业微信用户 +func (a *API) SendMsgToUser(msg *EnterpriseSendMsgReq) error { + a.CheckAccessTokenExpiresIn() + param := utils.Struct2Map(msg, "", false) + data, err := a.AccessAPI(WeChatBaseApi, SendMsgToUser, http.MethodPost, param) + if err != nil { + return err + } + + result := &EnterpriseSendMsgRes{} + if err := utils.Map2StructByJson(data, result, false); err != nil { + return err + } + if result.Errcode != 0 { + return errors.New(result.Errmsg) + } + + return nil +} From d3b6f4d6d1e303d3273c59e025de2c492abe7e78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E5=AE=97=E6=A5=A0?= Date: Sun, 29 Jan 2023 15:37:58 +0800 Subject: [PATCH 2/5] 1 --- platformapi/enterprise_wechat/wechat_msg.go | 75 ++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/platformapi/enterprise_wechat/wechat_msg.go b/platformapi/enterprise_wechat/wechat_msg.go index 7da0698e..a4a29c73 100644 --- a/platformapi/enterprise_wechat/wechat_msg.go +++ b/platformapi/enterprise_wechat/wechat_msg.go @@ -20,6 +20,8 @@ const ( MsgTypeTemplateCard = "template_card" // 模板卡片消息(文本型/图文型/按钮型/投票型/多项选择型) ) +const EnterpriseAgentid = 1000005 // 企业应用的id + // EnterpriseSendMsgReq 发送企业微信文本卡片消息请求参数 type EnterpriseSendMsgReq struct { Touser string `json:"touser"` // 成员ID列表(消息接收者,多个接收者用‘|’分隔,最多支持1000个) @@ -52,7 +54,7 @@ type EnterpriseSendMsgRes struct { ResponseCode string `json:"response_code"` // 仅消息类型为“按钮交互型”,“投票选择型”和“多项选择型”的模板卡片消息返回,应用可使用response_code调用更新模版卡片消息接口,72小时内有效,且只能使用一次 } -// SendMsgToUser 将消息发送给企业微信用户 +// SendMsgToUser 将消息发送给企业微信用户 msgtype = textcard func (a *API) SendMsgToUser(msg *EnterpriseSendMsgReq) error { a.CheckAccessTokenExpiresIn() param := utils.Struct2Map(msg, "", false) @@ -71,3 +73,74 @@ func (a *API) SendMsgToUser(msg *EnterpriseSendMsgReq) error { return nil } + +// 发送文本消息 +type SendTextMsgReq struct { + Touser string `json:"touser"` + Toparty string `json:"toparty"` + Totag string `json:"totag"` + Msgtype string `json:"msgtype"` + Agentid int `json:"agentid"` + Text struct { + Content string `json:"content"` + } `json:"text"` + Safe int `json:"safe"` + EnableIdTrans int `json:"enable_id_trans"` + EnableDuplicateCheck int `json:"enable_duplicate_check"` + DuplicateCheckInterval int `json:"duplicate_check_interval"` +} + +// SendMsgToUserTypeText 发送文本消息给用户 +func (a *API) SendMsgToUserTypeText(msg *SendTextMsgReq) error { + a.CheckAccessTokenExpiresIn() + param := utils.Struct2Map(msg, "", false) + data, err := a.AccessAPI(WeChatBaseApi, SendMsgToUser, http.MethodPost, param) + if err != nil { + return err + } + + result := &EnterpriseSendMsgRes{} + if err := utils.Map2StructByJson(data, result, false); err != nil { + return err + } + if result.Errcode != 0 { + return errors.New(result.Errmsg) + } + + return nil +} + +// 发送文本消息 +type SendMarkdownMsgReq struct { + Touser string `json:"touser"` + Toparty string `json:"toparty"` + Totag string `json:"totag"` + Msgtype string `json:"msgtype"` + Agentid int `json:"agentid"` + Markdown struct { + Content string `json:"content"` + } `json:"markdown"` + Safe int `json:"safe"` + EnableIdTrans int `json:"enable_id_trans"` + EnableDuplicateCheck int `json:"enable_duplicate_check"` + DuplicateCheckInterval int `json:"duplicate_check_interval"` +} + +// SendMsgToUserMarkdown 发送makedown消息 +func (a *API) SendMsgToUserMarkdown(msg *SendMarkdownMsgReq) error { + a.CheckAccessTokenExpiresIn() + param := utils.Struct2Map(msg, "", false) + data, err := a.AccessAPI(WeChatBaseApi, SendMsgToUser, http.MethodPost, param) + if err != nil { + return err + } + + result := &EnterpriseSendMsgRes{} + if err := utils.Map2StructByJson(data, result, false); err != nil { + return err + } + if result.Errcode != 0 { + return errors.New(result.Errmsg) + } + return err +} From 2dcecd6ff0a97ab44abc8d4de7cf6775c0576841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E5=AE=97=E6=A5=A0?= Date: Sun, 29 Jan 2023 16:47:34 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E5=88=A0=E9=99=A4=E8=B0=83=E8=AF=95?= =?UTF-8?q?=E8=AF=AD=E5=8F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- platformapi/enterprise_wechat/enterprise_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformapi/enterprise_wechat/enterprise_test.go b/platformapi/enterprise_wechat/enterprise_test.go index e3dc6a05..fed76f52 100644 --- a/platformapi/enterprise_wechat/enterprise_test.go +++ b/platformapi/enterprise_wechat/enterprise_test.go @@ -105,7 +105,7 @@ func TestCreateSession(t *testing.T) { // 发送消息到企业微信 func TestSendMsg(t *testing.T) { msg := &EnterpriseSendMsgReq{ - Touser: "LiuLei", + Touser: "HeJiaMeng2", Toparty: "", Totag: "", Msgtype: "textcard", @@ -126,6 +126,6 @@ func TestSendMsg(t *testing.T) { // 获取员工信息 func TestGetUserId(t *testing.T) { - phone, err := api.GetUserIdByMobile("18981810340") + phone, err := api.GetUserIdByMobile("15928865396") globals.SugarLogger.Debugf("phone := %s,err :=%s", phone, err) } From a67c57c92edee5e95b4a9935a3f25f1588e28917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E5=AE=97=E6=A5=A0?= Date: Mon, 30 Jan 2023 11:22:02 +0800 Subject: [PATCH 4/5] 1 --- .../enterprise_wechat/enterprise_test.go | 4 +- platformapi/kuaishou_mini/kuaishou_api.go | 73 +++++++++++++++++++ platformapi/kuaishou_mini/kuaishou_login.go | 8 ++ 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 platformapi/kuaishou_mini/kuaishou_api.go create mode 100644 platformapi/kuaishou_mini/kuaishou_login.go diff --git a/platformapi/enterprise_wechat/enterprise_test.go b/platformapi/enterprise_wechat/enterprise_test.go index fed76f52..e3dc6a05 100644 --- a/platformapi/enterprise_wechat/enterprise_test.go +++ b/platformapi/enterprise_wechat/enterprise_test.go @@ -105,7 +105,7 @@ func TestCreateSession(t *testing.T) { // 发送消息到企业微信 func TestSendMsg(t *testing.T) { msg := &EnterpriseSendMsgReq{ - Touser: "HeJiaMeng2", + Touser: "LiuLei", Toparty: "", Totag: "", Msgtype: "textcard", @@ -126,6 +126,6 @@ func TestSendMsg(t *testing.T) { // 获取员工信息 func TestGetUserId(t *testing.T) { - phone, err := api.GetUserIdByMobile("15928865396") + phone, err := api.GetUserIdByMobile("18981810340") globals.SugarLogger.Debugf("phone := %s,err :=%s", phone, err) } diff --git a/platformapi/kuaishou_mini/kuaishou_api.go b/platformapi/kuaishou_mini/kuaishou_api.go new file mode 100644 index 00000000..b2ad6c3d --- /dev/null +++ b/platformapi/kuaishou_mini/kuaishou_api.go @@ -0,0 +1,73 @@ +package kuaishou_mini + +import ( + "encoding/json" + "fmt" + "git.rosy.net.cn/baseapi/platformapi" + "net/http" + "strings" + "sync" +) + +const ( + // KuaiShouBashUrl 基础域名 + KuaiShouBashUrl = "https://open.kuaishou.com" // 域名 + + // 获取授权信息 + KuaiShouAuthLogin = "oauth2/mp/code2session" // 授权登录 +) + +type API struct { + appSecret string // 应用唯一标识对应的密钥 + appId string // 应用唯一标识 + client *http.Client + config *platformapi.APIConfig + locker sync.RWMutex + msgToken string // accessToken + expiresIn int64 // 过期时间 +} + +func (a *API) GetAppID() string { + return a.appId +} + +func (a *API) GetSecret() string { + return a.appSecret +} + +// New 初始化 +func New(appSecret, appId string, config ...*platformapi.APIConfig) *API { + curConfig := platformapi.DefAPIConfig + if len(config) > 0 { + curConfig = *config[0] + } + return &API{ + appSecret: appSecret, + appId: appId, + client: &http.Client{Timeout: curConfig.ClientTimeout}, + config: &curConfig, + } +} + +// AccessAPI2 发送请求 +func (a *API) AccessAPI2(url string, params map[string]interface{}) (retVal map[string]interface{}, err error) { + data, err := json.Marshal(params) + if err != nil { + return nil, err + } + err = platformapi.AccessPlatformAPIWithRetry(a.client, + func() *http.Request { + request, _ := http.NewRequest(http.MethodPost, url, strings.NewReader(string(data))) + request.Header.Set("Content-Type", "application/x-www-form-urlencoded") + return request + }, + a.config, + func(response *http.Response, bodyStr string, jsonResult1 map[string]interface{}) (errLevel string, err error) { + if jsonResult1 == nil { + return platformapi.ErrLevelRecoverableErr, fmt.Errorf("mapData is nil") + } + retVal = jsonResult1 + return platformapi.ErrLevelSuccess, nil + }) + return retVal, err +} diff --git a/platformapi/kuaishou_mini/kuaishou_login.go b/platformapi/kuaishou_mini/kuaishou_login.go new file mode 100644 index 00000000..9a600fd8 --- /dev/null +++ b/platformapi/kuaishou_mini/kuaishou_login.go @@ -0,0 +1,8 @@ +package kuaishou_mini + +// AuthLoginKuaiShou 快手授权登录 +func (a *API) AuthLoginKuaiShou(jsCode string) { + if a.appId == "" || a.appSecret == "" || jsCode == "" { + return + } +} From fac70ca58a509f0a0f45c072e0a95e80b342c45a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E5=AE=97=E6=A5=A0?= Date: Mon, 30 Jan 2023 14:45:04 +0800 Subject: [PATCH 5/5] 1 --- platformapi/kuaishou_mini/kuaishou_api.go | 13 ++++------ platformapi/kuaishou_mini/kuaishou_login.go | 25 ++++++++++++++++-- platformapi/kuaishou_mini/kuaishou_model.go | 17 +++++++++++++ platformapi/kuaishou_mini/kuaishou_test.go | 28 +++++++++++++++++++++ 4 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 platformapi/kuaishou_mini/kuaishou_model.go create mode 100644 platformapi/kuaishou_mini/kuaishou_test.go diff --git a/platformapi/kuaishou_mini/kuaishou_api.go b/platformapi/kuaishou_mini/kuaishou_api.go index b2ad6c3d..8231e08d 100644 --- a/platformapi/kuaishou_mini/kuaishou_api.go +++ b/platformapi/kuaishou_mini/kuaishou_api.go @@ -1,9 +1,9 @@ package kuaishou_mini import ( - "encoding/json" "fmt" "git.rosy.net.cn/baseapi/platformapi" + "git.rosy.net.cn/baseapi/utils" "net/http" "strings" "sync" @@ -14,7 +14,7 @@ const ( KuaiShouBashUrl = "https://open.kuaishou.com" // 域名 // 获取授权信息 - KuaiShouAuthLogin = "oauth2/mp/code2session" // 授权登录 + KuaiShouAuthLogin = KuaiShouBashUrl + "/oauth2/mp/code2session" // 授权登录 ) type API struct { @@ -51,14 +51,11 @@ func New(appSecret, appId string, config ...*platformapi.APIConfig) *API { // AccessAPI2 发送请求 func (a *API) AccessAPI2(url string, params map[string]interface{}) (retVal map[string]interface{}, err error) { - data, err := json.Marshal(params) - if err != nil { - return nil, err - } + err = platformapi.AccessPlatformAPIWithRetry(a.client, func() *http.Request { - request, _ := http.NewRequest(http.MethodPost, url, strings.NewReader(string(data))) - request.Header.Set("Content-Type", "application/x-www-form-urlencoded") + request, _ := http.NewRequest(http.MethodPost, url, strings.NewReader(utils.Map2URLValues(params).Encode())) + request.Header.Add("Content-Type", "application/x-www-form-urlencoded") return request }, a.config, diff --git a/platformapi/kuaishou_mini/kuaishou_login.go b/platformapi/kuaishou_mini/kuaishou_login.go index 9a600fd8..79bb20b1 100644 --- a/platformapi/kuaishou_mini/kuaishou_login.go +++ b/platformapi/kuaishou_mini/kuaishou_login.go @@ -1,8 +1,29 @@ package kuaishou_mini +import ( + "errors" + "git.rosy.net.cn/baseapi/utils" +) + // AuthLoginKuaiShou 快手授权登录 -func (a *API) AuthLoginKuaiShou(jsCode string) { +func (a *API) AuthLoginKuaiShou(jsCode string) (sessionKey, openId string, err error) { if a.appId == "" || a.appSecret == "" || jsCode == "" { - return + return "", "", err } + + result, err := a.AccessAPI2(KuaiShouAuthLogin, map[string]interface{}{"js_code": jsCode, "app_id": a.appId, "app_secret": a.appSecret}) + if err != nil { + return "", "", err + } + + auth := GetLoginAuth{} + if err := utils.Map2StructByJson(result, &auth, false); err != nil { + return "", "", err + } + + if auth.Error != "" { + return "", "", errors.New(auth.ErrorMsg) + } + + return auth.SessionKey, auth.OpenId, nil } diff --git a/platformapi/kuaishou_mini/kuaishou_model.go b/platformapi/kuaishou_mini/kuaishou_model.go new file mode 100644 index 00000000..2d500d9e --- /dev/null +++ b/platformapi/kuaishou_mini/kuaishou_model.go @@ -0,0 +1,17 @@ +package kuaishou_mini + +// GetLoginAuth 快手授权返回 +type GetLoginAuth struct { + Result int `json:"result"` // 返回数据条数 + Error string `json:"error"` //错误类型 + ErrorMsg string `json:"error_msg"` // 错误消息 + SessionKey string `json:"session_key"` // 会话秘钥 + OpenId string `json:"open_id"` // 用户当前小程序下唯一id + +} + +type ResultInfo struct { + Result int `json:"result"` // 返回数据条数 + Error string `json:"error"` //错误类型 + ErrorMsg string `json:"error_msg"` // 错误消息 +} diff --git a/platformapi/kuaishou_mini/kuaishou_test.go b/platformapi/kuaishou_mini/kuaishou_test.go new file mode 100644 index 00000000..359645cb --- /dev/null +++ b/platformapi/kuaishou_mini/kuaishou_test.go @@ -0,0 +1,28 @@ +package kuaishou_mini + +import ( + "git.rosy.net.cn/baseapi" + "git.rosy.net.cn/jx-callback/globals" + "go.uber.org/zap" + "testing" +) + +var ( + api *API + sugarLogger *zap.SugaredLogger +) + +func init() { + logger, _ := zap.NewDevelopment() + sugarLogger = logger.Sugar() + baseapi.Init(sugarLogger) + api = New("1wShCPqUzhg8W1vcb8OdvA", "ks680887971696897880") +} + +func TestAuthLogin(t *testing.T) { + jscode := "0F937BADB84C739B0C5DAFCACE1DB3BDFC9D83777CAF4A094AD513DD42BE2238" + sessionCode, openId, err := api.AuthLoginKuaiShou(jscode) + globals.SugarLogger.Debugf("session_code := %s", sessionCode) + globals.SugarLogger.Debugf("open_id := %s", openId) + globals.SugarLogger.Debugf("err := %v", err) +}