feat(challenges): 在挑战列表和详情中添加勋章信息展示
为睡眠挑战类型添加勋章信息支持,在挑战列表和详情接口中返回 sleepChallengeMonth 勋章配置数据。 - 在 ChallengesModule 中注册 BadgeConfig 模型 - 在 ChallengesService 中注入 BadgeConfig 仓库 - 查询列表时,若存在睡眠挑战则预加载勋章配置 - 查询详情时,若为睡眠挑战则附加勋章信息 - 在 DTO 中新增 BadgeInfoDto 接口定义勋章数据结构 - 仅对激活状态的 sleepChallengeMonth 勋章进行查询和展示
This commit is contained in:
@@ -7,10 +7,11 @@ import { ChallengeParticipant } from './models/challenge-participant.model';
|
||||
import { ChallengeProgressReport } from './models/challenge-progress-report.model';
|
||||
import { UsersModule } from '../users/users.module';
|
||||
import { User } from '../users/models/user.model';
|
||||
import { BadgeConfig } from '../users/models/badge-config.model';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
SequelizeModule.forFeature([Challenge, ChallengeParticipant, ChallengeProgressReport, User]),
|
||||
SequelizeModule.forFeature([Challenge, ChallengeParticipant, ChallengeProgressReport, User, BadgeConfig]),
|
||||
UsersModule,
|
||||
],
|
||||
controllers: [ChallengesController],
|
||||
|
||||
@@ -15,6 +15,7 @@ import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||
import { Logger as WinstonLogger } from 'winston';
|
||||
import { BadgeService } from '../users/services/badge.service';
|
||||
import { BadgeSource } from '../users/models/user-badge.model';
|
||||
import { BadgeConfig } from '../users/models/badge-config.model';
|
||||
|
||||
@Injectable()
|
||||
export class ChallengesService {
|
||||
@@ -28,6 +29,8 @@ export class ChallengesService {
|
||||
private readonly participantModel: typeof ChallengeParticipant,
|
||||
@InjectModel(ChallengeProgressReport)
|
||||
private readonly progressReportModel: typeof ChallengeProgressReport,
|
||||
@InjectModel(BadgeConfig)
|
||||
private readonly badgeConfigModel: typeof BadgeConfig,
|
||||
private readonly badgeService: BadgeService,
|
||||
) { }
|
||||
|
||||
@@ -103,6 +106,25 @@ export class ChallengesService {
|
||||
}
|
||||
}
|
||||
|
||||
// 🎖️ 查询 sleepChallengeMonth 勋章信息(仅在有睡眠挑战时查询)
|
||||
const hasSleepChallenge = challengesWithStatus.some(({ challenge }) => challenge.type === ChallengeType.SLEEP);
|
||||
let sleepBadge: ChallengeListItemDto['badge'] = undefined;
|
||||
if (hasSleepChallenge) {
|
||||
const badgeConfig = await this.badgeConfigModel.findOne({
|
||||
where: { code: 'sleepChallengeMonth', isActive: true },
|
||||
});
|
||||
|
||||
if (badgeConfig) {
|
||||
sleepBadge = {
|
||||
code: badgeConfig.code,
|
||||
name: badgeConfig.name,
|
||||
description: badgeConfig.description,
|
||||
imageUrl: badgeConfig.imageUrl,
|
||||
category: badgeConfig.category,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return challengesWithStatus.map(({ challenge, status }) => {
|
||||
const completionTarget = challenge.minimumCheckInDays
|
||||
const participation = participationMap.get(challenge.id);
|
||||
@@ -130,6 +152,7 @@ export class ChallengesService {
|
||||
progress,
|
||||
isJoined: Boolean(participation),
|
||||
type: challenge.type,
|
||||
badge: challenge.type === ChallengeType.SLEEP ? sleepBadge : undefined,
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -198,6 +221,24 @@ export class ChallengesService {
|
||||
|
||||
const userRank = participation ? await this.calculateUserRank(challengeId, participation) : undefined;
|
||||
|
||||
// 🎖️ 如果是睡眠挑战,获取 sleepChallengeMonth 勋章信息
|
||||
let badge: ChallengeDetailDto['badge'] = undefined;
|
||||
if (challenge.type === ChallengeType.SLEEP) {
|
||||
const badgeConfig = await this.badgeConfigModel.findOne({
|
||||
where: { code: 'sleepChallengeMonth', isActive: true },
|
||||
});
|
||||
|
||||
if (badgeConfig) {
|
||||
badge = {
|
||||
code: badgeConfig.code,
|
||||
name: badgeConfig.name,
|
||||
description: badgeConfig.description,
|
||||
imageUrl: badgeConfig.imageUrl,
|
||||
category: badgeConfig.category,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: challenge.id,
|
||||
title: challenge.title,
|
||||
@@ -217,6 +258,7 @@ export class ChallengesService {
|
||||
userRank,
|
||||
unit: challenge.progressUnit,
|
||||
type: challenge.type,
|
||||
badge,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import { ChallengeProgressDto, RankingItemDto } from './challenge-progress.dto';
|
||||
import { ChallengeType } from '../models/challenge.model';
|
||||
|
||||
export interface BadgeInfoDto {
|
||||
code: string;
|
||||
name: string;
|
||||
description: string;
|
||||
imageUrl: string;
|
||||
category: string;
|
||||
}
|
||||
|
||||
export interface ChallengeDetailDto {
|
||||
id: string;
|
||||
title: string;
|
||||
@@ -20,4 +28,5 @@ export interface ChallengeDetailDto {
|
||||
userRank?: number;
|
||||
type: ChallengeType;
|
||||
unit: string;
|
||||
badge?: BadgeInfoDto;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import { ChallengeStatus, ChallengeType } from '../models/challenge.model';
|
||||
import { ChallengeProgressDto } from './challenge-progress.dto';
|
||||
|
||||
export interface BadgeInfoDto {
|
||||
code: string;
|
||||
name: string;
|
||||
description: string;
|
||||
imageUrl: string;
|
||||
category: string;
|
||||
}
|
||||
|
||||
export interface ChallengeListItemDto {
|
||||
id: string;
|
||||
title: string;
|
||||
@@ -21,6 +29,7 @@ export interface ChallengeListItemDto {
|
||||
isJoined: boolean;
|
||||
type: ChallengeType;
|
||||
unit: string;
|
||||
badge?: BadgeInfoDto;
|
||||
}
|
||||
|
||||
export interface ChallengeListResponseDto {
|
||||
|
||||
Reference in New Issue
Block a user