contact_form.go 13 KB

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