feat: 新增目标子任务管理功能模块
- 实现目标子任务的完整功能,包括数据库表设计、API接口、业务逻辑和文档说明。 - 支持用户创建、管理和跟踪目标子任务,提供增删改查操作及任务完成记录功能。 - 引入惰性任务生成机制,优化任务管理体验,提升系统性能和用户交互。
This commit is contained in:
@@ -1,17 +1,30 @@
|
||||
import { Injectable, Logger, NotFoundException, BadRequestException } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/sequelize';
|
||||
import { Op, WhereOptions, Order } from 'sequelize';
|
||||
import { Goal, GoalStatus, GoalRepeatType } from './models/goal.model';
|
||||
import { GoalCompletion } from './models/goal-completion.model';
|
||||
import { GoalTask } from './models/goal-task.model';
|
||||
import { CreateGoalDto } from './dto/create-goal.dto';
|
||||
import { UpdateGoalDto } from './dto/update-goal.dto';
|
||||
import { GoalQueryDto } from './dto/goal-query.dto';
|
||||
import { CreateGoalCompletionDto } from './dto/goal-completion.dto';
|
||||
import { GoalTaskService } from './services/goal-task.service';
|
||||
import * as dayjs from 'dayjs';
|
||||
|
||||
@Injectable()
|
||||
export class GoalsService {
|
||||
private readonly logger = new Logger(GoalsService.name);
|
||||
|
||||
constructor(
|
||||
@InjectModel(Goal)
|
||||
private readonly goalModel: typeof Goal,
|
||||
@InjectModel(GoalCompletion)
|
||||
private readonly goalCompletionModel: typeof GoalCompletion,
|
||||
@InjectModel(GoalTask)
|
||||
private readonly goalTaskModel: typeof GoalTask,
|
||||
private readonly goalTaskService: GoalTaskService,
|
||||
) { }
|
||||
|
||||
/**
|
||||
* 创建目标
|
||||
*/
|
||||
@@ -27,13 +40,13 @@ export class GoalsService {
|
||||
throw new BadRequestException('结束日期不能早于开始日期');
|
||||
}
|
||||
|
||||
const goal = await Goal.create({
|
||||
const goal = await this.goalModel.create({
|
||||
userId,
|
||||
...createGoalDto,
|
||||
startDate: createGoalDto.startDate ? new Date(createGoalDto.startDate) : null,
|
||||
endDate: createGoalDto.endDate ? new Date(createGoalDto.endDate) : null,
|
||||
startTime: createGoalDto.startTime ? createGoalDto.startTime : null,
|
||||
endTime: createGoalDto.endTime ? createGoalDto.endTime : null,
|
||||
startDate: createGoalDto.startDate ? new Date(createGoalDto.startDate) : undefined,
|
||||
endDate: createGoalDto.endDate ? new Date(createGoalDto.endDate) : undefined,
|
||||
startTime: createGoalDto.startTime ? createGoalDto.startTime : undefined,
|
||||
endTime: createGoalDto.endTime ? createGoalDto.endTime : undefined,
|
||||
});
|
||||
|
||||
this.logger.log(`用户 ${userId} 创建了目标: ${goal.title}`);
|
||||
@@ -49,6 +62,9 @@ export class GoalsService {
|
||||
*/
|
||||
async getGoals(userId: string, query: GoalQueryDto) {
|
||||
try {
|
||||
// 惰性生成任务
|
||||
await this.goalTaskService.generateTasksLazily(userId);
|
||||
|
||||
const { page = 1, pageSize = 20, status, repeatType, category, startDate, endDate, sortBy = 'createdAt', sortOrder = 'desc' } = query;
|
||||
const offset = (page - 1) * pageSize;
|
||||
|
||||
@@ -85,7 +101,7 @@ export class GoalsService {
|
||||
// 构建排序条件
|
||||
const order: Order = [[sortBy, sortOrder.toUpperCase()]];
|
||||
|
||||
const { rows: goals, count } = await Goal.findAndCountAll({
|
||||
const { rows: goals, count } = await this.goalModel.findAndCountAll({
|
||||
where,
|
||||
order,
|
||||
offset,
|
||||
@@ -97,6 +113,14 @@ export class GoalsService {
|
||||
where: { deleted: false },
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
model: GoalTask,
|
||||
as: 'tasks',
|
||||
where: { deleted: false },
|
||||
required: false,
|
||||
limit: 5, // 只显示最近5个任务
|
||||
order: [['startDate', 'DESC']],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -117,7 +141,10 @@ export class GoalsService {
|
||||
*/
|
||||
async getGoal(userId: string, goalId: string): Promise<Goal> {
|
||||
try {
|
||||
const goal = await Goal.findOne({
|
||||
// 惰性生成任务
|
||||
await this.goalTaskService.generateTasksLazily(userId, goalId);
|
||||
|
||||
const goal = await this.goalModel.findOne({
|
||||
where: { id: goalId, userId, deleted: false },
|
||||
include: [
|
||||
{
|
||||
@@ -128,6 +155,14 @@ export class GoalsService {
|
||||
order: [['completedAt', 'DESC']],
|
||||
limit: 10, // 只显示最近10次完成记录
|
||||
},
|
||||
{
|
||||
model: GoalTask,
|
||||
as: 'tasks',
|
||||
where: { deleted: false },
|
||||
required: false,
|
||||
order: [['startDate', 'ASC']],
|
||||
limit: 20, // 显示最近20个任务
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -147,7 +182,7 @@ export class GoalsService {
|
||||
*/
|
||||
async updateGoal(userId: string, goalId: string, updateGoalDto: UpdateGoalDto): Promise<Goal> {
|
||||
try {
|
||||
const goal = await Goal.findOne({
|
||||
const goal = await this.goalModel.findOne({
|
||||
where: { id: goalId, userId, deleted: false },
|
||||
});
|
||||
|
||||
@@ -186,7 +221,7 @@ export class GoalsService {
|
||||
*/
|
||||
async deleteGoal(userId: string, goalId: string): Promise<boolean> {
|
||||
try {
|
||||
const goal = await Goal.findOne({
|
||||
const goal = await this.goalModel.findOne({
|
||||
where: { id: goalId, userId, deleted: false },
|
||||
});
|
||||
|
||||
@@ -198,7 +233,7 @@ export class GoalsService {
|
||||
await goal.update({ deleted: true });
|
||||
|
||||
// 软删除相关的完成记录
|
||||
await GoalCompletion.update(
|
||||
await this.goalCompletionModel.update(
|
||||
{ deleted: true },
|
||||
{ where: { goalId, userId } }
|
||||
);
|
||||
@@ -216,7 +251,7 @@ export class GoalsService {
|
||||
*/
|
||||
async completeGoal(userId: string, createCompletionDto: CreateGoalCompletionDto): Promise<GoalCompletion> {
|
||||
try {
|
||||
const goal = await Goal.findOne({
|
||||
const goal = await this.goalModel.findOne({
|
||||
where: { id: createCompletionDto.goalId, userId, deleted: false },
|
||||
});
|
||||
|
||||
@@ -232,7 +267,7 @@ export class GoalsService {
|
||||
const completedAt = createCompletionDto.completedAt ? new Date(createCompletionDto.completedAt) : new Date();
|
||||
|
||||
// 创建完成记录
|
||||
const completion = await GoalCompletion.create({
|
||||
const completion = await this.goalCompletionModel.create({
|
||||
goalId: createCompletionDto.goalId,
|
||||
userId,
|
||||
completedAt,
|
||||
@@ -266,7 +301,7 @@ export class GoalsService {
|
||||
const offset = (page - 1) * pageSize;
|
||||
|
||||
// 验证目标存在
|
||||
const goal = await Goal.findOne({
|
||||
const goal = await this.goalModel.findOne({
|
||||
where: { id: goalId, userId, deleted: false },
|
||||
});
|
||||
|
||||
@@ -291,7 +326,7 @@ export class GoalsService {
|
||||
}
|
||||
}
|
||||
|
||||
const { rows: completions, count } = await GoalCompletion.findAndCountAll({
|
||||
const { rows: completions, count } = await this.goalCompletionModel.findAndCountAll({
|
||||
where,
|
||||
order: [['completedAt', 'DESC']],
|
||||
offset,
|
||||
@@ -322,7 +357,7 @@ export class GoalsService {
|
||||
*/
|
||||
async getGoalStats(userId: string) {
|
||||
try {
|
||||
const goals = await Goal.findAll({
|
||||
const goals = await this.goalModel.findAll({
|
||||
where: { userId, deleted: false },
|
||||
include: [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user