talent_platform_link_info.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. package service
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "io/ioutil"
  8. "net/http"
  9. "strconv"
  10. "strings"
  11. )
  12. // RedBookNoteResponse 小红书链接接收结构体
  13. type RedBookNoteResponse struct {
  14. Code int `json:"code"`
  15. Data struct {
  16. ResponseBody struct {
  17. Data struct {
  18. Items []struct {
  19. NoteCard struct {
  20. InteractInfo struct {
  21. LikedCount string `json:"liked_count"` // 点赞数
  22. CommentCount string `json:"comment_count"` // 评论数
  23. CollectedCount string `json:"collected_count"` // 收藏数
  24. ShareCount string `json:"share_count"` // 分享数
  25. } `json:"interact_info"`
  26. } `json:"note_card"`
  27. } `json:"items"`
  28. } `json:"data"`
  29. } `json:"response_body"`
  30. } `json:"data"`
  31. }
  32. // WeiboPostResponse 微博链接接收结构体
  33. type WeiboPostResponse struct {
  34. Code int `json:"code"`
  35. Msg string `json:"msg"`
  36. Data struct {
  37. AttitudesCount int `json:"attitudes_count"` // 点赞数
  38. CommentsCount int `json:"comments_count"` // 评论数
  39. RepostsCount int `json:"reposts_count"` // 转发数
  40. ReadsCount int `json:"reads_count"` // 浏览数
  41. } `json:"data"`
  42. }
  43. // parseNoteStats 小红书接收方法
  44. func parseNoteStats(jsonData []byte) (likes, comments, collects, shares string, err error) {
  45. var resp RedBookNoteResponse
  46. if err := json.Unmarshal(jsonData, &resp); err != nil {
  47. return "", "", "", "", fmt.Errorf("JSON解析失败: %v", err)
  48. }
  49. // 检查数据是否存在
  50. if len(resp.Data.ResponseBody.Data.Items) == 0 {
  51. return "", "", "", "", errors.New("未找到笔记数据")
  52. }
  53. stats := resp.Data.ResponseBody.Data.Items[0].NoteCard.InteractInfo
  54. return stats.LikedCount, stats.CommentCount, stats.CollectedCount, stats.ShareCount, nil
  55. }
  56. // parseWeiboStats 微博接收方法
  57. func parseWeiboStats(jsonData []byte) (likes, comments, reposts, reads string, err error) {
  58. var resp WeiboPostResponse
  59. // 解析JSON
  60. if err := json.Unmarshal(jsonData, &resp); err != nil {
  61. return "", "", "", "", fmt.Errorf("JSON解析失败: %v", err)
  62. }
  63. // 检查状态码(微博通常用 code=200 表示成功)
  64. if resp.Code != 200 {
  65. return "", "", "", "", fmt.Errorf("API返回错误: %s", resp.Msg)
  66. }
  67. // 返回数值(转为字符串,保持与原函数一致)
  68. return strconv.Itoa(resp.Data.AttitudesCount),
  69. strconv.Itoa(resp.Data.CommentsCount),
  70. strconv.Itoa(resp.Data.RepostsCount),
  71. strconv.Itoa(resp.Data.ReadsCount),
  72. nil
  73. }
  74. // GetRedBookLinkDetail 获取小红书链接数据详情
  75. func GetRedBookLinkDetail(ctx context.Context, noteId string, xSecToken string) (int, int, int, int, error) {
  76. url := "http://120.46.92.62:6888/api/xhs/note_detail"
  77. method := "POST"
  78. // 构造请求体结构
  79. requestBody := struct {
  80. NoteId string `json:"note_id"`
  81. XSecToken string `json:"xsec_token"`
  82. ShareText string `json:"share_text"`
  83. Proxy string `json:"proxy"`
  84. }{
  85. NoteId: noteId,
  86. XSecToken: xSecToken,
  87. Proxy: "",
  88. }
  89. jsonData, err := json.Marshal(requestBody)
  90. if err != nil {
  91. fmt.Println("JSON编码失败:", err)
  92. return 0, 0, 0, 0, err
  93. }
  94. payload := strings.NewReader(string(jsonData))
  95. client := &http.Client{}
  96. req, err := http.NewRequest(method, url, payload)
  97. if err != nil {
  98. // fmt.Println(err)
  99. return 0, 0, 0, 0, err
  100. }
  101. req.Header.Add("Cookie", "abRequestId=87d8ec6f-314f-5914-af1d-21296bf34c5f; a1=197a0b8a06djmry91j6taqhe0kcutwojdcu15nypd50000386246; webId=ae01b40eef7780a778b6624a3b295f4f; gid=yjW08S8Y2yffyjW08DY08SIvKfV6FvjyVK907UfAxd8TSy2879ElV2888qYKJ4K82qfKy82D; webBuild=4.68.0; acw_tc=0a4a2d6e17510033969926232e7b39d55058a43fb37e1ddc19745e6642f746; websectiga=7750c37de43b7be9de8ed9ff8ea0e576519e8cd2157322eb972ecb429a7735d4; sec_poison_id=b4fae566-5d78-4269-85c6-28fc28ec20d3; xsecappid=xhs-pc-web; web_session=040069b22467fb3084e74f796b3a4b11424ee0; loadts=1751003476156; unread={%22ub%22:%22685b56850000000020019eef%22%2C%22ue%22:%22685df405000000002201fd00%22%2C%22uc%22:14}")
  102. req.Header.Add("Content-Type", "application/json")
  103. res, err := client.Do(req)
  104. if err != nil {
  105. // fmt.Println(err)
  106. return 0, 0, 0, 0, err
  107. }
  108. defer res.Body.Close()
  109. body, err := ioutil.ReadAll(res.Body)
  110. if err != nil {
  111. // fmt.Println(err)
  112. return 0, 0, 0, 0, err
  113. }
  114. fmt.Println(string(body))
  115. // 提取互动数据
  116. likes, comments, collects, shares, err := parseNoteStats(body)
  117. if err != nil {
  118. //fmt.Println("解析互动数据失败:", err)
  119. return 0, 0, 0, 0, err
  120. }
  121. like, likeErr := strconv.Atoi(likes)
  122. if likeErr != nil {
  123. }
  124. comment, commentErr := strconv.Atoi(comments)
  125. if commentErr != nil {
  126. }
  127. collect, collectErr := strconv.Atoi(collects)
  128. if collectErr != nil {
  129. }
  130. share, shareErr := strconv.Atoi(shares)
  131. if shareErr != nil {
  132. }
  133. return like, comment, collect, share, nil
  134. }
  135. // GetWeiBoLinkDetail 获取微博链接数据详情
  136. func GetWeiBoLinkDetail(ctx context.Context, shareText string) (int, int, int, int, error) {
  137. url := "http://120.46.92.62:6888/api/weibo/post_detail"
  138. method := "POST"
  139. requestBody := struct {
  140. Id string `json:"id"`
  141. ShareText string `json:"share_text"`
  142. Proxy string `json:"proxy"`
  143. }{
  144. Id: "",
  145. ShareText: shareText,
  146. Proxy: "",
  147. }
  148. jsonData, err := json.Marshal(requestBody)
  149. if err != nil {
  150. fmt.Println("JSON编码失败:", err)
  151. return 0, 0, 0, 0, err
  152. }
  153. payload := strings.NewReader(string(jsonData))
  154. client := &http.Client{}
  155. req, err := http.NewRequest(method, url, payload)
  156. fmt.Println(req)
  157. if err != nil {
  158. //fmt.Println(err)
  159. return 0, 0, 0, 0, err
  160. }
  161. req.Header.Add("Content-Type", "application/json")
  162. res, err := client.Do(req)
  163. if err != nil {
  164. fmt.Println(err)
  165. return 0, 0, 0, 0, err
  166. }
  167. defer res.Body.Close()
  168. body, err := ioutil.ReadAll(res.Body)
  169. if err != nil {
  170. fmt.Println(err)
  171. return 0, 0, 0, 0, err
  172. }
  173. // fmt.Println(string(body))
  174. likes, comments, collects, shares, err := parseWeiboStats(body)
  175. if err != nil {
  176. //fmt.Println("解析互动数据失败:", err)
  177. return 0, 0, 0, 0, err
  178. }
  179. like, likeErr := strconv.Atoi(likes)
  180. if likeErr != nil {
  181. }
  182. comment, commentErr := strconv.Atoi(comments)
  183. if commentErr != nil {
  184. }
  185. collect, collectErr := strconv.Atoi(collects)
  186. if collectErr != nil {
  187. }
  188. share, shareErr := strconv.Atoi(shares)
  189. if shareErr != nil {
  190. }
  191. return like, comment, collect, share, nil
  192. }
  193. // WeiboPostsResponse 微博多条数据接收结构体
  194. type WeiboPostsResponse struct {
  195. Code int `json:"code"`
  196. Msg string `json:"msg"`
  197. Data struct {
  198. Data struct {
  199. Cards []struct {
  200. Mblog struct {
  201. RetweetedStatus struct {
  202. Id string `json:"id"`
  203. Text string `json:"text"`
  204. CreatedAt string `json:"created_at"`
  205. AttitudesCount int `json:"attitudes_count"` // 点赞数
  206. CommentsCount int `json:"comments_count"` // 评论数
  207. RepostsCount int `json:"reposts_count"` // 转发数
  208. PendingApproval int `json:"pending_approval"` // 待审数
  209. PicIds []string `json:"pic_ids"` // 图片ID列表
  210. PageInfo struct {
  211. Type string `json:"type"` // 内容类型(video/article)
  212. PagePic struct {
  213. Url string `json:"url"` // 封面图URL
  214. } `json:"page_pic"`
  215. MediaInfo struct {
  216. StreamUrl string `json:"stream_url"` // 视频流地址
  217. } `json:"media_info"`
  218. } `json:"page_info"`
  219. } `json:"retweeted_status"`
  220. } `json:"mblog"`
  221. } `json:"cards"`
  222. } `json:"data"`
  223. } `json:"data"`
  224. }
  225. // WeiboPostStats 单条微博的互动数据
  226. type WeiboPostStats struct {
  227. Id string `json:"id"`
  228. CreatedAt string `json:"created_at"`
  229. Likes int `json:"likes"` // 点赞数
  230. Comments int `json:"comments"` // 评论数
  231. Reposts int `json:"reposts"` // 转发数
  232. HasImages bool `json:"has_images"` // 是否有图片
  233. HasVideo bool `json:"has_video"` // 是否有视频
  234. CoverImageUrl string `json:"cover_image_url"` // 封面图URL(视频/文章)
  235. VideoUrl string `json:"video_url"` // 视频地址(如果有)
  236. }
  237. // parseWeiboPosts 解析微博多条数据
  238. func parseWeiboPosts(body []byte) ([]WeiboPostStats, error) {
  239. var resp WeiboPostsResponse
  240. if err := json.Unmarshal(body, &resp); err != nil {
  241. return nil, fmt.Errorf("JSON解析失败: %v", err)
  242. }
  243. if resp.Code != 200 {
  244. return nil, fmt.Errorf("API返回错误: %s", resp.Msg)
  245. }
  246. var posts []WeiboPostStats
  247. for i, card := range resp.Data.Data.Cards {
  248. if i >= 5 { // 只取前5条
  249. break
  250. }
  251. mblog := card.Mblog.RetweetedStatus
  252. hasVideo := mblog.PageInfo.Type == "video"
  253. posts = append(posts, WeiboPostStats{
  254. Id: mblog.Id,
  255. CreatedAt: mblog.CreatedAt,
  256. Likes: mblog.AttitudesCount,
  257. Comments: mblog.CommentsCount,
  258. Reposts: mblog.RepostsCount,
  259. HasImages: len(mblog.PicIds) > 0,
  260. HasVideo: hasVideo,
  261. CoverImageUrl: mblog.PageInfo.PagePic.Url,
  262. VideoUrl: mblog.PageInfo.MediaInfo.StreamUrl,
  263. })
  264. }
  265. return posts, nil
  266. }
  267. // GetWeiBoHistoryList 微博历史作品数据
  268. func GetWeiBoHistoryList(ctx context.Context, homePageUrl string) ([]WeiboPostStats, error) {
  269. url := "http://120.46.92.62:6888/api/weibo/user_post" // 替换为实际API地址
  270. method := "POST"
  271. requestBody := struct {
  272. Uid string `json:"uid"`
  273. ShareText string `json:"share_text"`
  274. SinceId string `json:"since_id"`
  275. Proxy string `json:"proxy"`
  276. }{
  277. ShareText: homePageUrl,
  278. Proxy: "",
  279. }
  280. client := &http.Client{}
  281. jsonData, err := json.Marshal(requestBody)
  282. if err != nil {
  283. fmt.Println("JSON编码失败:", err)
  284. return nil, err
  285. }
  286. payload := strings.NewReader(string(jsonData))
  287. req, err := http.NewRequest(method, url, payload)
  288. if err != nil {
  289. fmt.Println(err)
  290. return nil, err
  291. }
  292. req.Header.Add("Content-Type", "application/json")
  293. res, err := client.Do(req)
  294. if err != nil {
  295. fmt.Println(err)
  296. return nil, err
  297. }
  298. defer res.Body.Close()
  299. body, err := ioutil.ReadAll(res.Body)
  300. if err != nil {
  301. fmt.Println(err)
  302. return nil, err
  303. }
  304. hisData, hisErr := parseWeiboPosts(body)
  305. if hisErr != nil {
  306. fmt.Println(hisErr)
  307. }
  308. return hisData, nil
  309. }
  310. // RedBookPostsResponse 小红书多条数据接收结构体
  311. type RedBookPostsResponse struct {
  312. Code int `json:"code"`
  313. Msg string `json:"msg"`
  314. Data struct {
  315. ResponseBody struct {
  316. Data struct {
  317. Note []struct {
  318. NoteId string `json:"note_id"`
  319. XsecToken string `json:"xsec_token"`
  320. } `json:"notes"`
  321. } `json:"data"`
  322. } `json:"response_body"`
  323. } `json:"data"`
  324. }
  325. func parseRedBookPosts(body []byte) ([]string, []string, error) {
  326. var resp RedBookPostsResponse
  327. if err := json.Unmarshal(body, &resp); err != nil {
  328. return nil, nil, fmt.Errorf("JSON解析失败: %v", err)
  329. }
  330. if resp.Code != 200 {
  331. return nil, nil, fmt.Errorf("API返回错误: %s", resp.Msg)
  332. }
  333. var linkList []string
  334. var secTokenList []string
  335. for i, card := range resp.Data.ResponseBody.Data.Note {
  336. if i >= 5 { // 只取前5条
  337. break
  338. }
  339. linkList = append(linkList, card.NoteId)
  340. secTokenList = append(secTokenList, card.XsecToken)
  341. }
  342. return linkList, secTokenList, nil
  343. }
  344. // GetRedBookHistoryList 小红书历史作品列表
  345. func GetRedBookHistoryList(ctx context.Context, homePageUrl string) ([]RedBookNoteResponse, error) {
  346. url := "http://120.46.92.62:6888/api/xhs/user_post"
  347. method := "POST"
  348. requestBody := struct {
  349. UserId string `json:"user_id"`
  350. Cursor string `json:"cursor"`
  351. Proxy string `json:"proxy"`
  352. }{
  353. UserId: homePageUrl,
  354. Proxy: "",
  355. }
  356. client := &http.Client{}
  357. jsonData, err := json.Marshal(requestBody)
  358. if err != nil {
  359. fmt.Println("JSON编码失败:", err)
  360. return nil, err
  361. }
  362. payload := strings.NewReader(string(jsonData))
  363. req, err := http.NewRequest(method, url, payload)
  364. if err != nil {
  365. fmt.Println(err)
  366. return nil, err
  367. }
  368. req.Header.Add("Cookie", "abRequestId=023e90e1-a95a-58db-830a-d66c0cc67f60; a1=19566f0f66eglh42hy8peqvpak3xqglp49289p8xb50000109075; webId=d189df044ca380c74b5b97bc300db5a1; gid=yj2KKiyyyY4Yyj2KKi8iKjUYKdk1x4JxvYUdJCDAhU0TqY28CAk1Uj888y8j8W288q4Jif4i; acw_tc=0a4a949617517192833111453ec142bc6ab7f9b09590524b2d59d413f0ffb7; websectiga=a9bdcaed0af874f3a1431e94fbea410e8f738542fbb02df1e8e30c29ef3d91ac; sec_poison_id=9bc0c279-b964-4448-87df-d08559819919; web_session=0400698c340cec15e62be2555c3a4b29a1365a; xsecappid=xhs-pc-web; webBuild=4.71.0; loadts=1751719579486; unread={%22ub%22:%22685be6260000000015023bc6%22%2C%22ue%22:%2268617298000000001c0337aa%22%2C%22uc%22:23}")
  369. req.Header.Add("Content-Type", "application/json")
  370. res, err := client.Do(req)
  371. if err != nil {
  372. fmt.Println(err)
  373. return nil, err
  374. }
  375. defer res.Body.Close()
  376. body, err := ioutil.ReadAll(res.Body)
  377. if err != nil {
  378. fmt.Println(err)
  379. return nil, err
  380. }
  381. linkList, tokenList, linkErr := parseRedBookPosts(body)
  382. if linkErr != nil {
  383. return nil, linkErr
  384. }
  385. if linkList != nil {
  386. fmt.Println(linkList)
  387. fmt.Println(GetRedBookLinkDetail(ctx, linkList[2], tokenList[2]))
  388. }
  389. return nil, nil
  390. }