package repository import ( "time" "github.com/topfans/backend/pkg/logger" "github.com/topfans/backend/services/taskService/model" "go.uber.org/zap" "gorm.io/gorm" ) // LikeBetRevenueRepository 点赞押注收益仓库接口 type LikeBetRevenueRepository interface { // BatchCreate 批量写入点赞押注收益记录(同一次 exhibition 到期,一次性写所有点赞者) BatchCreate(records []*model.LikeBetRevenueRecord) error // GetRecord 按 ID 查询单条记录(领取前先 Get 用于 userID 校验与状态校验) GetRecord(id int64) (*model.LikeBetRevenueRecord, error) // ListByUser 查询某用户某 star_id 下的收益记录(按 status 可选过滤) ListByUser(userID, starID int64, status string, page, pageSize int) ([]*model.LikeBetRevenueRecord, int64, error) // ClaimRecord 乐观锁领取:只有 status='claimable' 且 user_id 匹配时才更新为 claimed ClaimRecord(id int64, userID int64) (bool, error) } type likeBetRevenueRepository struct { db *gorm.DB } // NewLikeBetRevenueRepository 创建 LikeBetRevenueRepository 实例 func NewLikeBetRevenueRepository(db *gorm.DB) LikeBetRevenueRepository { return &likeBetRevenueRepository{db: db} } // BatchCreate 批量创建点赞押注收益记录 // 若 (exhibition_id, like_id) 唯一约束冲突(cleanup_worker 重跑场景),返回 error 但不影响主流程 // 调用方应在 CleanupWorker 中降级为 warn 日志 func (r *likeBetRevenueRepository) BatchCreate(records []*model.LikeBetRevenueRecord) error { if len(records) == 0 { return nil } now := time.Now().UnixMilli() for _, rec := range records { if rec.CreatedAt == 0 { rec.CreatedAt = now } if rec.Status == "" { rec.Status = "claimable" } } if err := r.db.Create(records).Error; err != nil { logger.Logger.Error("Failed to BatchCreate LikeBetRevenueRecords", zap.Int("count", len(records)), zap.Error(err)) return err } return nil } // GetRecord 按 ID 查询单条记录 func (r *likeBetRevenueRepository) GetRecord(id int64) (*model.LikeBetRevenueRecord, error) { var record model.LikeBetRevenueRecord if err := r.db.First(&record, id).Error; err != nil { return nil, err } return &record, nil } // ListByUser 查询某用户的点赞押注收益记录 // SQL: WHERE user_id=? AND star_id=? [AND status=?] ORDER BY created_at DESC func (r *likeBetRevenueRepository) ListByUser(userID, starID int64, status string, page, pageSize int) ([]*model.LikeBetRevenueRecord, int64, error) { var records []*model.LikeBetRevenueRecord var total int64 query := r.db.Model(&model.LikeBetRevenueRecord{}).Where("user_id = ? AND star_id = ?", userID, starID) if status != "" { query = query.Where("status = ?", status) } if err := query.Count(&total).Error; err != nil { logger.Logger.Error("Failed to count like_bet_revenue_records", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Error(err)) return nil, 0, err } offset := (page - 1) * pageSize if err := query.Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&records).Error; err != nil { logger.Logger.Error("Failed to ListByUser like_bet_revenue_records", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Error(err)) return nil, 0, err } return records, total, nil } // ClaimRecord 乐观锁领取:仅当 id/user_id/status='claimable' 同时匹配时才更新为 claimed // 返回 RowsAffected > 0 表示领取成功 func (r *likeBetRevenueRepository) ClaimRecord(id int64, userID int64) (bool, error) { now := time.Now().UnixMilli() result := r.db.Model(&model.LikeBetRevenueRecord{}). Where("id = ? AND user_id = ? AND status = ?", id, userID, "claimable"). Updates(map[string]interface{}{ "status": "claimed", "claimed_at": now, }) if result.Error != nil { logger.Logger.Error("Failed to ClaimRecord like_bet", zap.Int64("id", id), zap.Int64("user_id", userID), zap.Error(result.Error)) return false, result.Error } return result.RowsAffected > 0, nil }