From 477f5b4b79d9c8d5444c2deed33b6c43c81fdf00 Mon Sep 17 00:00:00 2001 From: richarjiang Date: Sat, 16 Aug 2025 13:42:36 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E6=96=87=E7=AB=A0=E6=8E=A7?= =?UTF-8?q?=E5=88=B6=E5=99=A8=E7=9A=84=E5=AE=89=E5=85=A8=E6=80=A7=EF=BC=8C?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0JWT=E8=BA=AB=E4=BB=BD=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E5=AE=88=E5=8D=AB=EF=BC=9B=E4=BC=98=E5=8C=96=E8=AE=AD=E7=BB=83?= =?UTF-8?q?=E8=AE=A1=E5=88=92=E6=9C=8D=E5=8A=A1=EF=BC=8C=E7=AE=80=E5=8C=96?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E7=A1=AE=E4=BF=9D=E4=BD=BF=E7=94=A8=E8=AE=A1=E5=88=92=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E8=AE=AD=E7=BB=83=E4=BC=9A=E8=AF=9D=E6=97=B6=E7=9A=84?= =?UTF-8?q?=E5=87=86=E7=A1=AE=E6=80=A7=EF=BC=9B=E6=9B=B4=E6=96=B0=E8=AE=AD?= =?UTF-8?q?=E7=BB=83=E4=BC=9A=E8=AF=9D=E6=A8=A1=E5=9E=8B=EF=BC=8C=E5=85=81?= =?UTF-8?q?=E8=AE=B8=E8=AE=AD=E7=BB=83=E8=AE=A1=E5=88=92ID=E4=B8=BA?= =?UTF-8?q?=E5=8F=AF=E7=A9=BA=E5=AD=97=E6=AE=B5=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/articles/articles.controller.ts | 5 +- src/training-plans/training-plans.service.ts | 10 ++- src/workouts/models/workout-session.model.ts | 2 +- src/workouts/workouts.service.ts | 69 +++++++++++--------- 4 files changed, 47 insertions(+), 39 deletions(-) diff --git a/src/articles/articles.controller.ts b/src/articles/articles.controller.ts index f2664a8..953b14d 100644 --- a/src/articles/articles.controller.ts +++ b/src/articles/articles.controller.ts @@ -6,7 +6,7 @@ import { CreateArticleDto, QueryArticlesDto, CreateArticleResponseDto, QueryArti @ApiTags('articles') @Controller('articles') -@UseGuards(JwtAuthGuard) + export class ArticlesController { constructor(private readonly articlesService: ArticlesService) { } @@ -15,12 +15,14 @@ export class ArticlesController { @ApiOperation({ summary: '创建文章' }) @ApiBody({ type: CreateArticleDto }) @ApiResponse({ status: 200 }) + @UseGuards(JwtAuthGuard) async create(@Body() dto: CreateArticleDto): Promise { return this.articlesService.create(dto); } @Get('list') @HttpCode(HttpStatus.OK) + @UseGuards(JwtAuthGuard) @ApiOperation({ summary: '查询文章列表(分页)' }) async list(@Query() query: QueryArticlesDto): Promise { return this.articlesService.query(query); @@ -36,6 +38,7 @@ export class ArticlesController { // 增加阅读数 @Post(':id/read-count') @HttpCode(HttpStatus.OK) + @UseGuards(JwtAuthGuard) @ApiOperation({ summary: '增加文章阅读数' }) async increaseReadCount(@Param('id') id: string): Promise { return this.articlesService.increaseReadCount(id); diff --git a/src/training-plans/training-plans.service.ts b/src/training-plans/training-plans.service.ts index 93b0045..91c81a5 100644 --- a/src/training-plans/training-plans.service.ts +++ b/src/training-plans/training-plans.service.ts @@ -17,14 +17,12 @@ export class TrainingPlansService { ) { } async create(userId: string, dto: CreateTrainingPlanDto) { - const id = `plan_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; const createdAt = new Date(); // 检查用户是否有其他激活的训练计划 const activePlans = await this.trainingPlanModel.findAll({ where: { userId, isActive: true, deleted: false } }); const plan = await this.trainingPlanModel.create({ - id, userId, name: dto.name ?? '', createdAt, @@ -37,10 +35,10 @@ export class TrainingPlansService { preferredTimeOfDay: dto.preferredTimeOfDay ?? '', isActive: activePlans.length === 0, }); - this.winstonLogger.info(`create plan ${id} for user ${userId} success`, { + this.winstonLogger.info(`create plan ${plan.id} for user ${userId} success`, { context: 'TrainingPlansService', userId, - id, + planId: plan.id, }); await this.activityLogsService.record({ userId, @@ -49,10 +47,10 @@ export class TrainingPlansService { entityId: plan.id, changes: plan.toJSON(), }); - this.winstonLogger.info(`create plan ${id} for user ${userId} success`, { + this.winstonLogger.info(`create plan ${plan.id} for user ${userId} success`, { context: 'TrainingPlansService', userId, - id, + planId: plan.id, }); return plan.toJSON(); } diff --git a/src/workouts/models/workout-session.model.ts b/src/workouts/models/workout-session.model.ts index cba3150..da45e48 100644 --- a/src/workouts/models/workout-session.model.ts +++ b/src/workouts/models/workout-session.model.ts @@ -20,7 +20,7 @@ export class WorkoutSession extends Model { declare userId: string; @ForeignKey(() => TrainingPlan) - @Column({ type: DataType.UUID, allowNull: false, comment: '关联的训练计划模板' }) + @Column({ type: DataType.UUID, allowNull: true, comment: '关联的训练计划模板' }) declare trainingPlanId: string; @BelongsTo(() => TrainingPlan) diff --git a/src/workouts/workouts.service.ts b/src/workouts/workouts.service.ts index 33d2be4..b60e932 100644 --- a/src/workouts/workouts.service.ts +++ b/src/workouts/workouts.service.ts @@ -50,12 +50,10 @@ export class WorkoutsService { dto.scheduledDate ? new Date(dto.scheduledDate) : new Date(), dto.name ); - } else if (dto.customExercises && dto.customExercises.length > 0) { + } else { // 基于自定义动作创建 return this.createCustomWorkoutSession(userId, dto); - } else { - throw new BadRequestException('必须提供训练计划ID或自定义动作列表'); - } + } } /** @@ -138,29 +136,31 @@ export class WorkoutsService { }, { transaction }); // 2. 创建自定义动作 - for (const customExercise of dto.customExercises!) { - // 如果有exerciseKey,验证动作是否存在 - if (customExercise.exerciseKey) { - const exercise = await this.exerciseModel.findByPk(customExercise.exerciseKey); - if (!exercise) { - throw new NotFoundException(`动作 "${customExercise.exerciseKey}" 不存在`); + if (dto.customExercises && dto.customExercises.length > 0) { + for (const customExercise of dto.customExercises!) { + // 如果有exerciseKey,验证动作是否存在 + if (customExercise.exerciseKey) { + const exercise = await this.exerciseModel.findByPk(customExercise.exerciseKey); + if (!exercise) { + throw new NotFoundException(`动作 "${customExercise.exerciseKey}" 不存在`); + } } - } - await this.workoutExerciseModel.create({ - workoutSessionId: workoutSession.id, - userId, - exerciseKey: customExercise.exerciseKey, - name: customExercise.name, - plannedSets: customExercise.plannedSets, - plannedReps: customExercise.plannedReps, - plannedDurationSec: customExercise.plannedDurationSec, - restSec: customExercise.restSec, - note: customExercise.note || '', - itemType: customExercise.itemType || 'exercise', - status: 'pending', - sortOrder: customExercise.sortOrder, - }, { transaction }); + await this.workoutExerciseModel.create({ + workoutSessionId: workoutSession.id, + userId, + exerciseKey: customExercise.exerciseKey, + name: customExercise.name, + plannedSets: customExercise.plannedSets, + plannedReps: customExercise.plannedReps, + plannedDurationSec: customExercise.plannedDurationSec, + restSec: customExercise.restSec, + note: customExercise.note || '', + itemType: customExercise.itemType || 'exercise', + status: 'pending', + sortOrder: customExercise.sortOrder, + }, { transaction }); + } } await transaction.commit(); @@ -169,10 +169,10 @@ export class WorkoutsService { context: 'WorkoutsService', userId, workoutSessionId: workoutSession.id, - exerciseCount: dto.customExercises!.length, - }); - - return this.getWorkoutSessionDetail(userId, workoutSession.id); + exerciseCount: dto.customExercises!.length, + }); + + return workoutSession.toJSON(); } catch (error) { await transaction.rollback(); throw error; @@ -262,6 +262,14 @@ export class WorkoutsService { throw new BadRequestException('只能开始计划中的训练会话'); } + // 是否有训练动作,没有的话提示添加 + const exercises = await this.workoutExerciseModel.findAll({ + where: { workoutSessionId: sessionId, deleted: false } + }); + if (exercises.length === 0) { + throw new BadRequestException('请先添加训练动作'); + } + const startTime = dto.startedAt ? new Date(dto.startedAt) : new Date(); session.startedAt = startTime; session.status = 'in_progress'; @@ -298,7 +306,6 @@ export class WorkoutsService { include: [ { model: TrainingPlan, - required: false, attributes: ['id', 'name', 'goal'] } ], @@ -308,7 +315,7 @@ export class WorkoutsService { }); return { - sessions: sessions.map(s => s.toJSON()), + sessions, pagination: { page, limit,