| @@ -4,6 +4,7 @@ import ( | |||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
| "encoding/json" | "encoding/json" | ||||
| "errors" | |||||
| "fmt" | "fmt" | ||||
| "github.com/go-resty/resty/v2" | "github.com/go-resty/resty/v2" | ||||
| "strconv" | "strconv" | ||||
| @@ -19,6 +20,7 @@ const ( | |||||
| ACCESS_TOKEN_PATH = "/cgi-bin/token" | ACCESS_TOKEN_PATH = "/cgi-bin/token" | ||||
| QR_CODE_PATH = "/cgi-bin/qrcode/create" | QR_CODE_PATH = "/cgi-bin/qrcode/create" | ||||
| GET_MATERIAL_PATH = "/cgi-bin/material/batchget_material" | GET_MATERIAL_PATH = "/cgi-bin/material/batchget_material" | ||||
| SEND_TEMPLATE_PATH = "/cgi-bin/message/template/send" | |||||
| ACTION_QR_STR_SCENE = "QR_STR_SCENE" | ACTION_QR_STR_SCENE = "QR_STR_SCENE" | ||||
| ERR_CODE_ACCESSTOKEN_EXPIRE = 42001 | ERR_CODE_ACCESSTOKEN_EXPIRE = 42001 | ||||
| @@ -41,12 +43,33 @@ type QRCodeRequest struct { | |||||
| Action_info ActionInfo `json:"action_info"` | Action_info ActionInfo `json:"action_info"` | ||||
| Expire_seconds int `json:"expire_seconds"` | Expire_seconds int `json:"expire_seconds"` | ||||
| } | } | ||||
| type MaterialRequest struct { | type MaterialRequest struct { | ||||
| Type string `json:"type"` | Type string `json:"type"` | ||||
| Offset int `json:"offset"` | Offset int `json:"offset"` | ||||
| Count int `json:"count"` | Count int `json:"count"` | ||||
| } | } | ||||
| type TemplateMsgRequest struct { | |||||
| ToUser string `json:"touser"` | |||||
| TemplateId string `json:"template_id"` | |||||
| Url string `json:"url"` | |||||
| ClientMsgId string `json:"client_msg_id"` | |||||
| Data interface{} `json:"data"` | |||||
| } | |||||
| type TemplateValue struct { | |||||
| Value string `json:"value"` | |||||
| Color string `json:"color"` | |||||
| } | |||||
| type CloudbrainTaskData struct { | |||||
| First TemplateValue `json:"first"` | |||||
| Keyword1 TemplateValue `json:"keyword1"` | |||||
| Keyword2 TemplateValue `json:"keyword2"` | |||||
| Keyword3 TemplateValue `json:"keyword3"` | |||||
| Remark TemplateValue `json:"remark"` | |||||
| } | |||||
| type ActionInfo struct { | type ActionInfo struct { | ||||
| Scene Scene `json:"scene"` | Scene Scene `json:"scene"` | ||||
| } | } | ||||
| @@ -161,3 +184,30 @@ func getErrorCodeFromResponse(r *resty.Response) int { | |||||
| c, _ := strconv.Atoi(fmt.Sprint(code)) | c, _ := strconv.Atoi(fmt.Sprint(code)) | ||||
| return c | return c | ||||
| } | } | ||||
| func sendTemplateMsg(req TemplateMsgRequest) (error, bool) { | |||||
| client := getWechatRestyClient() | |||||
| bodyJson, _ := json.Marshal(req) | |||||
| r, err := client.R(). | |||||
| SetHeader("Content-Type", "application/json"). | |||||
| SetQueryParam("access_token", GetWechatAccessToken()). | |||||
| SetBody(bodyJson). | |||||
| Post(setting.WechatApiHost + SEND_TEMPLATE_PATH) | |||||
| if err != nil { | |||||
| log.Error("sendTemplateMsg,e=%v", err) | |||||
| return nil, false | |||||
| } | |||||
| a := r.Body() | |||||
| resultMap := make(map[string]interface{}, 0) | |||||
| json.Unmarshal(a, &resultMap) | |||||
| errcode := resultMap["errcode"] | |||||
| log.Info("sendTemplateMsg,%v", r) | |||||
| if errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_EXPIRE) || errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_INVALID) { | |||||
| return nil, true | |||||
| } | |||||
| if errcode != "" { | |||||
| return errors.New(fmt.Sprintf("sendTemplateMsg error[%s]", errcode)), false | |||||
| } | |||||
| return nil, false | |||||
| } | |||||
| @@ -0,0 +1,110 @@ | |||||
| package wechat | |||||
| import ( | |||||
| "code.gitea.io/gitea/models" | |||||
| "code.gitea.io/gitea/modules/log" | |||||
| "code.gitea.io/gitea/modules/setting" | |||||
| "errors" | |||||
| "fmt" | |||||
| "time" | |||||
| ) | |||||
| type JobOperateType string | |||||
| const ( | |||||
| JobOperateTypeStart JobOperateType = "start" | |||||
| JobOperateTypeStop JobOperateType = "stop" | |||||
| JobOperateTypeFailed JobOperateType = "failed" | |||||
| ) | |||||
| func SendCloudbrainTemplateMsg(operateType JobOperateType, cloudbrain models.Cloudbrain, date time.Time) error { | |||||
| openId := models.GetUserWechatOpenId(cloudbrain.UserID) | |||||
| if openId == "" { | |||||
| return errors.New("Wechat openId not exist") | |||||
| } | |||||
| data := CloudbrainTaskData{ | |||||
| First: TemplateValue{Value: getCloudbrainTemplateTitle(operateType)}, | |||||
| Keyword1: TemplateValue{Value: cloudbrain.DisplayJobName}, | |||||
| Keyword2: TemplateValue{Value: getJobTypeDisplayName(cloudbrain.JobType)}, | |||||
| Keyword3: TemplateValue{Value: date.Format("2006-01-02 15:04:05")}, | |||||
| Remark: TemplateValue{Value: cloudbrain.DisplayJobName}, | |||||
| } | |||||
| req := TemplateMsgRequest{ | |||||
| ToUser: openId, | |||||
| TemplateId: setting.CloudbrainTemplateId, | |||||
| Url: getCloudbrainTemplateUrl(cloudbrain), | |||||
| ClientMsgId: string(operateType) + "_" + fmt.Sprint(cloudbrain.ID), | |||||
| Data: data, | |||||
| } | |||||
| err, retryFlag := sendTemplateMsg(req) | |||||
| if retryFlag { | |||||
| log.Info("retrySendCloudbrainTemplateMsg calling") | |||||
| refreshAccessToken() | |||||
| err, _ = sendTemplateMsg(req) | |||||
| if err != nil { | |||||
| log.Error("SendCloudbrainTemplateMsg err. %v", err) | |||||
| return err | |||||
| } | |||||
| return nil | |||||
| } | |||||
| if err != nil { | |||||
| log.Error("SendCloudbrainTemplateMsg err. %v", err) | |||||
| return err | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func getCloudbrainTemplateUrl(cloudbrain models.Cloudbrain) string { | |||||
| url := setting.AppURL | |||||
| switch cloudbrain.JobType { | |||||
| case string(models.JobTypeDebug): | |||||
| if cloudbrain.ComputeResource == "CPU/GPU" { | |||||
| url += "/cloudbrain/" + fmt.Sprint(cloudbrain.ID) | |||||
| } else { | |||||
| url += "/modelarts/notebook/" + fmt.Sprint(cloudbrain.ID) | |||||
| } | |||||
| case string(models.JobTypeBenchmark): | |||||
| url += "/cloudbrain/benchmark/" + fmt.Sprint(cloudbrain.ID) | |||||
| case string(models.JobTypeTrain): | |||||
| if cloudbrain.Type == models.TypeCloudBrainOne { | |||||
| url += "/cloudbrain/train-job/" + fmt.Sprint(cloudbrain.JobID) | |||||
| } else if cloudbrain.Type == models.TypeCloudBrainTwo { | |||||
| url += "/modelarts/train-job" + fmt.Sprint(cloudbrain.JobID) | |||||
| } else if cloudbrain.Type == models.TypeC2Net { | |||||
| url += "/grampus/train-job/" + fmt.Sprint(cloudbrain.JobID) | |||||
| } | |||||
| case string(models.JobTypeInference): | |||||
| url += "/modelarts/inference-job/" + fmt.Sprint(cloudbrain.JobID) | |||||
| } | |||||
| return url | |||||
| } | |||||
| func getCloudbrainTemplateTitle(operateType JobOperateType) string { | |||||
| var title string | |||||
| switch operateType { | |||||
| case JobOperateTypeStart: | |||||
| title = "您好,您提交的算力资源申请已通过,任务已启动,请您关注运行情况。" | |||||
| case JobOperateTypeFailed: | |||||
| title = "您好,您提交的任务启动失败,您可以通过日志查看失败原因。" | |||||
| case JobOperateTypeStop: | |||||
| title = "您好,您提交的任务已运行结束。" | |||||
| } | |||||
| return title | |||||
| } | |||||
| func getJobTypeDisplayName(jobType string) string { | |||||
| switch jobType { | |||||
| case string(models.JobTypeDebug): | |||||
| return "调试任务" | |||||
| case string(models.JobTypeBenchmark): | |||||
| return "评测任务" | |||||
| case string(models.JobTypeTrain): | |||||
| return "训练任务" | |||||
| case string(models.JobTypeInference): | |||||
| return "推理任务" | |||||
| } | |||||
| return "" | |||||
| } | |||||
| @@ -582,6 +582,9 @@ var ( | |||||
| TreePathOfAutoMsgReply string | TreePathOfAutoMsgReply string | ||||
| TreePathOfSubscribe string | TreePathOfSubscribe string | ||||
| //wechat template msg config | |||||
| CloudbrainTemplateId string | |||||
| //nginx proxy | //nginx proxy | ||||
| PROXYURL string | PROXYURL string | ||||
| RadarMap = struct { | RadarMap = struct { | ||||
| @@ -1440,6 +1443,7 @@ func NewContext() { | |||||
| RefNameOfWechatReply = sec.Key("AUTO_REPLY_REF_NAME").MustString("master") | RefNameOfWechatReply = sec.Key("AUTO_REPLY_REF_NAME").MustString("master") | ||||
| TreePathOfAutoMsgReply = sec.Key("AUTO_REPLY_TREE_PATH").MustString("wechat/auto_reply.json") | TreePathOfAutoMsgReply = sec.Key("AUTO_REPLY_TREE_PATH").MustString("wechat/auto_reply.json") | ||||
| TreePathOfSubscribe = sec.Key("SUBSCRIBE_TREE_PATH").MustString("wechat/subscribe_reply.json") | TreePathOfSubscribe = sec.Key("SUBSCRIBE_TREE_PATH").MustString("wechat/subscribe_reply.json") | ||||
| CloudbrainTemplateId = sec.Key("CLOUDBRAIN_TEMPLATE_ID").MustString("4qtOaImiFnzIrzLxxP99lYc12EYxvtAE1fNqd7fcihw") | |||||
| SetRadarMapConfig() | SetRadarMapConfig() | ||||
| @@ -373,7 +373,6 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { | |||||
| } | } | ||||
| } | } | ||||
| func CloudBrainInferenceJobCreate(ctx *context.Context, form auth.CreateCloudBrainInferencForm) { | func CloudBrainInferenceJobCreate(ctx *context.Context, form auth.CreateCloudBrainInferencForm) { | ||||
| ctx.Data["PageIsCloudBrain"] = true | ctx.Data["PageIsCloudBrain"] = true | ||||
| displayJobName := form.DisplayJobName | displayJobName := form.DisplayJobName | ||||
| @@ -494,6 +493,7 @@ func CloudBrainInferenceJobCreate(ctx *context.Context, form auth.CreateCloudBra | |||||
| ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/inference-job") | ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/inference-job") | ||||
| } | } | ||||
| /** | /** | ||||
| 检查用户传输的参数是否符合专属资源池 | 检查用户传输的参数是否符合专属资源池 | ||||
| */ | */ | ||||