| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485 |
- // video-media.dto.ts
- import {
- IsInt,
- IsOptional,
- IsString,
- IsIn,
- IsArray,
- IsMongoId,
- Min,
- Max,
- MaxLength,
- } from 'class-validator';
- import { Type } from 'class-transformer';
- import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
- import { PageListDto } from '@box/common/dto/page-list.dto';
- export class VideoMediaListQueryDto extends PageListDto {
- /**
- * 搜索关键词:匹配标题、文件名等(具体逻辑在 service 里实现)
- */
- @ApiPropertyOptional({
- type: String,
- maxLength: 100,
- description: '搜索关键词,用于匹配视频标题、文件名等',
- example: '产品演示视频',
- })
- @IsOptional()
- @IsString()
- @MaxLength(100)
- keyword?: string;
- // search tags
- @ApiPropertyOptional({
- type: String,
- maxLength: 100,
- description: '搜索标签关键词,用于匹配视频标签',
- example: '演示',
- })
- @IsOptional()
- @IsString()
- @MaxLength(100)
- tag?: string;
- /**
- * 上/下架状态过滤
- * 0 = 下架, 1 = 上架
- */
- @ApiPropertyOptional({
- type: Number,
- enum: [0, 1],
- description: '上下架状态过滤: 0=下架, 1=上架',
- example: 1,
- })
- @IsOptional()
- @Type(() => Number)
- @IsInt()
- @IsIn([0, 1])
- listStatus?: number;
- /**
- * 搜索 更新时间 范围 - 开始时间(EPOCH 秒)
- */
- @ApiPropertyOptional({
- type: Number,
- description: '更新时间范围过滤的开始时间(EPOCH 毫秒)',
- example: 1700000000,
- })
- @IsOptional()
- @Type(() => Number)
- @IsInt()
- @Min(0)
- updatedFrom?: number;
- /**
- * 搜索 更新时间 范围 - 结束时间(EPOCH 秒)
- */
- @ApiPropertyOptional({
- type: Number,
- description: '更新时间范围过滤的结束时间(EPOCH 毫秒)',
- example: 1705000000,
- })
- @IsOptional()
- @Type(() => Number)
- @IsInt()
- @Min(0)
- updatedTo?: number;
- }
- export class UpdateVideoMediaManageDto {
- @ApiPropertyOptional({
- type: String,
- maxLength: 200,
- description: '视频标题',
- example: '产品演示视频 2025',
- })
- @IsOptional()
- @IsString()
- @MaxLength(200)
- title?: string;
- /**
- * 分类 ID,可为空(取消分类)
- */
- @ApiPropertyOptional({
- type: String,
- nullable: true,
- description: '视频分类 MongoDB ID,设为 null 可取消分类',
- example: '507f1f77bcf86cd799439011',
- })
- @IsOptional()
- @IsMongoId()
- categoryId?: string | null;
- /**
- * 标签 ID 列表,最多 5 个
- */
- @ApiPropertyOptional({
- type: [String],
- maxItems: 5,
- description: '视频标签 MongoDB ID 列表,最多 5 个',
- example: ['507f1f77bcf86cd799439012', '507f1f77bcf86cd799439013'],
- })
- @IsOptional()
- @IsArray()
- @IsMongoId({ each: true })
- tagIds?: string[];
- /**
- * 上/下架状态,也可以在这里一起保存(可选)
- * 0 = 下架, 1 = 上架
- */
- @ApiPropertyOptional({
- type: Number,
- enum: [0, 1],
- description: '上下架状态: 0=下架, 1=上架',
- example: 1,
- })
- @IsOptional()
- @Type(() => Number)
- @IsInt()
- @IsIn([0, 1])
- listStatus?: number;
- }
- export class UpdateVideoMediaStatusDto {
- @ApiProperty({
- type: Number,
- enum: [0, 1],
- description: '上下架状态: 0=下架, 1=上架',
- example: 1,
- })
- @Type(() => Number)
- @IsInt()
- @IsIn([0, 1])
- listStatus!: number;
- }
- export class BatchUpdateVideoMediaStatusDto {
- @ApiProperty({
- type: [String],
- description: 'MongoDB 视频 ID 列表',
- example: ['507f1f77bcf86cd799439011', '507f1f77bcf86cd799439012'],
- })
- @IsArray()
- @IsMongoId({ each: true })
- ids!: string[];
- @ApiProperty({
- type: Number,
- enum: [0, 1],
- description: '上下架状态: 0=下架, 1=上架',
- example: 1,
- })
- @Type(() => Number)
- @IsInt()
- @IsIn([0, 1])
- listStatus!: number;
- }
- export class UpdateVideoMediaCoverResponseDto {
- @ApiProperty({
- type: String,
- description: '视频 MongoDB ID',
- example: '507f1f77bcf86cd799439011',
- })
- @IsString()
- id!: string;
- @ApiProperty({
- type: String,
- description: '新的封面图片 URL 或 S3 key',
- example: 'https://s3.example.com/covers/507f1f77bcf86cd799439011.jpg',
- })
- @IsString()
- coverImg!: string;
- /**
- * 新 editedAt 值
- */
- @ApiProperty({
- type: Number,
- description: '编辑时间戳(EPOCH 毫秒)',
- example: 1733304600000,
- })
- @IsInt()
- editedAt!: number;
- }
- export class VideoMediaListItemDto {
- @ApiProperty({
- type: String,
- description: '视频 MongoDB ID',
- example: '507f1f77bcf86cd799439011',
- })
- @IsString()
- id!: string;
- @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;
- /**
- * 文件大小(业务上 BigInt 存储,DTO 建议用字符串避免精度问题)
- */
- @ApiProperty({
- type: String,
- description: '文件大小(字节),以字符串形式存储避免精度问题',
- example: '1048576000',
- })
- @IsString()
- size!: string;
- /**
- * 封面 URL / key
- */
- @ApiProperty({
- type: String,
- description: '视频封面 URL 或 S3 key',
- example: 'https://s3.example.com/covers/507f1f77bcf86cd799439011.jpg',
- })
- @IsString()
- coverImg!: string;
- /**
- * 分类 ID,可空
- */
- @ApiPropertyOptional({
- type: String,
- nullable: true,
- description: '视频分类 MongoDB ID',
- example: '507f1f77bcf86cd799439012',
- })
- @IsOptional()
- @IsMongoId()
- categoryId?: string | null;
- /**
- * 当前选中的标签 ID 列表(最多 5 个)
- */
- @ApiProperty({
- type: [String],
- description: '视频标签 MongoDB ID 列表',
- example: ['507f1f77bcf86cd799439013', '507f1f77bcf86cd799439014'],
- })
- @IsArray()
- @IsMongoId({ each: true })
- tagIds!: string[];
- /**
- * 上/下架状态 0 = 下架, 1 = 上架
- */
- @ApiProperty({
- type: Number,
- enum: [0, 1],
- description: '上下架状态: 0=下架, 1=上架',
- example: 1,
- })
- @Type(() => Number)
- @IsInt()
- @IsIn([0, 1])
- listStatus!: number;
- /**
- * 编辑时间戳(EPOCH 毫秒)
- */
- @ApiProperty({
- type: Number,
- description: '编辑时间戳(EPOCH 毫秒)',
- example: 1733304600000,
- })
- @IsInt()
- 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 }[];
- }
|