Files
plates-server/src/users/dto/badge.dto.ts
richarjiang 7b4d7c4459 feat(badges): 添加用户勋章系统,支持睡眠挑战勋章自动授予
实现完整的用户勋章功能模块:
- 新增 BadgeConfig 和 UserBadge 数据模型,支持勋章配置和用户勋章管理
- 新增 BadgeService 服务,提供勋章授予、查询、展示状态管理等核心功能
- 在挑战服务中集成勋章授予逻辑,完成首次睡眠打卡授予 goodSleep 勋章,完成睡眠挑战授予 sleepChallengeMonth 勋章
- 新增用户勋章相关接口:获取用户勋章列表、获取可用勋章列表、标记勋章已展示
- 支持勋章分类(睡眠、运动、饮食等)、排序、启用状态管理
- 支持勋章来源追踪(挑战、系统、手动授予)和元数据记录
2025-11-14 17:08:02 +08:00

157 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 };
}