辣评比赛管理与资格认证系统(五)

 

比赛管理系统概述

比赛管理系统是辣评平台的核心业务模块,用于管理多届次的比赛、参赛资格认证、评分规则等。

系统目标

  • 支持多届次比赛管理
  • 实现灵活的资格认证机制
  • 提供完整的规则配置功能
  • 支持比赛数据统计

比赛届次管理

数据模型

// Competition 比赛模型
type Competition struct {
    ID              uint      `gorm:"primaryKey"`
    Number          int       `gorm:"uniqueIndex"` // 届次
    Name            string
    Description     string    `gorm:"type:text"`
    IsActive        bool      // 是否为当前活跃比赛
    IsLocked        bool      // 是否已锁定
    StartDate       time.Time
    EndDate         time.Time
    SubmissionStart time.Time // 投稿开始时间
    SubmissionEnd   time.Time // 投稿结束时间
    CommentStart    time.Time // 评论开始时间
    CommentEnd      time.Time // 评论结束时间
    CreatedAt       time.Time
    UpdatedAt       time.Time
}

// CompetitionRule 比赛规则模型
type CompetitionRule struct {
    ID              uint
    CompetitionID   uint      `gorm:"index"`
    RuleNumber      int       // 规则编号
    RuleContent     string    `gorm:"type:longtext"`
    RuleJson        string    `gorm:"type:text"` // JSON 格式规则
    IsActive        bool
    CreatedAt       time.Time
    UpdatedAt       time.Time
}

参赛资格认证逻辑

资格认证模型

// QualificationStatistics 资格统计模型
type QualificationStatistics struct {
    ID              uint      `gorm:"primaryKey"`
    UserID          uint      `gorm:"uniqueIndex:idx_user_competition"`
    CompetitionID   uint      `gorm:"uniqueIndex:idx_user_competition"`
    IsAuthor        bool      // 是否为作者
    IsReader        bool      // 是否为读者
    CommentCount    int       // 评论数
    SubmissionCount int       // 投稿数
    QualifiedAt     *time.Time
    CreatedAt       time.Time
    UpdatedAt       time.Time
}

// AuthorQualification 作者资格认证
type AuthorQualification struct {
    ID              uint
    UserID          uint
    CompetitionID   uint
    SubmissionCount int       // 投稿数
    IsQualified     bool
    QualifiedAt     *time.Time
}

资格认证逻辑

// CheckAuthorQualification 检查作者资格
func (s *QualificationService) CheckAuthorQualification(ctx context.Context, userID, competitionID uint) (bool, error) {
    // 1. 获取用户在该比赛的投稿数
    var submissionCount int64
    if err := s.db.Model(&Submission{}).
        Where("user_id = ? AND competition_id = ?", userID, competitionID).
        Count(&submissionCount).Error; err != nil {
        return false, err
    }
    
    // 2. 获取比赛规则
    rule, err := s.getCompetitionRule(ctx, competitionID)
    if err != nil {
        return false, err
    }
    
    // 3. 检查是否满足作者资格
    requiredSubmissions := rule.RequiredSubmissions // 假设规则中定义了所需投稿数
    isQualified := submissionCount >= int64(requiredSubmissions)
    
    // 4. 更新资格统计
    s.updateQualificationStatistics(ctx, userID, competitionID, isQualified, true)
    
    return isQualified, nil
}

// CheckReaderQualification 检查读者资格
func (s *QualificationService) CheckReaderQualification(ctx context.Context, userID, competitionID uint) (bool, error) {
    // 1. 获取用户在该比赛的评论数
    var commentCount int64
    if err := s.db.Model(&Comment{}).
        Where("user_id = ? AND competition_id = ?", userID, competitionID).
        Count(&commentCount).Error; err != nil {
        return false, err
    }
    
    // 2. 获取比赛规则
    rule, err := s.getCompetitionRule(ctx, competitionID)
    if err != nil {
        return false, err
    }
    
    // 3. 检查是否满足读者资格
    requiredComments := rule.RequiredComments
    isQualified := commentCount >= int64(requiredComments)
    
    // 4. 更新资格统计
    s.updateQualificationStatistics(ctx, userID, competitionID, isQualified, false)
    
    return isQualified, nil
}

资格统计与展示

统计计算

// CalculateQualificationStatistics 计算资格统计
func (s *QualificationService) CalculateQualificationStatistics(ctx context.Context, competitionID uint) error {
    // 1. 获取所有参与该比赛的用户
    var users []User
    if err := s.db.Distinct("user_id").
        Model(&Comment{}).
        Where("competition_id = ?", competitionID).
        Scan(&users).Error; err != nil {
        return err
    }
    
    // 2. 为每个用户计算资格
    for _, user := range users {
        authorQualified, _ := s.CheckAuthorQualification(ctx, user.ID, competitionID)
        readerQualified, _ := s.CheckReaderQualification(ctx, user.ID, competitionID)
        
        stats := &QualificationStatistics{
            UserID:        user.ID,
            CompetitionID: competitionID,
            IsAuthor:      authorQualified,
            IsReader:      readerQualified,
        }
        
        s.db.Save(stats)
    }
    
    return nil
}

// GetQualificationStatus 获取资格状态
func (s *QualificationService) GetQualificationStatus(ctx context.Context, userID, competitionID uint) (*QualificationStatistics, error) {
    stats := &QualificationStatistics{}
    
    err := s.db.Where("user_id = ? AND competition_id = ?", userID, competitionID).
        First(stats).Error
    
    return stats, err
}

补评欠债管理

欠债模型

// DebtRecord 欠债记录模型
type DebtRecord struct {
    ID              uint      `gorm:"primaryKey"`
    UserID          uint      `gorm:"index"`
    CompetitionID   uint      `gorm:"index"`
    DebtCount       int       // 欠债数量
    Status          string    // 状态:pending, completed, overdue
    DueDate         time.Time // 截止日期
    CompletedAt     *time.Time
    CreatedAt       time.Time
    UpdatedAt       time.Time
}

// MakeupComment 补评记录
type MakeupComment struct {
    ID              uint
    UserID          uint
    CompetitionID   uint
    DebtRecordID    uint
    SubmissionID    uint
    Content         string    `gorm:"type:text"`
    Score           int
    CreatedAt       time.Time
}

欠债处理

// CreateDebtRecord 创建欠债记录
func (s *DebtService) CreateDebtRecord(ctx context.Context, userID, competitionID uint, debtCount int) error {
    dueDate := time.Now().AddDate(0, 0, 7) // 7天截止
    
    record := &DebtRecord{
        UserID:        userID,
        CompetitionID: competitionID,
        DebtCount:     debtCount,
        Status:        "pending",
        DueDate:       dueDate,
    }
    
    return s.db.Create(record).Error
}

// CompleteDebt 完成欠债
func (s *DebtService) CompleteDebt(ctx context.Context, debtRecordID uint) error {
    now := time.Now()
    
    return s.db.Model(&DebtRecord{}).
        Where("id = ?", debtRecordID).
        Updates(map[string]interface{}{
            "status": "completed",
            "completed_at": now,
        }).Error
}

// CheckOverdueDebt 检查逾期欠债
func (s *DebtService) CheckOverdueDebt(ctx context.Context) error {
    return s.db.Model(&DebtRecord{}).
        Where("status = ? AND due_date < ?", "pending", time.Now()).
        Update("status", "overdue").Error
}

规则配置系统

规则管理

// RuleConfig 规则配置结构
type RuleConfig struct {
    RequiredSubmissions int    // 作者所需投稿数
    RequiredComments    int    // 读者所需评论数
    MinCommentScore     int    // 最低评分
    MaxDebtDays         int    // 欠债最多天数
    DebtPenalty         string // 欠债惩罚
}

// UpdateCompetitionRule 更新比赛规则
func (s *RuleService) UpdateCompetitionRule(ctx context.Context, competitionID uint, config *RuleConfig) error {
    // 1. 序列化规则配置
    ruleJson, err := json.Marshal(config)
    if err != nil {
        return err
    }
    
    // 2. 更新规则
    rule := &CompetitionRule{
        CompetitionID: competitionID,
        RuleJson:      string(ruleJson),
    }
    
    return s.db.Save(rule).Error
}

// GetCompetitionRule 获取比赛规则
func (s *RuleService) GetCompetitionRule(ctx context.Context, competitionID uint) (*RuleConfig, error) {
    rule := &CompetitionRule{}
    
    if err := s.db.Where("competition_id = ? AND is_active = ?", competitionID, true).
        First(rule).Error; err != nil {
        return nil, err
    }
    
    // 反序列化规则
    config := &RuleConfig{}
    if err := json.Unmarshal([]byte(rule.RuleJson), config); err != nil {
        return nil, err
    }
    
    return config, nil
}

比赛数据统计

统计功能

// CompetitionStatistics 比赛统计
type CompetitionStatistics struct {
    CompetitionID   uint
    TotalUsers      int64
    TotalSubmissions int64
    TotalComments   int64
    QualifiedAuthors int64
    QualifiedReaders int64
    AverageScore    float64
}

// GetCompetitionStatistics 获取比赛统计
func (s *StatisticsService) GetCompetitionStatistics(ctx context.Context, competitionID uint) (*CompetitionStatistics, error) {
    stats := &CompetitionStatistics{
        CompetitionID: competitionID,
    }
    
    // 统计用户数
    s.db.Model(&User{}).
        Joins("JOIN comments ON users.id = comments.user_id").
        Where("comments.competition_id = ?", competitionID).
        Distinct("users.id").
        Count(&stats.TotalUsers)
    
    // 统计投稿数
    s.db.Model(&Submission{}).
        Where("competition_id = ?", competitionID).
        Count(&stats.TotalSubmissions)
    
    // 统计评论数
    s.db.Model(&Comment{}).
        Where("competition_id = ?", competitionID).
        Count(&stats.TotalComments)
    
    // 统计资格用户
    s.db.Model(&QualificationStatistics{}).
        Where("competition_id = ? AND is_author = ?", competitionID, true).
        Count(&stats.QualifiedAuthors)
    
    s.db.Model(&QualificationStatistics{}).
        Where("competition_id = ? AND is_reader = ?", competitionID, true).
        Count(&stats.QualifiedReaders)
    
    // 计算平均评分
    s.db.Model(&Comment{}).
        Where("competition_id = ?", competitionID).
        Select("AVG(score)").
        Row().
        Scan(&stats.AverageScore)
    
    return stats, nil
}

API 接口设计

创建比赛

POST /api/admin/competitions
{
    "number": 1,
    "name": "第一届比赛",
    "start_date": "2024-01-01",
    "end_date": "2024-12-31"
}

获取比赛列表

GET /api/competitions

检查资格

GET /api/competitions/{id}/qualification/check

获取比赛统计

GET /api/competitions/{id}/statistics

总结

比赛管理与资格认证系统为辣评平台提供了完整的比赛管理能力,确保了比赛的公平性和规范性。

本文遵守 Attribution-NonCommercial 4.0 International 许可协议。 Attribution-NonCommercial 4.0 International