analyze_contact_field.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. package wechat
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "github.com/google/uuid"
  7. "github.com/zeromicro/go-zero/core/logx"
  8. "strconv"
  9. "strings"
  10. "time"
  11. "wechat-api/ent/contact"
  12. "wechat-api/ent/contactfield"
  13. "wechat-api/ent/contactfieldtemplate"
  14. "wechat-api/ent/custom_types"
  15. "wechat-api/ent/predicate"
  16. "wechat-api/ent/usagedetail"
  17. "wechat-api/ent/wx"
  18. "wechat-api/internal/svc"
  19. "wechat-api/internal/types"
  20. "wechat-api/internal/utils/compapi"
  21. )
  22. type ResponseItem struct {
  23. DataIndex string `json:"dataIndex"`
  24. Value []string `json:"value"`
  25. }
  26. type FieldPropsOptions struct {
  27. Label string `json:"label"`
  28. Value string `json:"value"`
  29. }
  30. type FieldProps struct {
  31. Options []FieldPropsOptions `json:"options"`
  32. }
  33. type FormData struct {
  34. Title string `json:"title"`
  35. DataIndex string `json:"dataIndex"`
  36. ValueType string `json:"valueType"`
  37. FieldProps FieldProps `json:"fieldProps"`
  38. }
  39. type AnalyzeContactField struct {
  40. ctx context.Context
  41. svcCtx *svc.ServiceContext
  42. }
  43. func NewAnalyzeContactField(ctx context.Context, svcCtx *svc.ServiceContext) *AnalyzeContactField {
  44. return &AnalyzeContactField{
  45. ctx: ctx,
  46. svcCtx: svcCtx,
  47. }
  48. }
  49. func (l *AnalyzeContactField) Analyze(bot_wxid *string, contact_wxid *string) {
  50. usageDetails := make(map[string]map[string]string)
  51. contactFieldTemplates := make(map[string][]custom_types.ContactFieldTemplate)
  52. template_type_text := "text"
  53. template_type_radio := "radio"
  54. //template_type_date := "date"
  55. template_sex_id := "sex"
  56. template_sex_label := "性别"
  57. template_sex_options_man_label := "男"
  58. template_sex_options_man_value := "男"
  59. template_sex_options_woman_label := "女"
  60. template_sex_options_woman_value := "男"
  61. template_phone_id := "phone"
  62. template_phone_label := "手机号"
  63. //template_name_id := "name"
  64. //template_name_label := "姓名"
  65. //
  66. //template_age_id := "age"
  67. //template_age_label := "年龄(以字符串形式返回阿拉伯数字)"
  68. //
  69. //template_area_id := "area"
  70. //template_area_label := "地区"
  71. //
  72. //template_birthday_id := "birthday"
  73. //template_birthday_label := "出生日期"
  74. //
  75. //template_birtharea_id := "birtharea"
  76. //template_birtharea_label := "出生地"
  77. //
  78. //template_idcard_no_id := "idcard_no"
  79. //template_idcard_no_label := "身份证号"
  80. //
  81. //template_title_id := "title"
  82. //template_title_label := "称呼"
  83. contactBasicFieldTemplates := []custom_types.ContactFieldTemplate{
  84. {
  85. Label: &template_sex_label,
  86. Id: &template_sex_id,
  87. Type: &template_type_radio,
  88. Options: []custom_types.ContactFieldTemplateOptions{
  89. {
  90. Label: &template_sex_options_man_label,
  91. Value: &template_sex_options_man_value,
  92. }, {
  93. Label: &template_sex_options_woman_label,
  94. Value: &template_sex_options_woman_value,
  95. },
  96. },
  97. },
  98. {
  99. Label: &template_phone_label,
  100. Id: &template_phone_id,
  101. Type: &template_type_text,
  102. },
  103. //{
  104. // Label: &template_name_label,
  105. // Id: &template_name_id,
  106. // Type: &template_type_text,
  107. //}, {
  108. // Label: &template_age_label,
  109. // Id: &template_age_id,
  110. // Type: &template_type_text,
  111. //}, {
  112. // Label: &template_area_label,
  113. // Id: &template_area_id,
  114. // Type: &template_type_text,
  115. //}, {
  116. // Label: &template_birthday_label,
  117. // Id: &template_birthday_id,
  118. // Type: &template_type_date,
  119. //}, {
  120. // Label: &template_birtharea_label,
  121. // Id: &template_birtharea_id,
  122. // Type: &template_type_text,
  123. //}, {
  124. // Label: &template_idcard_no_label,
  125. // Id: &template_idcard_no_id,
  126. // Type: &template_type_text,
  127. //}, {
  128. // Label: &template_title_label,
  129. // Id: &template_title_id,
  130. // Type: &template_type_text,
  131. //},
  132. }
  133. var predicates []predicate.UsageDetail
  134. predicates = append(predicates, usagedetail.TypeIn(1, 3, 4, 6))
  135. predicates = append(predicates, usagedetail.AppIn(1, 3, 4, 5))
  136. //yesterdayStart := time.Now().AddDate(0, 0, -1).Truncate(24 * time.Hour)
  137. //yesterdayEnd := yesterdayStart.Add(24 * time.Hour)
  138. loc, _ := time.LoadLocation("Asia/Shanghai")
  139. now := time.Now().In(loc)
  140. yesterdayEnd := now.Add(-8 * time.Hour)
  141. yesterdayStart := now.Add(-32 * time.Hour)
  142. //yesterdayEnd := now.Truncate(24 * time.Hour).Add(-16 * time.Hour)
  143. //yesterdayStart := yesterdayEnd.AddDate(0, 0, -1)
  144. predicates = append(predicates, usagedetail.CreatedAtGTE(yesterdayStart))
  145. predicates = append(predicates, usagedetail.CreatedAtLT(yesterdayEnd))
  146. if bot_wxid != nil && *bot_wxid != "" {
  147. predicates = append(predicates, usagedetail.BotIDEQ(*bot_wxid))
  148. }
  149. if bot_wxid != nil && *bot_wxid != "" && contact_wxid != nil && *contact_wxid != "" {
  150. predicates = append(predicates, usagedetail.BotIDEQ(*bot_wxid))
  151. predicates = append(predicates, usagedetail.ReceiverIDEQ(*contact_wxid))
  152. }
  153. //todayStart := time.Now().AddDate(0, 0, 0).Truncate(24 * time.Hour)
  154. //todayEnd := todayStart.Add(24 * time.Hour)
  155. //predicates = append(predicates, usagedetail.CreatedAtGTE(todayStart))
  156. //predicates = append(predicates, usagedetail.CreatedAtLT(todayEnd))
  157. data, err := l.svcCtx.DB.UsageDetail.Query().Where(predicates...).All(l.ctx)
  158. if err != nil {
  159. return
  160. }
  161. var wxPredicates []predicate.Wx
  162. if bot_wxid != nil && *bot_wxid != "" {
  163. wxPredicates = append(wxPredicates, wx.WxidEQ(*bot_wxid))
  164. }
  165. wxs, err := l.svcCtx.DB.Wx.Query().Where(wxPredicates...).All(l.ctx)
  166. if err != nil {
  167. return
  168. }
  169. logx.Info("data: ", data)
  170. logx.Info("wxs: ", wxs)
  171. for _, wx := range wxs {
  172. c, _ := l.svcCtx.DB.ContactFieldTemplate.Query().Where(contactfieldtemplate.OrganizationID(wx.OrganizationID)).First(l.ctx)
  173. if c != nil {
  174. contactFieldTemplates[wx.Wxid] = c.Template
  175. } else {
  176. contactFieldTemplates[wx.Wxid] = nil
  177. }
  178. }
  179. for _, u := range data {
  180. if contactFieldTemplates[u.BotID] == nil {
  181. continue
  182. }
  183. if _, ok := usageDetails[u.BotID]; !ok {
  184. usageDetails[u.BotID] = make(map[string]string)
  185. }
  186. usageDetails[u.BotID][u.ReceiverID] += fmt.Sprintf("用户:%s\n机器人:%s\n", u.Request, u.Response)
  187. }
  188. logx.Info("usageDetails: ", usageDetails)
  189. for botID, template := range contactFieldTemplates {
  190. if template == nil {
  191. template = contactBasicFieldTemplates
  192. } else {
  193. template = append(template, contactBasicFieldTemplates...)
  194. }
  195. for receiverID, messages := range usageDetails[botID] {
  196. logx.Info("messages: ", messages)
  197. logx.Info("template: ", template)
  198. result, _ := l.openaiRequest(messages, template)
  199. logx.Info("result: ", result)
  200. if result == nil {
  201. continue
  202. }
  203. _ = l.UpdateContactFields(botID, receiverID, result)
  204. }
  205. }
  206. }
  207. func (l *AnalyzeContactField) openaiRequest(messages string, template []custom_types.ContactFieldTemplate) ([]ResponseItem, error) {
  208. formData := ConvertFormData(template)
  209. jsonBytes, err := json.Marshal(formData)
  210. if err != nil {
  211. return nil, err
  212. }
  213. jsonStr := string(jsonBytes)
  214. logx.Info("contactFieldTemplates: ", jsonStr)
  215. req := &types.CompApiReq{
  216. types.CompCtlReq{
  217. "form",
  218. "",
  219. false,
  220. "",
  221. },
  222. types.StdCompApiReq{
  223. "gpt-4o",
  224. []types.StdCompMessage{},
  225. false,
  226. nil,
  227. },
  228. types.FastGptSpecReq{
  229. "",
  230. "",
  231. "",
  232. false,
  233. map[string]string{
  234. "form_data": jsonStr,
  235. "chat_history": messages,
  236. "external_id": uuid.New().String(),
  237. },
  238. },
  239. }
  240. resp, err := compapi.NewClient(l.ctx, compapi.WithApiBase("http://new-api.gkscrm.com/v1/"),
  241. compapi.WithApiKey("sk-wwttAtdLcTfeF7F2Eb9d3592Bd4c487f8e8fA544D6C4BbA9")).
  242. Chat(req)
  243. logx.Info("resp: ", resp)
  244. if err == nil && resp != nil && len(resp.Choices) > 0 {
  245. logx.Info("resp.Choices: ", resp.Choices[0].Message.Content)
  246. // 尝试第一层解析成 string
  247. items, err := parseContent(resp.Choices[0].Message.Content)
  248. if err != nil {
  249. return nil, err
  250. }
  251. return items, nil
  252. } else if resp != nil && len(resp.Choices) == 0 {
  253. return nil, err
  254. }
  255. //url := "https://toolsapi-debug.gkscrm.com/call_center/form/extract"
  256. //bodyData := map[string]interface{}{
  257. // "form_data": ConvertFormData(template),
  258. // "chat_history": messages,
  259. // "external_id": uuid.New().String(),
  260. //}
  261. //logx.Info("bodyData: %+v", bodyData)
  262. //bodyBytes, err := json.Marshal(bodyData)
  263. //if err != nil {
  264. // return nil, err
  265. //}
  266. //
  267. //req, err := http.NewRequest("POST", url, bytes.NewBuffer(bodyBytes))
  268. //if err != nil {
  269. // return nil, err
  270. //}
  271. //req.Header.Set("Content-Type", "application/json")
  272. //req.Header.Set("Authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIn0.ZS9jnsLPCnmc8L_lu4yaQFp34vwWF1mHlHSBYrY5JVs")
  273. //
  274. //client := &http.Client{}
  275. //resp, err := client.Do(req)
  276. //if err != nil || resp == nil || resp.Body == nil {
  277. // logx.Error("read body error: ", err)
  278. // return nil, err
  279. //}
  280. //
  281. //logx.Info("err: ", err)
  282. //if err != nil {
  283. // return nil, err
  284. //}
  285. //defer resp.Body.Close()
  286. //
  287. //if resp.StatusCode != http.StatusOK {
  288. // return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
  289. //}
  290. //
  291. ////var result []ResponseItem
  292. //var fullResp struct {
  293. // Data []ResponseItem `json:"data"`
  294. //}
  295. //err = json.NewDecoder(resp.Body).Decode(&fullResp)
  296. //if err != nil {
  297. // return nil, err
  298. //}
  299. //
  300. //return fullResp.Data, nil
  301. return nil, err
  302. }
  303. func (l *AnalyzeContactField) UpdateContactFields(botID string, receiverID string, fields []ResponseItem) error {
  304. basic_ids := []string{"sex", "phone", "name", "age", "area", "birthday", "birtharea", "idcard_no", "title"}
  305. c, _ := l.svcCtx.DB.Contact.Query().Where(contact.WxWxidEQ(botID), contact.WxidEQ(receiverID)).First(l.ctx)
  306. if c == nil {
  307. return fmt.Errorf("Contact not find")
  308. }
  309. for _, field := range fields {
  310. if contains(basic_ids, field.DataIndex) {
  311. if len(field.Value) == 0 {
  312. continue
  313. }
  314. value := 0
  315. if field.DataIndex == "sex" && c.Sex == 0 {
  316. if field.Value[0] == "男" {
  317. value = 1
  318. } else if field.Value[0] == "女" {
  319. value = 2
  320. }
  321. _, err := l.svcCtx.DB.Contact.Update().
  322. Where(contact.WxWxidEQ(botID), contact.WxidEQ(receiverID)).
  323. SetSex(value).
  324. Save(l.ctx)
  325. if err != nil {
  326. continue
  327. }
  328. } else if field.DataIndex == "phone" && c.Phone == "" {
  329. _, err := l.svcCtx.DB.Contact.Update().
  330. Where(contact.WxWxidEQ(botID), contact.WxidEQ(receiverID)).
  331. SetPhone(field.Value[0]).
  332. Save(l.ctx)
  333. if err != nil {
  334. continue
  335. }
  336. } else if field.DataIndex == "name" && c.Cname == "" {
  337. _, err := l.svcCtx.DB.Contact.Update().
  338. Where(contact.WxWxidEQ(botID), contact.WxidEQ(receiverID)).
  339. SetCname(field.Value[0]).
  340. Save(l.ctx)
  341. if err != nil {
  342. continue
  343. }
  344. } else if field.DataIndex == "age" && c.Cage == 0 {
  345. num, err := strconv.Atoi(field.Value[0])
  346. if err != nil {
  347. continue
  348. }
  349. _, err = l.svcCtx.DB.Contact.Update().
  350. Where(contact.WxWxidEQ(botID), contact.WxidEQ(receiverID)).
  351. SetCage(num).
  352. Save(l.ctx)
  353. if err != nil {
  354. continue
  355. }
  356. } else if field.DataIndex == "area" && c.Carea == "" {
  357. _, err := l.svcCtx.DB.Contact.Update().
  358. Where(contact.WxWxidEQ(botID), contact.WxidEQ(receiverID)).
  359. SetCarea(field.Value[0]).
  360. Save(l.ctx)
  361. if err != nil {
  362. continue
  363. }
  364. } else if field.DataIndex == "birthday" && c.Cbirthday == "" {
  365. _, err := l.svcCtx.DB.Contact.Update().
  366. Where(contact.WxWxidEQ(botID), contact.WxidEQ(receiverID)).
  367. SetCbirthday(field.Value[0]).
  368. Save(l.ctx)
  369. if err != nil {
  370. continue
  371. }
  372. } else if field.DataIndex == "birtharea" && c.Cbirtharea == "" {
  373. _, err := l.svcCtx.DB.Contact.Update().
  374. Where(contact.WxWxidEQ(botID), contact.WxidEQ(receiverID)).
  375. SetCbirtharea(field.Value[0]).
  376. Save(l.ctx)
  377. if err != nil {
  378. continue
  379. }
  380. } else if field.DataIndex == "idcard_no" && c.CidcardNo == "" {
  381. _, err := l.svcCtx.DB.Contact.Update().
  382. Where(contact.WxWxidEQ(botID), contact.WxidEQ(receiverID)).
  383. SetCidcardNo(field.Value[0]).
  384. Save(l.ctx)
  385. if err != nil {
  386. continue
  387. }
  388. } else if field.DataIndex == "title" && c.Ctitle == "" {
  389. _, err := l.svcCtx.DB.Contact.Update().
  390. Where(contact.WxWxidEQ(botID), contact.WxidEQ(receiverID)).
  391. SetCtitle(field.Value[0]).
  392. Save(l.ctx)
  393. if err != nil {
  394. continue
  395. }
  396. }
  397. } else {
  398. f, _ := l.svcCtx.DB.ContactField.Query().Where(contactfield.ContactID(c.ID), contactfield.FormID(field.DataIndex)).First(l.ctx)
  399. if f == nil {
  400. if field.Value != nil && len(field.Value) > 0 && field.Value[0] != "" {
  401. _, err := l.svcCtx.DB.ContactField.Create().
  402. SetContactID(c.ID).
  403. SetFormID(field.DataIndex).
  404. SetValue(field.Value).
  405. Save(l.ctx)
  406. if err != nil {
  407. continue
  408. }
  409. }
  410. } else if len(f.Value) == 0 || f.Value[0] == "" {
  411. if field.Value != nil && len(field.Value) > 0 && field.Value[0] != "" {
  412. _, err := l.svcCtx.DB.ContactField.UpdateOneID(f.ID).
  413. SetValue(field.Value).
  414. Save(l.ctx)
  415. if err != nil {
  416. continue
  417. }
  418. }
  419. }
  420. }
  421. }
  422. return nil
  423. }
  424. func ConvertFormData(input []custom_types.ContactFieldTemplate) []FormData {
  425. result := make([]FormData, len(input))
  426. for i, item := range input {
  427. options := make([]FieldPropsOptions, len(item.Options))
  428. for j, opt := range item.Options {
  429. options[j] = FieldPropsOptions{
  430. Label: *opt.Label,
  431. Value: *opt.Value,
  432. }
  433. }
  434. result[i] = FormData{
  435. Title: *item.Label,
  436. DataIndex: *item.Id,
  437. ValueType: *item.Type,
  438. FieldProps: FieldProps{
  439. Options: options,
  440. },
  441. }
  442. }
  443. return result
  444. }
  445. func contains(strs []string, target string) bool {
  446. for _, s := range strs {
  447. if s == target {
  448. return true
  449. }
  450. }
  451. return false
  452. }
  453. func parseContent(content string) ([]ResponseItem, error) {
  454. content = strings.TrimSpace(content)
  455. // ① 双引号包裹的再来一次:"{\"dataIndex\":...}"
  456. if unq, err := strconv.Unquote(content); err == nil {
  457. return parseContent(unq) // 尝试递归
  458. }
  459. // ② 直接数组形式: [...]
  460. if strings.HasPrefix(content, "[") {
  461. var list []ResponseItem
  462. if err := json.Unmarshal([]byte(content), &list); err == nil {
  463. return list, nil
  464. }
  465. }
  466. // ③ 对象形式
  467. if strings.HasPrefix(content, "{") {
  468. // 3‑1 尝试对象包裹数组: {"values":[...]}
  469. var wrapper struct {
  470. Values []ResponseItem `json:"values"`
  471. }
  472. if err := json.Unmarshal([]byte(content), &wrapper); err == nil && len(wrapper.Values) > 0 {
  473. return wrapper.Values, nil
  474. }
  475. // 3‑2 尝试单对象形式: {"dataIndex": ...}
  476. var item ResponseItem
  477. if err := json.Unmarshal([]byte(content), &item); err == nil {
  478. return []ResponseItem{item}, nil
  479. }
  480. }
  481. return nil, fmt.Errorf("unsupported content format: %q", content)
  482. }