| 1 | /* |
| 2 | * @Author: nevin |
| 3 | * @Date: 2024-06-17 19:19:15 |
| 4 | * @LastEditTime: 2024-09-05 15:19:25 |
| 5 | * @LastEditors: nevin |
| 6 | * @Description: PublishRecord |
| 7 | */ |
| 8 | import { Injectable } from '@nestjs/common' |
| 9 | import { InjectModel } from '@nestjs/mongoose' |
| 10 | import { AccountType, POST_DATA_UNAVAILABLE_WORK_STATUSES, WorkStatus } from '@yikart/common' |
| 11 | import { Model, RootFilterQuery, UpdateQuery } from 'mongoose' |
| 12 | import { PublishRecordSource, PublishStatus, PublishType } from '../enums' |
| 13 | import { PublishDayInfo, PublishInfo, PublishRecord } from '../schemas' |
| 14 | import { BaseRepository } from './base.repository' |
| 15 | |
| 16 | export interface PublishRecordPostDataCrawlerMonitorItem { |
| 17 | publishRecordId: string |
| 18 | materialGroupId?: string |
| 19 | dataId: string |
| 20 | taskId?: string |
| 21 | accountType: AccountType |
| 22 | accountId?: string |
| 23 | uid?: string |
| 24 | workLink?: string |
| 25 | originalWorkLink?: string |
| 26 | publishTime?: Date |
| 27 | status: PublishStatus |
| 28 | workStatus?: WorkStatus |
| 29 | errorMessage?: string |
| 30 | createdAt?: Date |
| 31 | updatedAt?: Date |
| 32 | } |
| 33 | |
| 34 | @Injectable() |
| 35 | export class PublishRecordRepository extends BaseRepository<PublishRecord> { |
| 36 | constructor( |
| 37 | @InjectModel(PublishRecord.name) |
| 38 | private readonly publishRecordModel: Model<PublishRecord>, |
| 39 | @InjectModel(PublishInfo.name) |
| 40 | private readonly publishInfoModel: Model<PublishInfo>, |
| 41 | @InjectModel(PublishDayInfo.name) |
| 42 | private readonly publishDayInfoModel: Model<PublishDayInfo>, |
| 43 | ) { |
| 44 | super(publishRecordModel) |
| 45 | } |
| 46 | |
| 47 | /** |
| 48 | * 创建 |
| 49 | * @param data |
| 50 | * @returns |
| 51 | */ |
| 52 | override async create(data: Partial<PublishRecord>) { |
| 53 | const res = await this.publishRecordModel.create(data) |
| 54 | return res |
| 55 | } |
| 56 | |
| 57 | /** |
| 58 | * 获取发布记录列表 |
| 59 | * @param query |
| 60 | * @returns |
| 61 | */ |
| 62 | async getPublishRecordList( |
| 63 | query: { |
| 64 | userId: string |
| 65 | accountId?: string |
| 66 | accountType?: AccountType |
| 67 | status?: PublishStatus |
| 68 | type?: PublishType |
| 69 | source?: PublishRecordSource |
| 70 | time?: [Date, Date] |
| 71 | uid?: string |
| 72 | }, |
| 73 | ): Promise<PublishRecord[]> { |
| 74 | const filters: RootFilterQuery<PublishRecord> = { |
| 75 | userId: query.userId, |
| 76 | source: query.source ?? { $nin: [PublishRecordSource.TaskLink, PublishRecordSource.OfflineQr] }, |
| 77 | ...(query.accountId !== undefined && { accountId: query.accountId }), |
| 78 | ...(query.accountType !== undefined && { |
| 79 | accountType: query.accountType, |
| 80 | }), |
| 81 | ...(query.status !== undefined && { |
| 82 | status: query.status, |
| 83 | }), |
| 84 | ...(query.type !== undefined && { type: query.type }), |
| 85 | ...(query.time !== undefined |
| 86 | && query.time.length === 2 && { |
| 87 | publishTime: { $gte: query.time[0], $lte: query.time[1] }, |
| 88 | }), |
| 89 | ...(query.uid !== undefined && { uid: query.uid }), |
| 90 | } |
| 91 | const db = this.publishRecordModel.find(filters).sort({ |
| 92 | createdAt: -1, |
| 93 | }).lean({ virtuals: true }) |
| 94 | const list = await db.exec() |
| 95 | |
| 96 | return list |
| 97 | } |
| 98 | |
| 99 | async getQueuedPublishRecords(query: { |
| 100 | userId: string |
| 101 | accountId?: string |
| 102 | accountType?: AccountType |
| 103 | source?: PublishRecordSource |
| 104 | time?: [Date, Date] |
| 105 | }): Promise<PublishRecord[]> { |
| 106 | // status not equal published |
| 107 | const filters: RootFilterQuery<PublishRecord> = { |
| 108 | status: { $ne: PublishStatus.Published }, |
| 109 | userId: query.userId, |
| 110 | source: query.source ?? { $nin: [PublishRecordSource.TaskLink, PublishRecordSource.OfflineQr] }, |
| 111 | } |
| 112 | if (query.accountId) { |
| 113 | filters.accountId = query.accountId |
| 114 | } |
| 115 | if (query.accountType) { |
| 116 | filters.accountType = query.accountType |
| 117 | } |
| 118 | if (query.time && query.time.length === 2) { |
| 119 | filters.publishTime = { $gte: query.time[0], $lte: query.time[1] } |
| 120 | } |
| 121 | return this.publishRecordModel.find(filters).sort({ |
| 122 | createdAt: -1, |
| 123 | }).lean({ virtuals: true }) |
| 124 | } |
| 125 | |
| 126 | async countPublishRecords(filter: RootFilterQuery<PublishRecord>) { |
| 127 | return this.publishRecordModel.countDocuments(filter).exec() |
| 128 | } |
| 129 | |
| 130 | async groupPublishRecords( |
| 131 | filter: RootFilterQuery<PublishRecord>, |
| 132 | field: keyof PublishRecord, |
| 133 | ) { |
| 134 | return this.publishRecordModel.aggregate<{ _id: string | number | null, count: number }>([ |
| 135 | { $match: filter }, |
| 136 | { $group: { _id: `$${String(field)}`, count: { $sum: 1 } } }, |
| 137 | { $sort: { count: -1 } }, |
| 138 | ]).exec() |
| 139 | } |
| 140 | |
| 141 | async getPublishRecordTrend(filter: RootFilterQuery<PublishRecord>) { |
| 142 | return this.publishRecordModel.aggregate<{ _id: string, count: number }>([ |
| 143 | { $match: filter }, |
| 144 | { |
| 145 | $group: { |
| 146 | _id: { |
| 147 | $dateToString: { |
| 148 | format: '%Y-%m-%d', |
| 149 | date: '$publishTime', |
| 150 | timezone: 'Asia/Shanghai', |
| 151 | }, |
| 152 | }, |
| 153 | count: { $sum: 1 }, |
| 154 | }, |
| 155 | }, |
| 156 | { $sort: { _id: 1 } }, |
| 157 | ]).exec() |
| 158 | } |
| 159 | |
| 160 | async getAdminPublishRecordList( |
| 161 | filter: RootFilterQuery<PublishRecord>, |
| 162 | page: number, |
| 163 | pageSize: number, |
| 164 | ) { |
| 165 | return this.publishRecordModel |
| 166 | .find(filter) |
| 167 | .sort({ publishTime: -1, createdAt: -1 }) |
| 168 | .skip((page - 1) * pageSize) |
| 169 | .limit(pageSize) |
| 170 | .lean({ virtuals: true }) |
| 171 | .exec() |
| 172 | } |
| 173 | |
| 174 | // 获取发布记录信息 |
| 175 | async getPublishRecordInfo(id: string) { |
| 176 | return this.publishRecordModel.findOne({ _id: id }).lean({ virtuals: true }) |
| 177 | } |
| 178 | |
| 179 | async getByAccountTypeAndPlatformWorkId(accountType: AccountType, platformWorkId: string) { |
| 180 | return this.publishRecordModel.findOne({ accountType, platformWorkId }).lean({ virtuals: true }).exec() |
| 181 | } |
| 182 | |
| 183 | async getByAccountTypeAndDataId(accountType: AccountType, dataId: string) { |
| 184 | return this.publishRecordModel.findOne({ accountType, dataId }).lean({ virtuals: true }).exec() |
| 185 | } |
| 186 | |
| 187 | // 删除发布记录 |
| 188 | async deletePublishRecordById(id: string): Promise<boolean> { |
| 189 | const res = await this.publishRecordModel.deleteOne({ _id: id }) |
| 190 | return res.deletedCount > 0 |
| 191 | } |
| 192 | |
| 193 | // 更新 |
| 194 | async updatePublishRecord( |
| 195 | filter: RootFilterQuery<PublishRecord>, |
| 196 | data: Partial<PublishRecord>, |
| 197 | ) { |
| 198 | const res = await this.publishRecordModel.updateOne(filter, { $set: data }) |
| 199 | return res.modifiedCount > 0 |
| 200 | } |
| 201 | |
| 202 | async updateByIdAndStatuses( |
| 203 | id: string, |
| 204 | statuses: readonly PublishStatus[], |
| 205 | update: UpdateQuery<PublishRecord>, |
| 206 | ): Promise<PublishRecord | null> { |
| 207 | return this.publishRecordModel |
| 208 | .findOneAndUpdate( |
| 209 | { _id: id, status: { $in: statuses } }, |
| 210 | update, |
| 211 | { new: true }, |
| 212 | ) |
| 213 | .lean({ virtuals: true }) |
| 214 | .exec() |
| 215 | } |
| 216 | |
| 217 | /** |
| 218 | * 创建 |
| 219 | * @param data |
| 220 | * @returns |
| 221 | */ |
| 222 | async createPublishInfo(data: Partial<PublishInfo>) { |
| 223 | const res = await this.publishInfoModel.create(data) |
| 224 | return res |
| 225 | } |
| 226 | |
| 227 | /** |
| 228 | * change day publish info |
| 229 | * if data had publish record, update it |
| 230 | * @param data |
| 231 | */ |
| 232 | async upDayPublishInfo(data: Pick<PublishRecord, 'userId'>) { |
| 233 | const today = new Date() |
| 234 | return this.publishDayInfoModel |
| 235 | .findOneAndUpdate( |
| 236 | { |
| 237 | userId: data.userId, |
| 238 | createdAt: { |
| 239 | $gte: new Date( |
| 240 | today.getFullYear(), |
| 241 | today.getMonth(), |
| 242 | today.getDate(), |
| 243 | ), |
| 244 | $lt: new Date( |
| 245 | today.getFullYear(), |
| 246 | today.getMonth(), |
| 247 | today.getDate() + 1, |
| 248 | ), |
| 249 | }, |
| 250 | }, |
| 251 | { |
| 252 | $inc: { publishTotal: 1 }, |
| 253 | }, |
| 254 | { |
| 255 | upsert: true, |
| 256 | new: true, |
| 257 | }, |
| 258 | ) |
| 259 | .lean({ virtuals: true }) |
| 260 | .exec() |
| 261 | } |
| 262 | |
| 263 | /** |
| 264 | * 获取发布每日信息列表 |
| 265 | * @param inFilter |
| 266 | * @param pageInfo |
| 267 | * @returns |
| 268 | */ |
| 269 | async getPublishDayInfoList( |
| 270 | inFilter: { |
| 271 | userId: string |
| 272 | time?: [Date, Date] |
| 273 | }, |
| 274 | pageInfo: { |
| 275 | pageNo: number |
| 276 | pageSize: number |
| 277 | }, |
| 278 | ) { |
| 279 | const { pageNo, pageSize } = pageInfo |
| 280 | const filter: RootFilterQuery<PublishDayInfo> = { |
| 281 | userId: inFilter.userId, |
| 282 | ...(inFilter.time && { |
| 283 | createdAt: { $gte: inFilter.time[0], $lte: inFilter.time[1] }, |
| 284 | }), |
| 285 | } |
| 286 | |
| 287 | const total = await this.publishDayInfoModel.countDocuments(filter) |
| 288 | const list = await this.publishDayInfoModel |
| 289 | .find(filter) |
| 290 | .sort({ createdAt: -1 }) |
| 291 | .skip((pageNo! - 1) * pageSize) |
| 292 | .limit(pageSize) |
| 293 | .lean({ virtuals: true }) |
| 294 | |
| 295 | return { |
| 296 | total, |
| 297 | list, |
| 298 | } |
| 299 | } |
| 300 | |
| 301 | // 发放发布奖励 |
| 302 | async getUserRecordInfo(userId: string) { |
| 303 | // 1. 查询发放状态 |
| 304 | const recordInfo = await this.publishInfoModel.findOne({ |
| 305 | userId, |
| 306 | }).lean({ virtuals: true }) |
| 307 | return recordInfo |
| 308 | } |
| 309 | |
| 310 | // 获取发布信息数据 |
| 311 | async getPublishInfoData(userId: string) { |
| 312 | const res = await this.publishInfoModel.findOne({ userId }).lean({ virtuals: true }) |
| 313 | return res |
| 314 | } |
| 315 | |
| 316 | async updateUserPublishInfo(userId: string, data: Partial<PublishInfo>) { |
| 317 | const res = await this.publishInfoModel.updateOne({ userId }, { |
| 318 | $set: data, |
| 319 | }) |
| 320 | return res |
| 321 | } |
| 322 | |
| 323 | // 根据获取发布记录信息 |
| 324 | async getPublishRecordByDataId(accountType: AccountType, dataId: string) { |
| 325 | const res = await this.publishInfoModel.findOne({ accountType, dataId }).lean({ virtuals: true }) |
| 326 | return res |
| 327 | } |
| 328 | |
| 329 | async getPublishRecordDetail(data: { |
| 330 | flowId: string |
| 331 | userId: string |
| 332 | }) { |
| 333 | const publishRecord = await this.publishRecordModel.findOne({ |
| 334 | flowId: data.flowId, |
| 335 | userId: data.userId, |
| 336 | }).lean({ virtuals: true }) |
| 337 | return publishRecord |
| 338 | } |
| 339 | |
| 340 | async getPublishRecordByTaskId(taskId: string, userId: string) { |
| 341 | const res = await this.publishRecordModel |
| 342 | .findOne({ taskId, userId }) |
| 343 | .sort({ createdAt: -1 }) |
| 344 | .lean({ virtuals: true }) |
| 345 | return res |
| 346 | } |
| 347 | |
| 348 | async listPublishedByTaskId( |
| 349 | taskId: string, |
| 350 | query?: { |
| 351 | accountType?: AccountType |
| 352 | }, |
| 353 | ): Promise<PublishRecord[]> { |
| 354 | return this.publishRecordModel |
| 355 | .find({ |
| 356 | taskId, |
| 357 | status: PublishStatus.Published, |
| 358 | ...(query?.accountType && { accountType: query.accountType }), |
| 359 | }) |
| 360 | .sort({ createdAt: -1 }) |
| 361 | .lean({ virtuals: true }) |
| 362 | .exec() |
| 363 | } |
| 364 | |
| 365 | async getPublishedByTaskIdAndDataId(taskId: string, dataId: string): Promise<PublishRecord | null> { |
| 366 | return this.publishRecordModel |
| 367 | .findOne({ |
| 368 | taskId, |
| 369 | dataId, |
| 370 | status: PublishStatus.Published, |
| 371 | }) |
| 372 | .sort({ createdAt: -1 }) |
| 373 | .lean({ virtuals: true }) |
| 374 | .exec() |
| 375 | } |
| 376 | |
| 377 | async listPublishedByPublishTimeRangeForCrawlerMonitor( |
| 378 | startDate: Date, |
| 379 | endDate: Date, |
| 380 | taskId?: string, |
| 381 | accountType?: string, |
| 382 | ): Promise<PublishRecordPostDataCrawlerMonitorItem[]> { |
| 383 | return this.publishRecordModel.aggregate<PublishRecordPostDataCrawlerMonitorItem>([ |
| 384 | { |
| 385 | $match: { |
| 386 | status: PublishStatus.Published, |
| 387 | publishTime: { $gte: startDate, $lt: endDate }, |
| 388 | source: { $nin: [PublishRecordSource.TaskLink, PublishRecordSource.OfflineQr] }, |
| 389 | dataId: { $exists: true, $nin: [null, ''] }, |
| 390 | workStatus: { $nin: POST_DATA_UNAVAILABLE_WORK_STATUSES }, |
| 391 | workLink: { $exists: true, $nin: [null, ''] }, |
| 392 | ...(taskId && { taskId }), |
| 393 | ...(accountType && { accountType }), |
| 394 | }, |
| 395 | }, |
| 396 | { $sort: { publishTime: -1, createdAt: -1 } }, |
| 397 | { |
| 398 | $group: { |
| 399 | _id: { |
| 400 | accountType: '$accountType', |
| 401 | dataId: '$dataId', |
| 402 | }, |
| 403 | publishRecordId: { $first: { $toString: '$_id' } }, |
| 404 | materialGroupId: { $first: '$materialGroupId' }, |
| 405 | dataId: { $first: '$dataId' }, |
| 406 | taskId: { $first: '$taskId' }, |
| 407 | accountType: { $first: '$accountType' }, |
| 408 | accountId: { $first: '$accountId' }, |
| 409 | uid: { $first: '$uid' }, |
| 410 | workLink: { $first: '$workLink' }, |
| 411 | originalWorkLink: { $first: '$originalWorkLink' }, |
| 412 | publishTime: { $first: '$publishTime' }, |
| 413 | status: { $first: '$status' }, |
| 414 | workStatus: { $first: '$workStatus' }, |
| 415 | errorMessage: { $first: '$errorMsg' }, |
| 416 | createdAt: { $first: '$createdAt' }, |
| 417 | updatedAt: { $first: '$updatedAt' }, |
| 418 | }, |
| 419 | }, |
| 420 | { |
| 421 | $project: { |
| 422 | _id: 0, |
| 423 | publishRecordId: 1, |
| 424 | materialGroupId: 1, |
| 425 | dataId: 1, |
| 426 | taskId: 1, |
| 427 | accountType: 1, |
| 428 | accountId: 1, |
| 429 | uid: 1, |
| 430 | workLink: 1, |
| 431 | originalWorkLink: 1, |
| 432 | publishTime: 1, |
| 433 | status: 1, |
| 434 | workStatus: 1, |
| 435 | errorMessage: 1, |
| 436 | createdAt: 1, |
| 437 | updatedAt: 1, |
| 438 | }, |
| 439 | }, |
| 440 | { $sort: { publishTime: -1 } }, |
| 441 | ]).exec() |
| 442 | } |
| 443 | |
| 444 | async listOfflineQrPublishedByCreatedAtRangeForCrawlerMonitor( |
| 445 | startDate: Date, |
| 446 | endDate: Date, |
| 447 | accountType?: string, |
| 448 | ): Promise<PublishRecordPostDataCrawlerMonitorItem[]> { |
| 449 | return this.publishRecordModel.aggregate<PublishRecordPostDataCrawlerMonitorItem>([ |
| 450 | { |
| 451 | $match: { |
| 452 | status: PublishStatus.Published, |
| 453 | source: PublishRecordSource.OfflineQr, |
| 454 | createdAt: { $gte: startDate, $lt: endDate }, |
| 455 | dataId: { $exists: true, $nin: [null, ''] }, |
| 456 | workStatus: { $nin: POST_DATA_UNAVAILABLE_WORK_STATUSES }, |
| 457 | isDeleted: { $ne: true }, |
| 458 | ...(accountType && { accountType }), |
| 459 | }, |
| 460 | }, |
| 461 | { $sort: { createdAt: -1, publishTime: -1 } }, |
| 462 | { |
| 463 | $group: { |
| 464 | _id: { |
| 465 | accountType: '$accountType', |
| 466 | uid: '$uid', |
| 467 | dataId: '$dataId', |
| 468 | }, |
| 469 | publishRecordId: { $first: { $toString: '$_id' } }, |
| 470 | materialGroupId: { $first: '$materialGroupId' }, |
| 471 | dataId: { $first: '$dataId' }, |
| 472 | taskId: { $first: '$taskId' }, |
| 473 | accountType: { $first: '$accountType' }, |
| 474 | accountId: { $first: '$accountId' }, |
| 475 | uid: { $first: '$uid' }, |
| 476 | workLink: { $first: '$workLink' }, |
| 477 | originalWorkLink: { $first: '$originalWorkLink' }, |
| 478 | publishTime: { $first: '$publishTime' }, |
| 479 | status: { $first: '$status' }, |
| 480 | workStatus: { $first: '$workStatus' }, |
| 481 | errorMessage: { $first: '$errorMsg' }, |
| 482 | createdAt: { $first: '$createdAt' }, |
| 483 | updatedAt: { $first: '$updatedAt' }, |
| 484 | }, |
| 485 | }, |
| 486 | { |
| 487 | $project: { |
| 488 | _id: 0, |
| 489 | publishRecordId: 1, |
| 490 | materialGroupId: 1, |
| 491 | dataId: 1, |
| 492 | taskId: 1, |
| 493 | accountType: 1, |
| 494 | accountId: 1, |
| 495 | uid: 1, |
| 496 | workLink: 1, |
| 497 | originalWorkLink: 1, |
| 498 | publishTime: 1, |
| 499 | status: 1, |
| 500 | workStatus: 1, |
| 501 | errorMessage: 1, |
| 502 | createdAt: 1, |
| 503 | updatedAt: 1, |
| 504 | }, |
| 505 | }, |
| 506 | { $sort: { createdAt: -1 } }, |
| 507 | ]).exec() |
| 508 | } |
| 509 | |
| 510 | async listPublishedByTaskIdAndDataId(taskId: string, dataId: string): Promise<PublishRecord[]> { |
| 511 | return this.publishRecordModel |
| 512 | .find({ |
| 513 | taskId, |
| 514 | dataId, |
| 515 | status: PublishStatus.Published, |
| 516 | }) |
| 517 | .sort({ createdAt: -1 }) |
| 518 | .lean({ virtuals: true }) |
| 519 | .exec() |
| 520 | } |
| 521 | |
| 522 | async getPublishRecordByDataIdAndUid(uid: string, dataId: string) { |
| 523 | const res = await this.publishRecordModel |
| 524 | .findOne({ uid, dataId }) |
| 525 | .sort({ createdAt: -1 }) |
| 526 | .lean({ virtuals: true }) |
| 527 | return res |
| 528 | } |
| 529 | |
| 530 | async updateWorkStatusById(id: string, workStatus: WorkStatus) { |
| 531 | return await this.updateById(id, { |
| 532 | $set: { |
| 533 | workStatus, |
| 534 | }, |
| 535 | }) |
| 536 | } |
| 537 | |
| 538 | // 完成发布 |
| 539 | async donePublishRecord( |
| 540 | filter: { dataId: string, uid: string }, |
| 541 | data: { |
| 542 | workLink?: string |
| 543 | dataOption?: unknown |
| 544 | }, |
| 545 | ) { |
| 546 | const res = await this.publishRecordModel.findOneAndUpdate({ ...filter, status: PublishStatus.Publishing }, { |
| 547 | $set: { |
| 548 | status: PublishStatus.Published, |
| 549 | ...data, |
| 550 | }, |
| 551 | }).lean({ virtuals: true }) |
| 552 | return res |
| 553 | } |
| 554 | |
| 555 | // 发布失败 |
| 556 | async failPublishRecordByData( |
| 557 | filter: { dataId: string, uid: string }, |
| 558 | errorMsg: string, |
| 559 | ) { |
| 560 | const res = await this.publishRecordModel.findOneAndUpdate({ ...filter, status: PublishStatus.Publishing }, { |
| 561 | $set: { |
| 562 | status: PublishStatus.Failed, |
| 563 | errorMsg, |
| 564 | queued: false, |
| 565 | inQueue: false, |
| 566 | }, |
| 567 | }).lean({ virtuals: true }) |
| 568 | return res |
| 569 | } |
| 570 | |
| 571 | async getActiveUserTotal(startDate: Date, endDate: Date): Promise<number> { |
| 572 | const res = await this.publishRecordModel.distinct('userId', { |
| 573 | createdAt: { $gte: startDate, $lte: endDate }, |
| 574 | }) |
| 575 | return res.length |
| 576 | } |
| 577 | |
| 578 | /** |
| 579 | * 根据草稿箱ID获取发布记录列表 |
| 580 | * @param materialGroupId 草稿箱ID |
| 581 | * @param query 查询条件 |
| 582 | * @returns 发布记录列表和总数 |
| 583 | */ |
| 584 | async getPublishRecordListByMaterialGroupId( |
| 585 | materialGroupId: string, |
| 586 | query?: { |
| 587 | status?: PublishStatus |
| 588 | accountType?: AccountType |
| 589 | pageNo?: number |
| 590 | pageSize?: number |
| 591 | }, |
| 592 | ): Promise<{ records: PublishRecord[], total: number }> { |
| 593 | const filters: RootFilterQuery<PublishRecord> = { |
| 594 | materialGroupId, |
| 595 | ...(query?.status !== undefined && { status: query.status }), |
| 596 | ...(query?.accountType !== undefined && { accountType: query.accountType }), |
| 597 | } |
| 598 | |
| 599 | const pageNo = query?.pageNo || 1 |
| 600 | const pageSize = query?.pageSize || 20 |
| 601 | |
| 602 | const total = await this.publishRecordModel.countDocuments(filters) |
| 603 | const records = await this.publishRecordModel |
| 604 | .find(filters) |
| 605 | .sort({ createdAt: -1 }) |
| 606 | .skip((pageNo - 1) * pageSize) |
| 607 | .limit(pageSize) |
| 608 | .lean({ virtuals: true }) |
| 609 | .exec() |
| 610 | |
| 611 | return { records, total } |
| 612 | } |
| 613 | |
| 614 | /** |
| 615 | * 根据素材组ID获取已发布的记录列表(用于统计数据融合) |
| 616 | * 仅返回 status=PUBLISHED 且 dataId 非空的记录 |
| 617 | * @param materialGroupId 素材组ID |
| 618 | * @param query 查询条件 |
| 619 | * @returns 发布记录列表和总数 |
| 620 | */ |
| 621 | async listPublishedByMaterialGroupIdWithPagination( |
| 622 | materialGroupId: string, |
| 623 | query?: { |
| 624 | accountType?: AccountType |
| 625 | source?: PublishRecordSource |
| 626 | pageNo?: number |
| 627 | pageSize?: number |
| 628 | }, |
| 629 | ): Promise<{ records: PublishRecord[], total: number }> { |
| 630 | const filters: RootFilterQuery<PublishRecord> = { |
| 631 | materialGroupId, |
| 632 | status: PublishStatus.Published, |
| 633 | dataId: { $exists: true, $ne: '' }, |
| 634 | workLink: { $exists: true, $nin: ['', null] }, |
| 635 | source: query?.source ?? { $nin: [PublishRecordSource.TaskLink, PublishRecordSource.OfflineQr] }, |
| 636 | ...(query?.accountType !== undefined && { accountType: query.accountType }), |
| 637 | } |
| 638 | |
| 639 | const pageNo = query?.pageNo || 1 |
| 640 | const pageSize = query?.pageSize || 20 |
| 641 | |
| 642 | const total = await this.publishRecordModel.countDocuments(filters) |
| 643 | const records = await this.publishRecordModel |
| 644 | .find(filters) |
| 645 | .sort({ createdAt: -1 }) |
| 646 | .skip((pageNo - 1) * pageSize) |
| 647 | .limit(pageSize) |
| 648 | .lean({ virtuals: true }) |
| 649 | .exec() |
| 650 | |
| 651 | return { records, total } |
| 652 | } |
| 653 | |
| 654 | async listPublishedByMaterialGroupId( |
| 655 | materialGroupId: string, |
| 656 | query?: { |
| 657 | accountType?: AccountType |
| 658 | source?: PublishRecordSource |
| 659 | }, |
| 660 | ): Promise<PublishRecord[]> { |
| 661 | const filters: RootFilterQuery<PublishRecord> = { |
| 662 | materialGroupId, |
| 663 | status: PublishStatus.Published, |
| 664 | dataId: { $exists: true, $ne: '' }, |
| 665 | workLink: { $exists: true, $nin: ['', null] }, |
| 666 | source: query?.source ?? { $nin: [PublishRecordSource.TaskLink, PublishRecordSource.OfflineQr] }, |
| 667 | ...(query?.accountType !== undefined && { accountType: query.accountType }), |
| 668 | } |
| 669 | |
| 670 | return this.publishRecordModel |
| 671 | .find(filters) |
| 672 | .sort({ createdAt: -1 }) |
| 673 | .lean({ virtuals: true }) |
| 674 | .exec() |
| 675 | } |
| 676 | |
| 677 | // ----- 迁移 ---- |
| 678 | async add(publishTask: Partial<PublishRecord>) { |
| 679 | return await this.publishRecordModel.create(publishTask) |
| 680 | } |
| 681 | |
| 682 | async updateQueueId(taskId: string, queueId: string, queued?: boolean) { |
| 683 | return await this.publishRecordModel.updateOne({ id: taskId }, { queueId, inQueue: queued === undefined ? undefined : queued }).exec() |
| 684 | } |
| 685 | |
| 686 | async findOneById(id: string) { |
| 687 | return await this.publishRecordModel.findOne({ _id: id }).lean({ virtuals: true }).exec() |
| 688 | } |
| 689 | |
| 690 | async findOneByFlowId(flowId: string) { |
| 691 | return await this.publishRecordModel.findOne({ flowId }).lean({ virtuals: true }).exec() |
| 692 | } |
| 693 | |
| 694 | async findOneByData(dataId: string, uid: string) { |
| 695 | return await this.publishRecordModel.findOne({ dataId, uid }).lean({ virtuals: true }).exec() |
| 696 | } |
| 697 | |
| 698 | async findOneByDataId(dataId: string, accountType: AccountType) { |
| 699 | return await this.publishRecordModel.findOne({ dataId, accountType }).lean({ virtuals: true }).exec() |
| 700 | } |
| 701 | |
| 702 | async complete(id: string, dataId: string, data?: { |
| 703 | workLink: string |
| 704 | dataOption?: Record<string, any> |
| 705 | }) { |
| 706 | return await this.publishRecordModel.updateOne( |
| 707 | { _id: id }, |
| 708 | { |
| 709 | status: PublishStatus.Published, |
| 710 | errorMsg: '', |
| 711 | dataId, |
| 712 | workLink: data?.workLink, |
| 713 | publishTime: new Date(), |
| 714 | queued: false, |
| 715 | inQueue: false, |
| 716 | }, |
| 717 | ).exec() |
| 718 | } |
| 719 | |
| 720 | async fail(id: string, errMsg: string) { |
| 721 | return await this.publishRecordModel.updateOne( |
| 722 | { _id: id }, |
| 723 | { status: PublishStatus.Failed, errorMsg: errMsg }, |
| 724 | ).exec() |
| 725 | } |
| 726 | |
| 727 | async updateStatus(id: string, status: PublishStatus, msg?: string) { |
| 728 | return await this.publishRecordModel.updateOne( |
| 729 | { _id: id }, |
| 730 | { $set: { status, errorMsg: msg } }, |
| 731 | ).exec() |
| 732 | } |
| 733 | |
| 734 | async getPublishTaskListByTime(end: Date): Promise<PublishRecord[]> { |
| 735 | const filters: RootFilterQuery<PublishRecord> = { |
| 736 | publishTime: { $lte: end }, |
| 737 | status: PublishStatus.WaitingForPublish, |
| 738 | } |
| 739 | const list = await this.publishRecordModel.find(filters).sort({ |
| 740 | publishTime: 1, |
| 741 | }).lean({ virtuals: true }) |
| 742 | |
| 743 | return list |
| 744 | } |
| 745 | |
| 746 | async getStalePublishingTasks(cutoffTime: Date, limit: number): Promise<PublishRecord[]> { |
| 747 | const filters: RootFilterQuery<PublishRecord> = { |
| 748 | status: PublishStatus.Publishing, |
| 749 | updatedAt: { $lte: cutoffTime }, |
| 750 | } |
| 751 | return await this.publishRecordModel |
| 752 | .find(filters) |
| 753 | .sort({ updatedAt: 1 }) |
| 754 | .limit(limit) |
| 755 | .lean({ virtuals: true }) |
| 756 | .exec() |
| 757 | } |
| 758 | |
| 759 | async listByFilter(query: any): Promise<PublishRecord[]> { |
| 760 | const filters: RootFilterQuery<PublishRecord> = { |
| 761 | userId: query.userId, |
| 762 | source: query.source ?? { $nin: [PublishRecordSource.TaskLink, PublishRecordSource.OfflineQr] }, |
| 763 | ...(query.flowId !== undefined && { flowId: query.flowId }), |
| 764 | ...(query.accountId !== undefined && { accountId: query.accountId }), |
| 765 | ...(query.accountType !== undefined && { |
| 766 | accountType: query.accountType, |
| 767 | }), |
| 768 | ...(query.status !== undefined && { |
| 769 | status: query.status, |
| 770 | }), |
| 771 | ...(query.type !== undefined && { type: query.type }), |
| 772 | ...(query.time !== undefined |
| 773 | && query.time.length === 2 && { |
| 774 | publishTime: { $gte: query.time[0], $lte: query.time[1] }, |
| 775 | }), |
| 776 | ...(query.uid !== undefined && { uid: query.uid }), |
| 777 | } |
| 778 | const db = this.publishRecordModel.find(filters).sort({ |
| 779 | createdAt: -1, |
| 780 | }).lean({ virtuals: true }) |
| 781 | const list = await db.exec() |
| 782 | |
| 783 | return list |
| 784 | } |
| 785 | |
| 786 | async getLatestPublishedByUserIdAndWorkIdentity(query: { |
| 787 | userId: string |
| 788 | accountType: AccountType |
| 789 | accountId?: string |
| 790 | platformWorkId: string |
| 791 | }): Promise<PublishRecord | null> { |
| 792 | const filters: RootFilterQuery<PublishRecord> = { |
| 793 | userId: query.userId, |
| 794 | accountType: query.accountType, |
| 795 | status: PublishStatus.Published, |
| 796 | $or: [ |
| 797 | { platformWorkId: query.platformWorkId }, |
| 798 | { dataId: query.platformWorkId }, |
| 799 | { uniqueId: query.platformWorkId }, |
| 800 | { uniqueId: `${query.accountType}_${query.platformWorkId}` }, |
| 801 | { workLink: query.platformWorkId }, |
| 802 | { originalWorkLink: query.platformWorkId }, |
| 803 | ], |
| 804 | } |
| 805 | if (query.accountId) { |
| 806 | filters.accountId = query.accountId |
| 807 | } |
| 808 | |
| 809 | return this.publishRecordModel |
| 810 | .findOne(filters) |
| 811 | .sort({ updatedAt: -1, createdAt: -1 }) |
| 812 | .lean({ virtuals: true }) |
| 813 | .exec() |
| 814 | } |
| 815 | |
| 816 | async listQueuedByFilter(query: { |
| 817 | userId: string |
| 818 | accountId?: string |
| 819 | accountType?: AccountType |
| 820 | time?: [Date?, Date?, ...unknown[]] |
| 821 | }): Promise<PublishRecord[]> { |
| 822 | const filters: RootFilterQuery<PublishRecord> = { |
| 823 | status: { $in: [PublishStatus.WaitingForPublish, PublishStatus.Queued] }, |
| 824 | userId: query.userId, |
| 825 | source: { $nin: [PublishRecordSource.TaskLink, PublishRecordSource.OfflineQr] }, |
| 826 | } |
| 827 | if (query.accountId) { |
| 828 | filters.accountId = query.accountId |
| 829 | } |
| 830 | if (query.accountType) { |
| 831 | filters.accountType = query.accountType |
| 832 | } |
| 833 | if (query.time && query.time.length === 2) { |
| 834 | filters.publishTime = { $gte: query.time[0], $lte: query.time[1] } |
| 835 | } |
| 836 | return this.publishRecordModel.find(filters).sort({ |
| 837 | createdAt: -1, |
| 838 | }).lean({ virtuals: true }) |
| 839 | } |
| 840 | |
| 841 | async listPublishedByFilter(query: { |
| 842 | userId: string |
| 843 | accountId?: string |
| 844 | accountType?: AccountType |
| 845 | time?: [Date?, Date?, ...unknown[]] |
| 846 | }): Promise<PublishRecord[]> { |
| 847 | const filters: RootFilterQuery<PublishRecord> = { |
| 848 | status: PublishStatus.Published, |
| 849 | userId: query.userId, |
| 850 | source: { $nin: [PublishRecordSource.TaskLink, PublishRecordSource.OfflineQr] }, |
| 851 | } |
| 852 | if (query.accountId) { |
| 853 | filters.accountId = query.accountId |
| 854 | } |
| 855 | if (query.accountType) { |
| 856 | filters.accountType = query.accountType |
| 857 | } |
| 858 | if (query.time && query.time.length === 2) { |
| 859 | filters.publishTime = { $gte: query.time[0], $lte: query.time[1] } |
| 860 | } |
| 861 | return this.publishRecordModel.find(filters).sort({ |
| 862 | createdAt: -1, |
| 863 | }).lean({ virtuals: true }) |
| 864 | } |
| 865 | |
| 866 | async listPublishedTaskLinkRecords(query: { |
| 867 | userId: string |
| 868 | accountId?: string |
| 869 | accountType?: AccountType |
| 870 | flowId?: string |
| 871 | time?: [Date?, Date?, ...unknown[]] |
| 872 | }): Promise<PublishRecord[]> { |
| 873 | const filters: RootFilterQuery<PublishRecord> = { |
| 874 | status: PublishStatus.Published, |
| 875 | userId: query.userId, |
| 876 | source: PublishRecordSource.TaskLink, |
| 877 | } |
| 878 | if (query.accountId) { |
| 879 | filters.accountId = query.accountId |
| 880 | } |
| 881 | if (query.accountType) { |
| 882 | filters.accountType = query.accountType |
| 883 | } |
| 884 | if (query.flowId) { |
| 885 | filters.flowId = query.flowId |
| 886 | } |
| 887 | if (query.time && query.time.length === 2) { |
| 888 | filters.publishTime = { $gte: query.time[0], $lte: query.time[1] } |
| 889 | } |
| 890 | |
| 891 | return this.publishRecordModel.find(filters).sort({ |
| 892 | createdAt: -1, |
| 893 | }).lean({ virtuals: true }) |
| 894 | } |
| 895 | |
| 896 | async listByFlowId( |
| 897 | flowId: string, |
| 898 | ): Promise<PublishRecord[]> { |
| 899 | const filters: RootFilterQuery<PublishRecord> = { |
| 900 | flowId, |
| 901 | } |
| 902 | const list = await this.publishRecordModel.find(filters).sort({ |
| 903 | publishTime: 1, |
| 904 | }).lean({ virtuals: true }) |
| 905 | return list |
| 906 | } |
| 907 | |
| 908 | async listByFlowIdAndUserId(flowId: string, userId: string): Promise<PublishRecord[]> { |
| 909 | return this.publishRecordModel.find({ flowId, userId }).sort({ |
| 910 | publishTime: 1, |
| 911 | }).lean({ virtuals: true }) |
| 912 | } |
| 913 | |
| 914 | async updatePublishTaskStatus( |
| 915 | id: string, |
| 916 | newData: { |
| 917 | errorMsg?: string |
| 918 | errorData?: { |
| 919 | type: string |
| 920 | code: string |
| 921 | message: string |
| 922 | originalData?: Record<string, unknown> |
| 923 | } |
| 924 | status: PublishStatus |
| 925 | publishTime?: Date |
| 926 | queued?: boolean |
| 927 | inQueue?: boolean |
| 928 | }, |
| 929 | ): Promise<boolean> { |
| 930 | const res = await this.publishRecordModel.updateOne({ _id: id }, newData) |
| 931 | return res.modifiedCount > 0 |
| 932 | } |
| 933 | |
| 934 | async updateAsPublishing(id: string, dataId: string, workLink?: string): Promise<boolean> { |
| 935 | const res = await this.publishRecordModel.updateOne( |
| 936 | { _id: id }, |
| 937 | { $set: { status: PublishStatus.Publishing, dataId, workLink, errorMsg: '', inQueue: false, queued: false } }, |
| 938 | ).exec() |
| 939 | return res.modifiedCount > 0 |
| 940 | } |
| 941 | |
| 942 | async deleteByIdAndUserId(id: string, userId: string): Promise<boolean> { |
| 943 | const res = await this.publishRecordModel.deleteOne({ _id: id, userId }) |
| 944 | return res.deletedCount > 0 |
| 945 | } |
| 946 | |
| 947 | async delById(id: string): Promise<boolean> { |
| 948 | const res = await this.publishRecordModel.deleteOne({ _id: id }) |
| 949 | return res.deletedCount > 0 |
| 950 | } |
| 951 | |
| 952 | async getByFlowIdAndUserId(flowId: string, userId: string) { |
| 953 | return await this.publishRecordModel.findOne({ flowId, userId }).lean({ virtuals: true }).exec() |
| 954 | } |
| 955 | |
| 956 | async getByIdAndUserId(id: string, userId: string) { |
| 957 | return await this.publishRecordModel.findOne({ _id: id, userId }).lean({ virtuals: true }).exec() |
| 958 | } |
| 959 | } |
| 960 |