feat(challenges): 新增挑战功能模块及完整接口实现

- 新增挑战列表、详情、加入/退出、进度上报等 REST 接口
- 定义 Challenge / ChallengeParticipant 数据模型与状态枚举
- 提供排行榜查询与用户排名计算
- 包含接口文档与数据库初始化脚本
This commit is contained in:
richarjiang
2025-09-28 12:02:39 +08:00
parent 8e51994e71
commit 1b7132a325
12 changed files with 1003 additions and 0 deletions

View File

@@ -0,0 +1,99 @@
import {
Table,
Column,
DataType,
Model,
ForeignKey,
BelongsTo,
Index,
} from 'sequelize-typescript';
import { Challenge } from './challenge.model';
import { User } from '../../users/models/user.model';
export enum ChallengeParticipantStatus {
ACTIVE = 'active',
COMPLETED = 'completed',
LEFT = 'left',
}
@Table({
tableName: 't_challenge_participants',
underscored: true,
})
export class ChallengeParticipant extends Model {
@Column({
type: DataType.CHAR(36),
defaultValue: DataType.UUIDV4,
primaryKey: true,
})
declare id: string;
@ForeignKey(() => Challenge)
@Column({
type: DataType.CHAR(36),
allowNull: false,
comment: '挑战 ID',
})
declare challengeId: string;
@ForeignKey(() => User)
@Column({
type: DataType.STRING(64),
allowNull: false,
comment: '用户 ID',
})
declare userId: string;
@Column({
type: DataType.INTEGER,
allowNull: false,
defaultValue: 0,
comment: '当前进度值',
})
declare progressValue: number;
@Column({
type: DataType.INTEGER,
allowNull: false,
comment: '目标值,通常与挑战 targetValue 相同',
})
declare targetValue: number;
@Column({
type: DataType.ENUM('active', 'completed', 'left'),
allowNull: false,
defaultValue: ChallengeParticipantStatus.ACTIVE,
comment: '参与状态',
})
declare status: ChallengeParticipantStatus;
@Column({
type: DataType.DATE,
allowNull: false,
defaultValue: DataType.NOW,
comment: '加入时间',
})
declare joinedAt: Date;
@Column({
type: DataType.DATE,
allowNull: true,
comment: '退出时间',
})
declare leftAt: Date | null;
@Column({
type: DataType.DATE,
allowNull: true,
comment: '最近一次更新进度的时间',
})
declare lastProgressAt: Date | null;
@BelongsTo(() => Challenge)
declare challenge?: Challenge;
@BelongsTo(() => User)
declare user?: User;
}