Jelajahi Sumber

refactor: enhance getSysCnf method for improved cache handling and error resilience; update importExcelTags method for better file processing and logging

Dave 3 minggu lalu
induk
melakukan
84dd2a4a39

+ 30 - 11
apps/box-app-api/src/feature/sys-params/sys-params.service.ts

@@ -88,23 +88,42 @@ export class SysParamsService {
   }
 
   async getSysCnf() {
+    const cacheKey = 'box:app:sysconf';
+
     try {
-      // Try to get from Redis cache first
-      const cacheKey = `box:app:sysconf`;
-      const conf = await this.redis.get(cacheKey);
-      if (conf) {
-        return JSON.parse(conf);
+      // 1) Try cache
+      const cached = await this.redis.get(cacheKey);
+      if (cached) {
+        try {
+          const parsed = JSON.parse(cached);
+          // Treat null/empty-object as invalid -> rebuild from DB
+          if (
+            parsed &&
+            (typeof parsed !== 'object' || Object.keys(parsed).length > 0)
+          ) {
+            return parsed;
+          }
+        } catch {
+          // Bad JSON -> rebuild from DB
+        }
+
+        // Cache exists but unusable -> remove it so we rebuild cleanly
+        await this.redis.del?.(cacheKey);
       }
 
-      const appConf = this.sysConfigReader.getAppConfig();
+      // 2) Cache miss -> read from DB
+      const appConf = await this.sysConfigReader.getAppConfig();
 
-      if (appConf) {
-        // Store in Redis cache for future requests
-        await this.redis.setJson(cacheKey, appConf, 24 * 3600); // Cache for 1 hour
+      // 3) Rebuild cache (only if we have something meaningful)
+      if (
+        appConf &&
+        (typeof appConf !== 'object' || Object.keys(appConf).length > 0)
+      ) {
+        await this.redis.setJson(cacheKey, appConf, 24 * 3600); // 24h
       }
 
-      return appConf;
-    } catch (error) {
+      return appConf ?? null;
+    } catch {
       return null;
     }
   }

+ 11 - 24
apps/box-mgnt-api/src/mgnt-backend/feature/video-media/video-media.controller.ts

@@ -3,7 +3,6 @@ import {
   Controller,
   Get,
   Param,
-  Query,
   Patch,
   Body,
   Post,
@@ -11,6 +10,7 @@ import {
   Req,
   Res,
   BadRequestException,
+  Logger,
 } from '@nestjs/common';
 import type { FastifyReply, FastifyRequest } from 'fastify';
 import {
@@ -39,6 +39,7 @@ import {
 @ApiTags('视频管理 (Video Media Management)')
 @Controller('video-media')
 export class VideoMediaController {
+  private readonly logger = new Logger(VideoMediaController.name);
   constructor(private readonly videoMediaService: VideoMediaService) {}
 
   /**
@@ -285,37 +286,23 @@ export class VideoMediaController {
     },
   })
   @Post('import/excel-tags')
-  async importExcelTags(@Req() req: FastifyRequest, @Body() _body: any) {
-    // fastify multipart
+  async importExcelTags(@Req() req: FastifyRequest) {
     const reqAny = req as any;
-    const body = reqAny.body;
-    const bodyFile = body?.file ?? body?.files;
-    let mpFile: MultipartFile | undefined = Array.isArray(bodyFile)
+
+    const bodyFile = reqAny.body?.file ?? reqAny.body?.files;
+    let file: MultipartFile | undefined = Array.isArray(bodyFile)
       ? bodyFile[0]
       : bodyFile;
-
-    if (!mpFile && body && typeof body === 'object') {
-      const values = Object.values(body).flatMap((value: any) =>
-        Array.isArray(value) ? value : [value],
-      );
-      mpFile = values.find(
-        (value: any) =>
-          value &&
-          (typeof value.toBuffer === 'function' ||
-            value.file ||
-            value.filename),
-      );
-    }
-
-    if (!mpFile && reqAny.isMultipart?.()) {
-      mpFile = await reqAny.file();
+    if (!file && typeof reqAny.file === 'function') {
+      file = await reqAny.file();
     }
 
-    if (!mpFile) {
+    // If this is null, the request is not multipart – nothing else to do
+    if (!file) {
       throw new BadRequestException('No file uploaded');
     }
 
-    return this.videoMediaService.importExcelTags(mpFile);
+    return this.videoMediaService.importExcelTags(file);
   }
 
   /**

+ 18 - 19
apps/box-mgnt-api/src/mgnt-backend/feature/video-media/video-media.service.ts

@@ -301,7 +301,7 @@ export class VideoMediaService {
     return Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer);
   }
 
-  private async readUploadBuffer(file: MultipartFile): Promise<Buffer> {
+  private async readUploadBuffer(file: any): Promise<Buffer> {
     const toBuffer = (value: unknown): Buffer | undefined => {
       if (!value) return undefined;
       if (Buffer.isBuffer(value)) return value;
@@ -311,32 +311,28 @@ export class VideoMediaService {
       return undefined;
     };
 
-    const source = (file as any)?.file as NodeJS.ReadableStream | undefined;
-    if (!source) {
-      const buffer = toBuffer(file);
+    if (typeof file?.toBuffer === 'function') {
+      const raw = await file.toBuffer();
+      const buffer = toBuffer(raw);
       if (buffer) return buffer;
     }
-    if (!source) {
-      const maybeBuffer = toBuffer(
-        (file as any).buffer ??
-          (file as any).value ??
-          (file as any).data ??
-          undefined,
-      );
-      if (maybeBuffer) return maybeBuffer;
-      if (typeof (file as any).toBuffer === 'function') {
-        const buffer = await (file as any).toBuffer();
-        if (Buffer.isBuffer(buffer)) return buffer;
-      }
-    }
-    if (!source) {
+
+    const direct = toBuffer(file);
+    if (direct) return direct;
+
+    const fallback = toBuffer(file?.buffer ?? file?.value ?? file?.data);
+    if (fallback) return fallback;
+
+    const stream = file?.file as NodeJS.ReadableStream | undefined;
+    if (!stream) {
       throw new BadRequestException('Upload stream is missing');
     }
 
     const chunks: Buffer[] = [];
-    for await (const chunk of source) {
+    for await (const chunk of stream) {
       chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
     }
+
     return Buffer.concat(chunks);
   }
 
@@ -345,6 +341,9 @@ export class VideoMediaService {
     if (!buffer.length) {
       throw new BadRequestException('Empty file');
     }
+    this.logger.log(
+      `[importExcelTags] upload buffer bytes=${buffer.length} filename=${(file as any)?.filename ?? 'unknown'} mimetype=${(file as any)?.mimetype ?? 'unknown'}`,
+    );
 
     const rowsAH: Array<{ idRaw: unknown; tagsRaw: unknown }> = [];