feat(badges): 添加用户勋章系统,支持睡眠挑战勋章自动授予

实现完整的用户勋章功能模块:
- 新增 BadgeConfig 和 UserBadge 数据模型,支持勋章配置和用户勋章管理
- 新增 BadgeService 服务,提供勋章授予、查询、展示状态管理等核心功能
- 在挑战服务中集成勋章授予逻辑,完成首次睡眠打卡授予 goodSleep 勋章,完成睡眠挑战授予 sleepChallengeMonth 勋章
- 新增用户勋章相关接口:获取用户勋章列表、获取可用勋章列表、标记勋章已展示
- 支持勋章分类(睡眠、运动、饮食等)、排序、启用状态管理
- 支持勋章来源追踪(挑战、系统、手动授予)和元数据记录
This commit is contained in:
richarjiang
2025-11-14 17:08:02 +08:00
parent f04c2ccd5d
commit 7b4d7c4459
8 changed files with 792 additions and 3 deletions

View File

@@ -0,0 +1,105 @@
import { Column, Model, Table, DataType, ForeignKey, BelongsTo } from 'sequelize-typescript';
import { User } from './user.model';
import { BadgeConfig } from './badge-config.model';
export enum BadgeSource {
CHALLENGE = 'challenge',
MANUAL = 'manual',
SYSTEM = 'system',
}
@Table({
tableName: 't_user_badges',
underscored: true,
timestamps: true,
indexes: [
{
unique: true,
fields: ['user_id', 'badge_code'],
name: 'unique_user_badge',
},
{
fields: ['user_id'],
},
{
fields: ['badge_code'],
},
{
fields: ['awarded_at'],
},
],
})
export class UserBadge extends Model {
@Column({
type: DataType.INTEGER,
primaryKey: true,
autoIncrement: true,
})
declare id: number;
@ForeignKey(() => User)
@Column({
type: DataType.STRING,
allowNull: false,
comment: '用户ID',
})
declare userId: string;
@ForeignKey(() => BadgeConfig)
@Column({
type: DataType.STRING(64),
allowNull: false,
comment: '勋章代码',
})
declare badgeCode: string;
@Column({
type: DataType.DATE,
allowNull: false,
defaultValue: DataType.NOW,
comment: '获得时间',
})
declare awardedAt: Date;
@Column({
type: DataType.ENUM(...Object.values(BadgeSource)),
allowNull: false,
defaultValue: BadgeSource.SYSTEM,
comment: '授予来源',
})
declare source: BadgeSource;
@Column({
type: DataType.STRING(128),
allowNull: true,
comment: '来源ID如挑战ID',
})
declare sourceId: string;
@Column({
type: DataType.JSON,
allowNull: true,
comment: '额外元数据',
})
declare metadata: Record<string, any>;
@Column({
type: DataType.BOOLEAN,
allowNull: false,
defaultValue: false,
comment: '是否已展示过客户端展示勋章获得动画后设置为true',
})
declare isShow: boolean;
@Column({
type: DataType.DATE,
defaultValue: DataType.NOW,
})
declare createdAt: Date;
@BelongsTo(() => User)
declare user: User;
@BelongsTo(() => BadgeConfig, 'badgeCode')
declare badge: BadgeConfig;
}