ad-pool.service.ts 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. import { Injectable, Logger } from '@nestjs/common';
  2. import { CacheKeys } from '../cache/cache-keys';
  3. import type { AdPoolEntry, AdType } from '../ads/ad-types';
  4. import { AdType as PrismaAdType } from '@prisma/mongo/client';
  5. import { RedisService } from '@box/db/redis/redis.service';
  6. import { MongoPrismaService } from '@box/db/prisma/mongo-prisma.service';
  7. @Injectable()
  8. export class AdPoolService {
  9. private readonly logger = new Logger(AdPoolService.name);
  10. constructor(
  11. private readonly redis: RedisService,
  12. private readonly mongoPrisma: MongoPrismaService,
  13. ) {}
  14. /** Rebuild all ad pools for every AdType. */
  15. async rebuildAllAdPools(): Promise<void> {
  16. const adTypes = Object.values(PrismaAdType) as AdType[];
  17. for (const adType of adTypes) {
  18. try {
  19. const count = await this.rebuildAdPoolByType(adType);
  20. this.logger.log(
  21. `AdPool warmup succeeded for adType=${adType}, ads=${count}`,
  22. );
  23. } catch (err) {
  24. this.logger.error(
  25. `AdPool warmup failed for adType=${adType}`,
  26. err instanceof Error ? err.stack : String(err),
  27. );
  28. }
  29. }
  30. }
  31. /** Rebuild a single ad pool keyed by AdType. */
  32. async rebuildAdPoolByType(adType: AdType): Promise<number> {
  33. const now = BigInt(Date.now());
  34. const ads = await this.mongoPrisma.ads.findMany({
  35. where: {
  36. adType,
  37. status: 1,
  38. startDt: { lte: now },
  39. OR: [{ expiryDt: BigInt(0) }, { expiryDt: { gte: now } }],
  40. },
  41. orderBy: { seq: 'asc' },
  42. select: {
  43. id: true,
  44. adType: true,
  45. advertiser: true,
  46. title: true,
  47. adsContent: true,
  48. adsCoverImg: true,
  49. adsUrl: true,
  50. imgSource: true,
  51. startDt: true,
  52. expiryDt: true,
  53. seq: true,
  54. },
  55. });
  56. const poolEntries: AdPoolEntry[] = ads.map((ad) => ({
  57. id: ad.id,
  58. adType: ad.adType,
  59. advertiser: ad.advertiser,
  60. title: ad.title,
  61. adsContent: ad.adsContent ?? null,
  62. adsCoverImg: ad.adsCoverImg ?? null,
  63. adsUrl: ad.adsUrl ?? null,
  64. imgSource: ad.imgSource ?? null,
  65. startDt: ad.startDt,
  66. expiryDt: ad.expiryDt,
  67. seq: ad.seq,
  68. }));
  69. const key = CacheKeys.appAdPoolByType(adType);
  70. await this.redis.atomicSwapJson([{ key, value: poolEntries }]);
  71. return poolEntries.length;
  72. }
  73. }