Browse Source

feat(video-media): update VideoMediaService to handle editedAt as a number and improve findOne method response structure

Dave 2 tháng trước cách đây
mục cha
commit
d074cc51a1

+ 166 - 166
apps/box-mgnt-api/src/mgnt-backend/feature/video-media/video-media.dto.ts

@@ -329,169 +329,169 @@ export class VideoMediaListItemDto {
   editedAt!: number;
 }
 
-export class VideoMediaDetailDto {
-  @ApiProperty({
-    type: String,
-    description: '视频 MongoDB ID',
-    example: '507f1f77bcf86cd799439011',
-  })
-  @IsString()
-  id!: string;
-
-  // --- Provider fields (read-only for mgnt) ---
-
-  @ApiProperty({
-    type: String,
-    description: '视频标题',
-    example: '产品演示视频',
-  })
-  @IsString()
-  title!: string;
-
-  @ApiProperty({
-    type: String,
-    description: '视频文件名',
-    example: 'product-demo.mp4',
-  })
-  @IsString()
-  filename!: string;
-
-  @ApiProperty({
-    type: Number,
-    description: '视频时长(秒)',
-    example: 120,
-  })
-  @Type(() => Number)
-  @IsInt()
-  videoTime!: number;
-
-  @ApiProperty({
-    type: String,
-    description: '文件大小(字节),以字符串形式存储避免精度问题',
-    example: '1048576000',
-  })
-  @IsString()
-  size!: string;
-
-  @ApiProperty({
-    type: String,
-    description: '视频封面 URL 或 S3 key',
-    example: 'https://s3.example.com/covers/507f1f77bcf86cd799439011.jpg',
-  })
-  @IsString()
-  coverImg!: string;
-
-  @ApiProperty({
-    type: String,
-    description: '视频类型',
-    example: 'video/mp4',
-  })
-  @IsString()
-  type!: string;
-
-  @ApiProperty({
-    type: Number,
-    description: '视频格式类型编码',
-    example: 1,
-  })
-  @Type(() => Number)
-  @IsInt()
-  formatType!: number;
-
-  @ApiProperty({
-    type: Number,
-    description: '内容类型编码',
-    example: 1,
-  })
-  @Type(() => Number)
-  @IsInt()
-  contentType!: number;
-
-  @ApiProperty({
-    type: String,
-    description: '国家/地区代码',
-    example: 'CN',
-  })
-  @IsString()
-  country!: string;
-
-  @ApiProperty({
-    type: String,
-    description: '视频状态',
-    example: 'active',
-  })
-  @IsString()
-  status!: string;
-
-  @ApiProperty({
-    type: String,
-    description: '视频描述',
-    example: '这是一个产品演示视频',
-  })
-  @IsString()
-  desc!: string;
-
-  // --- Local business fields ---
-
-  @ApiPropertyOptional({
-    type: String,
-    nullable: true,
-    description: '视频分类 MongoDB ID',
-    example: '507f1f77bcf86cd799439012',
-  })
-  @IsOptional()
-  @IsMongoId()
-  categoryId?: string | null;
-
-  @ApiProperty({
-    type: [String],
-    description: '视频标签 MongoDB ID 列表',
-    example: ['507f1f77bcf86cd799439013', '507f1f77bcf86cd799439014'],
-  })
-  @IsArray()
-  @IsMongoId({ each: true })
-  tagIds!: string[];
-
-  @ApiProperty({
-    type: Number,
-    enum: [0, 1],
-    description: '上下架状态: 0=下架, 1=上架',
-    example: 1,
-  })
-  @Type(() => Number)
-  @IsInt()
-  @IsIn([0, 1])
-  listStatus!: number;
-
-  @ApiProperty({
-    type: Number,
-    description: '编辑时间戳(EPOCH 毫秒)',
-    example: 1733304600000,
-  })
-  @IsInt()
-  editedAt!: number;
-
-  // --- Optional denormalized information for UI convenience ---
-
-  @ApiPropertyOptional({
-    type: String,
-    nullable: true,
-    description: '分类名称(反范式化字段,用于 UI 便利性)',
-    example: '产品介绍',
-  })
-  @IsOptional()
-  @IsString()
-  categoryName?: string | null;
-
-  @ApiPropertyOptional({
-    type: [Object],
-    description: '标签对象数组(反范式化字段,包含 id 和 name)',
-    example: [
-      { id: '507f1f77bcf86cd799439013', name: '演示' },
-      { id: '507f1f77bcf86cd799439014', name: '产品' },
-    ],
-  })
-  @IsOptional()
-  @IsArray()
-  tags?: { id: string; name: string }[];
-}
+// export class VideoMediaDetailDto {
+//   @ApiProperty({
+//     type: String,
+//     description: '视频 MongoDB ID',
+//     example: '507f1f77bcf86cd799439011',
+//   })
+//   @IsString()
+//   id!: string;
+
+//   // --- Provider fields (read-only for mgnt) ---
+
+//   @ApiProperty({
+//     type: String,
+//     description: '视频标题',
+//     example: '产品演示视频',
+//   })
+//   @IsString()
+//   title!: string;
+
+//   @ApiProperty({
+//     type: String,
+//     description: '视频文件名',
+//     example: 'product-demo.mp4',
+//   })
+//   @IsString()
+//   filename!: string;
+
+//   @ApiProperty({
+//     type: Number,
+//     description: '视频时长(秒)',
+//     example: 120,
+//   })
+//   @Type(() => Number)
+//   @IsInt()
+//   videoTime!: number;
+
+//   @ApiProperty({
+//     type: String,
+//     description: '文件大小(字节),以字符串形式存储避免精度问题',
+//     example: '1048576000',
+//   })
+//   @IsString()
+//   size!: string;
+
+//   @ApiProperty({
+//     type: String,
+//     description: '视频封面 URL 或 S3 key',
+//     example: 'https://s3.example.com/covers/507f1f77bcf86cd799439011.jpg',
+//   })
+//   @IsString()
+//   coverImg!: string;
+
+//   @ApiProperty({
+//     type: String,
+//     description: '视频类型',
+//     example: 'video/mp4',
+//   })
+//   @IsString()
+//   type!: string;
+
+//   @ApiProperty({
+//     type: Number,
+//     description: '视频格式类型编码',
+//     example: 1,
+//   })
+//   @Type(() => Number)
+//   @IsInt()
+//   formatType!: number;
+
+//   @ApiProperty({
+//     type: Number,
+//     description: '内容类型编码',
+//     example: 1,
+//   })
+//   @Type(() => Number)
+//   @IsInt()
+//   contentType!: number;
+
+//   @ApiProperty({
+//     type: String,
+//     description: '国家/地区代码',
+//     example: 'CN',
+//   })
+//   @IsString()
+//   country!: string;
+
+//   @ApiProperty({
+//     type: String,
+//     description: '视频状态',
+//     example: 'active',
+//   })
+//   @IsString()
+//   status!: string;
+
+//   @ApiProperty({
+//     type: String,
+//     description: '视频描述',
+//     example: '这是一个产品演示视频',
+//   })
+//   @IsString()
+//   desc!: string;
+
+//   // --- Local business fields ---
+
+//   @ApiPropertyOptional({
+//     type: String,
+//     nullable: true,
+//     description: '视频分类 MongoDB ID',
+//     example: '507f1f77bcf86cd799439012',
+//   })
+//   @IsOptional()
+//   @IsMongoId()
+//   categoryId?: string | null;
+
+//   @ApiProperty({
+//     type: [String],
+//     description: '视频标签 MongoDB ID 列表',
+//     example: ['507f1f77bcf86cd799439013', '507f1f77bcf86cd799439014'],
+//   })
+//   @IsArray()
+//   @IsMongoId({ each: true })
+//   tagIds!: string[];
+
+//   @ApiProperty({
+//     type: Number,
+//     enum: [0, 1],
+//     description: '上下架状态: 0=下架, 1=上架',
+//     example: 1,
+//   })
+//   @Type(() => Number)
+//   @IsInt()
+//   @IsIn([0, 1])
+//   listStatus!: number;
+
+//   @ApiProperty({
+//     type: Number,
+//     description: '编辑时间戳(EPOCH 毫秒)',
+//     example: 1733304600000,
+//   })
+//   @IsInt()
+//   editedAt!: number;
+
+//   // --- Optional denormalized information for UI convenience ---
+
+//   @ApiPropertyOptional({
+//     type: String,
+//     nullable: true,
+//     description: '分类名称(反范式化字段,用于 UI 便利性)',
+//     example: '产品介绍',
+//   })
+//   @IsOptional()
+//   @IsString()
+//   categoryName?: string | null;
+
+//   @ApiPropertyOptional({
+//     type: [Object],
+//     description: '标签对象数组(反范式化字段,包含 id 和 name)',
+//     example: [
+//       { id: '507f1f77bcf86cd799439013', name: '演示' },
+//       { id: '507f1f77bcf86cd799439014', name: '产品' },
+//     ],
+//   })
+//   @IsOptional()
+//   @IsArray()
+//   tags?: { id: string; name: string }[];
+// }

+ 9 - 5
apps/box-mgnt-api/src/mgnt-backend/feature/video-media/video-media.service.ts

@@ -6,7 +6,6 @@ import {
 import { MongoPrismaService } from '@box/db/prisma/mongo-prisma.service';
 import {
   VideoMediaListQueryDto,
-  VideoMediaDetailDto,
   UpdateVideoMediaManageDto,
   UpdateVideoMediaStatusDto,
   BatchUpdateVideoMediaStatusDto,
@@ -16,7 +15,7 @@ import {
 export class VideoMediaService {
   constructor(private readonly prisma: MongoPrismaService) {}
 
-  async findAll(query: VideoMediaListQueryDto) {
+  async findAll(query: VideoMediaListQueryDto): Promise<any> {
     const page = query.page ?? 1;
     const pageSize = query.size ?? 20;
     const skip = (page - 1) * pageSize;
@@ -81,14 +80,17 @@ export class VideoMediaService {
         categoryId: row.categoryId ?? null,
         tagIds: row.tagIds ?? [],
         listStatus: row.listStatus ?? 0,
-        editedAt: row.editedAt ?? 0,
+        editedAt: Number(row.editedAt ?? 0),
+        tags: row.tags ?? [],
+        tagsFlat: row.tagsFlat ?? '',
+        secondTags: row.secondTags ?? [],
         // NOTE: We keep list DTO backward compatible.
         // If you later want to show tag names in list, we can add e.g. `tagsFlat` or `tagNames` here.
       })),
     };
   }
 
-  async findOne(id: string): Promise<VideoMediaDetailDto> {
+  async findOne(id: string): Promise<any> {
     const video = await this.prisma.videoMedia.findUnique({
       where: { id },
     });
@@ -130,7 +132,9 @@ export class VideoMediaService {
       editedAt: Number(video.editedAt ?? 0),
       categoryName: category?.name ?? null,
       // Existing DTO: tags as {id, name}[]
-      tags: tags.map((t: any) => ({ id: t.id, name: t.name })),
+      tags: video.tags ?? [],
+      tagsFlat: video.tagsFlat ?? '',
+      secondTags: video.secondTags ?? [],
     };
   }