helper.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. package controller
  2. import (
  3. "errors"
  4. "strconv"
  5. "strings"
  6. "time"
  7. "github.com/2930134478/AI-CS/backend/service"
  8. "github.com/gin-gonic/gin"
  9. "gorm.io/gorm"
  10. )
  11. const timeFormat = "2006-01-02T15:04:05Z07:00"
  12. // parseUintParam 将路径参数转换为 uint64。
  13. func parseUintParam(c *gin.Context, name string) (uint64, error) {
  14. value := c.Param(name)
  15. return strconv.ParseUint(value, 10, 64)
  16. }
  17. // parseUintQuery 将查询参数转换为 uint64。
  18. func parseUintQuery(c *gin.Context, name string) (uint64, error) {
  19. value := c.Query(name)
  20. if value == "" {
  21. return 0, strconv.ErrSyntax
  22. }
  23. return strconv.ParseUint(value, 10, 64)
  24. }
  25. // getUserIDFromHeader 从请求头 X-User-Id 读取当前用户 ID(用于知识库开关校验)
  26. // 若未设置则返回 0(调用方可按需放行或拒绝)
  27. func getUserIDFromHeader(c *gin.Context) uint {
  28. value := c.GetHeader("X-User-Id")
  29. if value == "" {
  30. return 0
  31. }
  32. id, err := strconv.ParseUint(value, 10, 64)
  33. if err != nil {
  34. return 0
  35. }
  36. return uint(id)
  37. }
  38. // formatTimeValue 按统一格式输出时间字符串。
  39. func formatTimeValue(t time.Time) string {
  40. return t.Format(timeFormat)
  41. }
  42. // formatTimePointer 在指针为空时返回空字符串。
  43. func formatTimePointer(t *time.Time) string {
  44. if t == nil {
  45. return ""
  46. }
  47. return t.Format(timeFormat)
  48. }
  49. // getTraceID 从请求上下文读取 trace_id(由中间件注入)。
  50. func getTraceID(c *gin.Context) string {
  51. if v, ok := c.Get("trace_id"); ok {
  52. if s, ok2 := v.(string); ok2 {
  53. return s
  54. }
  55. }
  56. return ""
  57. }
  58. // requirePermission 统一的权限校验(基于 X-User-Id)。
  59. // 返回 true 表示允许继续;false 表示已输出错误响应。
  60. func requirePermission(c *gin.Context, userSvc *service.UserService, perm string) bool {
  61. if userSvc == nil {
  62. c.JSON(500, gin.H{"error": "权限服务未初始化"})
  63. return false
  64. }
  65. userID := getUserIDFromHeader(c)
  66. if err := userSvc.CheckPermission(userID, perm); err != nil {
  67. // 未授权/无权限统一 403(避免泄露过多信息)
  68. c.JSON(403, gin.H{"error": err.Error()})
  69. return false
  70. }
  71. return true
  72. }
  73. const conversationAccessTokenHeader = "X-Conversation-Token"
  74. // getConversationAccessToken 从 Header 或 Query 读取访客会话令牌。
  75. func getConversationAccessToken(c *gin.Context) string {
  76. if token := strings.TrimSpace(c.GetHeader(conversationAccessTokenHeader)); token != "" {
  77. return token
  78. }
  79. return strings.TrimSpace(c.Query("access_token"))
  80. }
  81. // authorizeConversationAccess 校验对会话的访问权限(客服或持 token 的访客)。
  82. // 失败时已写入 HTTP 响应;成功返回会话详情。
  83. func authorizeConversationAccess(
  84. c *gin.Context,
  85. convSvc *service.ConversationService,
  86. userSvc *service.UserService,
  87. conversationID uint,
  88. ) (*service.ConversationDetail, bool) {
  89. userID := getUserIDFromHeader(c)
  90. accessToken := getConversationAccessToken(c)
  91. detail, err := convSvc.GetConversationDetail(conversationID, userID)
  92. if err != nil {
  93. if errors.Is(err, gorm.ErrRecordNotFound) {
  94. c.JSON(404, gin.H{"error": "会话不存在"})
  95. } else {
  96. c.JSON(500, gin.H{"error": "查询失败"})
  97. }
  98. return nil, false
  99. }
  100. if detail.ConversationType == "internal" {
  101. if userID == 0 || detail.AgentID != userID {
  102. c.JSON(403, gin.H{"error": "无权限访问内部会话"})
  103. return nil, false
  104. }
  105. if userSvc != nil {
  106. if err := userSvc.CheckPermission(userID, string(service.PermKBTest)); err != nil {
  107. c.JSON(403, gin.H{"error": err.Error()})
  108. return nil, false
  109. }
  110. }
  111. return detail, true
  112. }
  113. if userID > 0 {
  114. if userSvc != nil {
  115. if err := userSvc.CheckPermission(userID, string(service.PermChat)); err != nil {
  116. c.JSON(403, gin.H{"error": err.Error()})
  117. return nil, false
  118. }
  119. }
  120. return detail, true
  121. }
  122. if err := convSvc.ValidateVisitorAccessToken(conversationID, accessToken); err != nil {
  123. if errors.Is(err, service.ErrConversationNotFound) {
  124. c.JSON(404, gin.H{"error": "会话不存在"})
  125. } else {
  126. c.JSON(403, gin.H{"error": "无权限访问该会话"})
  127. }
  128. return nil, false
  129. }
  130. return detail, true
  131. }