返回 AiToEarn
adminUserTask.service.ts
根目录 / project / aitoearn-electron / server / src / modules / task / adminUserTask.service.ts
1 import { Injectable } from '@nestjs/common';
2 import { InjectModel } from '@nestjs/mongoose';
3 import { Model, RootFilterQuery, Types } from 'mongoose';
4 import { paginateModel } from '../../common/paginate/create-pagination';
5 import { Task, TaskType } 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 { AdminQueryUserTaskDto } from './dto/userTask.dto';
9 import { FinanceService } from '../finance/finance.service';
10 import { UserTaskService } from './user-task.service';
11 import { TaskService } from './task.service';
12 import { TaskUtilService } from './util.service';
13 import { AccountType } from '../../db/schema/account.schema';
14
15 @Injectable()
16 export class AdminUserTaskService {
17 constructor(
18 @InjectModel(UserTask.name) private userTaskModel: Model<UserTask>,
19 @InjectModel(Task.name) private taskModel: Model<Task>,
20 private readonly financeService: FinanceService,
21 readonly userTaskService: UserTaskService,
22 readonly taskUtilService: TaskUtilService,
23 readonly taskService: TaskService,
24 ) {}
25
26 async getUserTaskInfoById(id: string): Promise<UserTask> {
27 const task = await this.userTaskModel.findById(id).exec();
28 return task;
29 }
30
31 async getUserTaskInfoByTaskIdOfUser(
32 taskId: string,
33 userId: string,
34 ): Promise<UserTask> {
35 const res = await this.userTaskModel
36 .findOne({
37 userId: new Types.ObjectId(userId),
38 taskId: new Types.ObjectId(taskId),
39 })
40 .exec();
41 return res;
42 }
43
44 /**
45 * 获取用户的任务列表
46 * @param query
47 * @returns
48 */
49 async getList(query: AdminQueryUserTaskDto) {
50 const { page, pageSize, status, keyword, time } = query;
51 const filter: RootFilterQuery<UserTask> = {
52 ...(status !== undefined && { status }),
53 ...(keyword && { title: { $regex: keyword, $options: 'i' } }),
54 ...(time && {
55 createdAt: {
56 $gte: new Date(time[0]),
57 $lte: new Date(time[1]),
58 },
59 }),
60 };
61
62 return paginateModel(
63 this.userTaskModel,
64 {
65 page,
66 pageSize,
67 },
68 filter,
69 'taskId',
70 { _id: -1 },
71 );
72 }
73
74 /**
75 * 获取任务列表进行审核
76 * @param query
77 * @returns
78 */
79 async getTasksForVerification(query: QueryVerificationDto) {
80 const { page = 1, pageSize = 10, status } = query;
81
82 const filter: any = {};
83 if (status) filter.status = status;
84
85 return paginateModel(
86 this.userTaskModel,
87 {
88 page,
89 pageSize,
90 },
91 filter,
92 'taskId',
93 { _id: -1 },
94 );
95 }
96
97 /**
98 * 验证任务-通过
99 * @param userTask
100 * @returns
101 */
102 async verifyUserTaskApproved(
103 userTask: UserTask,
104 data: {
105 verifierUserId?: string;
106 },
107 ): Promise<boolean> {
108 const task = await this.taskModel.findById(userTask.taskId);
109 if (!task) return false;
110
111 const res = await this.userTaskModel.updateOne(
112 { _id: userTask.id },
113 {
114 $set: {
115 status: UserTaskStatus.APPROVED,
116 verificationNote: '审核通过',
117 ...data,
118 rewardTime: new Date(),
119 reward: task.reward,
120 },
121 },
122 );
123
124 // 更新用户余额
125 await this.financeService.updateUserWalletBalance(
126 userTask.userId,
127 task.reward,
128 );
129
130 return res.modifiedCount > 0;
131 }
132
133 /**
134 * 验证任务-拒绝
135 * @param userTask
136 * @returns
137 */
138 async verifyUserTaskRejected(
139 userTask: UserTask,
140 data: {
141 verifierUserId?: string;
142 verificationNote?: string; // 人工核查备注
143 },
144 ): Promise<boolean> {
145 if (userTask.status === UserTaskStatus.PENDING) return false;
146
147 const res = await this.userTaskModel.updateOne(
148 { _id: userTask.id },
149 {
150 $set: {
151 status: UserTaskStatus.REJECTED,
152 ...data,
153 },
154 },
155 );
156
157 // 该对应的任务,发布次数+1
158 await this.taskModel.updateOne(
159 { _id: userTask.taskId },
160 {
161 $inc: {
162 currentRecruits: +1,
163 },
164 },
165 );
166
167 return res.modifiedCount > 0;
168 }
169
170 /**
171 * 验证任务-回退
172 * @param userTask
173 * @returns
174 */
175 async rollbackUserTaskApproved(
176 userTask: UserTask,
177 data: {
178 verifierUserId: string;
179 verificationNote: string; // 人工核查备注
180 },
181 ): Promise<boolean> {
182 const task = await this.taskModel.findById(userTask.taskId);
183 if (!task) return false;
184
185 const res = await this.userTaskModel.updateOne(
186 { _id: userTask.id },
187 {
188 $set: {
189 status: UserTaskStatus.DEL,
190 verificationNote: '任务撤回,并扣除余额',
191 ...data,
192 },
193 },
194 );
195
196 // 更新用户余额
197 await this.financeService.updateUserWalletBalance(
198 userTask.userId,
199 -task.reward,
200 );
201
202 // 该对应的任务,发布次数+1
203 await this.taskModel.updateOne(
204 { _id: userTask.taskId },
205 {
206 $inc: {
207 currentRecruits: +1,
208 },
209 },
210 );
211
212 return res.modifiedCount > 0;
213 }
214
215 // 获取已完成任务的总数
216 getCompletedTaskCount() {
217 return this.userTaskModel.countDocuments({
218 status: UserTaskStatus.APPROVED,
219 });
220 }
221
222 // 获取完成过任务的用户总数
223 async getCompletedUserCount(): Promise<number> {
224 const result = await this.userTaskModel
225 .aggregate([
226 { $match: { status: UserTaskStatus.APPROVED } },
227 { $group: { _id: '$userId' } },
228 { $count: 'uniqueUsers' },
229 ])
230 .exec();
231 return result[0]?.uniqueUsers || 0;
232 }
233
234 /**
235 * 自动审核
236 * @param userTaskId
237 */
238 async autoAuditTask(userTaskId: string): Promise<{
239 status: 0 | 1; // 0:成功 1:失败
240 message: string;
241 retry: boolean;
242 data?: boolean;
243 }> {
244 const userTaskInfo =
245 await this.userTaskService.getUserTaskInfoById(userTaskId);
246 if (!userTaskInfo)
247 return {
248 status: 0,
249 message: '用户任务不存在',
250 retry: false,
251 };
252
253 if (userTaskInfo.status !== UserTaskStatus.PENDING)
254 return {
255 status: 0,
256 message: '用户任务状态错误',
257 retry: false,
258 };
259
260 const taskInfo = await this.taskService.findOne(
261 userTaskInfo.taskId.toString(),
262 );
263 if (!taskInfo)
264 return {
265 status: 0,
266 message: '任务不存在',
267 retry: false,
268 };
269
270 // 获取使用的素材信息
271 const taskMaterialInfo = await this.taskService.getTaskMaterialById(
272 userTaskInfo.taskMaterialId,
273 );
274 if (!taskMaterialInfo)
275 return {
276 status: 0,
277 message: '素材信息不存在',
278 retry: false,
279 };
280
281 if (
282 taskInfo.type === TaskType.ARTICLE &&
283 userTaskInfo.accountType === AccountType.Xhs
284 ) {
285 // 对比文本内容
286 const { status: contentCheckStatus, extent } =
287 await this.taskUtilService.articleXhsContentCheck(
288 taskMaterialInfo.desc,
289 userTaskInfo.submissionUrl,
290 );
291
292 if (contentCheckStatus === 0) {
293 return {
294 status: 0,
295 message: '内容检测失败,请重试',
296 retry: true,
297 };
298 }
299
300 if (extent <= 0.8) {
301 return {
302 status: 1,
303 message: '内容相似度小于80%,请重新提交',
304 retry: false,
305 data: false,
306 };
307 }
308
309 // 进行奖励发放
310 const res = await this.verifyUserTaskApproved(userTaskInfo, {});
311 if (!res) {
312 return {
313 status: 0,
314 message: '自动审核或发放奖励失败,请重试',
315 retry: true,
316 };
317 }
318
319 return {
320 status: 1,
321 message: '成功',
322 retry: false,
323 data: true,
324 };
325 }
326 }
327
328 // 更新用户任务自动状态
329 async updateUserTaskAutoStatus(
330 userTaskId: string,
331 data: { status: 0 | 1; message: string },
332 ): Promise<boolean> {
333 const res = await this.userTaskModel.updateOne(
334 { _id: userTaskId },
335 {
336 $set: {
337 autoData: data,
338 },
339 },
340 );
341
342 return res.modifiedCount > 0;
343 }
344 }
345
345 lines TYPESCRIPT