feat: 删除心情打卡和目标子任务API测试文件

- 移除test-goal-tasks.http和test-mood-checkins.http文件,清理不再使用的测试文件。
- 更新GoalsService中的目标删除逻辑,增加事务处理以确保数据一致性。
- 优化GoalTaskService中的任务生成逻辑,增加日志记录以便于调试和监控。
This commit is contained in:
2025-08-23 14:31:15 +08:00
parent f6b4c99e75
commit cba56021de
5 changed files with 99 additions and 300 deletions

View File

@@ -1,6 +1,6 @@
import { IsString, IsNotEmpty, IsOptional, IsEnum, IsInt, IsDateString, IsBoolean, Min, Max, IsArray, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { GoalRepeatType, GoalStatus } from '../models/goal.model';
import { GoalRepeatType } from '../models/goal.model';
export class CustomRepeatRuleDto {
@IsOptional()

View File

@@ -1,6 +1,7 @@
import { Injectable, Logger, NotFoundException, BadRequestException } from '@nestjs/common';
import { InjectModel } from '@nestjs/sequelize';
import { Op, WhereOptions, Order } from 'sequelize';
import { InjectModel, InjectConnection } from '@nestjs/sequelize';
import { Op, WhereOptions, Order, Transaction } from 'sequelize';
import { Sequelize } from 'sequelize-typescript';
import { Goal, GoalStatus, GoalRepeatType } from './models/goal.model';
import { GoalCompletion } from './models/goal-completion.model';
import { GoalTask } from './models/goal-task.model';
@@ -22,6 +23,8 @@ export class GoalsService {
private readonly goalCompletionModel: typeof GoalCompletion,
@InjectModel(GoalTask)
private readonly goalTaskModel: typeof GoalTask,
@InjectConnection()
private readonly sequelize: Sequelize,
private readonly goalTaskService: GoalTaskService,
) { }
@@ -180,27 +183,58 @@ export class GoalsService {
* 删除目标
*/
async deleteGoal(userId: string, goalId: string): Promise<boolean> {
const transaction = await this.sequelize.transaction();
try {
// 验证目标存在
const goal = await this.goalModel.findOne({
where: { id: goalId, userId, deleted: false },
transaction,
});
if (!goal) {
await transaction.rollback();
throw new NotFoundException('目标不存在');
}
// 软删除目标
await goal.update({ deleted: true });
// 使用事务删除目标及其相关数据
await Promise.all([
// 软删除目标本身
this.goalModel.update(
{ deleted: true },
{
where: { id: goalId, userId, deleted: false },
transaction
}
),
// 软删除目标完成记录
this.goalCompletionModel.update(
{ deleted: true },
{
where: { goalId, userId, deleted: false },
transaction
}
),
// 软删除与目标关联的任务
this.goalTaskModel.update(
{ deleted: true },
{
where: { goalId, userId, deleted: false },
transaction
}
),
]);
// 软删除相关的完成记录
await this.goalCompletionModel.update(
{ deleted: true },
{ where: { goalId, userId } }
);
// 提交事务
await transaction.commit();
this.logger.log(`用户 ${userId} 删除了目标: ${goal.title}`);
return true;
} catch (error) {
// 回滚事务
await transaction.rollback();
this.logger.error(`删除目标失败: ${error.message}`);
throw error;
}

View File

@@ -42,8 +42,11 @@ export class GoalTaskService {
}
const goals = await this.goalModel.findAll({ where });
this.logger.log(`为用户 ${userId} 找到 ${goals.length} 个活跃目标`);
for (const goal of goals) {
this.logger.log(`开始为目标 ${goal.title} (${goal.repeatType}) 生成任务`);
await this.generateTasksForGoal(goal);
}
} catch (error) {
@@ -83,6 +86,9 @@ export class GoalTaskService {
case GoalRepeatType.CUSTOM:
await this.generateCustomTasks(goal, startDate, endDate, existingTasks);
break;
default:
this.logger.warn(`未知的重复类型: ${goal.repeatType}`);
break;
}
// 更新过期任务状态
@@ -191,41 +197,66 @@ export class GoalTaskService {
existingTasks: GoalTask[]
): Promise<void> {
const today = dayjs();
const generateUntil = today.add(3, 'month'); // 提前生成3个月的任务
const generateUntil = today.add(6, 'month'); // 提前生成6个月的任务
const actualEndDate = endDate.isBefore(generateUntil) ? endDate : generateUntil;
// 检查是否有自定义重复规则指定每月第几天
let targetDayOfMonth = 1; // 默认每月1号
if (goal.customRepeatRule && goal.customRepeatRule.dayOfMonth) {
targetDayOfMonth = goal.customRepeatRule.dayOfMonth;
this.logger.log(`目标 ${goal.title} 设置为每月第 ${targetDayOfMonth}`);
}
// 从开始日期开始,逐月生成任务
let current = startDate.startOf('month');
let generatedCount = 0;
this.logger.log(`开始生成每月任务,目标日期:每月第 ${targetDayOfMonth}`);
while (current.isSameOrBefore(actualEndDate)) {
const monthStart = current.startOf('month');
const monthEnd = current.endOf('month');
// 计算该月的目标日期
const targetDate = current.date(targetDayOfMonth);
// 如果目标日期超出了该月的天数,则使用该月的最后一天
const daysInMonth = current.daysInMonth();
const actualTargetDate = targetDayOfMonth > daysInMonth ? current.date(daysInMonth) : targetDate;
// 检查是否已经过了该月的目标日期
if (actualTargetDate.isBefore(today)) {
this.logger.log(`跳过 ${current.format('YYYY年MM月')},目标日期 ${actualTargetDate.format('MM-DD')} 已过期`);
current = current.add(1, 'month');
continue;
}
// 检查是否已存在该月的任务
const existingTask = existingTasks.find(task => {
const taskMonthStart = dayjs(task.startDate).startOf('month');
return taskMonthStart.isSame(monthStart);
const taskDate = dayjs(task.startDate);
return taskDate.isSame(actualTargetDate, 'day');
});
if (!existingTask && monthStart.isSameOrAfter(startDate)) {
const taskTitle = `${goal.title} - ${current.format('YYYY年MM月')}`;
if (!existingTask && actualTargetDate.isSameOrAfter(startDate)) {
const taskTitle = `${goal.title} - ${actualTargetDate.format('YYYY年MM月DD日')}`;
await this.goalTaskModel.create({
goalId: goal.id,
userId: goal.userId,
title: taskTitle,
description: `每月目标:完成${goal.frequency}`,
startDate: monthStart.toDate(),
endDate: monthEnd.toDate(),
startDate: actualTargetDate.toDate(),
endDate: actualTargetDate.toDate(), // 任务在当天完成
targetCount: goal.frequency,
currentCount: 0,
status: TaskStatus.PENDING,
});
generatedCount++;
this.logger.log(`为目标 ${goal.title} 生成每月任务: ${taskTitle}`);
}
current = current.add(1, 'month');
}
this.logger.log(`为目标 ${goal.title} 生成了 ${generatedCount} 个每月任务`);
}
/**
@@ -238,18 +269,26 @@ export class GoalTaskService {
existingTasks: GoalTask[]
): Promise<void> {
if (!goal.customRepeatRule) {
this.logger.warn(`目标 ${goal.title} 缺少自定义重复规则`);
return;
}
const { weekdays } = goal.customRepeatRule;
this.logger.log(`为目标 ${goal.title} 生成自定义任务,重复规则: ${JSON.stringify(goal.customRepeatRule)}`);
if (weekdays && weekdays.length > 0) {
// 按指定星期几生成任务
const today = dayjs();
const generateUntil = today.add(7, 'day');
const generateUntil = today.add(4, 'week'); // 提前生成4周的任务确保有足够的任务
const actualEndDate = endDate.isBefore(generateUntil) ? endDate : generateUntil;
let current = startDate;
// 从今天开始生成,如果开始日期晚于今天则从开始日期开始
let current = startDate.isBefore(today) ? today : startDate;
let generatedCount = 0;
this.logger.log(`开始生成自定义任务,日期范围: ${current.format('YYYY-MM-DD')}${actualEndDate.format('YYYY-MM-DD')}`);
while (current.isSameOrBefore(actualEndDate)) {
const dayOfWeek = current.day(); // 0=周日, 6=周六
@@ -277,12 +316,17 @@ export class GoalTaskService {
status: TaskStatus.PENDING,
});
generatedCount++;
this.logger.log(`为目标 ${goal.title} 生成自定义任务: ${taskTitle}`);
}
}
current = current.add(1, 'day');
}
this.logger.log(`为目标 ${goal.title} 生成了 ${generatedCount} 个自定义任务`);
} else {
this.logger.warn(`目标 ${goal.title} 的自定义重复规则中没有指定星期几`);
}
}