package crontask import ( "encoding/json" "fmt" "github.com/google/uuid" "github.com/zeromicro/go-zero/core/logx" "strconv" "strings" "time" "wechat-api/ent/contact" "wechat-api/ent/contactfield" "wechat-api/ent/contactfieldtemplate" "wechat-api/ent/custom_types" "wechat-api/ent/predicate" "wechat-api/ent/usagedetail" "wechat-api/internal/types" "wechat-api/internal/utils/compapi" ) type ResponseItem struct { DataIndex string `json:"dataIndex"` Value []string `json:"value"` } type FieldPropsOptions struct { Label string `json:"label"` Value string `json:"value"` } type FieldProps struct { Options []FieldPropsOptions `json:"options"` } type FormData struct { Title string `json:"title"` DataIndex string `json:"dataIndex"` ValueType string `json:"valueType"` FieldProps FieldProps `json:"fieldProps"` } func (l *CronTask) analyze() { usageDetails := make(map[string]map[string]string) contactFieldTemplates := make(map[string][]custom_types.ContactFieldTemplate) template_type_text := "text" template_type_radio := "radio" //template_type_date := "date" template_sex_id := "sex" template_sex_label := "性别" template_sex_options_man_label := "男" template_sex_options_man_value := "男" template_sex_options_woman_label := "女" template_sex_options_woman_value := "男" template_phone_id := "phone" template_phone_label := "手机号" //template_name_id := "name" //template_name_label := "姓名" // //template_age_id := "age" //template_age_label := "年龄(以字符串形式返回阿拉伯数字)" // //template_area_id := "area" //template_area_label := "地区" // //template_birthday_id := "birthday" //template_birthday_label := "出生日期" // //template_birtharea_id := "birtharea" //template_birtharea_label := "出生地" // //template_idcard_no_id := "idcard_no" //template_idcard_no_label := "身份证号" // //template_title_id := "title" //template_title_label := "称呼" contactBasicFieldTemplates := []custom_types.ContactFieldTemplate{ { Label: &template_sex_label, Id: &template_sex_id, Type: &template_type_radio, Options: []custom_types.ContactFieldTemplateOptions{ { Label: &template_sex_options_man_label, Value: &template_sex_options_man_value, }, { Label: &template_sex_options_woman_label, Value: &template_sex_options_woman_value, }, }, }, { Label: &template_phone_label, Id: &template_phone_id, Type: &template_type_text, }, //{ // Label: &template_name_label, // Id: &template_name_id, // Type: &template_type_text, //}, { // Label: &template_age_label, // Id: &template_age_id, // Type: &template_type_text, //}, { // Label: &template_area_label, // Id: &template_area_id, // Type: &template_type_text, //}, { // Label: &template_birthday_label, // Id: &template_birthday_id, // Type: &template_type_date, //}, { // Label: &template_birtharea_label, // Id: &template_birtharea_id, // Type: &template_type_text, //}, { // Label: &template_idcard_no_label, // Id: &template_idcard_no_id, // Type: &template_type_text, //}, { // Label: &template_title_label, // Id: &template_title_id, // Type: &template_type_text, //}, } var predicates []predicate.UsageDetail predicates = append(predicates, usagedetail.TypeIn(1, 3, 4, 6)) predicates = append(predicates, usagedetail.AppIn(1, 3, 4, 5)) //yesterdayStart := time.Now().AddDate(0, 0, -1).Truncate(24 * time.Hour) //yesterdayEnd := yesterdayStart.Add(24 * time.Hour) loc, _ := time.LoadLocation("Asia/Shanghai") now := time.Now().In(loc) yesterdayEnd := now.Truncate(24 * time.Hour) yesterdayStart := yesterdayEnd.AddDate(0, 0, -1) predicates = append(predicates, usagedetail.CreatedAtGTE(yesterdayStart)) predicates = append(predicates, usagedetail.CreatedAtLT(yesterdayEnd)) //todayStart := time.Now().AddDate(0, 0, 0).Truncate(24 * time.Hour) //todayEnd := todayStart.Add(24 * time.Hour) //predicates = append(predicates, usagedetail.CreatedAtGTE(todayStart)) //predicates = append(predicates, usagedetail.CreatedAtLT(todayEnd)) data, err := l.svcCtx.DB.UsageDetail.Query().Where(predicates...).All(l.ctx) logx.Info("usageDetails: ", data) if err != nil { return } wxs, err := l.svcCtx.DB.Wx.Query().All(l.ctx) if err != nil { return } for _, wx := range wxs { c, _ := l.svcCtx.DB.ContactFieldTemplate.Query().Where(contactfieldtemplate.OrganizationID(wx.OrganizationID)).First(l.ctx) if c != nil { contactFieldTemplates[wx.Wxid] = c.Template } else { contactFieldTemplates[wx.Wxid] = nil } } for _, u := range data { if contactFieldTemplates[u.BotID] == nil { continue } if _, ok := usageDetails[u.BotID]; !ok { usageDetails[u.BotID] = make(map[string]string) } usageDetails[u.BotID][u.ReceiverID] += fmt.Sprintf("用户:%s\n机器人:%s\n", u.Request, u.Response) } logx.Info("usageDetails: ", usageDetails) for botID, template := range contactFieldTemplates { if template == nil { template = contactBasicFieldTemplates } else { template = append(template, contactBasicFieldTemplates...) } for receiverID, messages := range usageDetails[botID] { result, _ := l.openaiRequest(messages, template) logx.Info("result: ", result) if result == nil { continue } _ = l.UpdateContactFields(botID, receiverID, result) } } } func (l *CronTask) openaiRequest(messages string, template []custom_types.ContactFieldTemplate) ([]ResponseItem, error) { formData := ConvertFormData(template) jsonBytes, err := json.Marshal(formData) if err != nil { return nil, err } jsonStr := string(jsonBytes) logx.Info("contactFieldTemplates: ", jsonStr) req := &types.CompApiReq{ types.CompCtlReq{ "form", "", false, "", }, types.StdCompApiReq{ "gpt-4o", []types.StdCompMessage{}, false, nil, }, types.FastGptSpecReq{ "", "", "", false, map[string]string{ "form_data": jsonStr, "chat_history": messages, "external_id": uuid.New().String(), }, }, } resp, err := compapi.NewClient(l.ctx, compapi.WithApiBase("http://new-api.gkscrm.com/v1/"), compapi.WithApiKey("sk-wwttAtdLcTfeF7F2Eb9d3592Bd4c487f8e8fA544D6C4BbA9")). Chat(req) logx.Info("resp: ", resp) if err == nil && resp != nil && len(resp.Choices) > 0 { logx.Info("resp.Choices: ", resp.Choices[0].Message.Content) // 尝试第一层解析成 string items, err := parseContent(resp.Choices[0].Message.Content) if err != nil { return nil, err } return items, nil } else if resp != nil && len(resp.Choices) == 0 { return nil, err } //url := "https://toolsapi-debug.gkscrm.com/call_center/form/extract" //bodyData := map[string]interface{}{ // "form_data": ConvertFormData(template), // "chat_history": messages, // "external_id": uuid.New().String(), //} //logx.Info("bodyData: %+v", bodyData) //bodyBytes, err := json.Marshal(bodyData) //if err != nil { // return nil, err //} // //req, err := http.NewRequest("POST", url, bytes.NewBuffer(bodyBytes)) //if err != nil { // return nil, err //} //req.Header.Set("Content-Type", "application/json") //req.Header.Set("Authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIn0.ZS9jnsLPCnmc8L_lu4yaQFp34vwWF1mHlHSBYrY5JVs") // //client := &http.Client{} //resp, err := client.Do(req) //if err != nil || resp == nil || resp.Body == nil { // logx.Error("read body error: ", err) // return nil, err //} // //logx.Info("err: ", err) //if err != nil { // return nil, err //} //defer resp.Body.Close() // //if resp.StatusCode != http.StatusOK { // return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) //} // ////var result []ResponseItem //var fullResp struct { // Data []ResponseItem `json:"data"` //} //err = json.NewDecoder(resp.Body).Decode(&fullResp) //if err != nil { // return nil, err //} // //return fullResp.Data, nil return nil, err } func (l *CronTask) UpdateContactFields(botID string, receiverID string, fields []ResponseItem) error { basic_ids := []string{"sex", "phone", "name", "age", "area", "birthday", "birtharea", "idcard_no", "title"} c, _ := l.svcCtx.DB.Contact.Query().Where(contact.WxWxidEQ(botID), contact.WxidEQ(receiverID)).First(l.ctx) if c == nil { return fmt.Errorf("Contact not find") } for _, field := range fields { if contains(basic_ids, field.DataIndex) { if len(field.Value) == 0 { continue } value := 0 if field.DataIndex == "sex" && c.Sex == 0 { if field.Value[0] == "男" { value = 1 } else if field.Value[0] == "女" { value = 2 } _, err := l.svcCtx.DB.Contact.Update(). Where(contact.WxidEQ(receiverID)). SetSex(value). Save(l.ctx) if err != nil { continue } } else if field.DataIndex == "phone" && c.Phone == "" { _, err := l.svcCtx.DB.Contact.Update(). Where(contact.WxidEQ(receiverID)). SetPhone(field.Value[0]). Save(l.ctx) if err != nil { continue } } else if field.DataIndex == "name" && c.Cname == "" { _, err := l.svcCtx.DB.Contact.Update(). Where(contact.WxidEQ(receiverID)). SetCname(field.Value[0]). Save(l.ctx) if err != nil { continue } } else if field.DataIndex == "age" && c.Cage == 0 { num, err := strconv.Atoi(field.Value[0]) if err != nil { continue } _, err = l.svcCtx.DB.Contact.Update(). Where(contact.WxidEQ(receiverID)). SetCage(num). Save(l.ctx) if err != nil { continue } } else if field.DataIndex == "area" && c.Carea == "" { _, err := l.svcCtx.DB.Contact.Update(). Where(contact.WxidEQ(receiverID)). SetCarea(field.Value[0]). Save(l.ctx) if err != nil { continue } } else if field.DataIndex == "birthday" && c.Cbirthday == "" { _, err := l.svcCtx.DB.Contact.Update(). Where(contact.WxidEQ(receiverID)). SetCbirthday(field.Value[0]). Save(l.ctx) if err != nil { continue } } else if field.DataIndex == "birtharea" && c.Cbirtharea == "" { _, err := l.svcCtx.DB.Contact.Update(). Where(contact.WxidEQ(receiverID)). SetCbirtharea(field.Value[0]). Save(l.ctx) if err != nil { continue } } else if field.DataIndex == "idcard_no" && c.CidcardNo == "" { _, err := l.svcCtx.DB.Contact.Update(). Where(contact.WxidEQ(receiverID)). SetCidcardNo(field.Value[0]). Save(l.ctx) if err != nil { continue } } else if field.DataIndex == "title" && c.Ctitle == "" { _, err := l.svcCtx.DB.Contact.Update(). Where(contact.WxidEQ(receiverID)). SetCtitle(field.Value[0]). Save(l.ctx) if err != nil { continue } } } else { f, _ := l.svcCtx.DB.ContactField.Query().Where(contactfield.ContactID(c.ID), contactfield.FormID(field.DataIndex)).First(l.ctx) if f == nil { if field.Value != nil && len(field.Value) > 0 && field.Value[0] != "" { _, err := l.svcCtx.DB.ContactField.Create(). SetContactID(c.ID). SetFormID(field.DataIndex). SetValue(field.Value). Save(l.ctx) if err != nil { continue } } } else if len(f.Value) == 0 || f.Value[0] == "" { if field.Value != nil && len(field.Value) > 0 && field.Value[0] != "" { _, err := l.svcCtx.DB.ContactField.UpdateOneID(f.ID). SetValue(field.Value). Save(l.ctx) if err != nil { continue } } } } } return nil } func ConvertFormData(input []custom_types.ContactFieldTemplate) []FormData { result := make([]FormData, len(input)) for i, item := range input { options := make([]FieldPropsOptions, len(item.Options)) for j, opt := range item.Options { options[j] = FieldPropsOptions{ Label: *opt.Label, Value: *opt.Value, } } result[i] = FormData{ Title: *item.Label, DataIndex: *item.Id, ValueType: *item.Type, FieldProps: FieldProps{ Options: options, }, } } return result } func contains(strs []string, target string) bool { for _, s := range strs { if s == target { return true } } return false } func parseContent(content string) ([]ResponseItem, error) { content = strings.TrimSpace(content) // ① 双引号包裹的再来一次:"{\"dataIndex\":...}" if unq, err := strconv.Unquote(content); err == nil { return parseContent(unq) // 尝试递归 } // ② 直接数组形式: [...] if strings.HasPrefix(content, "[") { var list []ResponseItem if err := json.Unmarshal([]byte(content), &list); err == nil { return list, nil } } // ③ 对象形式 if strings.HasPrefix(content, "{") { // 3‑1 尝试对象包裹数组: {"values":[...]} var wrapper struct { Values []ResponseItem `json:"values"` } if err := json.Unmarshal([]byte(content), &wrapper); err == nil && len(wrapper.Values) > 0 { return wrapper.Values, nil } // 3‑2 尝试单对象形式: {"dataIndex": ...} var item ResponseItem if err := json.Unmarshal([]byte(content), &item); err == nil { return []ResponseItem{item}, nil } } return nil, fmt.Errorf("unsupported content format: %q", content) }