Преглед изворни кода

feat: enhance category and tag services with metadata refresh and cache synchronization methods

Dave пре 1 месец
родитељ
комит
2c00af840a

+ 1 - 1
apps/box-mgnt-api/src/cache-sync/admin/video-cache-admin.controller.ts

@@ -33,7 +33,7 @@ import { tsCacheKeys } from '@box/common/cache/ts-cache-key.provider';
  * ═══════════════════════════════════════════════════════════════════════════
  */
 @ApiTags('缓存管理 - Admin (生产)', 'Cache Management - Admin (Production)')
-@Controller('api/v1/mgnt/admin/cache/video')
+@Controller('admin/cache/video')
 export class VideoCacheAdminController {
   private readonly logger = new Logger(VideoCacheAdminController.name);
 

+ 2 - 1
apps/box-mgnt-api/src/mgnt-backend/core/user/user.dto.ts

@@ -44,8 +44,8 @@ export class CreateUserDto {
     required: false,
   })
   @IsArray()
-  @IsArray({ each: true })
   @IsOptional()
+  @IsString({ each: true })
   @ApiProperty({ required: false, description: 'Array of role IDs' })
   roleIds?: string[];
 
@@ -94,6 +94,7 @@ export class UpdateUserDto {
   })
   @IsArray()
   @IsOptional()
+  @IsString({ each: true })
   @ApiProperty({ required: false, description: 'Array of role IDs' })
   roleIds?: string[];
 

+ 54 - 0
apps/box-mgnt-api/src/mgnt-backend/feature/category/category.service.ts

@@ -270,4 +270,58 @@ export class CategoryService {
       throw e;
     }
   }
+
+  async refreshTagsMetadata(categoryId: string): Promise<void> {
+    const tags = await this.mongoPrismaService.tag.findMany({
+      where: { categoryId },
+      orderBy: { seq: 'asc' },
+      select: { id: true, name: true },
+    });
+    await this.rebuildCategoryTagPayload(categoryId, tags);
+  }
+
+  async ensureTagChildren(categoryId: string): Promise<void> {
+    const [category, tags] = await this.mongoPrismaService.$transaction([
+      this.mongoPrismaService.category.findUnique({
+        where: { id: categoryId },
+        select: { tags: true },
+      }),
+      this.mongoPrismaService.tag.findMany({
+        where: { categoryId },
+        orderBy: { seq: 'asc' },
+        select: { id: true, name: true },
+      }),
+    ]);
+
+    const storedIds = new Set<string>();
+    if (Array.isArray(category?.tags)) {
+      for (const child of category!.tags as any[]) {
+        if (child?.id) storedIds.add(String(child.id));
+      }
+    }
+
+    const actualIds = tags.map((tag) => tag.id);
+    const needsRebuild =
+      storedIds.size !== actualIds.length ||
+      actualIds.some((id) => !storedIds.has(id));
+
+    if (needsRebuild) {
+      await this.rebuildCategoryTagPayload(categoryId, tags);
+    }
+  }
+
+  private async rebuildCategoryTagPayload(
+    categoryId: string,
+    tags: Array<{ id: string; name: string }>,
+  ): Promise<void> {
+    const formatted = tags.map((tag) => ({ id: tag.id, name: tag.name }));
+    await this.mongoPrismaService.category.update({
+      where: { id: categoryId },
+      data: {
+        tags: formatted,
+        tagNames: tags.map((tag) => tag.name),
+        updateAt: this.now(),
+      },
+    });
+  }
 }

+ 2 - 1
apps/box-mgnt-api/src/mgnt-backend/feature/tag/tag.module.ts

@@ -3,9 +3,10 @@ import { PrismaModule } from '@box/db/prisma/prisma.module';
 import { CacheSyncModule } from '../../../cache-sync/cache-sync.module';
 import { TagService } from './tag.service';
 import { TagController } from './tag.controller';
+import { CategoryModule } from '../category/category.module';
 
 @Module({
-  imports: [PrismaModule, CacheSyncModule],
+  imports: [PrismaModule, CacheSyncModule, CategoryModule],
   providers: [TagService],
   controllers: [TagController],
   exports: [TagService],

+ 25 - 2
apps/box-mgnt-api/src/mgnt-backend/feature/tag/tag.service.ts

@@ -12,12 +12,14 @@ import {
 } from '../../../cache-sync/cache-sync.types';
 import { CreateTagDto, ListTagDto, UpdateTagDto } from './tag.dto';
 import { CommonStatus } from '../common/status.enum';
+import { CategoryService } from '../category/category.service';
 
 @Injectable()
 export class TagService {
   constructor(
     private readonly mongoPrismaService: MongoPrismaService,
     private readonly cacheSyncService: CacheSyncService,
+    private readonly categoryService: CategoryService,
   ) {}
 
   /**
@@ -32,6 +34,15 @@ export class TagService {
     return Date.now();
   }
 
+  private async scheduleCategoryCaches(categoryId: string): Promise<void> {
+    await this.cacheSyncService.scheduleAction({
+      entityType: CacheEntityType.CATEGORY,
+      operation: CacheOperation.REFRESH,
+      payload: { categoryId },
+    });
+    await this.cacheSyncService.scheduleCategoryRefreshAll();
+  }
+
   /**
    * Ensure category exists.
    * NOTE: Categories are no longer tied to channels, so we only validate category existence.
@@ -78,6 +89,12 @@ export class TagService {
       },
     });
 
+    await this.categoryService.refreshTagsMetadata(tag.categoryId);
+    await this.scheduleCategoryCaches(tag.categoryId);
+
+    await this.categoryService.refreshTagsMetadata(tag.categoryId);
+    await this.scheduleCategoryCaches(tag.categoryId);
+
     // Schedule cache sync actions
     await this.cacheSyncService.scheduleAction({
       entityType: CacheEntityType.TAG,
@@ -142,15 +159,18 @@ export class TagService {
       data,
     });
 
-    // Schedule cache sync actions for both old and new category
     if (oldCategoryId !== dto.categoryId) {
-      // Category changed: refresh both old and new
+      await this.categoryService.ensureTagChildren(oldCategoryId);
+      await this.scheduleCategoryCaches(oldCategoryId);
       await this.cacheSyncService.scheduleAction({
         entityType: CacheEntityType.TAG,
         operation: CacheOperation.REFRESH,
         payload: { categoryId: oldCategoryId },
       });
     }
+    await this.categoryService.refreshTagsMetadata(dto.categoryId);
+    await this.scheduleCategoryCaches(dto.categoryId);
+
     await this.cacheSyncService.scheduleAction({
       entityType: CacheEntityType.TAG,
       operation: CacheOperation.REFRESH,
@@ -224,6 +244,9 @@ export class TagService {
     try {
       await this.mongoPrismaService.tag.delete({ where: { id } });
 
+      await this.categoryService.ensureTagChildren(categoryId);
+      await this.scheduleCategoryCaches(categoryId);
+
       // Schedule cache sync actions
       await this.cacheSyncService.scheduleAction({
         entityType: CacheEntityType.TAG,