log.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. package admin
  2. import (
  3. "context"
  4. "fmt"
  5. "math"
  6. "strings"
  7. "time"
  8. v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
  9. admin "github.com/go-nunu/nunu-layout-advanced/api/v1/admin"
  10. "github.com/go-nunu/nunu-layout-advanced/internal/model"
  11. "github.com/go-nunu/nunu-layout-advanced/internal/repository"
  12. "gorm.io/gorm"
  13. )
  14. type LogRepository interface {
  15. GetLog(ctx context.Context, id int64) (*model.Log, error)
  16. GetLogList(ctx context.Context, req admin.SearchLogParams) (*v1.PaginatedResponse[model.Log], error)
  17. }
  18. func NewLogRepository(
  19. repository *repository.Repository,
  20. ) LogRepository {
  21. return &logRepository{
  22. Repository: repository,
  23. }
  24. }
  25. type logRepository struct {
  26. *repository.Repository
  27. }
  28. func (r *logRepository) GetLog(ctx context.Context, id int64) (*model.Log, error) {
  29. var res model.Log
  30. // 使用依赖注入的分表管理器
  31. shardingMgr := r.Manager
  32. // 获取存在的分表
  33. existingTables := shardingMgr.GetTableNamesWithExistenceCheck(r.DBWithName(ctx, "admin"), "log", nil, nil)
  34. // 在各个分表中查找
  35. for _, tableName := range existingTables {
  36. err := r.DBWithName(ctx, "admin").Table(tableName).Where("id = ?", id).First(&res).Error
  37. if err == nil {
  38. res.SetTableName(tableName)
  39. return &res, nil
  40. }
  41. }
  42. return nil, fmt.Errorf("未找到ID为 %d 的日志记录", id)
  43. }
  44. func (r *logRepository) GetLogList(ctx context.Context, req admin.SearchLogParams) (*v1.PaginatedResponse[model.Log], error) {
  45. // 使用依赖注入的分表管理器
  46. shardingMgr := r.Manager
  47. // 解析时间范围(如果有的话)
  48. var startTime, endTime *time.Time
  49. // TODO: 这里可以根据req中的时间字段来确定查询范围
  50. // 暂时查询最近3个月的数据
  51. // 获取需要查询的表
  52. existingTables := shardingMgr.GetTableNamesWithExistenceCheck(r.DBWithName(ctx, "admin"), "log", startTime, endTime)
  53. if len(existingTables) == 0 {
  54. // 没有分表,返回空结果
  55. return &v1.PaginatedResponse[model.Log]{
  56. Records: []model.Log{},
  57. Page: 1,
  58. PageSize: 10,
  59. Total: 0,
  60. TotalPages: 0,
  61. }, nil
  62. }
  63. if len(existingTables) == 1 {
  64. // 只有一个表,直接查询
  65. return r.queryFromSingleTable(ctx, req, existingTables[0])
  66. }
  67. // 跨表分页查询
  68. return r.queryFromMultipleTables(ctx, req, existingTables)
  69. }
  70. // queryFromSingleTable 单表查询
  71. func (r *logRepository) queryFromSingleTable(ctx context.Context, req admin.SearchLogParams, tableName string) (*v1.PaginatedResponse[model.Log], error) {
  72. var res []model.Log
  73. var total int64
  74. query := r.DBWithName(ctx, "admin").Table(tableName)
  75. query = r.applyFilters(query, req)
  76. if err := query.Count(&total).Error; err != nil {
  77. return nil, err
  78. }
  79. page := req.Current
  80. pageSize := req.PageSize
  81. if page <= 0 {
  82. page = 1
  83. }
  84. if pageSize <= 0 {
  85. pageSize = 10
  86. } else if pageSize > 100 {
  87. pageSize = 100
  88. }
  89. offset := (page - 1) * pageSize
  90. if req.Column != "" {
  91. query = query.Order(req.Column + " " + req.Order)
  92. }
  93. result := query.Offset(offset).Limit(pageSize).Find(&res)
  94. if result.Error != nil {
  95. return nil, result.Error
  96. }
  97. return &v1.PaginatedResponse[model.Log]{
  98. Records: res,
  99. Page: page,
  100. PageSize: pageSize,
  101. Total: total,
  102. TotalPages: int(math.Ceil(float64(total) / float64(pageSize))),
  103. }, nil
  104. }
  105. // queryFromMultipleTables 多表联合查询
  106. func (r *logRepository) queryFromMultipleTables(ctx context.Context, req admin.SearchLogParams, tableNames []string) (*v1.PaginatedResponse[model.Log], error) {
  107. var allResults []model.Log
  108. var totalCount int64
  109. // 先计算总数
  110. for _, tableName := range tableNames {
  111. var count int64
  112. query := r.DBWithName(ctx, "admin").Table(tableName)
  113. query = r.applyFilters(query, req)
  114. if err := query.Count(&count).Error; err != nil {
  115. return nil, err
  116. }
  117. totalCount += count
  118. }
  119. page := req.Current
  120. pageSize := req.PageSize
  121. if page <= 0 {
  122. page = 1
  123. }
  124. if pageSize <= 0 {
  125. pageSize = 10
  126. } else if pageSize > 100 {
  127. pageSize = 100
  128. }
  129. // 计算需要跳过的记录数
  130. offset := (page - 1) * pageSize
  131. limit := pageSize
  132. currentOffset := 0
  133. // 逐表查询直到获取足够的记录
  134. for _, tableName := range tableNames {
  135. if limit <= 0 {
  136. break
  137. }
  138. var tableCount int64
  139. countQuery := r.DBWithName(ctx, "admin").Table(tableName)
  140. countQuery = r.applyFilters(countQuery, req)
  141. if err := countQuery.Count(&tableCount).Error; err != nil {
  142. return nil, err
  143. }
  144. // 如果当前表的记录数不足以满足offset要求,跳过这个表
  145. if currentOffset+int(tableCount) <= offset {
  146. currentOffset += int(tableCount)
  147. continue
  148. }
  149. // 计算在当前表中的offset
  150. tableOffset := offset - currentOffset
  151. if tableOffset < 0 {
  152. tableOffset = 0
  153. }
  154. var tableResults []model.Log
  155. query := r.DBWithName(ctx, "admin").Table(tableName)
  156. query = r.applyFilters(query, req)
  157. if req.Column != "" {
  158. query = query.Order(req.Column + " " + req.Order)
  159. }
  160. err := query.Offset(tableOffset).Limit(limit).Find(&tableResults).Error
  161. if err != nil {
  162. return nil, err
  163. }
  164. // 设置表名
  165. for i := range tableResults {
  166. tableResults[i].SetTableName(tableName)
  167. }
  168. allResults = append(allResults, tableResults...)
  169. limit -= len(tableResults)
  170. currentOffset += int(tableCount)
  171. }
  172. return &v1.PaginatedResponse[model.Log]{
  173. Records: allResults,
  174. Page: page,
  175. PageSize: pageSize,
  176. Total: totalCount,
  177. TotalPages: int(math.Ceil(float64(totalCount) / float64(pageSize))),
  178. }, nil
  179. }
  180. // applyFilters 应用查询过滤条件
  181. func (r *logRepository) applyFilters(query *gorm.DB, req admin.SearchLogParams) *gorm.DB {
  182. if req.RequestIp != "" {
  183. trimmedName := strings.TrimSpace(req.RequestIp)
  184. query = query.Where("request_ip LIKE CONCAT('%', ?, '%')", trimmedName)
  185. }
  186. if req.Uid != 0 {
  187. query = query.Where("uid = ?", req.Uid)
  188. }
  189. if req.Api != "" {
  190. trimmedName := strings.TrimSpace(req.Api)
  191. query = query.Where("api LIKE CONCAT('%', ?, '%')", trimmedName)
  192. }
  193. if req.Message != "" {
  194. trimmedName := strings.TrimSpace(req.Message)
  195. query = query.Where("message LIKE CONCAT('%', ?, '%')", trimmedName)
  196. }
  197. if req.ExtraData != "" {
  198. trimmedName := strings.TrimSpace(req.ExtraData)
  199. query = query.Where("extra_data LIKE CONCAT('%', ?, '%')", trimmedName)
  200. }
  201. return query
  202. }