diff --git a/config/settings.dev.yml b/config/settings.dev.yml index e8ebce91..1f2a4cc8 100644 --- a/config/settings.dev.yml +++ b/config/settings.dev.yml @@ -17,6 +17,11 @@ settings: password: 123456 port: 3306 username: root + dingtalk: + agentid: 1234567890 + appkey: your dingtalk appkey + appsecret: your dingtalk appsecret + enable: false domain: gethost: 1 url: localhost:9527 diff --git a/config/settings.yml b/config/settings.yml index 095296a6..07f997c7 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -17,6 +17,11 @@ settings: password: 123456 port: 3306 username: root + dingtalk: + agentid: 1234567890 + appkey: your dingtalk appkey + appsecret: your dingtalk appsecret + enable: false domain: gethost: 1 url: localhost:9527 diff --git a/pkg/notify/dingtalk/dingtalk.go b/pkg/notify/dingtalk/dingtalk.go new file mode 100644 index 00000000..407d9628 --- /dev/null +++ b/pkg/notify/dingtalk/dingtalk.go @@ -0,0 +1,201 @@ +/* + * @Author: licon licon.ye@gmail.com + * @LastEditors: licon licon.ye@gmail.com + * @FilePath: /pkg/notify/dingtalk/dingtalk.go + * @Description: 发送钉钉第三方应用消息, 需要工单系统内的用户手机号与钉钉上一致 + */ +package dingtalk + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strings" + "time" + "ferry/pkg/logger" + + "github.com/go-redis/redis" + "github.com/spf13/viper" +) + +// var appKey = viper.GetString("settings.dingtalk.appkey") +// var appSecret = viper.GetString("settings.dingtalk.appsecret") +// var agentId = viper.GetInt64("settings.dingtalk.agentid") + +const ( + GetAccessTokenUrl = "https://oapi.dingtalk.com/gettoken" + SendWorkMsgUrl = "https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2" + GetUserInfoByMobileUrl = "https://oapi.dingtalk.com/topapi/v2/user/getbymobile" +) + +type UserDetailInfo struct { + LoginName string `gorm:"login_name" json:"login_name"` + UserId string `gorm:"user_id" json:"user_id"` +} +type TokenResp struct { + Errcode int64 `json:"errcode"` + AccessToken string `json:"access_token"` + Errmsg string `json:"errmsg"` + ExpiresIn int64 `json:"expires_in"` +} + +type MsgBody struct { + Msgtype string `json:"msgtype"` + Markdown struct { + Title string `json:"title"` + Text string `json:"text"` + } `json:"markdown"` +} +type SendFlowMsgBody struct { + AgentId int64 `json:"agent_id"` + UseridList string `json:"userid_list"` + Msg MsgBody `json:"msg"` +} + +type SendFlowResp struct { + Errcode int64 `json:"errcode"` + TaskId int64 `json:"task_id"` + RequestId string `json:"request_id"` +} + +type GetUserByMobileReq struct { + Mobile string `json:"mobile"` +} + +type Result struct { + Userid string `json:"userid"` +} + +type GetUserByMobileResp struct { + Errcode int64 `json:"errcode"` + RequestId string `json:"request_id"` + Result *Result `json:"result"` +} + +func GetDingUidByMobile(token string, req GetUserByMobileReq) string { + b, _ := json.Marshal(req) + resp, err := http.Post(fmt.Sprintf("%s?access_token=%s", GetUserInfoByMobileUrl, token), "application/json", bytes.NewBuffer(b)) + if err != nil { + logger.Info(err) + } + defer resp.Body.Close() + body, _ := ioutil.ReadAll(resp.Body) + var jsonBody GetUserByMobileResp + json.Unmarshal(body, &jsonBody) + if jsonBody.Errcode != 0 { + logger.Info("GetDingUidByMobile error") + return "" + } + if jsonBody.Result != nil { + return jsonBody.Result.Userid + } else { + return "" + } +} + +func GetAccessToken(appKey, appSecret string) string { + res, err := http.Get(fmt.Sprintf("%s?appkey=%s&appsecret=%s", GetAccessTokenUrl, appKey, appSecret)) + if err != nil { + logger.Info(err) + return "" + } + defer res.Body.Close() + body, err := ioutil.ReadAll(res.Body) + if err != nil { + logger.Info(err) + return "" + } + var token TokenResp + json.Unmarshal(body, &token) + if err != nil { + logger.Info(err) + return "" + } + if token.Errcode == 0 { + return token.AccessToken + } else { + logger.Info(token.Errmsg) + return "" + } + +} + +func SendFlowMsg(token string, content SendFlowMsgBody) error { + b, _ := json.Marshal(content) + resp, err := http.Post(fmt.Sprintf("%s?access_token=%s", SendWorkMsgUrl, token), "application/json", bytes.NewBuffer(b)) + if err != nil { + logger.Info(err) + } + defer resp.Body.Close() + body, _ := ioutil.ReadAll(resp.Body) + var jsonBody SendFlowResp + json.Unmarshal(body, &jsonBody) + if jsonBody.Errcode != 0 { + logger.Info(err) + } + return nil + +} + +func SendDingMsg(phoneList []string, url string, msgTitle string, msgCreator string, priority string, createdAt string) { + appKey := viper.GetString("settings.dingtalk.appkey") + appSecret := viper.GetString("settings.dingtalk.appsecret") + agentId := viper.GetInt64("settings.dingtalk.agentid") + + client := redis.NewClient(&redis.Options{ + Addr: viper.GetString("settings.redis.host"), + Password: viper.GetString("settings.redis.pwd"), + DB: 1, // use default DB + }) + + var token string + tokenByte, err := client.Get("accessTokenDingtalk").Result() + + if err != nil { + logger.Info(err) + token = GetAccessToken(appKey, appSecret) + err = client.Set("accessTokenDingtalk", token, 120*time.Minute).Err() + if err != nil { + logger.Info(err) + } + } else { + token = string(tokenByte) + } + var userId string + + var uids []string + var getDingUidByMobileReq GetUserByMobileReq + for i := 0; i < len(phoneList); i++ { + getDingUidByMobileReq.Mobile = phoneList[i] + userId = GetDingUidByMobile(token, getDingUidByMobileReq) + if userId != "" { + uids = append(uids, userId) + } + } + if len(uids) == 0 { + return + } + uidsString := strings.Join(uids, ",") + nowStr := time.Now().Format("2006-01-02 15:04:05") + var content SendFlowMsgBody + content.AgentId = agentId + content.UseridList = uidsString + content.Msg.Msgtype = "markdown" + content.Msg.Markdown.Title = "新工单提醒" + content.Msg.Markdown.Text = "# 你有一个待办工单需要处理: " + + "\n **标题        :** " + msgTitle + " " + + "\n **创建人    :** " + msgCreator + " " + + "\n **优先级    :** " + priority + " " + + "\n **创建时间:** " + createdAt + " " + + "\n **当前时间:** " + nowStr + " " + + "\n [去处理](" + url + ")" + + err = SendFlowMsg(token, content) + if err != nil { + logger.Info(err) + } + logger.Info("send dingtalk notify successfully") + return +} diff --git a/pkg/notify/send.go b/pkg/notify/send.go index a3b18721..f1156d12 100644 --- a/pkg/notify/send.go +++ b/pkg/notify/send.go @@ -4,7 +4,9 @@ import ( "bytes" "ferry/models/system" "ferry/pkg/logger" + "ferry/pkg/notify/dingtalk" "ferry/pkg/notify/email" + "fmt" "text/template" "github.com/spf13/viper" @@ -57,6 +59,7 @@ func (b *BodyData) ParsingTemplate() (err error) { func (b *BodyData) SendNotify() (err error) { var ( emailList []string + phoneList []string ) switch b.Priority { @@ -75,6 +78,7 @@ func (b *BodyData) SendNotify() (err error) { if len(users) > 0 { for _, user := range users { emailList = append(emailList, user.Email) + phoneList = append(phoneList, user.Phone) } err = b.ParsingTemplate() if err != nil { @@ -82,6 +86,11 @@ func (b *BodyData) SendNotify() (err error) { return } go email.SendMail(emailList, b.EmailCcTo, b.Subject, b.Content) + dingtalkEnable := viper.GetBool("settings.dingtalk.enable") + if dingtalkEnable { + url := fmt.Sprintf("%s/#/process/handle-ticket?workOrderId=%d&processId=%d", b.Domain, b.Id, b.ProcessId) + go dingtalk.SendDingMsg(phoneList, url, b.Title, b.Creator, b.PriorityValue, b.CreatedAt) + } } } }