boweniac 3 päivää sitten
vanhempi
commit
96743284b3
2 muutettua tiedostoa jossa 494 lisäystä ja 453 poistoa
  1. 2 453
      crontask/contact_form.go
  2. 492 0
      internal/service/wechat/analyze_contact_field.go

+ 2 - 453
crontask/contact_form.go

@@ -1,22 +1,6 @@
 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"
-)
+import "wechat-api/internal/service/wechat"
 
 type ResponseItem struct {
 	DataIndex string   `json:"dataIndex"`
@@ -40,440 +24,5 @@ type FormData struct {
 }
 
 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, -2)
-	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.WxWxidEQ(botID), 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.WxWxidEQ(botID), 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.WxWxidEQ(botID), 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.WxWxidEQ(botID), 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.WxWxidEQ(botID), 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.WxWxidEQ(botID), 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.WxWxidEQ(botID), 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.WxWxidEQ(botID), 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.WxWxidEQ(botID), 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)
+	wechat.NewAnalyzeContactField(l.ctx, l.svcCtx).Analyze()
 }

+ 492 - 0
internal/service/wechat/analyze_contact_field.go

@@ -0,0 +1,492 @@
+package wechat
+
+import (
+	"context"
+	"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/svc"
+	"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"`
+}
+
+type AnalyzeContactField struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+func NewAnalyzeContactField(ctx context.Context, svcCtx *svc.ServiceContext) *AnalyzeContactField {
+	return &AnalyzeContactField{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+	}
+}
+
+func (l *AnalyzeContactField) 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, -2)
+	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 *AnalyzeContactField) 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 *AnalyzeContactField) 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.WxWxidEQ(botID), 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.WxWxidEQ(botID), 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.WxWxidEQ(botID), 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.WxWxidEQ(botID), 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.WxWxidEQ(botID), 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.WxWxidEQ(botID), 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.WxWxidEQ(botID), 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.WxWxidEQ(botID), 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.WxWxidEQ(botID), 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)
+}