返回 AiToEarn
user-task.service.ts
根目录 / project / aitoearn-electron / server / src / modules / task / user-task.service.ts
1 import { BadRequestException, Injectable } from '@nestjs/common';
2 import { InjectModel } from '@nestjs/mongoose';
3 import { Model, Types } from 'mongoose';
4 import { paginateModel } from '../../common/paginate/create-pagination';
5 import { Task } from '../../db/schema/task.schema';
6 import { UserTask, UserTaskStatus } from '../../db/schema/user-task.schema';
7 import { QueryVerificationDto } from './dto/query-verification.dto';
8 import { QueryMineTaskDto } from './dto/query-task.dto';
9 import { SubmitTaskDto } from './dto/submit-task.dto';
10 import { ApplyTaskDto } from './dto/applyTask.dto';
11
12 @Injectable()
13 export class UserTaskService {
14 constructor(
15 @InjectModel(UserTask.name) private userTaskModel: Model<UserTask>,
16 @InjectModel(Task.name) private taskModel: Model<Task>,
17 ) {}
18
19 async getUserTaskInfoById(id: string): Promise<UserTask> {
20 const task = await this.userTaskModel.findById(id).exec();
21 return task;
22 }
23
24 async getUserTaskInfoByTaskIdOfUser(
25 taskId: string,
26 userId: string,
27 ): Promise<UserTask> {
28 const res = await this.userTaskModel
29 .findOne({
30 userId: new Types.ObjectId(userId),
31 taskId: new Types.ObjectId(taskId),
32 })
33 .exec();
34 return res;
35 }
36
37 /**
38 * 用户接受任务
39 * @param userId
40 * @param taskId
41 * @returns
42 */
43 async userApplyTask(
44 userId: string,
45 task: Task,
46 account: ApplyTaskDto,
47 ): Promise<{
48 code: number;
49 msg: string;
50 data: UserTask;
51 }> {
52 const res = {
53 code: 0,
54 msg: '任务申请中',
55 data: null,
56 };
57
58 // 最大人数
59 if (task.currentRecruits >= task.maxRecruits) {
60 res.msg = '该任务已超过最大人数';
61 return res;
62 }
63
64 const session = await this.userTaskModel.db.startSession();
65 session.startTransaction();
66
67 try {
68 const oldUserTask = await this.userTaskModel
69 .findOne({
70 userId: new Types.ObjectId(userId),
71 taskId: new Types.ObjectId(task.id),
72 ...account,
73 })
74 .session(session);
75
76 if (!!oldUserTask) {
77 res.msg = '你已经用该账号接受过该任务';
78 return res;
79 }
80
81 // 创建
82 const userTask = new this.userTaskModel({
83 userId: new Types.ObjectId(userId),
84 taskId: new Types.ObjectId(task.id),
85 status: UserTaskStatus.DODING,
86 isFirstTimeSubmission: true,
87 reward: task.reward,
88 ...account,
89 });
90
91 // Increment current recruits and applicant count
92 await this.taskModel
93 .findByIdAndUpdate(task.id, {
94 $inc: {
95 currentRecruits: 1,
96 applicantCount: 1,
97 },
98 })
99 .session(session);
100
101 const newData = await userTask.save({ session });
102
103 await session.commitTransaction();
104 session.endSession();
105
106 return {
107 code: 0,
108 msg: '任务申请完成',
109 data: newData,
110 };
111 } catch (error) {
112 await session.abortTransaction();
113 session.endSession();
114 throw error;
115 }
116 }
117
118 /**
119 * 提交任务
120 * @param userTask
121 * @param data
122 * @returns
123 */
124 async submitTask(userTask: UserTask, data: SubmitTaskDto): Promise<UserTask> {
125 const userTaskD = await this.userTaskModel.findById(userTask.id);
126
127 // 只有拒绝和进行中的才能提交
128 if (
129 ![UserTaskStatus.REJECTED, UserTaskStatus.DODING].includes(
130 userTask.status,
131 )
132 ) {
133 throw new BadRequestException('Cannot submit task in current status');
134 }
135
136 userTaskD.submissionUrl = data.submissionUrl;
137 userTaskD.screenshotUrls = data.screenshotUrls;
138 userTaskD.qrCodeScanResult = data.qrCodeScanResult;
139 userTaskD.submissionTime = new Date();
140 userTaskD.status = UserTaskStatus.PENDING;
141
142 // 保存
143 return userTaskD.save();
144 }
145
146 /**
147 * 获取用户的任务列表
148 * @param userId
149 * @param query
150 * @returns
151 */
152 async getUserTasks(userId: string, query: QueryMineTaskDto) {
153 const { page, pageSize, status } = query;
154 const filter: any = { userId: new Types.ObjectId(userId) };
155
156 if (status) filter.status = status;
157
158 return paginateModel(
159 this.userTaskModel,
160 {
161 page,
162 pageSize,
163 },
164 filter,
165 'taskId',
166 { _id: -1 },
167 );
168 }
169
170 /**
171 * 获取任务列表进行审核
172 * @param query
173 * @returns
174 */
175 async getTasksForVerification(query: QueryVerificationDto) {
176 const { page = 1, pageSize = 10, status } = query;
177
178 const filter: any = {};
179 if (status) filter.status = status;
180
181 return paginateModel(
182 this.userTaskModel,
183 {
184 page,
185 pageSize,
186 },
187 filter,
188 ['taskId', 'userId'],
189 { _id: -1 },
190 );
191 }
192
193 // TODO: 定时任务,每天凌晨1点清理过期的用户任务,并补充回任务的剩余量
194 // @Cron('0 1 * * *')
195 async clearExpiredUserTasks() {
196 const now = new Date();
197 const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000);
198
199 const expiredUserTasks = await this.userTaskModel.find({
200 status: UserTaskStatus.DODING,
201 createdAt: { $lt: oneDayAgo },
202 });
203
204 for (const userTask of expiredUserTasks) {
205 if (
206 userTask.createTime.getTime() + userTask.keepTime * 1000 >
207 now.getTime()
208 )
209 continue;
210
211 const task = await this.taskModel.findById(userTask.taskId);
212 if (task) {
213 task.currentRecruits += 1;
214 await task.save();
215 }
216 // 删除过期的用户任务
217 await this.userTaskModel.findByIdAndDelete(userTask._id);
218 }
219 }
220 }
221
221 lines TYPESCRIPT