feat: 删除心情打卡和目标子任务API测试文件
- 移除test-goal-tasks.http和test-mood-checkins.http文件,清理不再使用的测试文件。 - 更新GoalsService中的目标删除逻辑,增加事务处理以确保数据一致性。 - 优化GoalTaskService中的任务生成逻辑,增加日志记录以便于调试和监控。
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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} 的自定义重复规则中没有指定星期几`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user