feat(badges): 添加用户勋章系统,支持睡眠挑战勋章自动授予
实现完整的用户勋章功能模块: - 新增 BadgeConfig 和 UserBadge 数据模型,支持勋章配置和用户勋章管理 - 新增 BadgeService 服务,提供勋章授予、查询、展示状态管理等核心功能 - 在挑战服务中集成勋章授予逻辑,完成首次睡眠打卡授予 goodSleep 勋章,完成睡眠挑战授予 sleepChallengeMonth 勋章 - 新增用户勋章相关接口:获取用户勋章列表、获取可用勋章列表、标记勋章已展示 - 支持勋章分类(睡眠、运动、饮食等)、排序、启用状态管理 - 支持勋章来源追踪(挑战、系统、手动授予)和元数据记录
This commit is contained in:
157
src/users/dto/badge.dto.ts
Normal file
157
src/users/dto/badge.dto.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsOptional, IsString, IsBoolean, IsDateString, IsNotEmpty } from 'class-validator';
|
||||
import { BaseResponseDto } from '../../base.dto';
|
||||
|
||||
// 勋章基本信息
|
||||
export class BadgeInfoDto {
|
||||
@ApiProperty({ description: '勋章代码', example: 'goodSleep' })
|
||||
code: string;
|
||||
|
||||
@ApiProperty({ description: '勋章名称', example: '好眠达人' })
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ description: '勋章描述', example: '完成首次睡眠挑战' })
|
||||
description: string;
|
||||
|
||||
@ApiProperty({ description: '勋章图片URL' })
|
||||
imageUrl: string;
|
||||
|
||||
@ApiProperty({ description: '勋章分类', example: 'sleep' })
|
||||
category: string;
|
||||
|
||||
@ApiProperty({ description: '排序顺序', example: 1 })
|
||||
sortOrder: number;
|
||||
}
|
||||
|
||||
// 用户勋章信息
|
||||
export class UserBadgeDto {
|
||||
@ApiProperty({ description: '记录ID', example: 1 })
|
||||
id: number;
|
||||
|
||||
@ApiProperty({ description: '勋章代码', example: 'goodSleep' })
|
||||
code: string;
|
||||
|
||||
@ApiProperty({ description: '勋章名称', example: '好眠达人' })
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ description: '勋章描述', example: '完成首次睡眠挑战' })
|
||||
description: string;
|
||||
|
||||
@ApiProperty({ description: '勋章图片URL' })
|
||||
imageUrl: string;
|
||||
|
||||
@ApiProperty({ description: '勋章分类', example: 'sleep' })
|
||||
category: string;
|
||||
|
||||
@ApiProperty({ description: '获得时间' })
|
||||
awardedAt: Date;
|
||||
|
||||
@ApiProperty({ description: '授予来源', example: 'challenge' })
|
||||
source: string;
|
||||
|
||||
@ApiProperty({ description: '来源ID(如挑战ID)', required: false })
|
||||
sourceId?: string;
|
||||
|
||||
@ApiProperty({ description: '元数据', required: false })
|
||||
metadata?: Record<string, any>;
|
||||
|
||||
@ApiProperty({ description: '是否已展示过', example: false })
|
||||
isShow: boolean;
|
||||
}
|
||||
|
||||
// 可用勋章信息(包含是否已获得)
|
||||
export class AvailableBadgeDto extends BadgeInfoDto {
|
||||
@ApiProperty({ description: '是否已获得', example: false })
|
||||
isAwarded: boolean;
|
||||
|
||||
@ApiProperty({ description: '获得时间(如果已获得)', required: false })
|
||||
awardedAt?: Date;
|
||||
|
||||
@ApiProperty({ description: '是否已展示过(如果已获得)', required: false })
|
||||
isShow?: boolean;
|
||||
}
|
||||
|
||||
// 获取用户勋章列表响应
|
||||
export class GetUserBadgesResponseDto implements BaseResponseDto<{
|
||||
badges: UserBadgeDto[];
|
||||
total: number;
|
||||
}> {
|
||||
@ApiProperty({ description: '响应状态码', example: 0 })
|
||||
code: number;
|
||||
|
||||
@ApiProperty({ description: '响应消息', example: 'success' })
|
||||
message: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: '用户勋章数据',
|
||||
example: {
|
||||
badges: [
|
||||
{
|
||||
id: 1,
|
||||
code: 'goodSleep',
|
||||
name: '好眠达人',
|
||||
description: '完成首次睡眠挑战',
|
||||
imageUrl: 'https://example.com/badge.png',
|
||||
category: 'sleep',
|
||||
awardedAt: '2025-01-14T07:00:00Z',
|
||||
source: 'challenge',
|
||||
sourceId: 'challenge-uuid',
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
},
|
||||
})
|
||||
data: {
|
||||
badges: UserBadgeDto[];
|
||||
total: number;
|
||||
};
|
||||
}
|
||||
|
||||
// 获取所有可用勋章响应
|
||||
export class GetAvailableBadgesResponseDto implements BaseResponseDto<AvailableBadgeDto[]> {
|
||||
@ApiProperty({ description: '响应状态码', example: 0 })
|
||||
code: number;
|
||||
|
||||
@ApiProperty({ description: '响应消息', example: 'success' })
|
||||
message: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: '所有可用勋章列表',
|
||||
example: [
|
||||
{
|
||||
code: 'goodSleep',
|
||||
name: '好眠达人',
|
||||
description: '完成首次睡眠挑战',
|
||||
imageUrl: 'https://example.com/badge.png',
|
||||
category: 'sleep',
|
||||
sortOrder: 1,
|
||||
isAwarded: true,
|
||||
awardedAt: '2025-01-14T07:00:00Z',
|
||||
},
|
||||
],
|
||||
})
|
||||
data: AvailableBadgeDto[];
|
||||
}
|
||||
|
||||
// 标记勋章已展示请求
|
||||
export class MarkBadgeShownDto {
|
||||
@ApiProperty({ description: '勋章代码', example: 'goodSleep' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
badgeCode: string;
|
||||
}
|
||||
|
||||
// 标记勋章已展示响应
|
||||
export class MarkBadgeShownResponseDto implements BaseResponseDto<{ success: boolean }> {
|
||||
@ApiProperty({ description: '响应状态码', example: 0 })
|
||||
code: number;
|
||||
|
||||
@ApiProperty({ description: '响应消息', example: 'success' })
|
||||
message: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: '操作结果',
|
||||
example: { success: true },
|
||||
})
|
||||
data: { success: boolean };
|
||||
}
|
||||
Reference in New Issue
Block a user