analyze_contact_field.go 14 KB

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