| 1 | /* |
| 2 | * @Author: nevin |
| 3 | * @Date: 2025-02-18 22:32:02 |
| 4 | * @LastEditTime: 2025-04-27 17:55:50 |
| 5 | * @LastEditors: nevin |
| 6 | * @Description: |
| 7 | */ |
| 8 | import { Injectable, NotFoundException } from '@nestjs/common'; |
| 9 | import { InjectModel } from '@nestjs/mongoose'; |
| 10 | import { Model } from 'mongoose'; |
| 11 | import { paginateModel } from '../../common/paginate/create-pagination'; |
| 12 | import { |
| 13 | Task, |
| 14 | TaskFile, |
| 15 | TaskStatus, |
| 16 | TaskType, |
| 17 | } from '../../db/schema/task.schema'; |
| 18 | import { UserTask } from '../../db/schema/user-task.schema'; |
| 19 | import { CreateTaskDto } from './dto/create-task.dto'; |
| 20 | import { QueryTaskDto } from './dto/query-task.dto'; |
| 21 | import { UpdateTaskDto } from './dto/task.dto'; |
| 22 | import { TaskMaterial } from 'src/db/schema/taskMaterial.schema'; |
| 23 | import { ActionTaskMaterialDto } from './dto/adminTask.dto'; |
| 24 | import { PagerDto } from 'src/common/dto/pager.dto'; |
| 25 | import { AppHttpException } from 'src/filters/http-exception.filter'; |
| 26 | import { ErrHttpBack } from 'src/filters/http-exception.back-code'; |
| 27 | |
| 28 | @Injectable() |
| 29 | export class AdminTaskService { |
| 30 | constructor( |
| 31 | @InjectModel(Task.name) private taskModel: Model<Task>, |
| 32 | @InjectModel(UserTask.name) private userTaskModel: Model<UserTask>, |
| 33 | @InjectModel(TaskMaterial.name) |
| 34 | private taskMaterialModel: Model<TaskMaterial>, |
| 35 | ) {} |
| 36 | |
| 37 | async create(createTaskDto: CreateTaskDto): Promise<Task> { |
| 38 | const createdTask = new this.taskModel({ |
| 39 | ...createTaskDto, |
| 40 | keepTime: 5 * 60, // TODO: 先暂时默认为5分钟 |
| 41 | }); |
| 42 | return createdTask.save(); |
| 43 | } |
| 44 | |
| 45 | /** |
| 46 | * 获取任务列表 |
| 47 | * @param query |
| 48 | * @returns |
| 49 | */ |
| 50 | async findAll(query: QueryTaskDto) { |
| 51 | const { |
| 52 | page = 1, |
| 53 | pageSize = 10, |
| 54 | type, |
| 55 | keyword, |
| 56 | productLevel, |
| 57 | requiresShoppingCart, |
| 58 | status, |
| 59 | } = query; |
| 60 | const filter: any = {}; |
| 61 | |
| 62 | if (type) filter.type = type; |
| 63 | if (productLevel) filter.productLevel = productLevel; |
| 64 | if (requiresShoppingCart !== undefined) |
| 65 | filter.requiresShoppingCart = requiresShoppingCart; |
| 66 | if (keyword) filter.title = new RegExp(keyword, 'i'); |
| 67 | if (status !== undefined) filter.status = status; |
| 68 | |
| 69 | return paginateModel( |
| 70 | this.taskModel, |
| 71 | { |
| 72 | page, |
| 73 | pageSize, |
| 74 | }, |
| 75 | filter, |
| 76 | undefined, |
| 77 | { _id: -1 }, |
| 78 | ); |
| 79 | } |
| 80 | |
| 81 | async findOne(id: string): Promise<Task> { |
| 82 | const task = await this.taskModel.findById(id).exec(); |
| 83 | if (!task) { |
| 84 | throw new NotFoundException('Task not found'); |
| 85 | } |
| 86 | return task; |
| 87 | } |
| 88 | |
| 89 | async update(id: string, data: UpdateTaskDto): Promise<Task> { |
| 90 | const updatedTask = await this.taskModel.findByIdAndUpdate(id, data).exec(); |
| 91 | if (!updatedTask) throw new NotFoundException('Task not found'); |
| 92 | return updatedTask; |
| 93 | } |
| 94 | |
| 95 | /** |
| 96 | * 更新任务状态 |
| 97 | * @param task |
| 98 | * @param status |
| 99 | * @returns |
| 100 | */ |
| 101 | async updateStatus(task: Task, status: TaskStatus): Promise<Task> { |
| 102 | if (status === TaskStatus.ACTIVE && task.type === TaskType.ARTICLE) { |
| 103 | // 查询是否存在任务的素材 |
| 104 | const material = await this.taskMaterialModel.findOne({ |
| 105 | taskId: task.id, |
| 106 | }); |
| 107 | |
| 108 | if (!material) throw new AppHttpException(ErrHttpBack.task_no_material); |
| 109 | } |
| 110 | |
| 111 | const res = await this.taskModel.findByIdAndUpdate( |
| 112 | { _id: task.id }, |
| 113 | { |
| 114 | status, |
| 115 | }, |
| 116 | ); |
| 117 | |
| 118 | return res; |
| 119 | } |
| 120 | |
| 121 | /** |
| 122 | * 更新任务状态 |
| 123 | * @param id |
| 124 | * @param status |
| 125 | * @returns |
| 126 | */ |
| 127 | async delTask(id: string): Promise<Task> { |
| 128 | const res = await this.taskModel.findByIdAndUpdate(id, { |
| 129 | status: TaskStatus.DEL, |
| 130 | }); |
| 131 | |
| 132 | return res; |
| 133 | } |
| 134 | |
| 135 | // 添加任务文件 |
| 136 | async addTaskFile(id: string, file: TaskFile): Promise<TaskFile[]> { |
| 137 | const res = await this.taskModel.findByIdAndUpdate(id, { |
| 138 | $push: { |
| 139 | fileList: file, |
| 140 | }, |
| 141 | }); |
| 142 | return res.fileList; |
| 143 | } |
| 144 | |
| 145 | // 删除任务文件 |
| 146 | async deleteTaskFile(id: string, file: TaskFile): Promise<TaskFile[]> { |
| 147 | const res = await this.taskModel.findByIdAndUpdate(id, { |
| 148 | $pull: { |
| 149 | fileList: file, |
| 150 | }, |
| 151 | }); |
| 152 | return res.fileList; |
| 153 | } |
| 154 | |
| 155 | /** |
| 156 | * 获取任务素材列表 |
| 157 | * @param query |
| 158 | * @returns |
| 159 | */ |
| 160 | async findTaskMaterialList(taskId: string, query: PagerDto) { |
| 161 | const { page = 1, pageSize = 10 } = query; |
| 162 | return paginateModel( |
| 163 | this.taskMaterialModel, |
| 164 | { |
| 165 | page, |
| 166 | pageSize, |
| 167 | }, |
| 168 | { |
| 169 | taskId, |
| 170 | }, |
| 171 | undefined, |
| 172 | { usedCount: 1 }, |
| 173 | ); |
| 174 | } |
| 175 | |
| 176 | /** |
| 177 | * 创建任务素材 |
| 178 | * @param data |
| 179 | * @returns |
| 180 | */ |
| 181 | async createTaskMaterial(data: ActionTaskMaterialDto): Promise<TaskMaterial> { |
| 182 | const taskMaterial = new this.taskMaterialModel(data); |
| 183 | return taskMaterial.save(); |
| 184 | } |
| 185 | |
| 186 | /** |
| 187 | * 更新任务素材 |
| 188 | * @param data |
| 189 | * @returns |
| 190 | */ |
| 191 | async upTaskMaterial( |
| 192 | id: string, |
| 193 | data: ActionTaskMaterialDto, |
| 194 | ): Promise<boolean> { |
| 195 | const res = await this.taskMaterialModel.updateOne({ _id: id }, data); |
| 196 | return res.modifiedCount > 0; |
| 197 | } |
| 198 | |
| 199 | // 根据ID获取 |
| 200 | async getTaskMaterialById(id: string): Promise<TaskMaterial> { |
| 201 | return this.taskMaterialModel.findById(id); |
| 202 | } |
| 203 | |
| 204 | /** |
| 205 | * 删除任务素材 |
| 206 | * @param id |
| 207 | * @returns |
| 208 | */ |
| 209 | async deleteTaskMaterial(id: string): Promise<TaskMaterial> { |
| 210 | const res = await this.taskMaterialModel.findByIdAndDelete(id); |
| 211 | return res; |
| 212 | } |
| 213 | } |
| 214 |