Эх сурвалжийг харах

refactor: update ESLint configuration and improve code formatting

- Disable unused variable rule in ESLint.
- Refactor imports and formatting in main.ts, auth.constants.ts, auth.dto.ts, public.decorator.ts, rbac.guard.ts, core.module.ts, operation-log.module.ts, operation-log.service.ts, quota-log.module.ts, menu.constants.ts, role.constants.ts, user.constants.ts, oss.config.ts, oss.module.ts, province-city.service.ts, s3.config.ts, s3.module.ts, sync-videomedia.service.ts, system-param.dto.ts, system-params.service.ts, all-exceptions.filter.ts, logging.interceptor.ts, operation-log.interceptor.ts, and utils.service.ts for consistency.
- Introduce IOperationLogger interface for operation logging.
- Update exception handling and response structure in ExceptionService.
- Add type definitions for FastifyRequest to include user information.
- Update package.json scripts for type checking and linting.
- Add tsconfig.json for path mapping and project structure.
Dave 7 цаг өмнө
parent
commit
917ab8b993
40 өөрчлөгдсөн 303 нэмэгдсэн , 197 устгасан
  1. 1 1
      .eslintrc.js
  2. 19 8
      apps/box-mgnt-api/src/main.ts
  3. 1 1
      apps/box-mgnt-api/src/mgnt-backend/core/auth/auth.constants.ts
  4. 4 4
      apps/box-mgnt-api/src/mgnt-backend/core/auth/auth.dto.ts
  5. 3 3
      apps/box-mgnt-api/src/mgnt-backend/core/auth/decorators/public.decorator.ts
  6. 10 10
      apps/box-mgnt-api/src/mgnt-backend/core/auth/guards/rbac.guard.ts
  7. 8 8
      apps/box-mgnt-api/src/mgnt-backend/core/core.module.ts
  8. 14 7
      apps/box-mgnt-api/src/mgnt-backend/core/logging/operation-log/operation-log.module.ts
  9. 4 3
      apps/box-mgnt-api/src/mgnt-backend/core/logging/operation-log/operation-log.service.ts
  10. 5 5
      apps/box-mgnt-api/src/mgnt-backend/core/logging/quota-log/quota-log.module.ts
  11. 1 1
      apps/box-mgnt-api/src/mgnt-backend/core/menu/menu.constants.ts
  12. 2 2
      apps/box-mgnt-api/src/mgnt-backend/core/role/role.constants.ts
  13. 2 2
      apps/box-mgnt-api/src/mgnt-backend/core/user/user.constants.ts
  14. 4 4
      apps/box-mgnt-api/src/mgnt-backend/feature/oss/oss.config.ts
  15. 5 5
      apps/box-mgnt-api/src/mgnt-backend/feature/oss/oss.module.ts
  16. 6 2
      apps/box-mgnt-api/src/mgnt-backend/feature/province-city/province-city.service.ts
  17. 9 13
      apps/box-mgnt-api/src/mgnt-backend/feature/s3/s3.config.ts
  18. 1 1
      apps/box-mgnt-api/src/mgnt-backend/feature/s3/s3.module.ts
  19. 22 7
      apps/box-mgnt-api/src/mgnt-backend/feature/sync-videomedia/sync-videomedia.service.ts
  20. 9 1
      apps/box-mgnt-api/src/mgnt-backend/feature/system-params/system-param.dto.ts
  21. 2 1
      apps/box-mgnt-api/src/mgnt-backend/feature/system-params/system-params.service.ts
  22. 1 2
      libs/common/src/common.module.ts
  23. 9 6
      libs/common/src/decorators/auth-user.decorator.ts
  24. 10 10
      libs/common/src/decorators/operation-log.decorator.ts
  25. 10 10
      libs/common/src/dto/page-list-response.dto.ts
  26. 22 22
      libs/common/src/dto/page-list.dto.ts
  27. 9 9
      libs/common/src/filters/all-exceptions.filter.ts
  28. 12 12
      libs/common/src/interceptors/logging.interceptor.ts
  29. 16 3
      libs/common/src/interceptors/operation-log.interceptor.ts
  30. 1 0
      libs/common/src/interfaces/index.ts
  31. 19 0
      libs/common/src/interfaces/operation-logger.interface.ts
  32. 3 3
      libs/common/src/interfaces/response.interface.ts
  33. 2 3
      libs/common/src/services/exception.service.ts
  34. 8 0
      libs/common/src/types/fastify.d.ts
  35. 4 4
      libs/db/src/prisma/mongo-prisma.service.ts
  36. 4 4
      libs/db/src/prisma/mysql-prisma.service.ts
  37. 4 4
      libs/db/src/shared.module.ts
  38. 15 15
      libs/db/src/utils.service.ts
  39. 6 1
      package.json
  40. 16 0
      tsconfig.json

+ 1 - 1
.eslintrc.js

@@ -21,6 +21,6 @@ module.exports = {
     '@typescript-eslint/explicit-function-return-type': 'off',
     '@typescript-eslint/explicit-function-return-type': 'off',
     '@typescript-eslint/explicit-module-boundary-types': 'off',
     '@typescript-eslint/explicit-module-boundary-types': 'off',
     '@typescript-eslint/no-explicit-any': 'off',
     '@typescript-eslint/no-explicit-any': 'off',
-    'semi': ['error', 'never'],
+    '@typescript-eslint/no-unused-vars': 'off',
   },
   },
 };
 };

+ 19 - 8
apps/box-mgnt-api/src/main.ts

@@ -2,7 +2,10 @@ import 'reflect-metadata';
 import { ValidationPipe } from '@nestjs/common';
 import { ValidationPipe } from '@nestjs/common';
 import { ConfigService } from '@nestjs/config';
 import { ConfigService } from '@nestjs/config';
 import { NestFactory } from '@nestjs/core';
 import { NestFactory } from '@nestjs/core';
-import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
+import {
+  FastifyAdapter,
+  NestFastifyApplication,
+} from '@nestjs/platform-fastify';
 import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
 import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
 import { Logger } from 'nestjs-pino';
 import { Logger } from 'nestjs-pino';
 import multipart from '@fastify/multipart';
 import multipart from '@fastify/multipart';
@@ -28,10 +31,14 @@ async function bootstrap() {
     },
     },
   });
   });
 
 
-  const app = await NestFactory.create<NestFastifyApplication>(AppModule, fastifyAdapter, {
-    bufferLogs: true,
-    snapshot: true,
-  });
+  const app = await NestFactory.create<NestFastifyApplication>(
+    AppModule,
+    fastifyAdapter,
+    {
+      bufferLogs: true,
+      snapshot: true,
+    },
+  );
 
 
   // nestjs-pino logger
   // nestjs-pino logger
   app.useLogger(app.get(Logger));
   app.useLogger(app.get(Logger));
@@ -50,9 +57,12 @@ async function bootstrap() {
   const configService = app.get(ConfigService);
   const configService = app.get(ConfigService);
 
 
   const host =
   const host =
-    configService.get<string>('APP_HOST') ?? configService.get<string>('HOST') ?? '0.0.0.0';
+    configService.get<string>('APP_HOST') ??
+    configService.get<string>('HOST') ??
+    '0.0.0.0';
 
 
-  const port = configService.get<number>('APP_PORT') ?? Number(process.env.PORT ?? 3300);
+  const port =
+    configService.get<number>('APP_PORT') ?? Number(process.env.PORT ?? 3300);
 
 
   const crossOrigin =
   const crossOrigin =
     configService.get<string>('APP_CROSS_ORIGIN') ??
     configService.get<string>('APP_CROSS_ORIGIN') ??
@@ -60,7 +70,8 @@ async function bootstrap() {
     '*';
     '*';
 
 
   app.enableCors({
   app.enableCors({
-    origin: crossOrigin === '*' ? true : crossOrigin.split(',').map((o) => o.trim()),
+    origin:
+      crossOrigin === '*' ? true : crossOrigin.split(',').map((o) => o.trim()),
     methods: 'GET,PUT,PATCH,POST,DELETE',
     methods: 'GET,PUT,PATCH,POST,DELETE',
   });
   });
 
 

+ 1 - 1
apps/box-mgnt-api/src/mgnt-backend/core/auth/auth.constants.ts

@@ -3,4 +3,4 @@ export const WHITE_API_LIST = [
   { method: 'POST', url: '/auth/logout' },
   { method: 'POST', url: '/auth/logout' },
   { method: 'PUT', url: '/users/me/password' },
   { method: 'PUT', url: '/users/me/password' },
   { method: 'GET', url: '/users/me' },
   { method: 'GET', url: '/users/me' },
-]
+];

+ 4 - 4
apps/box-mgnt-api/src/mgnt-backend/core/auth/auth.dto.ts

@@ -1,13 +1,13 @@
-import { IsString, MinLength } from 'class-validator'
-import { ApiProperty } from '@nestjs/swagger'
+import { IsString, MinLength } from 'class-validator';
+import { ApiProperty } from '@nestjs/swagger';
 
 
 export class SignInDto {
 export class SignInDto {
   @ApiProperty()
   @ApiProperty()
   @IsString()
   @IsString()
-  account: string
+  account: string;
 
 
   @ApiProperty()
   @ApiProperty()
   @MinLength(6)
   @MinLength(6)
   @IsString()
   @IsString()
-  password: string
+  password: string;
 }
 }

+ 3 - 3
apps/box-mgnt-api/src/mgnt-backend/core/auth/decorators/public.decorator.ts

@@ -1,4 +1,4 @@
-import { SetMetadata } from '@nestjs/common'
+import { SetMetadata } from '@nestjs/common';
 
 
-export const IS_PUBLIC_KEY = 'isPublic'
-export const Public = () => SetMetadata(IS_PUBLIC_KEY, true)
+export const IS_PUBLIC_KEY = 'isPublic';
+export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);

+ 10 - 10
apps/box-mgnt-api/src/mgnt-backend/core/auth/guards/rbac.guard.ts

@@ -3,11 +3,11 @@ import {
   ExecutionContext,
   ExecutionContext,
   ForbiddenException,
   ForbiddenException,
   Injectable,
   Injectable,
-} from '@nestjs/common'
-import { Reflector } from '@nestjs/core'
-import type { FastifyRequest } from 'fastify'
-import { AuthService } from '../auth.service'
-import { IS_PUBLIC_KEY } from '../decorators/public.decorator'
+} from '@nestjs/common';
+import { Reflector } from '@nestjs/core';
+import type { FastifyRequest } from 'fastify';
+import { AuthService } from '../auth.service';
+import { IS_PUBLIC_KEY } from '../decorators/public.decorator';
 
 
 @Injectable()
 @Injectable()
 export class RbacGuard implements CanActivate {
 export class RbacGuard implements CanActivate {
@@ -20,17 +20,17 @@ export class RbacGuard implements CanActivate {
     const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
     const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
       context.getHandler(),
       context.getHandler(),
       context.getClass(),
       context.getClass(),
-    ])
+    ]);
     if (isPublic) {
     if (isPublic) {
-      return true
+      return true;
     }
     }
 
 
-    const req = context.switchToHttp().getRequest<FastifyRequest>()
+    const req = context.switchToHttp().getRequest<FastifyRequest>();
 
 
     if (await this.authService.checkApiPermission(req)) {
     if (await this.authService.checkApiPermission(req)) {
-      return true
+      return true;
     }
     }
 
 
-    throw new ForbiddenException('访问被拒绝,您无权执行此操作')
+    throw new ForbiddenException('访问被拒绝,您无权执行此操作');
   }
   }
 }
 }

+ 8 - 8
apps/box-mgnt-api/src/mgnt-backend/core/core.module.ts

@@ -1,11 +1,11 @@
-import { Module } from '@nestjs/common'
-import { AuthModule } from './auth/auth.module'
-import { LoginLogModule } from './logging/login-log/login-log.module'
-import { OperationLogModule } from './logging/operation-log/operation-log.module'
-import { QuotaLogModule } from './logging/quota-log/quota-log.module'
-import { MenuModule } from './menu/menu.module'
-import { RoleModule } from './role/role.module'
-import { UserModule } from './user/user.module'
+import { Module } from '@nestjs/common';
+import { AuthModule } from './auth/auth.module';
+import { LoginLogModule } from './logging/login-log/login-log.module';
+import { OperationLogModule } from './logging/operation-log/operation-log.module';
+import { QuotaLogModule } from './logging/quota-log/quota-log.module';
+import { MenuModule } from './menu/menu.module';
+import { RoleModule } from './role/role.module';
+import { UserModule } from './user/user.module';
 
 
 @Module({
 @Module({
   imports: [
   imports: [

+ 14 - 7
apps/box-mgnt-api/src/mgnt-backend/core/logging/operation-log/operation-log.module.ts

@@ -1,13 +1,20 @@
-import { Module } from '@nestjs/common'
-import { MenuModule } from '../../menu/menu.module'
-import { UserModule } from '../../user/user.module'
-import { OperationLogController } from './operation-log.controller'
-import { OperationLogService } from './operation-log.service'
+import { Module } from '@nestjs/common';
+import { OPERATION_LOGGER } from '@box/common/interfaces/operation-logger.interface';
+import { MenuModule } from '../../menu/menu.module';
+import { UserModule } from '../../user/user.module';
+import { OperationLogController } from './operation-log.controller';
+import { OperationLogService } from './operation-log.service';
 
 
 @Module({
 @Module({
   imports: [UserModule, MenuModule],
   imports: [UserModule, MenuModule],
   controllers: [OperationLogController],
   controllers: [OperationLogController],
-  providers: [OperationLogService],
-  exports: [OperationLogService],
+  providers: [
+    OperationLogService,
+    {
+      provide: OPERATION_LOGGER,
+      useExisting: OperationLogService,
+    },
+  ],
+  exports: [OperationLogService, OPERATION_LOGGER],
 })
 })
 export class OperationLogModule {}
 export class OperationLogModule {}

+ 4 - 3
apps/box-mgnt-api/src/mgnt-backend/core/logging/operation-log/operation-log.service.ts

@@ -2,13 +2,14 @@ import { Injectable, Logger } from '@nestjs/common';
 import { OperationType, Prisma } from '@prisma/mysql/client';
 import { OperationType, Prisma } from '@prisma/mysql/client';
 import type { FastifyRequest } from 'fastify';
 import type { FastifyRequest } from 'fastify';
 import { OperationLogOptions } from '@box/common/decorators/operation-log.decorator';
 import { OperationLogOptions } from '@box/common/decorators/operation-log.decorator';
-import { HttpResponse } from '@box/common/interfaces/response.interface';
+import { ApiResponse } from '@box/common/interfaces/api-response.interface';
+import { IOperationLogger } from '@box/common/interfaces/operation-logger.interface';
 import { MysqlPrismaService } from '@box/db/prisma/mysql-prisma.service';
 import { MysqlPrismaService } from '@box/db/prisma/mysql-prisma.service';
 import { UtilsService } from '@box/db/utils.service';
 import { UtilsService } from '@box/db/utils.service';
 import { MenuService } from '../../menu/menu.service';
 import { MenuService } from '../../menu/menu.service';
 
 
 @Injectable()
 @Injectable()
-export class OperationLogService {
+export class OperationLogService implements IOperationLogger {
   private readonly logger = new Logger(OperationLogService.name);
   private readonly logger = new Logger(OperationLogService.name);
 
 
   constructor(
   constructor(
@@ -20,7 +21,7 @@ export class OperationLogService {
   async createLog(
   async createLog(
     req: FastifyRequest,
     req: FastifyRequest,
     options: OperationLogOptions,
     options: OperationLogOptions,
-    data: HttpResponse,
+    data: ApiResponse<unknown>,
     callMethod: string,
     callMethod: string,
     status: boolean,
     status: boolean,
   ) {
   ) {

+ 5 - 5
apps/box-mgnt-api/src/mgnt-backend/core/logging/quota-log/quota-log.module.ts

@@ -1,8 +1,8 @@
-import { Module } from '@nestjs/common'
-import { MenuModule } from '../../menu/menu.module'
-import { UserModule } from '../../user/user.module'
-import { QuotaLogController } from './quota-log.controller'
-import { QuotaLogService } from './quota-log.service'
+import { Module } from '@nestjs/common';
+import { MenuModule } from '../../menu/menu.module';
+import { UserModule } from '../../user/user.module';
+import { QuotaLogController } from './quota-log.controller';
+import { QuotaLogService } from './quota-log.service';
 
 
 @Module({
 @Module({
   imports: [UserModule, MenuModule],
   imports: [UserModule, MenuModule],

+ 1 - 1
apps/box-mgnt-api/src/mgnt-backend/core/menu/menu.constants.ts

@@ -1 +1 @@
-export const MENU_FRONTEND_AUTH = '/system/menu'
+export const MENU_FRONTEND_AUTH = '/system/menu';

+ 2 - 2
apps/box-mgnt-api/src/mgnt-backend/core/role/role.constants.ts

@@ -1,2 +1,2 @@
-export const ROLE_FRONTEND_AUTH = '/system/role'
-export const CHANNEL_ROLE_NAME = '渠道商'
+export const ROLE_FRONTEND_AUTH = '/system/role';
+export const CHANNEL_ROLE_NAME = '渠道商';

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

@@ -1,2 +1,2 @@
-export const USER_FRONTEND_AUTH = '/system/user'
-export const STATS_USER_FRONTEND_AUTH = '/stats/user'
+export const USER_FRONTEND_AUTH = '/system/user';
+export const STATS_USER_FRONTEND_AUTH = '/stats/user';

+ 4 - 4
apps/box-mgnt-api/src/mgnt-backend/feature/oss/oss.config.ts

@@ -1,5 +1,5 @@
-import { registerAs } from '@nestjs/config'
-import OSS from 'ali-oss'
+import { registerAs } from '@nestjs/config';
+import OSS from 'ali-oss';
 
 
 export const ossConfigFactory = registerAs(
 export const ossConfigFactory = registerAs(
   'oss',
   'oss',
@@ -10,6 +10,6 @@ export const ossConfigFactory = registerAs(
       bucket: process.env.OSS_BUCKET,
       bucket: process.env.OSS_BUCKET,
       region: process.env.OSS_REGION,
       region: process.env.OSS_REGION,
     }) as OSS.Options,
     }) as OSS.Options,
-)
+);
 
 
-export type IOssConfig = ReturnType<typeof ossConfigFactory>
+export type IOssConfig = ReturnType<typeof ossConfigFactory>;

+ 5 - 5
apps/box-mgnt-api/src/mgnt-backend/feature/oss/oss.module.ts

@@ -1,8 +1,8 @@
-import { Module } from '@nestjs/common'
-import { ConfigModule } from '@nestjs/config'
-import { ossConfigFactory } from './oss.config'
-import { OssController } from './oss.controller'
-import { OssService } from './oss.service'
+import { Module } from '@nestjs/common';
+import { ConfigModule } from '@nestjs/config';
+import { ossConfigFactory } from './oss.config';
+import { OssController } from './oss.controller';
+import { OssService } from './oss.service';
 
 
 @Module({
 @Module({
   imports: [ConfigModule.forFeature(ossConfigFactory)],
   imports: [ConfigModule.forFeature(ossConfigFactory)],

+ 6 - 2
apps/box-mgnt-api/src/mgnt-backend/feature/province-city/province-city.service.ts

@@ -15,7 +15,9 @@ export class ProvinceCityService {
     data.sort((a, b) => {
     data.sort((a, b) => {
       return pinyin(a.province, { toneType: 'none', type: 'array' })
       return pinyin(a.province, { toneType: 'none', type: 'array' })
         .join('')
         .join('')
-        .localeCompare(pinyin(b.province, { toneType: 'none', type: 'array' }).join(''));
+        .localeCompare(
+          pinyin(b.province, { toneType: 'none', type: 'array' }).join(''),
+        );
     });
     });
 
 
     // 每个城市列表也排序
     // 每个城市列表也排序
@@ -23,7 +25,9 @@ export class ProvinceCityService {
       item.cities.sort((a: string, b: string) => {
       item.cities.sort((a: string, b: string) => {
         return pinyin(a, { toneType: 'none', type: 'array' })
         return pinyin(a, { toneType: 'none', type: 'array' })
           .join('')
           .join('')
-          .localeCompare(pinyin(b, { toneType: 'none', type: 'array' }).join(''));
+          .localeCompare(
+            pinyin(b, { toneType: 'none', type: 'array' }).join(''),
+          );
       });
       });
     }
     }
 
 

+ 9 - 13
apps/box-mgnt-api/src/mgnt-backend/feature/s3/s3.config.ts

@@ -1,15 +1,11 @@
-import { registerAs } from '@nestjs/config'
+import { registerAs } from '@nestjs/config';
 
 
-export const s3ConfigFactory = registerAs(
-  's3',
-  () =>
-    ({
-      accessKeyId: process.env.AWS_ACCESS_KEY_ID,
-      accessKeySecret: process.env.AWS_SECRET_ACCESS_KEY,
-      bucket: process.env.AWS_STORAGE_BUCKET_NAME,
-      region: process.env.AWS_S3_REGION_NAME,
-      endpoint: process.env.AWS_S3_ENDPOINT_URL,
-    })
-)
+export const s3ConfigFactory = registerAs('s3', () => ({
+  accessKeyId: process.env.AWS_ACCESS_KEY_ID,
+  accessKeySecret: process.env.AWS_SECRET_ACCESS_KEY,
+  bucket: process.env.AWS_STORAGE_BUCKET_NAME,
+  region: process.env.AWS_S3_REGION_NAME,
+  endpoint: process.env.AWS_S3_ENDPOINT_URL,
+}));
 
 
-export type IS3Config = ReturnType<typeof s3ConfigFactory>
+export type IS3Config = ReturnType<typeof s3ConfigFactory>;

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

@@ -6,6 +6,6 @@ import { S3Controller } from './s3.controller';
   imports: [],
   imports: [],
   providers: [S3Service],
   providers: [S3Service],
   controllers: [S3Controller],
   controllers: [S3Controller],
-  exports: [S3Service]
+  exports: [S3Service],
 })
 })
 export class S3Module {}
 export class S3Module {}

+ 22 - 7
apps/box-mgnt-api/src/mgnt-backend/feature/sync-videomedia/sync-videomedia.service.ts

@@ -82,7 +82,9 @@ export class SyncVideomediaService {
 
 
     const normalized = list.map((item) => this.normalizeItem(item));
     const normalized = list.map((item) => this.normalizeItem(item));
 
 
-    console.log(`[sync-videomedia] Ready to import ${normalized.length} records`);
+    console.log(
+      `[sync-videomedia] Ready to import ${normalized.length} records`,
+    );
     console.log('[sync-videomedia] First record sample:', normalized[0]);
     console.log('[sync-videomedia] First record sample:', normalized[0]);
 
 
     // Batch processing - try to create each record individually and catch duplicate errors
     // Batch processing - try to create each record individually and catch duplicate errors
@@ -90,7 +92,7 @@ export class SyncVideomediaService {
     let created = 0;
     let created = 0;
     let updated = 0;
     let updated = 0;
     let skipped = 0;
     let skipped = 0;
-    let errors: any[] = [];
+    const errors: any[] = [];
 
 
     for (let i = 0; i < normalized.length; i += BATCH_SIZE) {
     for (let i = 0; i < normalized.length; i += BATCH_SIZE) {
       const batch = normalized.slice(i, i + BATCH_SIZE);
       const batch = normalized.slice(i, i + BATCH_SIZE);
@@ -130,7 +132,10 @@ export class SyncVideomediaService {
                 errors.push({ id: record.id, error: updateError.message });
                 errors.push({ id: record.id, error: updateError.message });
               }
               }
             } else {
             } else {
-              console.error(`[sync-videomedia] Skipped ${record.id}:`, error.message);
+              console.error(
+                `[sync-videomedia] Skipped ${record.id}:`,
+                error.message,
+              );
               skipped++;
               skipped++;
               errors.push({ id: record.id, error: error.message });
               errors.push({ id: record.id, error: error.message });
             }
             }
@@ -173,7 +178,9 @@ export class SyncVideomediaService {
       return data.list as RawVideoMedia[];
       return data.list as RawVideoMedia[];
     }
     }
 
 
-    throw new BadRequestException('Invalid JSON structure: expected array, data.list, or list');
+    throw new BadRequestException(
+      'Invalid JSON structure: expected array, data.list, or list',
+    );
   }
   }
 
 
   /**
   /**
@@ -186,15 +193,23 @@ export class SyncVideomediaService {
       throw new BadRequestException('Each item must have an id');
       throw new BadRequestException('Each item must have an id');
     }
     }
     if (!item.addedTime || !item.createdAt || !item.updatedAt) {
     if (!item.addedTime || !item.createdAt || !item.updatedAt) {
-      throw new BadRequestException(`Item ${item.id} is missing required datetime fields`);
+      throw new BadRequestException(
+        `Item ${item.id} is missing required datetime fields`,
+      );
     }
     }
 
 
     const addedTime = new Date(item.addedTime);
     const addedTime = new Date(item.addedTime);
     const createdAt = new Date(item.createdAt);
     const createdAt = new Date(item.createdAt);
     const updatedAt = new Date(item.updatedAt);
     const updatedAt = new Date(item.updatedAt);
 
 
-    if (isNaN(addedTime.getTime()) || isNaN(createdAt.getTime()) || isNaN(updatedAt.getTime())) {
-      throw new BadRequestException(`Item ${item.id} has invalid datetime format`);
+    if (
+      isNaN(addedTime.getTime()) ||
+      isNaN(createdAt.getTime()) ||
+      isNaN(updatedAt.getTime())
+    ) {
+      throw new BadRequestException(
+        `Item ${item.id} has invalid datetime format`,
+      );
     }
     }
 
 
     return {
     return {

+ 9 - 1
apps/box-mgnt-api/src/mgnt-backend/feature/system-params/system-param.dto.ts

@@ -1,6 +1,14 @@
 // system-param.dto.ts
 // system-param.dto.ts
 import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger';
 import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger';
-import { IsEnum, IsInt, IsOptional, IsString, Length, MaxLength, Min } from 'class-validator';
+import {
+  IsEnum,
+  IsInt,
+  IsOptional,
+  IsString,
+  Length,
+  MaxLength,
+  Min,
+} from 'class-validator';
 import { Transform, Type } from 'class-transformer';
 import { Transform, Type } from 'class-transformer';
 import { PageListDto } from '@box/common/dto/page-list.dto';
 import { PageListDto } from '@box/common/dto/page-list.dto';
 
 

+ 2 - 1
apps/box-mgnt-api/src/mgnt-backend/feature/system-params/system-params.service.ts

@@ -55,7 +55,8 @@ export class SystemParamsService {
           return String(sec);
           return String(sec);
         }
         }
         const parsed = Date.parse(v);
         const parsed = Date.parse(v);
-        if (!Number.isFinite(parsed)) throw new Error('value must be a valid time');
+        if (!Number.isFinite(parsed))
+          throw new Error('value must be a valid time');
         return String(Math.floor(parsed / 1000));
         return String(Math.floor(parsed / 1000));
       }
       }
 
 

+ 1 - 2
libs/common/src/common.module.ts

@@ -1,7 +1,6 @@
 import { Module } from '@nestjs/common';
 import { Module } from '@nestjs/common';
 import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core';
 import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core';
 import { LoggerModule } from 'nestjs-pino';
 import { LoggerModule } from 'nestjs-pino';
-import { OperationLogModule } from '../mgnt-backend/core/logging/operation-log/operation-log.module';
 import pinoConfig from './config/pino.config';
 import pinoConfig from './config/pino.config';
 import { HttpExceptionFilter } from './filters/http-exception.filter';
 import { HttpExceptionFilter } from './filters/http-exception.filter';
 import { LoggingInterceptor } from './interceptors/logging.interceptor';
 import { LoggingInterceptor } from './interceptors/logging.interceptor';
@@ -11,7 +10,7 @@ import { CorrelationInterceptor } from './interceptors/correlation.interceptor';
 import { ExceptionService } from './services/exception.service';
 import { ExceptionService } from './services/exception.service';
 
 
 @Module({
 @Module({
-  imports: [LoggerModule.forRoot(pinoConfig), OperationLogModule],
+  imports: [LoggerModule.forRoot(pinoConfig)],
   providers: [
   providers: [
     {
     {
       provide: APP_INTERCEPTOR,
       provide: APP_INTERCEPTOR,

+ 9 - 6
libs/common/src/decorators/auth-user.decorator.ts

@@ -1,11 +1,14 @@
-import { createParamDecorator, ExecutionContext } from '@nestjs/common'
-import { User } from '@prisma/mysql/client'
+import { createParamDecorator, ExecutionContext } from '@nestjs/common';
+import { User } from '@prisma/mysql/client';
+import type { FastifyRequest } from 'fastify';
 
 
-import type { FastifyRequest } from 'fastify'
+interface AuthenticatedRequest extends FastifyRequest {
+  user: User;
+}
 
 
 export const AuthUser = createParamDecorator(
 export const AuthUser = createParamDecorator(
   (_data, ctx: ExecutionContext): User => {
   (_data, ctx: ExecutionContext): User => {
-    const req = ctx.switchToHttp().getRequest<FastifyRequest>()
-    return req.user
+    const req = ctx.switchToHttp().getRequest<AuthenticatedRequest>();
+    return req.user;
   },
   },
-)
+);

+ 10 - 10
libs/common/src/decorators/operation-log.decorator.ts

@@ -1,14 +1,14 @@
-import { SetMetadata } from '@nestjs/common'
-import { OperationType } from '@prisma/mysql/client'
+import { SetMetadata } from '@nestjs/common';
+import { OperationType } from '@prisma/mysql/client';
 
 
-export const OPERATION_LOG_KEY = 'operationLog'
+export const OPERATION_LOG_KEY = 'operationLog';
 
 
 export interface OperationLogOptions {
 export interface OperationLogOptions {
-  description: string
-  type: OperationType
-  frontendAuth: string
-  isSaveRequest: boolean
-  isSaveResponse: boolean
+  description: string;
+  type: OperationType;
+  frontendAuth: string;
+  isSaveRequest: boolean;
+  isSaveResponse: boolean;
 }
 }
 
 
 /**
 /**
@@ -32,5 +32,5 @@ export const OperationLog = (
     frontendAuth,
     frontendAuth,
     isSaveRequest,
     isSaveRequest,
     isSaveResponse,
     isSaveResponse,
-  } as OperationLogOptions)
-}
+  } as OperationLogOptions);
+};

+ 10 - 10
libs/common/src/dto/page-list-response.dto.ts

@@ -1,10 +1,10 @@
-export class PageListResponseDto<T> {
-  list: T[];
-  total: number;
-  page: number;
-  size: number;
-
-  constructor(data: Partial<PageListResponseDto<T>>) {
-    Object.assign(this, data);
-  }
-}
+export class PageListResponseDto<T> {
+  list: T[];
+  total: number;
+  page: number;
+  size: number;
+
+  constructor(data: Partial<PageListResponseDto<T>>) {
+    Object.assign(this, data);
+  }
+}

+ 22 - 22
libs/common/src/dto/page-list.dto.ts

@@ -1,22 +1,22 @@
-import { ApiProperty } from '@nestjs/swagger';
-import { IsInt, Min } from 'class-validator';
-
-export class PageListDto {
-  @ApiProperty({
-    description: 'Page number (starts from 1)',
-    example: 1,
-    default: 1,
-  })
-  @IsInt()
-  @Min(1)
-  page: number;
-
-  @ApiProperty({
-    description: 'Number of records per page',
-    example: 10,
-    default: 10,
-  })
-  @IsInt()
-  @Min(1)
-  size: number;
-}
+import { ApiProperty } from '@nestjs/swagger';
+import { IsInt, Min } from 'class-validator';
+
+export class PageListDto {
+  @ApiProperty({
+    description: 'Page number (starts from 1)',
+    example: 1,
+    default: 1,
+  })
+  @IsInt()
+  @Min(1)
+  page: number;
+
+  @ApiProperty({
+    description: 'Number of records per page',
+    example: 10,
+    default: 10,
+  })
+  @IsInt()
+  @Min(1)
+  size: number;
+}

+ 9 - 9
libs/common/src/filters/all-exceptions.filter.ts

@@ -4,21 +4,21 @@ import {
   ExceptionFilter,
   ExceptionFilter,
   HttpStatus,
   HttpStatus,
   Logger,
   Logger,
-} from '@nestjs/common'
-import type { FastifyReply } from 'fastify'
-import { ExceptionService } from '../services/exception.service'
+} from '@nestjs/common';
+import type { FastifyReply } from 'fastify';
+import { ExceptionService } from '../services/exception.service';
 
 
 @Catch()
 @Catch()
 export class AllExceptionsFilter implements ExceptionFilter {
 export class AllExceptionsFilter implements ExceptionFilter {
-  private readonly logger = new Logger(AllExceptionsFilter.name)
+  private readonly logger = new Logger(AllExceptionsFilter.name);
 
 
   constructor(private readonly exceptionService: ExceptionService) {}
   constructor(private readonly exceptionService: ExceptionService) {}
 
 
   catch(exception: unknown, host: ArgumentsHost) {
   catch(exception: unknown, host: ArgumentsHost) {
-    const ctx = host.switchToHttp()
-    const response = ctx.getResponse<FastifyReply>()
-    const responseObj = this.exceptionService.getHttpResponse(exception)
-    response.status(HttpStatus.OK).send(responseObj)
-    this.logger.error(exception)
+    const ctx = host.switchToHttp();
+    const response = ctx.getResponse<FastifyReply>();
+    const responseObj = this.exceptionService.getHttpResponse(exception);
+    response.status(HttpStatus.OK).send(responseObj);
+    this.logger.error(exception);
   }
   }
 }
 }

+ 12 - 12
libs/common/src/interceptors/logging.interceptor.ts

@@ -4,25 +4,25 @@ import {
   Injectable,
   Injectable,
   Logger,
   Logger,
   NestInterceptor,
   NestInterceptor,
-} from '@nestjs/common'
-import type { FastifyRequest } from 'fastify'
-import { Observable } from 'rxjs'
-import { tap } from 'rxjs/operators'
+} from '@nestjs/common';
+import type { FastifyRequest } from 'fastify';
+import { Observable } from 'rxjs';
+import { tap } from 'rxjs/operators';
 
 
 @Injectable()
 @Injectable()
 export class LoggingInterceptor implements NestInterceptor {
 export class LoggingInterceptor implements NestInterceptor {
-  private readonly logger = new Logger(LoggingInterceptor.name)
+  private readonly logger = new Logger(LoggingInterceptor.name);
 
 
   intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
   intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
-    const req = context.switchToHttp().getRequest<FastifyRequest>()
-    const content = `${req.method} -> ${req.url}`
-    this.logger.log(`+++ 请求:${content}`)
-    const now = Date.now()
+    const req = context.switchToHttp().getRequest<FastifyRequest>();
+    const content = `${req.method} -> ${req.url}`;
+    this.logger.log(`+++ 请求:${content}`);
+    const now = Date.now();
     return next.handle().pipe(
     return next.handle().pipe(
       tap(() => {
       tap(() => {
-        const duration = Date.now() - now
-        this.logger.log(`--- 响应:${content} +${duration}ms`)
+        const duration = Date.now() - now;
+        this.logger.log(`--- 响应:${content} +${duration}ms`);
       }),
       }),
-    )
+    );
   }
   }
 }
 }

+ 16 - 3
libs/common/src/interceptors/operation-log.interceptor.ts

@@ -1,26 +1,33 @@
 import {
 import {
   CallHandler,
   CallHandler,
   ExecutionContext,
   ExecutionContext,
+  Inject,
   Injectable,
   Injectable,
   NestInterceptor,
   NestInterceptor,
+  Optional,
 } from '@nestjs/common';
 } from '@nestjs/common';
 import { Reflector } from '@nestjs/core';
 import { Reflector } from '@nestjs/core';
 import type { FastifyRequest } from 'fastify';
 import type { FastifyRequest } from 'fastify';
 import { Observable } from 'rxjs';
 import { Observable } from 'rxjs';
 import { tap } from 'rxjs/operators';
 import { tap } from 'rxjs/operators';
-import { OperationLogService } from '../../mgnt-backend/core/logging/operation-log/operation-log.service';
 import {
 import {
   OPERATION_LOG_KEY,
   OPERATION_LOG_KEY,
   OperationLogOptions,
   OperationLogOptions,
 } from '../decorators/operation-log.decorator';
 } from '../decorators/operation-log.decorator';
 import { ApiResponse } from '../interfaces/api-response.interface';
 import { ApiResponse } from '../interfaces/api-response.interface';
+import {
+  IOperationLogger,
+  OPERATION_LOGGER,
+} from '../interfaces/operation-logger.interface';
 import { ExceptionService } from '../services/exception.service';
 import { ExceptionService } from '../services/exception.service';
 
 
 @Injectable()
 @Injectable()
 export class OperationLogInterceptor implements NestInterceptor {
 export class OperationLogInterceptor implements NestInterceptor {
   constructor(
   constructor(
     private readonly reflector: Reflector,
     private readonly reflector: Reflector,
-    private readonly operationLogService: OperationLogService,
+    @Optional()
+    @Inject(OPERATION_LOGGER)
+    private readonly operationLogger: IOperationLogger | null,
     private readonly exceptionService: ExceptionService,
     private readonly exceptionService: ExceptionService,
   ) {}
   ) {}
 
 
@@ -43,6 +50,11 @@ export class OperationLogInterceptor implements NestInterceptor {
     data: ApiResponse<unknown>,
     data: ApiResponse<unknown>,
     status: boolean,
     status: boolean,
   ) {
   ) {
+    if (!this.operationLogger) {
+      // No operation logger provided, skip logging
+      return;
+    }
+
     const options = this.reflector.get<OperationLogOptions>(
     const options = this.reflector.get<OperationLogOptions>(
       OPERATION_LOG_KEY,
       OPERATION_LOG_KEY,
       context.getHandler(),
       context.getHandler(),
@@ -53,7 +65,8 @@ export class OperationLogInterceptor implements NestInterceptor {
     const methodKey = context.getHandler().name;
     const methodKey = context.getHandler().name;
     const callMethod = `${className}.${methodKey}()`;
     const callMethod = `${className}.${methodKey}()`;
     const req = context.switchToHttp().getRequest<FastifyRequest>();
     const req = context.switchToHttp().getRequest<FastifyRequest>();
-    await this.operationLogService.createLog(
+
+    await this.operationLogger.createLog(
       req,
       req,
       options,
       options,
       data,
       data,

+ 1 - 0
libs/common/src/interfaces/index.ts

@@ -1,2 +1,3 @@
 export * from './api-response.interface';
 export * from './api-response.interface';
+export * from './operation-logger.interface';
 export * from './response.interface';
 export * from './response.interface';

+ 19 - 0
libs/common/src/interfaces/operation-logger.interface.ts

@@ -0,0 +1,19 @@
+import type { FastifyRequest } from 'fastify';
+import type { OperationLogOptions } from '../decorators/operation-log.decorator';
+import type { ApiResponse } from './api-response.interface';
+
+/**
+ * Abstract interface for operation logging
+ * Implementations should be provided at the app level
+ */
+export interface IOperationLogger {
+  createLog(
+    req: FastifyRequest,
+    options: OperationLogOptions,
+    data: ApiResponse<unknown>,
+    callMethod: string,
+    status: boolean,
+  ): Promise<void>;
+}
+
+export const OPERATION_LOGGER = Symbol('OPERATION_LOGGER');

+ 3 - 3
libs/common/src/interfaces/response.interface.ts

@@ -1,7 +1,7 @@
 export interface HttpResponse {
 export interface HttpResponse {
   // 请求出错时 error 会返回错误信息
   // 请求出错时 error 会返回错误信息
-  error: string
+  error: string;
   // 为 1 时表示请求成功,为 0 时表示接口需要登录或者登录状态失效,需要重新登录
   // 为 1 时表示请求成功,为 0 时表示接口需要登录或者登录状态失效,需要重新登录
-  status: 1 | 0
-  data: unknown | null
+  status: 1 | 0;
+  data: unknown | null;
 }
 }

+ 2 - 3
libs/common/src/services/exception.service.ts

@@ -11,7 +11,6 @@ import { ApiResponse } from '../interfaces/api-response.interface';
 export class ExceptionService {
 export class ExceptionService {
   getHttpResponse(exception: unknown): ApiResponse<null> {
   getHttpResponse(exception: unknown): ApiResponse<null> {
     let message: string;
     let message: string;
-    let success = false;
     let code = 'INTERNAL_ERROR';
     let code = 'INTERNAL_ERROR';
 
 
     if (exception instanceof HttpException) {
     if (exception instanceof HttpException) {
@@ -22,9 +21,9 @@ export class ExceptionService {
     }
     }
 
 
     return {
     return {
-      success,
+      error: message,
+      status: 0,
       code,
       code,
-      message,
       data: null,
       data: null,
       timestamp: new Date().toISOString(),
       timestamp: new Date().toISOString(),
     };
     };

+ 8 - 0
libs/common/src/types/fastify.d.ts

@@ -0,0 +1,8 @@
+import type { User } from '@prisma/mysql/client';
+
+declare module 'fastify' {
+  interface FastifyRequest {
+    user: User;
+    mfaVerified?: boolean;
+  }
+}

+ 4 - 4
libs/db/src/prisma/mongo-prisma.service.ts

@@ -1,5 +1,5 @@
-import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common'
-import { PrismaClient as MongoPrismaClient } from '@prisma/mongo/client'
+import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
+import { PrismaClient as MongoPrismaClient } from '@prisma/mongo/client';
 
 
 @Injectable()
 @Injectable()
 export class MongoPrismaService
 export class MongoPrismaService
@@ -7,10 +7,10 @@ export class MongoPrismaService
   implements OnModuleInit, OnModuleDestroy
   implements OnModuleInit, OnModuleDestroy
 {
 {
   async onModuleInit() {
   async onModuleInit() {
-    await this.$connect()
+    await this.$connect();
   }
   }
 
 
   async onModuleDestroy() {
   async onModuleDestroy() {
-    await this.$disconnect()
+    await this.$disconnect();
   }
   }
 }
 }

+ 4 - 4
libs/db/src/prisma/mysql-prisma.service.ts

@@ -1,5 +1,5 @@
-import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common'
-import { PrismaClient as MysqlPrismaClient } from '@prisma/mysql/client'
+import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
+import { PrismaClient as MysqlPrismaClient } from '@prisma/mysql/client';
 
 
 @Injectable()
 @Injectable()
 export class MysqlPrismaService
 export class MysqlPrismaService
@@ -7,10 +7,10 @@ export class MysqlPrismaService
   implements OnModuleInit, OnModuleDestroy
   implements OnModuleInit, OnModuleDestroy
 {
 {
   async onModuleInit() {
   async onModuleInit() {
-    await this.$connect()
+    await this.$connect();
   }
   }
 
 
   async onModuleDestroy() {
   async onModuleDestroy() {
-    await this.$disconnect()
+    await this.$disconnect();
   }
   }
 }
 }

+ 4 - 4
libs/db/src/shared.module.ts

@@ -1,7 +1,7 @@
-import { HttpModule } from '@nestjs/axios'
-import { Global, Module } from '@nestjs/common'
-import { UtilsService } from './utils.service'
-import { PrismaModule } from './prisma/prisma.module'
+import { HttpModule } from '@nestjs/axios';
+import { Global, Module } from '@nestjs/common';
+import { UtilsService } from './utils.service';
+import { PrismaModule } from './prisma/prisma.module';
 
 
 @Global()
 @Global()
 @Module({
 @Module({

+ 15 - 15
libs/db/src/utils.service.ts

@@ -1,35 +1,35 @@
-import { Injectable } from '@nestjs/common'
-import crypto from 'crypto'
-import type { FastifyRequest } from 'fastify'
-import Qqwry from 'lib-qqwry'
-import { isArray } from 'lodash'
+import { Injectable } from '@nestjs/common';
+import crypto from 'crypto';
+import type { FastifyRequest } from 'fastify';
+import Qqwry from 'lib-qqwry';
+import { isArray } from 'lodash';
 
 
 @Injectable()
 @Injectable()
 export class UtilsService {
 export class UtilsService {
-  private qqwry = new Qqwry(true)
+  private qqwry = new Qqwry(true);
 
 
   getRealIp(req: FastifyRequest) {
   getRealIp(req: FastifyRequest) {
-    let clientIp = req.headers['x-forwarded-for'] || req.ip
+    let clientIp = req.headers['x-forwarded-for'] || req.ip;
     if (isArray(clientIp)) {
     if (isArray(clientIp)) {
-      clientIp = clientIp[0]
+      clientIp = clientIp[0];
     }
     }
-    clientIp = clientIp.split(',')[0]
-    return clientIp
+    clientIp = clientIp.split(',')[0];
+    return clientIp;
   }
   }
 
 
   getIpRegion(ip: string) {
   getIpRegion(ip: string) {
     if (ip == null) {
     if (ip == null) {
-      return
+      return;
     }
     }
     try {
     try {
-      const result = this.qqwry.searchIP(ip)
-      return result.Country
+      const result = this.qqwry.searchIP(ip);
+      return result.Country;
     } catch (err) {
     } catch (err) {
-      return '未知地区'
+      return '未知地区';
     }
     }
   }
   }
 
 
   getMd5(str: string) {
   getMd5(str: string) {
-    return crypto.createHash('md5').update(str).digest('hex')
+    return crypto.createHash('md5').update(str).digest('hex');
   }
   }
 }
 }

+ 6 - 1
package.json

@@ -12,7 +12,12 @@
     "prisma:generate:mongo": "prisma generate --schema prisma/mongo/schema",
     "prisma:generate:mongo": "prisma generate --schema prisma/mongo/schema",
     "prisma:generate": "pnpm prisma:generate:mysql && pnpm prisma:generate:mongo",
     "prisma:generate": "pnpm prisma:generate:mysql && pnpm prisma:generate:mongo",
     "prisma:seed:mysql": "dotenv -e .env.mgnt.dev -- tsx prisma/mysql/seed.ts",
     "prisma:seed:mysql": "dotenv -e .env.mgnt.dev -- tsx prisma/mysql/seed.ts",
-    "prisma:setup:mysql": "pnpm prisma:migrate:dev:mysql && pnpm prisma:seed:mysql"
+    "prisma:setup:mysql": "pnpm prisma:migrate:dev:mysql && pnpm prisma:seed:mysql",
+    "typecheck": "tsc --noEmit --project tsconfig.base.json",
+    "typecheck:watch": "tsc --noEmit --watch --project tsconfig.base.json",
+    "lint": "eslint \"{apps,libs}/**/*.ts\" --max-warnings 0",
+    "lint:fix": "eslint \"{apps,libs}/**/*.ts\" --fix",
+    "test": "pnpm typecheck && pnpm lint && pnpm build:mgnt"
   },
   },
   "dependencies": {
   "dependencies": {
     "@aws-sdk/client-s3": "3.828.0",
     "@aws-sdk/client-s3": "3.828.0",

+ 16 - 0
tsconfig.json

@@ -0,0 +1,16 @@
+{
+  "extends": "./tsconfig.base.json",
+  "compilerOptions": {
+    "baseUrl": ".",
+    "paths": {
+      "@box/common/*": ["libs/common/src/*"],
+      "@box/db/*": ["libs/db/src/*"],
+      "@box/core/*": ["libs/core/src/*"],
+      "@box/mgnt/*": ["apps/box-mgnt-api/src/*"],
+      "@prisma/mysql/client": ["node_modules/@prisma/mysql/client/index.d.ts"],
+      "@prisma/mongo/client": ["node_modules/@prisma/mongo/client/index.d.ts"]
+    }
+  },
+  "include": ["apps/**/*", "libs/**/*", "prisma/**/*"],
+  "exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"]
+}