tracking.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. package controllers
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "math/rand"
  6. "shudao-chat-go/models"
  7. "shudao-chat-go/utils"
  8. "strconv"
  9. "strings"
  10. "time"
  11. "github.com/beego/beego/v2/server/web"
  12. )
  13. // TrackingController 埋点记录控制器
  14. type TrackingController struct {
  15. web.Controller
  16. }
  17. // TrackingRequest 埋点请求结构(user_id从token中获取)
  18. type TrackingRequest struct {
  19. ApiPath string `json:"api_path"`
  20. Method string `json:"method"`
  21. ExtraData string `json:"extra_data"`
  22. }
  23. // TrackingResponse 埋点响应结构
  24. type TrackingResponse struct {
  25. Code int `json:"code"`
  26. Message string `json:"message"`
  27. Data struct {
  28. RequestId string `json:"request_id"`
  29. } `json:"data"`
  30. }
  31. // RecordTracking 记录埋点数据
  32. func (c *TrackingController) RecordTracking() {
  33. // 从token中获取用户信息
  34. userInfo, err := utils.GetUserInfoFromContext(c.Ctx.Input.GetData("userInfo"))
  35. if err != nil {
  36. c.Data["json"] = map[string]interface{}{
  37. "code": 401,
  38. "message": "获取用户信息失败: " + err.Error(),
  39. }
  40. c.ServeJSON()
  41. return
  42. }
  43. userID := int(userInfo.ID)
  44. if userID == 0 {
  45. userID = 1
  46. }
  47. var req TrackingRequest
  48. err = json.Unmarshal(c.Ctx.Input.RequestBody, &req)
  49. if err != nil {
  50. c.Data["json"] = map[string]interface{}{
  51. "code": 400,
  52. "message": "请求参数解析失败",
  53. }
  54. c.ServeJSON()
  55. return
  56. }
  57. if req.ApiPath == "" {
  58. c.Data["json"] = map[string]interface{}{
  59. "code": 400,
  60. "message": "接口路径不能为空",
  61. }
  62. c.ServeJSON()
  63. return
  64. }
  65. // 生成请求ID
  66. requestId := c.generateRequestID()
  67. // 获取客户端IP
  68. ipAddress := c.getClientIP()
  69. // 获取User-Agent
  70. userAgent := c.Ctx.Input.Header("User-Agent")
  71. if userAgent == "" {
  72. userAgent = "Unknown"
  73. }
  74. // 通过接口路径查找接口名称
  75. apiName := c.getApiNameByPath(req.ApiPath)
  76. // 创建埋点记录
  77. trackingRecord := models.TrackingRecord{
  78. UserID: userID,
  79. ApiPath: req.ApiPath,
  80. ApiName: apiName,
  81. Method: req.Method,
  82. UserAgent: userAgent,
  83. IpAddress: ipAddress,
  84. RequestId: requestId,
  85. Status: 1,
  86. Duration: 0, // 前端调用时暂时设为0,后续可以扩展
  87. ExtraData: req.ExtraData,
  88. }
  89. // 保存到数据库
  90. result := models.DB.Create(&trackingRecord)
  91. if result.Error != nil {
  92. c.Data["json"] = map[string]interface{}{
  93. "code": 500,
  94. "message": "埋点记录保存失败: " + result.Error.Error(),
  95. }
  96. c.ServeJSON()
  97. return
  98. }
  99. // 返回成功响应
  100. c.Data["json"] = TrackingResponse{
  101. Code: 200,
  102. Message: "埋点记录成功",
  103. Data: struct {
  104. RequestId string `json:"request_id"`
  105. }{
  106. RequestId: requestId,
  107. },
  108. }
  109. c.ServeJSON()
  110. }
  111. // getClientIP 获取客户端真实IP
  112. func (c *TrackingController) getClientIP() string {
  113. // 优先从X-Forwarded-For获取
  114. xForwardedFor := c.Ctx.Input.Header("X-Forwarded-For")
  115. if xForwardedFor != "" {
  116. ips := strings.Split(xForwardedFor, ",")
  117. if len(ips) > 0 {
  118. return strings.TrimSpace(ips[0])
  119. }
  120. }
  121. // 从X-Real-IP获取
  122. xRealIP := c.Ctx.Input.Header("X-Real-IP")
  123. if xRealIP != "" {
  124. return xRealIP
  125. }
  126. // 从RemoteAddr获取
  127. remoteAddr := c.Ctx.Input.Context.Request.RemoteAddr
  128. if remoteAddr != "" {
  129. // 去掉端口号
  130. if colonIndex := strings.LastIndex(remoteAddr, ":"); colonIndex != -1 {
  131. return remoteAddr[:colonIndex]
  132. }
  133. return remoteAddr
  134. }
  135. return "Unknown"
  136. }
  137. // getApiNameByPath 通过接口路径获取接口名称
  138. func (c *TrackingController) getApiNameByPath(apiPath string) string {
  139. // 首先从数据库查找映射关系
  140. var count int64
  141. models.DB.Model(&models.ApiPathMapping{}).Where("api_path = ? AND status = 1", apiPath).Count(&count)
  142. if count > 0 {
  143. var mapping models.ApiPathMapping
  144. models.DB.Where("api_path = ? AND status = 1", apiPath).First(&mapping)
  145. return mapping.ApiName
  146. }
  147. // 如果数据库中没有找到,使用默认的路径解析规则
  148. return c.parseApiNameFromPath(apiPath)
  149. }
  150. // parseApiNameFromPath 从路径解析接口名称
  151. func (c *TrackingController) parseApiNameFromPath(apiPath string) string {
  152. // 移除前缀 /apiv1/
  153. if strings.HasPrefix(apiPath, "/apiv1/") {
  154. apiPath = strings.TrimPrefix(apiPath, "/apiv1/")
  155. }
  156. // 移除开头的 /
  157. apiPath = strings.TrimPrefix(apiPath, "/")
  158. // 将路径转换为更友好的名称
  159. parts := strings.Split(apiPath, "/")
  160. var nameParts []string
  161. for _, part := range parts {
  162. if part != "" {
  163. // 将下划线替换为空格,并转换为标题格式
  164. part = strings.ReplaceAll(part, "_", " ")
  165. nameParts = append(nameParts, strings.Title(part))
  166. }
  167. }
  168. if len(nameParts) > 0 {
  169. return strings.Join(nameParts, " ")
  170. }
  171. return "未知接口"
  172. }
  173. // GetTrackingRecords 获取埋点记录列表
  174. func (c *TrackingController) GetTrackingRecords() {
  175. // 获取查询参数
  176. userIDStr := c.GetString("user_id")
  177. apiPath := c.GetString("api_path")
  178. pageStr := c.GetString("page", "1")
  179. pageSizeStr := c.GetString("page_size", "20")
  180. // 转换分页参数
  181. page, err := strconv.Atoi(pageStr)
  182. if err != nil || page < 1 {
  183. page = 1
  184. }
  185. pageSize, err := strconv.Atoi(pageSizeStr)
  186. if err != nil || pageSize < 1 || pageSize > 100 {
  187. pageSize = 20
  188. }
  189. // 构建查询条件
  190. query := models.DB.Model(&models.TrackingRecord{})
  191. if userIDStr != "" {
  192. userID, err := strconv.Atoi(userIDStr)
  193. if err == nil {
  194. query = query.Where("user_id = ?", userID)
  195. }
  196. }
  197. if apiPath != "" {
  198. query = query.Where("api_path LIKE ?", "%"+apiPath+"%")
  199. }
  200. // 获取总数
  201. var total int64
  202. query.Count(&total)
  203. // 分页查询
  204. var records []models.TrackingRecord
  205. offset := (page - 1) * pageSize
  206. result := query.Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&records)
  207. if result.Error != nil {
  208. c.Data["json"] = map[string]interface{}{
  209. "code": 500,
  210. "message": "查询失败: " + result.Error.Error(),
  211. }
  212. c.ServeJSON()
  213. return
  214. }
  215. // 返回结果
  216. c.Data["json"] = map[string]interface{}{
  217. "code": 200,
  218. "message": "查询成功",
  219. "data": map[string]interface{}{
  220. "list": records,
  221. "total": total,
  222. "page": page,
  223. "page_size": pageSize,
  224. },
  225. }
  226. c.ServeJSON()
  227. }
  228. // AddApiMapping 添加接口路径映射
  229. func (c *TrackingController) AddApiMapping() {
  230. var req struct {
  231. ApiPath string `json:"api_path"`
  232. ApiName string `json:"api_name"`
  233. ApiDesc string `json:"api_desc"`
  234. }
  235. err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
  236. if err != nil {
  237. c.Data["json"] = map[string]interface{}{
  238. "code": 400,
  239. "message": "请求参数解析失败",
  240. }
  241. c.ServeJSON()
  242. return
  243. }
  244. // 验证必要参数
  245. if req.ApiPath == "" || req.ApiName == "" {
  246. c.Data["json"] = map[string]interface{}{
  247. "code": 400,
  248. "message": "接口路径和接口名称不能为空",
  249. }
  250. c.ServeJSON()
  251. return
  252. }
  253. // 检查是否已存在
  254. var existingMapping models.ApiPathMapping
  255. result := models.DB.Where("api_path = ?", req.ApiPath).First(&existingMapping)
  256. if result.Error == nil {
  257. c.Data["json"] = map[string]interface{}{
  258. "code": 400,
  259. "message": "该接口路径已存在映射",
  260. }
  261. c.ServeJSON()
  262. return
  263. }
  264. // 创建新映射
  265. mapping := models.ApiPathMapping{
  266. ApiPath: req.ApiPath,
  267. ApiName: req.ApiName,
  268. ApiDesc: req.ApiDesc,
  269. Status: 1,
  270. }
  271. result = models.DB.Create(&mapping)
  272. if result.Error != nil {
  273. c.Data["json"] = map[string]interface{}{
  274. "code": 500,
  275. "message": "添加映射失败: " + result.Error.Error(),
  276. }
  277. c.ServeJSON()
  278. return
  279. }
  280. c.Data["json"] = map[string]interface{}{
  281. "code": 200,
  282. "message": "添加映射成功",
  283. "data": mapping,
  284. }
  285. c.ServeJSON()
  286. }
  287. // GetApiMappings 获取接口路径映射列表
  288. func (c *TrackingController) GetApiMappings() {
  289. var mappings []models.ApiPathMapping
  290. result := models.DB.Where("status = 1").Order("created_at DESC").Find(&mappings)
  291. if result.Error != nil {
  292. c.Data["json"] = map[string]interface{}{
  293. "code": 500,
  294. "message": "查询失败: " + result.Error.Error(),
  295. }
  296. c.ServeJSON()
  297. return
  298. }
  299. c.Data["json"] = map[string]interface{}{
  300. "code": 200,
  301. "message": "查询成功",
  302. "data": mappings,
  303. }
  304. c.ServeJSON()
  305. }
  306. // generateRequestID 生成请求ID
  307. func (c *TrackingController) generateRequestID() string {
  308. // 使用时间戳和随机数生成唯一ID
  309. timestamp := time.Now().UnixNano()
  310. randomNum := rand.Intn(10000)
  311. return fmt.Sprintf("%d_%d", timestamp, randomNum)
  312. }