Ver código fonte

feat: add exportExcel method to VideoMediaService for exporting video media data to Excel; implement exportExcel endpoint in VideoMediaController

Dave 1 mês atrás
pai
commit
34b42bda7b

+ 18 - 1
apps/box-mgnt-api/src/mgnt-backend/feature/video-media/video-media.controller.ts

@@ -9,9 +9,10 @@ import {
   Post,
   Delete,
   Req,
+  Res,
   BadRequestException,
 } from '@nestjs/common';
-import type { FastifyRequest } from 'fastify';
+import type { FastifyReply, FastifyRequest } from 'fastify';
 import {
   ApiTags,
   ApiOperation,
@@ -263,4 +264,20 @@ export class VideoMediaController {
   async delete(@Param('id') id: string) {
     return this.videoMediaService.delete(id);
   }
+
+  /**
+   * 导出所有视频媒体为 Excel
+   * GET /video-media/export/excel
+   */
+  @Get('export/excel')
+  async exportExcel(@Res() res: FastifyReply) {
+    const buffer = await this.videoMediaService.exportExcel();
+    res
+      .header(
+        'Content-Type',
+        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+      )
+      .header('Content-Disposition', 'attachment; filename="video-media.xlsx"')
+      .send(buffer);
+  }
 }

+ 40 - 0
apps/box-mgnt-api/src/mgnt-backend/feature/video-media/video-media.service.ts

@@ -7,6 +7,7 @@ import {
 } from '@nestjs/common';
 import type { MultipartFile } from '@fastify/multipart';
 import { MongoPrismaService } from '@box/db/prisma/mongo-prisma.service';
+import ExcelJS from 'exceljs';
 import { CacheSyncService } from '../../../cache-sync/cache-sync.service';
 import { CacheEntityType } from '../../../cache-sync/cache-sync.types';
 import { MediaManagerService } from '@box/core/media-manager/media-manager.service';
@@ -219,6 +220,45 @@ export class VideoMediaService {
     };
   }
 
+  async exportExcel(): Promise<Buffer> {
+    const rows = await this.prisma.videoMedia.findMany({});
+    const workbook = new ExcelJS.Workbook();
+    const sheet = workbook.addWorksheet('video-media');
+
+    sheet.columns = [
+      { header: 'id', key: 'id', width: 28 },
+      { header: 'vid', key: 'vid', width: 10 },
+      { header: 'title', key: 'title', width: 40 },
+      { header: 'filename', key: 'filename', width: 40 },
+      { header: 'videoTime', key: 'videoTime', width: 12 },
+      { header: 'size', key: 'size', width: 16 },
+      { header: 'listStatus', key: 'listStatus', width: 12 },
+      { header: 'tags', key: 'tags', width: 40 },
+      { header: 'secondTags', key: 'secondTags', width: 40 },
+      { header: 'updatedAt', key: 'updatedAt', width: 24 },
+    ];
+
+    sheet.addRows(
+      rows.map((row: any) => ({
+        id: this.normalizeMongoIdToString(row.id),
+        vid: row.vid ?? '',
+        title: row.title ?? '',
+        filename: row.filename ?? '',
+        videoTime: row.videoTime ?? '',
+        size: row.size?.toString?.() ?? '',
+        listStatus: row.listStatus ?? '',
+        tags: Array.isArray(row.tags) ? row.tags.join(',') : '',
+        secondTags: Array.isArray(row.secondTags)
+          ? row.secondTags.join(',')
+          : '',
+        updatedAt: row.updatedAt ? new Date(row.updatedAt).toISOString() : '',
+      })),
+    );
+
+    const buffer = await workbook.xlsx.writeBuffer();
+    return Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer);
+  }
+
   private buildVideoListBaseFilter(
     query: VideoMediaListQueryDto,
   ): Record<string, any> {