Quellcode durchsuchen

feat: implement adId allocation and backfill functionality in AdsService

Dave vor 1 Monat
Ursprung
Commit
7ab3240676

+ 55 - 0
apps/box-mgnt-api/src/mgnt-backend/feature/ads/ads.service.ts

@@ -33,6 +33,7 @@ import { nowSecBigInt, toSecBigInt } from '@box/common/time/time.util';
 @Injectable()
 export class AdsService {
   private readonly logger = new Logger(AdsService.name);
+  private readonly ADS_ADID_COUNTER_KEY = 'ads_adId';
 
   constructor(
     private readonly mongoPrismaService: MongoPrismaService,
@@ -101,6 +102,27 @@ export class AdsService {
     return adsModule.adType;
   }
 
+  private async nextCounterSeq(counterId: string): Promise<number> {
+    // Mongo command: findAndModify on "counters" collection
+    const res = (await this.mongoPrismaService.$runCommandRaw({
+      findAndModify: 'counters',
+      query: { _id: counterId },
+      update: { $inc: { seq: 1 } },
+      upsert: true,
+      new: true,
+    })) as any;
+
+    const seq = res?.value?.seq;
+    if (typeof seq !== 'number') {
+      throw new Error(`Failed to allocate counter seq for ${counterId}`);
+    }
+    return seq;
+  }
+
+  private async allocateAdId(): Promise<number> {
+    return this.nextCounterSeq(this.ADS_ADID_COUNTER_KEY);
+  }
+
   /**
    * Ensure the channel exists.
    */
@@ -136,6 +158,37 @@ export class AdsService {
     };
   }
 
+  async backfillAdIds(batchSize = 200): Promise<{ updated: number }> {
+    let updated = 0;
+    let cursorId: string | undefined = undefined;
+
+    while (true) {
+      const rows = await this.mongoPrismaService.ads.findMany({
+        where: {
+          adId: null,
+          ...(cursorId ? { id: { gt: cursorId } } : {}),
+        },
+        select: { id: true },
+        orderBy: { id: 'asc' },
+        take: batchSize,
+      });
+
+      if (rows.length === 0) break;
+
+      for (const r of rows) {
+        const adId = await this.allocateAdId();
+        await this.mongoPrismaService.ads.update({
+          where: { id: r.id },
+          data: { adId },
+        });
+        updated++;
+        cursorId = r.id;
+      }
+    }
+
+    return { updated };
+  }
+
   async create(dto: CreateAdsDto) {
     this.ensureTimeRange(dto.startDt, dto.expiryDt);
 
@@ -153,8 +206,10 @@ export class AdsService {
     }
 
     const now = this.nowSeconds();
+    const adId = await this.allocateAdId();
 
     const adData: any = {
+      adId,
       adType,
       advertiser: this.trimOptional(dto.advertiser),
       title: this.trimOptional(dto.title),

+ 1 - 0
prisma/mongo/schema/ads.prisma

@@ -1,5 +1,6 @@
 model Ads {
   id           String     @id @map("_id") @default(auto()) @db.ObjectId
+  adId         Int?       @unique
   adType       AdType     // Redis key & module type
   advertiser   String     // 广告商 (业务上限制 max 20 字符)
   title        String     // 标题 (业务上限制 max 20 字符)