package admin import ( "context" "fmt" "math" "strings" "time" v1 "github.com/go-nunu/nunu-layout-advanced/api/v1" admin "github.com/go-nunu/nunu-layout-advanced/api/v1/admin" "github.com/go-nunu/nunu-layout-advanced/internal/model" "github.com/go-nunu/nunu-layout-advanced/internal/repository" "gorm.io/gorm" ) type LogRepository interface { GetLog(ctx context.Context, id int64) (*model.Log, error) GetLogList(ctx context.Context, req admin.SearchLogParams) (*v1.PaginatedResponse[model.Log], error) } func NewLogRepository( repository *repository.Repository, ) LogRepository { return &logRepository{ Repository: repository, } } type logRepository struct { *repository.Repository } func (r *logRepository) GetLog(ctx context.Context, id int64) (*model.Log, error) { var res model.Log // 使用依赖注入的分表管理器 shardingMgr := r.Manager // 获取存在的分表 existingTables := shardingMgr.GetTableNamesWithExistenceCheck(r.DBWithName(ctx, "admin"), "log", nil, nil) // 在各个分表中查找 for _, tableName := range existingTables { err := r.DBWithName(ctx, "admin").Table(tableName).Where("id = ?", id).First(&res).Error if err == nil { res.SetTableName(tableName) return &res, nil } } return nil, fmt.Errorf("未找到ID为 %d 的日志记录", id) } func (r *logRepository) GetLogList(ctx context.Context, req admin.SearchLogParams) (*v1.PaginatedResponse[model.Log], error) { // 使用依赖注入的分表管理器 shardingMgr := r.Manager // 解析时间范围(如果有的话) var startTime, endTime *time.Time // TODO: 这里可以根据req中的时间字段来确定查询范围 // 暂时查询最近3个月的数据 // 获取需要查询的表 existingTables := shardingMgr.GetTableNamesWithExistenceCheck(r.DBWithName(ctx, "admin"), "log", startTime, endTime) if len(existingTables) == 0 { // 没有分表,返回空结果 return &v1.PaginatedResponse[model.Log]{ Records: []model.Log{}, Page: 1, PageSize: 10, Total: 0, TotalPages: 0, }, nil } if len(existingTables) == 1 { // 只有一个表,直接查询 return r.queryFromSingleTable(ctx, req, existingTables[0]) } // 跨表分页查询 return r.queryFromMultipleTables(ctx, req, existingTables) } // queryFromSingleTable 单表查询 func (r *logRepository) queryFromSingleTable(ctx context.Context, req admin.SearchLogParams, tableName string) (*v1.PaginatedResponse[model.Log], error) { var res []model.Log var total int64 query := r.DBWithName(ctx, "admin").Table(tableName) query = r.applyFilters(query, req) if err := query.Count(&total).Error; err != nil { return nil, err } page := req.Current pageSize := req.PageSize if page <= 0 { page = 1 } if pageSize <= 0 { pageSize = 10 } else if pageSize > 100 { pageSize = 100 } offset := (page - 1) * pageSize if req.Column != "" { query = query.Order(req.Column + " " + req.Order) } result := query.Offset(offset).Limit(pageSize).Find(&res) if result.Error != nil { return nil, result.Error } return &v1.PaginatedResponse[model.Log]{ Records: res, Page: page, PageSize: pageSize, Total: total, TotalPages: int(math.Ceil(float64(total) / float64(pageSize))), }, nil } // queryFromMultipleTables 多表联合查询 func (r *logRepository) queryFromMultipleTables(ctx context.Context, req admin.SearchLogParams, tableNames []string) (*v1.PaginatedResponse[model.Log], error) { var allResults []model.Log var totalCount int64 // 先计算总数 for _, tableName := range tableNames { var count int64 query := r.DBWithName(ctx, "admin").Table(tableName) query = r.applyFilters(query, req) if err := query.Count(&count).Error; err != nil { return nil, err } totalCount += count } page := req.Current pageSize := req.PageSize if page <= 0 { page = 1 } if pageSize <= 0 { pageSize = 10 } else if pageSize > 100 { pageSize = 100 } // 计算需要跳过的记录数 offset := (page - 1) * pageSize limit := pageSize currentOffset := 0 // 逐表查询直到获取足够的记录 for _, tableName := range tableNames { if limit <= 0 { break } var tableCount int64 countQuery := r.DBWithName(ctx, "admin").Table(tableName) countQuery = r.applyFilters(countQuery, req) if err := countQuery.Count(&tableCount).Error; err != nil { return nil, err } // 如果当前表的记录数不足以满足offset要求,跳过这个表 if currentOffset+int(tableCount) <= offset { currentOffset += int(tableCount) continue } // 计算在当前表中的offset tableOffset := offset - currentOffset if tableOffset < 0 { tableOffset = 0 } var tableResults []model.Log query := r.DBWithName(ctx, "admin").Table(tableName) query = r.applyFilters(query, req) if req.Column != "" { query = query.Order(req.Column + " " + req.Order) } err := query.Offset(tableOffset).Limit(limit).Find(&tableResults).Error if err != nil { return nil, err } // 设置表名 for i := range tableResults { tableResults[i].SetTableName(tableName) } allResults = append(allResults, tableResults...) limit -= len(tableResults) currentOffset += int(tableCount) } return &v1.PaginatedResponse[model.Log]{ Records: allResults, Page: page, PageSize: pageSize, Total: totalCount, TotalPages: int(math.Ceil(float64(totalCount) / float64(pageSize))), }, nil } // applyFilters 应用查询过滤条件 func (r *logRepository) applyFilters(query *gorm.DB, req admin.SearchLogParams) *gorm.DB { if req.RequestIp != "" { trimmedName := strings.TrimSpace(req.RequestIp) query = query.Where("request_ip LIKE CONCAT('%', ?, '%')", trimmedName) } if req.Uid != 0 { query = query.Where("uid = ?", req.Uid) } if req.Api != "" { trimmedName := strings.TrimSpace(req.Api) query = query.Where("api LIKE CONCAT('%', ?, '%')", trimmedName) } if req.Message != "" { trimmedName := strings.TrimSpace(req.Message) query = query.Where("message LIKE CONCAT('%', ?, '%')", trimmedName) } if req.ExtraData != "" { trimmedName := strings.TrimSpace(req.ExtraData) query = query.Where("extra_data LIKE CONCAT('%', ?, '%')", trimmedName) } return query }