增强文章控制器的安全性,添加JWT身份验证守卫;优化训练计划服务,简化日志记录逻辑,确保使用计划创建训练会话时的准确性;更新训练会话模型,允许训练计划ID为可空字段。

This commit is contained in:
2025-08-16 13:42:36 +08:00
parent fafb618c32
commit 477f5b4b79
4 changed files with 47 additions and 39 deletions

View File

@@ -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)

View File

@@ -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,