From 54a86e1501c58dac55ba96249a3ed6b598dd9eff Mon Sep 17 00:00:00 2001 From: luch <32321690+luch1994@users.noreply.github.com> Date: Sun, 22 Sep 2024 01:42:27 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=AE=E6=94=B9=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E5=92=8C=E7=99=BB=E5=BD=95=E7=8A=B6=E6=80=81=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=E4=BB=A3=E7=A0=81CR=E9=97=AE=E9=A2=98=20(#431)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/.env | 4 +- server/src/app.module.ts | 8 +- server/src/config/index.ts | 21 -- .../src/guards/__test/session.guard.spec.ts | 68 +++++ server/src/logger/index.ts | 6 +- server/src/logger/logger.provider.ts | 6 +- .../src/middlewares/logRequest.middleware.ts | 5 +- server/src/models/downloadTask.entity.ts | 6 +- server/src/models/surveyResponse.entity.ts | 4 +- .../auth/controllers/auth.controller.ts | 34 ++- .../src/modules/file/services/file.service.ts | 6 +- .../services/uploadHandlers/local.handler.ts | 6 +- .../__test/collaborator.controller.spec.ts | 8 +- .../__test/collaborator.service.spec.ts | 8 +- .../__test/dataStatistic.controller.spec.ts | 22 +- .../__test/dataStatistic.service.spec.ts | 8 +- .../__test/downloadTask.controller.spec.ts | 259 ++++++++++++++++++ .../__test/downloadTask.service.spec.ts | 192 +++++++++++++ .../survey/__test/session.controller.spec.ts | 87 ++++++ .../survey/__test/survey.controller.spec.ts | 185 +++++-------- .../__test/surveyHistory.controller.spec.ts | 4 +- .../__test/surveyMeta.controller.spec.ts | 32 ++- .../survey/__test/surveyMeta.service.spec.ts | 8 +- .../controllers/collaborator.controller.ts | 4 +- .../controllers/dataStatistic.controller.ts | 16 +- .../controllers/downloadTask.controller.ts | 19 +- .../survey/controllers/session.controller.ts | 5 +- .../survey/controllers/survey.controller.ts | 6 +- .../controllers/surveyHistory.controller.ts | 4 +- .../controllers/surveyMeta.controller.ts | 4 +- .../modules/survey/dto/createSurvey.dto.ts | 4 +- .../modules/survey/dto/downloadTask.dto.ts | 4 +- .../survey/services/collaborator.service.ts | 4 +- .../survey/services/downloadTask.service.ts | 33 ++- .../survey/services/surveyMeta.service.ts | 4 +- .../__test/surveyResponse.controller.spec.ts | 10 +- .../controllers/responseSchema.controller.ts | 4 +- .../controllers/surveyResponse.controller.ts | 4 +- .../_test/workspace.controller.spec.ts | 4 +- .../controllers/workspace.controller.ts | 4 +- server/src/securityPlugin/interface.ts | 4 +- .../securityPlugin/pluginManager.provider.ts | 4 +- server/src/securityPlugin/pluginManager.ts | 10 +- .../responseSecurityPlugin/index.ts | 10 +- .../responseSecurityPlugin/utils.ts | 2 +- web/package.json | 2 +- web/src/management/App.vue | 18 +- web/src/management/api/auth.js | 4 + web/src/management/api/downloadTask.js | 4 +- web/src/management/components/TopNav.vue | 91 ++++++ web/src/management/main.js | 4 + .../pages/analysis/pages/DataTablePage.vue | 19 +- .../pages/download/DownloadPage.vue | 27 ++ .../components/DownloadTaskList.vue | 6 - .../pages/downloadTask/TaskList.vue | 103 ------- web/src/management/pages/list/index.vue | 76 +---- web/src/management/router/index.ts | 2 +- .../setters/widgets/QuestionTime.vue | 2 - .../setters/widgets/QuestionTimeHour.vue | 3 - web/src/render/stores/survey.js | 4 - 60 files changed, 1016 insertions(+), 499 deletions(-) delete mode 100644 server/src/config/index.ts create mode 100644 server/src/guards/__test/session.guard.spec.ts create mode 100644 server/src/modules/survey/__test/downloadTask.controller.spec.ts create mode 100644 server/src/modules/survey/__test/downloadTask.service.spec.ts create mode 100644 server/src/modules/survey/__test/session.controller.spec.ts create mode 100644 web/src/management/components/TopNav.vue create mode 100644 web/src/management/pages/download/DownloadPage.vue rename web/src/management/pages/{downloadTask => download}/components/DownloadTaskList.vue (97%) delete mode 100644 web/src/management/pages/downloadTask/TaskList.vue diff --git a/server/.env b/server/.env index b2286da4..22187306 100644 --- a/server/.env +++ b/server/.env @@ -1,6 +1,6 @@ XIAOJU_SURVEY_MONGO_DB_NAME=xiaojuSurvey -XIAOJU_SURVEY_MONGO_URL= # mongodb://127.0.0.1:27017 # 建议设置强密码 -XIAOJU_SURVEY_MONGO_AUTH_SOURCE= # admin +XIAOJU_SURVEY_MONGO_URL=#mongodb://127.0.0.1:27017 # 建议设置强密码,放开注释不能有空格 +XIAOJU_SURVEY_MONGO_AUTH_SOURCE=admin XIAOJU_SURVEY_REDIS_HOST= XIAOJU_SURVEY_REDIS_PORT= diff --git a/server/src/app.module.ts b/server/src/app.module.ts index bbc87911..d9ea22e3 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -39,8 +39,8 @@ import { Collaborator } from './models/collaborator.entity'; import { LoggerProvider } from './logger/logger.provider'; import { PluginManagerProvider } from './securityPlugin/pluginManager.provider'; import { LogRequestMiddleware } from './middlewares/logRequest.middleware'; -import { XiaojuSurveyPluginManager } from './securityPlugin/pluginManager'; -import { XiaojuSurveyLogger } from './logger'; +import { PluginManager } from './securityPlugin/pluginManager'; +import { Logger } from './logger'; import { DownloadTask } from './models/downloadTask.entity'; import { Session } from './models/session.entity'; @@ -118,7 +118,7 @@ import { Session } from './models/session.entity'; export class AppModule { constructor( private readonly configService: ConfigService, - private readonly pluginManager: XiaojuSurveyPluginManager, + private readonly pluginManager: PluginManager, ) {} configure(consumer: MiddlewareConsumer) { consumer.apply(LogRequestMiddleware).forRoutes('*'); @@ -132,7 +132,7 @@ export class AppModule { ), new SurveyUtilPlugin(), ); - XiaojuSurveyLogger.init({ + Logger.init({ filename: this.configService.get('XIAOJU_SURVEY_LOGGER_FILENAME'), }); } diff --git a/server/src/config/index.ts b/server/src/config/index.ts deleted file mode 100644 index 2068c4de..00000000 --- a/server/src/config/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -const mongo = { - url: process.env.XIAOJU_SURVEY_MONGO_URL || 'mongodb://localhost:27017', - dbName: process.env.XIAOJU_SURVER_MONGO_DBNAME || 'xiaojuSurvey', -}; - -const session = { - expireTime: - parseInt(process.env.XIAOJU_SURVEY_JWT_EXPIRES_IN) || 8 * 3600 * 1000, -}; - -const encrypt = { - type: process.env.XIAOJU_SURVEY_ENCRYPT_TYPE || 'aes', - aesCodelength: parseInt(process.env.XIAOJU_SURVEY_ENCRYPT_TYPE_LEN) || 10, //aes密钥长度 -}; - -const jwt = { - secret: process.env.XIAOJU_SURVEY_JWT_SECRET || 'xiaojuSurveyJwtSecret', - expiresIn: process.env.XIAOJU_SURVEY_JWT_EXPIRES_IN || '8h', -}; - -export { mongo, session, encrypt, jwt }; diff --git a/server/src/guards/__test/session.guard.spec.ts b/server/src/guards/__test/session.guard.spec.ts new file mode 100644 index 00000000..1904fc22 --- /dev/null +++ b/server/src/guards/__test/session.guard.spec.ts @@ -0,0 +1,68 @@ +import { ExecutionContext } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { SessionService } from 'src/modules/survey/services/session.service'; +import { SessionGuard } from '../session.guard'; +import { NoPermissionException } from 'src/exceptions/noPermissionException'; + +describe('SessionGuard', () => { + let sessionGuard: SessionGuard; + let reflector: Reflector; + let sessionService: SessionService; + + beforeEach(() => { + reflector = new Reflector(); + sessionService = { + findOne: jest.fn(), + } as unknown as SessionService; + sessionGuard = new SessionGuard(reflector, sessionService); + }); + + it('should return true when sessionId exists and sessionService returns sessionInfo', async () => { + const mockSessionId = '12345'; + const mockSessionInfo = { id: mockSessionId, name: 'test session' }; + + const context = { + switchToHttp: jest.fn().mockReturnThis(), + getRequest: jest.fn().mockReturnValue({ + sessionId: mockSessionId, + }), + getHandler: jest.fn(), + } as unknown as ExecutionContext; + + jest.spyOn(reflector, 'get').mockReturnValue('sessionId'); + + jest + .spyOn(sessionService, 'findOne') + .mockResolvedValue(mockSessionInfo as any); + + const result = await sessionGuard.canActivate(context); + + const request = context.switchToHttp().getRequest(); + + expect(result).toBe(true); + expect(reflector.get).toHaveBeenCalledWith( + 'sessionId', + context.getHandler(), + ); + expect(sessionService.findOne).toHaveBeenCalledWith(mockSessionId); + expect(request.sessionInfo).toEqual(mockSessionInfo); + }); + + it('should throw NoPermissionException when sessionId is missing', async () => { + const context = { + switchToHttp: jest.fn().mockReturnThis(), + getRequest: jest.fn().mockReturnValue({}), + getHandler: jest.fn(), + } as unknown as ExecutionContext; + + jest.spyOn(reflector, 'get').mockReturnValue('sessionId'); + + await expect(sessionGuard.canActivate(context)).rejects.toThrow( + NoPermissionException, + ); + expect(reflector.get).toHaveBeenCalledWith( + 'sessionId', + context.getHandler(), + ); + }); +}); diff --git a/server/src/logger/index.ts b/server/src/logger/index.ts index a933fe93..f6fd8b17 100644 --- a/server/src/logger/index.ts +++ b/server/src/logger/index.ts @@ -4,12 +4,12 @@ import { Injectable, Scope } from '@nestjs/common'; const log4jsLogger = log4js.getLogger(); @Injectable({ scope: Scope.REQUEST }) -export class XiaojuSurveyLogger { +export class Logger { private static inited = false; private traceId: string; static init(config: { filename: string }) { - if (XiaojuSurveyLogger.inited) { + if (Logger.inited) { return; } log4js.configure({ @@ -30,7 +30,7 @@ export class XiaojuSurveyLogger { default: { appenders: ['app'], level: 'trace' }, }, }); - XiaojuSurveyLogger.inited = true; + Logger.inited = true; } _log(message, options: { dltag?: string; level: string }) { diff --git a/server/src/logger/logger.provider.ts b/server/src/logger/logger.provider.ts index cf7c7bbe..2a298dd4 100644 --- a/server/src/logger/logger.provider.ts +++ b/server/src/logger/logger.provider.ts @@ -1,8 +1,8 @@ import { Provider } from '@nestjs/common'; -import { XiaojuSurveyLogger } from './index'; +import { Logger } from './index'; export const LoggerProvider: Provider = { - provide: XiaojuSurveyLogger, - useClass: XiaojuSurveyLogger, + provide: Logger, + useClass: Logger, }; diff --git a/server/src/middlewares/logRequest.middleware.ts b/server/src/middlewares/logRequest.middleware.ts index d906b479..20d4b676 100644 --- a/server/src/middlewares/logRequest.middleware.ts +++ b/server/src/middlewares/logRequest.middleware.ts @@ -1,12 +1,11 @@ -// logger.middleware.ts import { Injectable, NestMiddleware } from '@nestjs/common'; import { Request, Response, NextFunction } from 'express'; -import { XiaojuSurveyLogger } from '../logger/index'; // 替换为你实际的logger路径 +import { Logger } from '../logger/index'; // 替换为你实际的logger路径 import { genTraceId } from '../logger/util'; @Injectable() export class LogRequestMiddleware implements NestMiddleware { - constructor(private readonly logger: XiaojuSurveyLogger) {} + constructor(private readonly logger: Logger) {} use(req: Request, res: Response, next: NextFunction) { const { method, originalUrl, ip } = req; diff --git a/server/src/models/downloadTask.entity.ts b/server/src/models/downloadTask.entity.ts index 90bb7f0b..a1e371bc 100644 --- a/server/src/models/downloadTask.entity.ts +++ b/server/src/models/downloadTask.entity.ts @@ -19,7 +19,11 @@ export class DownloadTask extends BaseEntity { // 任务创建人 @Column() - ownerId: string; + creatorId: string; + + // 任务创建人 + @Column() + creator: string; // 文件名 @Column() diff --git a/server/src/models/surveyResponse.entity.ts b/server/src/models/surveyResponse.entity.ts index fce0bfd1..c1e5db4b 100644 --- a/server/src/models/surveyResponse.entity.ts +++ b/server/src/models/surveyResponse.entity.ts @@ -27,11 +27,11 @@ export class SurveyResponse extends BaseEntity { @BeforeInsert() async onDataInsert() { - return await pluginManager.triggerHook('beforeResponseDataCreate', this); + return await pluginManager.triggerHook('encryptResponseData', this); } @AfterLoad() async onDataLoaded() { - return await pluginManager.triggerHook('afterResponseDataReaded', this); + return await pluginManager.triggerHook('decryptResponseData', this); } } diff --git a/server/src/modules/auth/controllers/auth.controller.ts b/server/src/modules/auth/controllers/auth.controller.ts index 764e4d81..6f0ce3c2 100644 --- a/server/src/modules/auth/controllers/auth.controller.ts +++ b/server/src/modules/auth/controllers/auth.controller.ts @@ -1,4 +1,12 @@ -import { Controller, Post, Body, HttpCode, Get, Query } from '@nestjs/common'; +import { + Controller, + Post, + Body, + HttpCode, + Get, + Query, + Request, +} from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { UserService } from '../services/user.service'; import { CaptchaService } from '../services/captcha.service'; @@ -214,4 +222,28 @@ export class AuthController { data: 'Weak', }; } + + @Get('/verifyToken') + @HttpCode(200) + async verifyToken(@Request() req) { + const token = req.headers.authorization?.split(' ')[1]; + if (!token) { + return { + code: 200, + data: false, + }; + } + try { + await this.authService.verifyToken(token); + return { + code: 200, + data: true, + }; + } catch (error) { + return { + code: 200, + data: false, + }; + } + } } diff --git a/server/src/modules/file/services/file.service.ts b/server/src/modules/file/services/file.service.ts index 6cace9fa..55b13439 100644 --- a/server/src/modules/file/services/file.service.ts +++ b/server/src/modules/file/services/file.service.ts @@ -14,17 +14,17 @@ export class FileService { configKey, file, pathPrefix, - keepOriginFilename, + filename, }: { configKey: string; file: Express.Multer.File; pathPrefix: string; - keepOriginFilename?: boolean; + filename?: string; }) { const handler = this.getHandler(configKey); const { key } = await handler.upload(file, { pathPrefix, - keepOriginFilename, + filename, }); const url = await handler.getUrl(key); return { diff --git a/server/src/modules/file/services/uploadHandlers/local.handler.ts b/server/src/modules/file/services/uploadHandlers/local.handler.ts index 83a95c5b..6639bb1b 100644 --- a/server/src/modules/file/services/uploadHandlers/local.handler.ts +++ b/server/src/modules/file/services/uploadHandlers/local.handler.ts @@ -12,11 +12,11 @@ export class LocalHandler implements FileUploadHandler { async upload( file: Express.Multer.File, - options?: { pathPrefix?: string; keepOriginFilename?: boolean }, + options?: { pathPrefix?: string; filename?: string }, ): Promise<{ key: string }> { let filename; - if (options?.keepOriginFilename) { - filename = file.originalname; + if (options?.filename) { + filename = file.filename; } else { filename = await generateUniqueFilename(file.originalname); } diff --git a/server/src/modules/survey/__test/collaborator.controller.spec.ts b/server/src/modules/survey/__test/collaborator.controller.spec.ts index 2d9f458a..db5e2700 100644 --- a/server/src/modules/survey/__test/collaborator.controller.spec.ts +++ b/server/src/modules/survey/__test/collaborator.controller.spec.ts @@ -1,7 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { CollaboratorController } from '../controllers/collaborator.controller'; import { CollaboratorService } from '../services/collaborator.service'; -import { XiaojuSurveyLogger } from 'src/logger'; +import { Logger } from 'src/logger'; import { HttpException } from 'src/exceptions/httpException'; import { CreateCollaboratorDto } from '../dto/createCollaborator.dto'; import { Collaborator } from 'src/models/collaborator.entity'; @@ -25,7 +25,7 @@ jest.mock('src/guards/workspace.guard'); describe('CollaboratorController', () => { let controller: CollaboratorController; let collaboratorService: CollaboratorService; - let logger: XiaojuSurveyLogger; + let logger: Logger; let userService: UserService; let surveyMetaService: SurveyMetaService; let workspaceMemberServie: WorkspaceMemberService; @@ -50,7 +50,7 @@ describe('CollaboratorController', () => { }, }, { - provide: XiaojuSurveyLogger, + provide: Logger, useValue: { error: jest.fn(), info: jest.fn(), @@ -84,7 +84,7 @@ describe('CollaboratorController', () => { controller = module.get(CollaboratorController); collaboratorService = module.get(CollaboratorService); - logger = module.get(XiaojuSurveyLogger); + logger = module.get(Logger); userService = module.get(UserService); surveyMetaService = module.get(SurveyMetaService); workspaceMemberServie = module.get( diff --git a/server/src/modules/survey/__test/collaborator.service.spec.ts b/server/src/modules/survey/__test/collaborator.service.spec.ts index f276cf9a..34a1157e 100644 --- a/server/src/modules/survey/__test/collaborator.service.spec.ts +++ b/server/src/modules/survey/__test/collaborator.service.spec.ts @@ -3,13 +3,13 @@ import { CollaboratorService } from '../services/collaborator.service'; import { getRepositoryToken } from '@nestjs/typeorm'; import { Collaborator } from 'src/models/collaborator.entity'; import { MongoRepository } from 'typeorm'; -import { XiaojuSurveyLogger } from 'src/logger'; +import { Logger } from 'src/logger'; import { InsertManyResult, ObjectId } from 'mongodb'; describe('CollaboratorService', () => { let service: CollaboratorService; let repository: MongoRepository; - let logger: XiaojuSurveyLogger; + let logger: Logger; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -20,7 +20,7 @@ describe('CollaboratorService', () => { useClass: MongoRepository, }, { - provide: XiaojuSurveyLogger, + provide: Logger, useValue: { info: jest.fn(), }, @@ -32,7 +32,7 @@ describe('CollaboratorService', () => { repository = module.get>( getRepositoryToken(Collaborator), ); - logger = module.get(XiaojuSurveyLogger); + logger = module.get(Logger); }); describe('create', () => { diff --git a/server/src/modules/survey/__test/dataStatistic.controller.spec.ts b/server/src/modules/survey/__test/dataStatistic.controller.spec.ts index 9d40ced0..bdc6ee7f 100644 --- a/server/src/modules/survey/__test/dataStatistic.controller.spec.ts +++ b/server/src/modules/survey/__test/dataStatistic.controller.spec.ts @@ -8,8 +8,8 @@ import { SurveyMetaService } from '../services/surveyMeta.service'; import { ResponseSchemaService } from '../../surveyResponse/services/responseScheme.service'; import { PluginManagerProvider } from 'src/securityPlugin/pluginManager.provider'; -import { XiaojuSurveyPluginManager } from 'src/securityPlugin/pluginManager'; -import { XiaojuSurveyLogger } from 'src/logger'; +import { PluginManager } from 'src/securityPlugin/pluginManager'; +import { Logger } from 'src/logger'; import { UserService } from 'src/modules/auth/services/user.service'; import { ResponseSecurityPlugin } from 'src/securityPlugin/responseSecurityPlugin'; @@ -27,8 +27,8 @@ describe('DataStatisticController', () => { let controller: DataStatisticController; let dataStatisticService: DataStatisticService; let responseSchemaService: ResponseSchemaService; - let pluginManager: XiaojuSurveyPluginManager; - let logger: XiaojuSurveyLogger; + let pluginManager: PluginManager; + let logger: Logger; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -56,7 +56,7 @@ describe('DataStatisticController', () => { })), }, { - provide: XiaojuSurveyLogger, + provide: Logger, useValue: { error: jest.fn(), }, @@ -70,10 +70,8 @@ describe('DataStatisticController', () => { responseSchemaService = module.get( ResponseSchemaService, ); - pluginManager = module.get( - XiaojuSurveyPluginManager, - ); - logger = module.get(XiaojuSurveyLogger); + pluginManager = module.get(PluginManager); + logger = module.get(Logger); pluginManager.registerPlugin( new ResponseSecurityPlugin('dataAesEncryptSecretKey'), @@ -90,7 +88,7 @@ describe('DataStatisticController', () => { const mockRequest = { query: { surveyId, - isDesensitive: false, + isMasked: false, page: 1, pageSize: 10, }, @@ -131,12 +129,12 @@ describe('DataStatisticController', () => { }); }); - it('should return data table with isDesensitive', async () => { + it('should return data table with isMasked', async () => { const surveyId = new ObjectId().toString(); const mockRequest = { query: { surveyId, - isDesensitive: true, + isMasked: true, page: 1, pageSize: 10, }, diff --git a/server/src/modules/survey/__test/dataStatistic.service.spec.ts b/server/src/modules/survey/__test/dataStatistic.service.spec.ts index bf7b69bd..b53bfa0d 100644 --- a/server/src/modules/survey/__test/dataStatistic.service.spec.ts +++ b/server/src/modules/survey/__test/dataStatistic.service.spec.ts @@ -11,7 +11,7 @@ import { cloneDeep } from 'lodash'; import { getRepositoryToken } from '@nestjs/typeorm'; import { RECORD_STATUS } from 'src/enums'; import { PluginManagerProvider } from 'src/securityPlugin/pluginManager.provider'; -import { XiaojuSurveyPluginManager } from 'src/securityPlugin/pluginManager'; +import { PluginManager } from 'src/securityPlugin/pluginManager'; import { ResponseSecurityPlugin } from 'src/securityPlugin/responseSecurityPlugin'; describe('DataStatisticService', () => { @@ -34,9 +34,7 @@ describe('DataStatisticService', () => { surveyResponseRepository = module.get>( getRepositoryToken(SurveyResponse), ); - const manager = module.get( - XiaojuSurveyPluginManager, - ); + const manager = module.get(PluginManager); manager.registerPlugin( new ResponseSecurityPlugin('dataAesEncryptSecretKey'), ); @@ -204,7 +202,7 @@ describe('DataStatisticService', () => { }); }); - it('should return desensitive table data', async () => { + it('should return desensitized table data', async () => { const mockSchema = cloneDeep(mockSensitiveResponseSchema); const surveyResponseList: Array = [ { diff --git a/server/src/modules/survey/__test/downloadTask.controller.spec.ts b/server/src/modules/survey/__test/downloadTask.controller.spec.ts new file mode 100644 index 00000000..48cd4f94 --- /dev/null +++ b/server/src/modules/survey/__test/downloadTask.controller.spec.ts @@ -0,0 +1,259 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ObjectId } from 'mongodb'; +import { DownloadTaskController } from '../controllers/downloadTask.controller'; +import { ResponseSchemaService } from '../../surveyResponse/services/responseScheme.service'; +import { AuthService } from 'src/modules/auth/services/auth.service'; +import { DownloadTaskService } from '../services/downloadTask.service'; +import { CollaboratorService } from '../services/collaborator.service'; +import { SurveyMetaService } from '../services/surveyMeta.service'; +import { WorkspaceMemberService } from 'src/modules/workspace/services/workspaceMember.service'; + +import { Logger } from 'src/logger'; +import { HttpException } from 'src/exceptions/httpException'; +import { NoPermissionException } from 'src/exceptions/noPermissionException'; +import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; + +import { Authentication } from 'src/guards/authentication.guard'; +import { SurveyGuard } from 'src/guards/survey.guard'; + +describe('DownloadTaskController', () => { + let controller: DownloadTaskController; + let responseSchemaService: ResponseSchemaService; + let downloadTaskService: DownloadTaskService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [DownloadTaskController], + providers: [ + { + provide: ResponseSchemaService, + useValue: { + getResponseSchemaByPageId: jest.fn(), + }, + }, + { + provide: DownloadTaskService, + useValue: { + createDownloadTask: jest.fn(), + processDownloadTask: jest.fn(), + getDownloadTaskList: jest.fn(), + getDownloadTaskById: jest.fn(), + deleteDownloadTask: jest.fn(), + }, + }, + { + provide: Logger, + useValue: { + error: jest.fn(), + }, + }, + { + provide: AuthService, + useClass: jest.fn().mockImplementation(() => ({ + varifytoken() { + return {}; + }, + })), + }, + { + provide: CollaboratorService, + useValue: {}, + }, + { + provide: SurveyMetaService, + useValue: {}, + }, + { + provide: WorkspaceMemberService, + useValue: {}, + }, + { + provide: Authentication, + useClass: jest.fn().mockImplementation(() => ({ + canActivate: () => true, + })), + }, + { + provide: SurveyGuard, + useClass: jest.fn().mockImplementation(() => ({ + canActivate: () => true, + })), + }, + ], + }).compile(); + + controller = module.get(DownloadTaskController); + responseSchemaService = module.get( + ResponseSchemaService, + ); + downloadTaskService = module.get(DownloadTaskService); + }); + + describe('createTask', () => { + it('should create a download task successfully', async () => { + const mockReqBody = { + surveyId: new ObjectId().toString(), + isMasked: false, + }; + const mockReq = { user: { _id: 'mockUserId', username: 'mockUsername' } }; + const mockTaskId = 'mockTaskId'; + jest + .spyOn(responseSchemaService, 'getResponseSchemaByPageId') + .mockResolvedValue({} as any); + jest + .spyOn(downloadTaskService, 'createDownloadTask') + .mockResolvedValue(mockTaskId); + + const result = await controller.createTask(mockReqBody, mockReq); + + expect( + responseSchemaService.getResponseSchemaByPageId, + ).toHaveBeenCalledWith(mockReqBody.surveyId); + expect(downloadTaskService.createDownloadTask).toHaveBeenCalledWith({ + surveyId: mockReqBody.surveyId, + responseSchema: {}, + creatorId: mockReq.user._id.toString(), + creator: mockReq.user.username, + params: { isMasked: mockReqBody.isMasked }, + }); + expect(downloadTaskService.processDownloadTask).toHaveBeenCalledWith({ + taskId: mockTaskId, + }); + expect(result).toEqual({ code: 200, data: { taskId: mockTaskId } }); + }); + + it('should throw HttpException if validation fails', async () => { + const mockReqBody: any = { isMasked: false }; + const mockReq = { user: { _id: 'mockUserId', username: 'mockUsername' } }; + + await expect(controller.createTask(mockReqBody, mockReq)).rejects.toThrow( + HttpException, + ); + }); + }); + + describe('downloadList', () => { + it('should return the download task list', async () => { + const mockQueryInfo = { pageIndex: 1, pageSize: 10 }; + const mockReq = { user: { _id: 'mockUserId' } }; + const mockTaskList: any = { + total: 1, + list: [ + { + _id: 'mockTaskId', + curStatus: 'completed', + filename: 'mockFile.csv', + url: 'http://mock-url.com', + fileSize: 1024, + createDate: Date.now(), + }, + ], + }; + jest + .spyOn(downloadTaskService, 'getDownloadTaskList') + .mockResolvedValue(mockTaskList); + + const result = await controller.downloadList(mockQueryInfo, mockReq); + + expect(downloadTaskService.getDownloadTaskList).toHaveBeenCalledWith({ + creatorId: mockReq.user._id.toString(), + pageIndex: mockQueryInfo.pageIndex, + pageSize: mockQueryInfo.pageSize, + }); + expect(result.data.total).toEqual(mockTaskList.total); + expect(result.data.list[0].taskId).toEqual( + mockTaskList.list[0]._id.toString(), + ); + }); + + it('should throw HttpException if validation fails', async () => { + const mockQueryInfo: any = { pageIndex: 'invalid', pageSize: 10 }; + const mockReq = { user: { _id: 'mockUserId' } }; + + await expect( + controller.downloadList(mockQueryInfo, mockReq), + ).rejects.toThrow(HttpException); + }); + }); + + describe('getDownloadTask', () => { + it('should return a download task', async () => { + const mockQuery = { taskId: 'mockTaskId' }; + const mockReq = { user: { _id: 'mockUserId' } }; + const mockTaskInfo: any = { + _id: 'mockTaskId', + creatorId: 'mockUserId', + curStatus: 'completed', + }; + jest + .spyOn(downloadTaskService, 'getDownloadTaskById') + .mockResolvedValue(mockTaskInfo); + + const result = await controller.getDownloadTask(mockQuery, mockReq); + + expect(downloadTaskService.getDownloadTaskById).toHaveBeenCalledWith({ + taskId: mockQuery.taskId, + }); + expect(result.data.taskId).toEqual(mockTaskInfo._id.toString()); + }); + + it('should throw NoPermissionException if user has no permission', async () => { + const mockQuery = { taskId: 'mockTaskId' }; + const mockReq = { user: { _id: new ObjectId() } }; + const mockTaskInfo: any = { + _id: 'mockTaskId', + creatorId: 'mockUserId', + curStatus: 'completed', + }; + + jest + .spyOn(downloadTaskService, 'getDownloadTaskById') + .mockResolvedValue(mockTaskInfo); + + await expect( + controller.getDownloadTask(mockQuery, mockReq), + ).rejects.toThrow(new NoPermissionException('没有权限')); + }); + }); + + describe('deleteFileByName', () => { + it('should delete a download task successfully', async () => { + const mockBody = { taskId: 'mockTaskId' }; + const mockReq = { user: { _id: 'mockUserId' } }; + const mockTaskInfo: any = { + _id: new ObjectId(), + creatorId: 'mockUserId', + }; + const mockDelRes = { modifiedCount: 1 }; + + jest + .spyOn(downloadTaskService, 'getDownloadTaskById') + .mockResolvedValue(mockTaskInfo); + jest + .spyOn(downloadTaskService, 'deleteDownloadTask') + .mockResolvedValue(mockDelRes); + + const result = await controller.deleteFileByName(mockBody, mockReq); + + expect(downloadTaskService.deleteDownloadTask).toHaveBeenCalledWith({ + taskId: mockBody.taskId, + }); + expect(result).toEqual({ code: 200, data: true }); + }); + + it('should throw HttpException if task does not exist', async () => { + const mockBody = { taskId: 'mockTaskId' }; + const mockReq = { user: { _id: 'mockUserId' } }; + + jest + .spyOn(downloadTaskService, 'getDownloadTaskById') + .mockResolvedValue(null); + + await expect( + controller.deleteFileByName(mockBody, mockReq), + ).rejects.toThrow( + new HttpException('任务不存在', EXCEPTION_CODE.PARAMETER_ERROR), + ); + }); + }); +}); diff --git a/server/src/modules/survey/__test/downloadTask.service.spec.ts b/server/src/modules/survey/__test/downloadTask.service.spec.ts new file mode 100644 index 00000000..b26b9d20 --- /dev/null +++ b/server/src/modules/survey/__test/downloadTask.service.spec.ts @@ -0,0 +1,192 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { DownloadTaskService } from '../services/downloadTask.service'; +import { MongoRepository } from 'typeorm'; +import { getRepositoryToken } from '@nestjs/typeorm'; +import { DownloadTask } from 'src/models/downloadTask.entity'; +import { SurveyResponse } from 'src/models/surveyResponse.entity'; +import { ResponseSchemaService } from 'src/modules/surveyResponse/services/responseScheme.service'; +import { DataStatisticService } from '../services/dataStatistic.service'; +import { FileService } from 'src/modules/file/services/file.service'; +import { Logger } from 'src/logger'; +import { ObjectId } from 'mongodb'; +import { RECORD_STATUS } from 'src/enums'; + +describe('DownloadTaskService', () => { + let service: DownloadTaskService; + let downloadTaskRepository: MongoRepository; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + DownloadTaskService, + { + provide: getRepositoryToken(DownloadTask), + useClass: MongoRepository, + }, + { + provide: getRepositoryToken(SurveyResponse), + useClass: MongoRepository, + }, + { + provide: ResponseSchemaService, + useValue: { + getResponseSchemaByPageId: jest.fn(), + }, + }, + { + provide: DataStatisticService, + useValue: { + getDataTable: jest.fn(), + }, + }, + { + provide: FileService, + useValue: { + upload: jest.fn(), + }, + }, + { + provide: Logger, + useValue: { + info: jest.fn(), + error: jest.fn(), + }, + }, + ], + }).compile(); + + service = module.get(DownloadTaskService); + downloadTaskRepository = module.get>( + getRepositoryToken(DownloadTask), + ); + }); + + describe('createDownloadTask', () => { + it('should create and save a download task', async () => { + const mockTaskId = new ObjectId().toString(); + const mockDownloadTask = { _id: new ObjectId(mockTaskId) }; + const mockParams: any = { + surveyId: 'survey1', + responseSchema: { title: 'test-title', surveyPath: '/path' }, + creatorId: 'creator1', + creator: 'creatorName', + params: { isMasked: true }, + }; + + jest + .spyOn(downloadTaskRepository, 'create') + .mockReturnValue(mockDownloadTask as any); + jest + .spyOn(downloadTaskRepository, 'save') + .mockResolvedValue(mockDownloadTask as any); + + const result = await service.createDownloadTask(mockParams); + + expect(downloadTaskRepository.create).toHaveBeenCalledWith({ + surveyId: mockParams.surveyId, + surveyPath: mockParams.responseSchema.surveyPath, + fileSize: '计算中', + creatorId: mockParams.creatorId, + creator: mockParams.creator, + params: { + ...mockParams.params, + title: mockParams.responseSchema.title, + }, + filename: expect.any(String), + }); + expect(downloadTaskRepository.save).toHaveBeenCalled(); + expect(result).toEqual(mockTaskId); + }); + }); + + describe('getDownloadTaskList', () => { + it('should return task list and total count', async () => { + const mockCreatorId = 'creator1'; + const mockTasks = [{ _id: '1' }, { _id: '2' }]; + const mockTotal = 2; + + jest + .spyOn(downloadTaskRepository, 'findAndCount') + .mockResolvedValue([mockTasks as any, mockTotal]); + + const result = await service.getDownloadTaskList({ + creatorId: mockCreatorId, + pageIndex: 1, + pageSize: 10, + }); + + expect(downloadTaskRepository.findAndCount).toHaveBeenCalledWith({ + where: { + creatorId: mockCreatorId, + 'curStatus.status': { $ne: RECORD_STATUS.REMOVED }, + }, + take: 10, + skip: 0, + order: { createDate: -1 }, + }); + + expect(result).toEqual({ + total: mockTotal, + list: mockTasks, + }); + }); + }); + + describe('getDownloadTaskById', () => { + it('should return task by id', async () => { + const mockTaskId = new ObjectId().toString(); + const mockTask = { _id: new ObjectId(mockTaskId) }; + + jest + .spyOn(downloadTaskRepository, 'find') + .mockResolvedValue([mockTask as any]); + + const result = await service.getDownloadTaskById({ taskId: mockTaskId }); + + expect(downloadTaskRepository.find).toHaveBeenCalledWith({ + where: { _id: new ObjectId(mockTaskId) }, + }); + expect(result).toEqual(mockTask); + }); + + it('should return null if task is not found', async () => { + const mockTaskId = new ObjectId().toString(); + + jest.spyOn(downloadTaskRepository, 'find').mockResolvedValue([]); + + const result = await service.getDownloadTaskById({ taskId: mockTaskId }); + + expect(result).toBeNull(); + }); + }); + + describe('deleteDownloadTask', () => { + it('should update task status to REMOVED', async () => { + const mockTaskId = new ObjectId().toString(); + const mockUpdateResult = { matchedCount: 1 }; + + jest + .spyOn(downloadTaskRepository, 'updateOne') + .mockResolvedValue(mockUpdateResult as any); + + const result = await service.deleteDownloadTask({ taskId: mockTaskId }); + + expect(downloadTaskRepository.updateOne).toHaveBeenCalledWith( + { + _id: new ObjectId(mockTaskId), + 'curStatus.status': { $ne: RECORD_STATUS.REMOVED }, + }, + { + $set: { + curStatus: { + status: RECORD_STATUS.REMOVED, + date: expect.any(Number), + }, + }, + $push: { statusList: expect.any(Object) }, + }, + ); + expect(result).toEqual(mockUpdateResult); + }); + }); +}); diff --git a/server/src/modules/survey/__test/session.controller.spec.ts b/server/src/modules/survey/__test/session.controller.spec.ts new file mode 100644 index 00000000..46e94775 --- /dev/null +++ b/server/src/modules/survey/__test/session.controller.spec.ts @@ -0,0 +1,87 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { SessionController } from '../controllers/session.controller'; +import { SessionService } from '../services/session.service'; +import { Logger } from 'src/logger'; +import { HttpException } from 'src/exceptions/httpException'; +import { Authentication } from 'src/guards/authentication.guard'; +import { SurveyGuard } from 'src/guards/survey.guard'; +import { SessionGuard } from 'src/guards/session.guard'; + +describe('SessionController', () => { + let controller: SessionController; + let sessionService: jest.Mocked; + let logger: jest.Mocked; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [SessionController], + providers: [ + { + provide: SessionService, + useValue: { + create: jest.fn(), + updateSessionToEditing: jest.fn(), + }, + }, + { + provide: Logger, + useValue: { + error: jest.fn(), + }, + }, + ], + }) + .overrideGuard(Authentication) + .useValue({ canActivate: () => true }) + .overrideGuard(SurveyGuard) + .useValue({ canActivate: () => true }) + .overrideGuard(SessionGuard) + .useValue({ canActivate: () => true }) + .compile(); + + controller = module.get(SessionController); + sessionService = module.get>(SessionService); + logger = module.get>(Logger); + }); + + it('should create a session', async () => { + const reqBody = { surveyId: '123' }; + const req = { user: { _id: 'userId' } }; + const session: any = { _id: 'sessionId' }; + + sessionService.create.mockResolvedValue(session); + + const result = await controller.create(reqBody, req); + + expect(sessionService.create).toHaveBeenCalledWith({ + surveyId: '123', + userId: 'userId', + }); + expect(result).toEqual({ code: 200, data: { sessionId: 'sessionId' } }); + }); + + it('should throw an exception if validation fails', async () => { + const reqBody = { surveyId: null }; + const req = { user: { _id: 'userId' } }; + + try { + await controller.create(reqBody, req); + } catch (error) { + expect(error).toBeInstanceOf(HttpException); + expect(logger.error).toHaveBeenCalled(); + } + }); + + it('should seize a session', async () => { + const req = { + sessionInfo: { _id: 'sessionId', surveyId: 'surveyId' }, + }; + + await controller.seize(req); + + expect(sessionService.updateSessionToEditing).toHaveBeenCalledWith({ + sessionId: 'sessionId', + surveyId: 'surveyId', + }); + }); +}); diff --git a/server/src/modules/survey/__test/survey.controller.spec.ts b/server/src/modules/survey/__test/survey.controller.spec.ts index a23f56d8..655212c8 100644 --- a/server/src/modules/survey/__test/survey.controller.spec.ts +++ b/server/src/modules/survey/__test/survey.controller.spec.ts @@ -5,18 +5,22 @@ import { SurveyConfService } from '../services/surveyConf.service'; import { ResponseSchemaService } from '../../surveyResponse/services/responseScheme.service'; import { ContentSecurityService } from '../services/contentSecurity.service'; import { SurveyHistoryService } from '../services/surveyHistory.service'; +import { CounterService } from '../../surveyResponse/services/counter.service'; +import { SessionService } from '../services/session.service'; +import { UserService } from '../../auth/services/user.service'; import { ObjectId } from 'mongodb'; import { SurveyMeta } from 'src/models/surveyMeta.entity'; import { SurveyConf } from 'src/models/surveyConf.entity'; -import { HttpException } from 'src/exceptions/httpException'; -import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; -import { LoggerProvider } from 'src/logger/logger.provider'; +import { Logger } from 'src/logger'; jest.mock('../services/surveyMeta.service'); jest.mock('../services/surveyConf.service'); jest.mock('../../surveyResponse/services/responseScheme.service'); jest.mock('../services/contentSecurity.service'); jest.mock('../services/surveyHistory.service'); +jest.mock('../services/session.service'); +jest.mock('../../surveyResponse/services/counter.service'); +jest.mock('../../auth/services/user.service'); jest.mock('src/guards/authentication.guard'); jest.mock('src/guards/survey.guard'); @@ -27,19 +31,36 @@ describe('SurveyController', () => { let surveyMetaService: SurveyMetaService; let surveyConfService: SurveyConfService; let responseSchemaService: ResponseSchemaService; - let contentSecurityService: ContentSecurityService; let surveyHistoryService: SurveyHistoryService; + let sessionService: SessionService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [SurveyController], providers: [ SurveyMetaService, - SurveyConfService, + { + provide: SurveyConfService, + useValue: { + getSurveyConfBySurveyId: jest.fn(), + getSurveyContentByCode: jest.fn(), + createSurveyConf: jest.fn(), + saveSurveyConf: jest.fn(), + }, + }, ResponseSchemaService, ContentSecurityService, SurveyHistoryService, - LoggerProvider, + SessionService, + CounterService, + UserService, + { + provide: Logger, + useValue: { + error: jest.fn(), + info: jest.fn(), + }, + }, ], }).compile(); @@ -49,17 +70,14 @@ describe('SurveyController', () => { responseSchemaService = module.get( ResponseSchemaService, ); - contentSecurityService = module.get( - ContentSecurityService, - ); surveyHistoryService = module.get(SurveyHistoryService); + sessionService = module.get(SessionService); }); describe('getBannerData', () => { it('should return banner data', async () => { const result = await controller.getBannerData(); - expect(result.code).toBe(200); expect(result.data).toBeDefined(); }); @@ -71,33 +89,17 @@ describe('SurveyController', () => { surveyType: 'normal', remark: '问卷调研', title: '问卷调研', - } as SurveyMeta; + }; + const newId = new ObjectId(); - jest - .spyOn(surveyMetaService, 'createSurveyMeta') - .mockImplementation(() => { - const result = { - _id: newId, - } as SurveyMeta; - return Promise.resolve(result); - }); - jest - .spyOn(surveyConfService, 'createSurveyConf') - .mockImplementation( - (params: { - surveyId: string; - surveyType: string; - createMethod: string; - createFrom: string; - }) => { - const result = { - _id: new ObjectId(), - pageId: params.surveyId, - code: {}, - } as SurveyConf; - return Promise.resolve(result); - }, - ); + jest.spyOn(surveyMetaService, 'createSurveyMeta').mockResolvedValue({ + _id: newId, + } as SurveyMeta); + + jest.spyOn(surveyConfService, 'createSurveyConf').mockResolvedValue({ + _id: new ObjectId(), + pageId: newId.toString(), + } as SurveyConf); const result = await controller.createSurvey(surveyInfo, { user: { username: 'testUser', _id: new ObjectId() }, @@ -126,19 +128,15 @@ describe('SurveyController', () => { createFrom: existsSurveyId.toString(), }; - jest - .spyOn(surveyMetaService, 'createSurveyMeta') - .mockImplementation(() => { - const result = { - _id: new ObjectId(), - } as SurveyMeta; - return Promise.resolve(result); - }); + jest.spyOn(surveyMetaService, 'createSurveyMeta').mockResolvedValue({ + _id: new ObjectId(), + } as SurveyMeta); const request = { user: { username: 'testUser', _id: new ObjectId() }, surveyMeta: existsSurveyMeta, - }; // 模拟请求对象,根据实际情况进行调整 + }; + const result = await controller.createSurvey(params, request); expect(result?.data?.id).toBeDefined(); }); @@ -159,6 +157,12 @@ describe('SurveyController', () => { jest .spyOn(surveyHistoryService, 'addHistory') .mockResolvedValue(undefined); + jest + .spyOn(sessionService, 'findLatestEditingOne') + .mockResolvedValue(null); + jest + .spyOn(sessionService, 'updateSessionToEditing') + .mockResolvedValue(undefined); const reqBody = { surveyId: surveyId.toString(), @@ -178,6 +182,7 @@ describe('SurveyController', () => { dataList: [], }, }, + sessionId: 'mock-session-id', }; const result = await controller.updateConf(reqBody, { @@ -229,12 +234,10 @@ describe('SurveyController', () => { jest .spyOn(surveyConfService, 'getSurveyConfBySurveyId') - .mockResolvedValue( - Promise.resolve({ - _id: new ObjectId(), - pageId: surveyId.toString(), - } as SurveyConf), - ); + .mockResolvedValue({ + _id: new ObjectId(), + pageId: surveyId.toString(), + } as SurveyConf); const request = { user: { username: 'testUser', _id: new ObjectId() }, @@ -250,7 +253,7 @@ describe('SurveyController', () => { }); describe('publishSurvey', () => { - it('should publish a survey success', async () => { + it('should publish a survey successfully', async () => { const surveyId = new ObjectId(); const surveyMeta = { _id: surveyId, @@ -260,80 +263,24 @@ describe('SurveyController', () => { jest .spyOn(surveyConfService, 'getSurveyConfBySurveyId') - .mockResolvedValue( - Promise.resolve({ - _id: new ObjectId(), - pageId: surveyId.toString(), - } as SurveyConf), - ); + .mockResolvedValue({ + _id: new ObjectId(), + pageId: surveyId.toString(), + code: {}, + } as SurveyConf); jest .spyOn(surveyConfService, 'getSurveyContentByCode') - .mockResolvedValue({ - text: '题目1', - }); - - jest - .spyOn(contentSecurityService, 'isForbiddenContent') - .mockResolvedValue(false); - jest - .spyOn(surveyMetaService, 'publishSurveyMeta') - .mockResolvedValue(undefined); - jest - .spyOn(responseSchemaService, 'publishResponseSchema') - .mockResolvedValue(undefined); - jest - .spyOn(surveyHistoryService, 'addHistory') - .mockResolvedValue(undefined); + .mockResolvedValue({ text: '' }); const result = await controller.publishSurvey( { surveyId: surveyId.toString() }, - { user: { username: 'testUser', _id: 'testUserId' }, surveyMeta }, - ); - - expect(result).toEqual({ - code: 200, - }); - }); - - it('should not publish a survey with forbidden content', async () => { - const surveyId = new ObjectId(); - const surveyMeta = { - _id: surveyId, - surveyType: 'normal', - owner: 'testUser', - } as SurveyMeta; - - jest - .spyOn(surveyConfService, 'getSurveyConfBySurveyId') - .mockResolvedValue( - Promise.resolve({ - _id: new ObjectId(), - pageId: surveyId.toString(), - } as SurveyConf), - ); - - jest - .spyOn(surveyConfService, 'getSurveyContentByCode') - .mockResolvedValue({ - text: '违禁词', - }); - - jest - .spyOn(contentSecurityService, 'isForbiddenContent') - .mockResolvedValue(true); - - await expect( - controller.publishSurvey( - { surveyId: surveyId.toString() }, - { user: { username: 'testUser', _id: 'testUserId' }, surveyMeta }, - ), - ).rejects.toThrow( - new HttpException( - '问卷存在非法关键字,不允许发布', - EXCEPTION_CODE.SURVEY_CONTENT_NOT_ALLOW, - ), + { + user: { username: 'testUser', _id: new ObjectId() }, + surveyMeta, + }, ); + expect(result.code).toBe(200); }); }); }); diff --git a/server/src/modules/survey/__test/surveyHistory.controller.spec.ts b/server/src/modules/survey/__test/surveyHistory.controller.spec.ts index 54a975fd..e0a32417 100644 --- a/server/src/modules/survey/__test/surveyHistory.controller.spec.ts +++ b/server/src/modules/survey/__test/surveyHistory.controller.spec.ts @@ -7,7 +7,7 @@ import { SurveyMetaService } from '../services/surveyMeta.service'; import { UserService } from 'src/modules/auth/services/user.service'; import { AuthService } from 'src/modules/auth/services/auth.service'; -import { XiaojuSurveyLogger } from 'src/logger'; +import { Logger } from 'src/logger'; jest.mock('src/guards/authentication.guard'); jest.mock('src/guards/survey.guard'); @@ -49,7 +49,7 @@ describe('SurveyHistoryController', () => { useClass: jest.fn().mockImplementation(() => ({})), }, { - provide: XiaojuSurveyLogger, + provide: Logger, useValue: { info: jest.fn(), error: jest.fn(), diff --git a/server/src/modules/survey/__test/surveyMeta.controller.spec.ts b/server/src/modules/survey/__test/surveyMeta.controller.spec.ts index b98236d0..5cd801eb 100644 --- a/server/src/modules/survey/__test/surveyMeta.controller.spec.ts +++ b/server/src/modules/survey/__test/surveyMeta.controller.spec.ts @@ -1,7 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { SurveyMetaController } from '../controllers/surveyMeta.controller'; import { SurveyMetaService } from '../services/surveyMeta.service'; -import { LoggerProvider } from 'src/logger/logger.provider'; +import { Logger } from 'src/logger'; import { HttpException } from 'src/exceptions/httpException'; import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; import { CollaboratorService } from '../services/collaborator.service'; @@ -28,7 +28,12 @@ describe('SurveyMetaController', () => { .mockResolvedValue({ count: 0, data: [] }), }, }, - LoggerProvider, + { + provide: Logger, + useValue: { + error() {}, + }, + }, { provide: CollaboratorService, useValue: { @@ -116,6 +121,7 @@ describe('SurveyMetaController', () => { curStatus: { date: date, }, + surveyType: 'normal', }, ], }); @@ -140,10 +146,12 @@ describe('SurveyMetaController', () => { /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/, ), }), + surveyType: 'normal', }), ]), }, }); + expect(surveyMetaService.getSurveyMetaList).toHaveBeenCalledWith({ pageNum: queryInfo.curPage, pageSize: queryInfo.pageSize, @@ -194,4 +202,24 @@ describe('SurveyMetaController', () => { workspaceId: undefined, }); }); + + it('should handle Joi validation in getList', async () => { + const invalidQueryInfo: any = { + curPage: 'invalid', + pageSize: 10, + }; + const req = { + user: { + username: 'test-user', + _id: new ObjectId(), + }, + }; + + try { + await controller.getList(invalidQueryInfo, req); + } catch (error) { + expect(error).toBeInstanceOf(HttpException); + expect(error.code).toBe(EXCEPTION_CODE.PARAMETER_ERROR); + } + }); }); diff --git a/server/src/modules/survey/__test/surveyMeta.service.spec.ts b/server/src/modules/survey/__test/surveyMeta.service.spec.ts index f05502e7..b48e6818 100644 --- a/server/src/modules/survey/__test/surveyMeta.service.spec.ts +++ b/server/src/modules/survey/__test/surveyMeta.service.spec.ts @@ -3,7 +3,7 @@ import { SurveyMetaService } from '../services/surveyMeta.service'; import { MongoRepository } from 'typeorm'; import { SurveyMeta } from 'src/models/surveyMeta.entity'; import { PluginManagerProvider } from 'src/securityPlugin/pluginManager.provider'; -import { XiaojuSurveyPluginManager } from 'src/securityPlugin/pluginManager'; +import { PluginManager } from 'src/securityPlugin/pluginManager'; import { RECORD_STATUS } from 'src/enums'; import { getRepositoryToken } from '@nestjs/typeorm'; import { HttpException } from 'src/exceptions/httpException'; @@ -13,7 +13,7 @@ import { ObjectId } from 'mongodb'; describe('SurveyMetaService', () => { let service: SurveyMetaService; let surveyRepository: MongoRepository; - let pluginManager: XiaojuSurveyPluginManager; + let pluginManager: PluginManager; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -37,8 +37,8 @@ describe('SurveyMetaService', () => { surveyRepository = module.get>( getRepositoryToken(SurveyMeta), ); - pluginManager = module.get( - XiaojuSurveyPluginManager, + pluginManager = module.get( + PluginManager, ); pluginManager.registerPlugin(new SurveyUtilPlugin()); }); diff --git a/server/src/modules/survey/controllers/collaborator.controller.ts b/server/src/modules/survey/controllers/collaborator.controller.ts index 0545aeb1..da1d5b5e 100644 --- a/server/src/modules/survey/controllers/collaborator.controller.ts +++ b/server/src/modules/survey/controllers/collaborator.controller.ts @@ -20,7 +20,7 @@ import { SURVEY_PERMISSION, SURVEY_PERMISSION_DESCRIPTION, } from 'src/enums/surveyPermission'; -import { XiaojuSurveyLogger } from 'src/logger'; +import { Logger } from 'src/logger'; import { WorkspaceMemberService } from 'src/modules/workspace/services/workspaceMember.service'; import { CollaboratorService } from '../services/collaborator.service'; @@ -40,7 +40,7 @@ import { SurveyMetaService } from '../services/surveyMeta.service'; export class CollaboratorController { constructor( private readonly collaboratorService: CollaboratorService, - private readonly logger: XiaojuSurveyLogger, + private readonly logger: Logger, private readonly userService: UserService, private readonly surveyMetaService: SurveyMetaService, private readonly workspaceMemberServie: WorkspaceMemberService, diff --git a/server/src/modules/survey/controllers/dataStatistic.controller.ts b/server/src/modules/survey/controllers/dataStatistic.controller.ts index 9a396b6c..185e6354 100644 --- a/server/src/modules/survey/controllers/dataStatistic.controller.ts +++ b/server/src/modules/survey/controllers/dataStatistic.controller.ts @@ -13,10 +13,10 @@ import { DataStatisticService } from '../services/dataStatistic.service'; import { ResponseSchemaService } from '../../surveyResponse/services/responseScheme.service'; import { Authentication } from 'src/guards/authentication.guard'; -import { XiaojuSurveyPluginManager } from 'src/securityPlugin/pluginManager'; +import { PluginManager } from 'src/securityPlugin/pluginManager'; import { SurveyGuard } from 'src/guards/survey.guard'; import { SURVEY_PERMISSION } from 'src/enums/surveyPermission'; -import { XiaojuSurveyLogger } from 'src/logger'; +import { Logger } from 'src/logger'; import { HttpException } from 'src/exceptions/httpException'; import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; import { AggregationStatisDto } from '../dto/aggregationStatis.dto'; @@ -30,8 +30,8 @@ export class DataStatisticController { constructor( private readonly responseSchemaService: ResponseSchemaService, private readonly dataStatisticService: DataStatisticService, - private readonly pluginManager: XiaojuSurveyPluginManager, - private readonly logger: XiaojuSurveyLogger, + private readonly pluginManager: PluginManager, + private readonly logger: Logger, ) {} @Get('/dataTable') @@ -46,7 +46,7 @@ export class DataStatisticController { ) { const { value, error } = await Joi.object({ surveyId: Joi.string().required(), - isDesensitive: Joi.boolean().default(true), // 默认true就是需要脱敏 + isMasked: Joi.boolean().default(true), // 默认true就是需要脱敏 page: Joi.number().default(1), pageSize: Joi.number().default(10), }).validate(queryInfo); @@ -54,7 +54,7 @@ export class DataStatisticController { this.logger.error(error.message); throw new HttpException('参数有误', EXCEPTION_CODE.PARAMETER_ERROR); } - const { surveyId, isDesensitive, page, pageSize } = value; + const { surveyId, isMasked, page, pageSize } = value; const responseSchema = await this.responseSchemaService.getResponseSchemaByPageId(surveyId); const { total, listHead, listBody } = @@ -65,10 +65,10 @@ export class DataStatisticController { pageSize, }); - if (isDesensitive) { + if (isMasked) { // 脱敏 listBody.forEach((item) => { - this.pluginManager.triggerHook('desensitiveData', item); + this.pluginManager.triggerHook('maskData', item); }); } diff --git a/server/src/modules/survey/controllers/downloadTask.controller.ts b/server/src/modules/survey/controllers/downloadTask.controller.ts index 23ac42cb..8247acf7 100644 --- a/server/src/modules/survey/controllers/downloadTask.controller.ts +++ b/server/src/modules/survey/controllers/downloadTask.controller.ts @@ -8,7 +8,6 @@ import { Request, Post, Body, - // Response, } from '@nestjs/common'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; @@ -17,10 +16,9 @@ import { ResponseSchemaService } from '../../surveyResponse/services/responseSch import { Authentication } from 'src/guards/authentication.guard'; import { SurveyGuard } from 'src/guards/survey.guard'; import { SURVEY_PERMISSION } from 'src/enums/surveyPermission'; -import { XiaojuSurveyLogger } from 'src/logger'; +import { Logger } from 'src/logger'; import { HttpException } from 'src/exceptions/httpException'; import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; -//后添加 import { DownloadTaskService } from '../services/downloadTask.service'; import { GetDownloadTaskDto, @@ -38,7 +36,7 @@ export class DownloadTaskController { constructor( private readonly responseSchemaService: ResponseSchemaService, private readonly downloadTaskService: DownloadTaskService, - private readonly logger: XiaojuSurveyLogger, + private readonly logger: Logger, ) {} @Post('/createTask') @@ -57,14 +55,15 @@ export class DownloadTaskController { this.logger.error(error.message); throw new HttpException('参数有误', EXCEPTION_CODE.PARAMETER_ERROR); } - const { surveyId, isDesensitive } = value; + const { surveyId, isMasked } = value; const responseSchema = await this.responseSchemaService.getResponseSchemaByPageId(surveyId); const id = await this.downloadTaskService.createDownloadTask({ surveyId, responseSchema, - operatorId: req.user._id.toString(), - params: { isDesensitive }, + creatorId: req.user._id.toString(), + creator: req.user.username, + params: { isMasked }, }); this.downloadTaskService.processDownloadTask({ taskId: id }); return { @@ -88,7 +87,7 @@ export class DownloadTaskController { } const { pageIndex, pageSize } = value; const { total, list } = await this.downloadTaskService.getDownloadTaskList({ - ownerId: req.user._id.toString(), + creatorId: req.user._id.toString(), pageIndex, pageSize, }); @@ -139,7 +138,7 @@ export class DownloadTaskController { throw new HttpException('任务不存在', EXCEPTION_CODE.PARAMETER_ERROR); } - if (taskInfo.ownerId !== req.user._id.toString()) { + if (taskInfo.creatorId !== req.user._id.toString()) { throw new NoPermissionException('没有权限'); } const res: Record = { @@ -172,7 +171,7 @@ export class DownloadTaskController { throw new HttpException('任务不存在', EXCEPTION_CODE.PARAMETER_ERROR); } - if (taskInfo.ownerId !== req.user._id.toString()) { + if (taskInfo.creatorId !== req.user._id.toString()) { throw new NoPermissionException('没有权限'); } diff --git a/server/src/modules/survey/controllers/session.controller.ts b/server/src/modules/survey/controllers/session.controller.ts index 88fce79b..f17dbaef 100644 --- a/server/src/modules/survey/controllers/session.controller.ts +++ b/server/src/modules/survey/controllers/session.controller.ts @@ -15,7 +15,7 @@ import { SessionService } from '../services/session.service'; import { Authentication } from 'src/guards/authentication.guard'; import { SurveyGuard } from 'src/guards/survey.guard'; import { SURVEY_PERMISSION } from 'src/enums/surveyPermission'; -import { XiaojuSurveyLogger } from 'src/logger'; +import { Logger } from 'src/logger'; import { HttpException } from 'src/exceptions/httpException'; import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; import { SessionGuard } from 'src/guards/session.guard'; @@ -25,7 +25,7 @@ import { SessionGuard } from 'src/guards/session.guard'; export class SessionController { constructor( private readonly sessionService: SessionService, - private readonly logger: XiaojuSurveyLogger, + private readonly logger: Logger, ) {} @Post('/create') @@ -69,7 +69,6 @@ export class SessionController { @HttpCode(200) @UseGuards(SurveyGuard) @UseGuards(SessionGuard) - @SetMetadata('sessionId', 'body.sessionId') @SetMetadata('surveyPermission', [SURVEY_PERMISSION.SURVEY_CONF_MANAGE]) @UseGuards(Authentication) diff --git a/server/src/modules/survey/controllers/survey.controller.ts b/server/src/modules/survey/controllers/survey.controller.ts index 19f80af8..2a7bc4ee 100644 --- a/server/src/modules/survey/controllers/survey.controller.ts +++ b/server/src/modules/survey/controllers/survey.controller.ts @@ -17,7 +17,6 @@ import { SurveyConfService } from '../services/surveyConf.service'; import { ResponseSchemaService } from '../../surveyResponse/services/responseScheme.service'; import { ContentSecurityService } from '../services/contentSecurity.service'; import { SurveyHistoryService } from '../services/surveyHistory.service'; -import { CounterService } from 'src/modules/surveyResponse/services/counter.service'; import BannerData from '../template/banner/index.json'; import { CreateSurveyDto } from '../dto/createSurvey.dto'; @@ -26,7 +25,7 @@ import { Authentication } from 'src/guards/authentication.guard'; import { HISTORY_TYPE } from 'src/enums'; import { HttpException } from 'src/exceptions/httpException'; import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; -import { XiaojuSurveyLogger } from 'src/logger'; +import { Logger } from 'src/logger'; import { SurveyGuard } from 'src/guards/survey.guard'; import { SURVEY_PERMISSION } from 'src/enums/surveyPermission'; @@ -44,8 +43,7 @@ export class SurveyController { private readonly responseSchemaService: ResponseSchemaService, private readonly contentSecurityService: ContentSecurityService, private readonly surveyHistoryService: SurveyHistoryService, - private readonly logger: XiaojuSurveyLogger, - private readonly counterService: CounterService, + private readonly logger: Logger, private readonly sessionService: SessionService, private readonly userService: UserService, ) {} diff --git a/server/src/modules/survey/controllers/surveyHistory.controller.ts b/server/src/modules/survey/controllers/surveyHistory.controller.ts index 53c999c1..286b47f0 100644 --- a/server/src/modules/survey/controllers/surveyHistory.controller.ts +++ b/server/src/modules/survey/controllers/surveyHistory.controller.ts @@ -14,7 +14,7 @@ import { SurveyHistoryService } from '../services/surveyHistory.service'; import { Authentication } from 'src/guards/authentication.guard'; import { SurveyGuard } from 'src/guards/survey.guard'; import { SURVEY_PERMISSION } from 'src/enums/surveyPermission'; -import { XiaojuSurveyLogger } from 'src/logger'; +import { Logger } from 'src/logger'; import { HttpException } from 'src/exceptions/httpException'; import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; @ApiTags('survey') @@ -22,7 +22,7 @@ import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; export class SurveyHistoryController { constructor( private readonly surveyHistoryService: SurveyHistoryService, - private readonly logger: XiaojuSurveyLogger, + private readonly logger: Logger, ) {} @Get('/getList') diff --git a/server/src/modules/survey/controllers/surveyMeta.controller.ts b/server/src/modules/survey/controllers/surveyMeta.controller.ts index f9839a6b..ec50148e 100644 --- a/server/src/modules/survey/controllers/surveyMeta.controller.ts +++ b/server/src/modules/survey/controllers/surveyMeta.controller.ts @@ -19,7 +19,7 @@ import { getFilter, getOrder } from 'src/utils/surveyUtil'; import { HttpException } from 'src/exceptions/httpException'; import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; import { Authentication } from 'src/guards/authentication.guard'; -import { XiaojuSurveyLogger } from 'src/logger'; +import { Logger } from 'src/logger'; import { SurveyGuard } from 'src/guards/survey.guard'; import { SURVEY_PERMISSION } from 'src/enums/surveyPermission'; import { WorkspaceGuard } from 'src/guards/workspace.guard'; @@ -33,7 +33,7 @@ import { CollaboratorService } from '../services/collaborator.service'; export class SurveyMetaController { constructor( private readonly surveyMetaService: SurveyMetaService, - private readonly logger: XiaojuSurveyLogger, + private readonly logger: Logger, private readonly collaboratorService: CollaboratorService, ) {} diff --git a/server/src/modules/survey/dto/createSurvey.dto.ts b/server/src/modules/survey/dto/createSurvey.dto.ts index 537996e6..f34ea9fb 100644 --- a/server/src/modules/survey/dto/createSurvey.dto.ts +++ b/server/src/modules/survey/dto/createSurvey.dto.ts @@ -12,10 +12,10 @@ export class CreateSurveyDto { surveyType: string; @ApiProperty({ description: '创建方法', required: false }) - createMethod: string; + createMethod?: string; @ApiProperty({ description: '创建来源', required: false }) - createFrom: string; + createFrom?: string; @ApiProperty({ description: '问卷创建在哪个空间下', required: false }) workspaceId?: string; diff --git a/server/src/modules/survey/dto/downloadTask.dto.ts b/server/src/modules/survey/dto/downloadTask.dto.ts index c5f11e3a..6a950eb1 100644 --- a/server/src/modules/survey/dto/downloadTask.dto.ts +++ b/server/src/modules/survey/dto/downloadTask.dto.ts @@ -5,12 +5,12 @@ export class CreateDownloadDto { @ApiProperty({ description: '问卷id', required: true }) surveyId: string; @ApiProperty({ description: '是否脱敏', required: false }) - isDesensitive: boolean; + isMasked: boolean; static validate(data) { return Joi.object({ surveyId: Joi.string().required(), - isDesensitive: Joi.boolean().allow(null).default(false), + isMasked: Joi.boolean().allow(null).default(false), }).validate(data); } } diff --git a/server/src/modules/survey/services/collaborator.service.ts b/server/src/modules/survey/services/collaborator.service.ts index 59d70dd8..cfd6c628 100644 --- a/server/src/modules/survey/services/collaborator.service.ts +++ b/server/src/modules/survey/services/collaborator.service.ts @@ -3,14 +3,14 @@ import { Collaborator } from 'src/models/collaborator.entity'; import { InjectRepository } from '@nestjs/typeorm'; import { MongoRepository } from 'typeorm'; import { ObjectId } from 'mongodb'; -import { XiaojuSurveyLogger } from 'src/logger'; +import { Logger } from 'src/logger'; @Injectable() export class CollaboratorService { constructor( @InjectRepository(Collaborator) private readonly collaboratorRepository: MongoRepository, - private readonly logger: XiaojuSurveyLogger, + private readonly logger: Logger, ) {} async create({ surveyId, userId, permissions }) { diff --git a/server/src/modules/survey/services/downloadTask.service.ts b/server/src/modules/survey/services/downloadTask.service.ts index fe81043b..9dce616a 100644 --- a/server/src/modules/survey/services/downloadTask.service.ts +++ b/server/src/modules/survey/services/downloadTask.service.ts @@ -12,13 +12,13 @@ import xlsx from 'node-xlsx'; import { load } from 'cheerio'; import { get } from 'lodash'; import { FileService } from 'src/modules/file/services/file.service'; -import { XiaojuSurveyLogger } from 'src/logger'; +import { Logger } from 'src/logger'; import moment from 'moment'; @Injectable() export class DownloadTaskService { - private static taskList: Array = []; - private static isExecuting: boolean = false; + static taskList: Array = []; + static isExecuting: boolean = false; constructor( @InjectRepository(DownloadTask) @@ -28,26 +28,29 @@ export class DownloadTaskService { private readonly surveyResponseRepository: MongoRepository, private readonly dataStatisticService: DataStatisticService, private readonly fileService: FileService, - private readonly logger: XiaojuSurveyLogger, + private readonly logger: Logger, ) {} async createDownloadTask({ surveyId, responseSchema, - operatorId, + creatorId, + creator, params, }: { surveyId: string; responseSchema: ResponseSchema; - operatorId: string; + creatorId: string; + creator: string; params: any; }) { - const filename = `${responseSchema.title}-${params.isDesensitive ? '脱敏' : '原'}回收数据-${moment().format('YYYYMMDDHHmmss')}.xlsx`; + const filename = `${responseSchema.title}-${params.isMasked ? '脱敏' : '原'}回收数据-${moment().format('YYYYMMDDHHmmss')}.xlsx`; const downloadTask = this.downloadTaskRepository.create({ surveyId, surveyPath: responseSchema.surveyPath, fileSize: '计算中', - ownerId: operatorId, + creatorId, + creator, params: { ...params, title: responseSchema.title, @@ -59,16 +62,16 @@ export class DownloadTaskService { } async getDownloadTaskList({ - ownerId, + creatorId, pageIndex, pageSize, }: { - ownerId: string; + creatorId: string; pageIndex: number; pageSize: number; }) { const where = { - ownerId, + creatorId, 'curStatus.status': { $ne: RECORD_STATUS.REMOVED, }, @@ -131,9 +134,11 @@ export class DownloadTaskService { } } - private async executeTask() { + async executeTask() { try { - for (const taskId of DownloadTaskService.taskList) { + while (DownloadTaskService.taskList.length > 0) { + const taskId = DownloadTaskService.taskList.shift(); + this.logger.info(`handle taskId: ${taskId}`); const taskInfo = await this.getDownloadTaskById({ taskId }); if (!taskInfo || taskInfo.curStatus.status === RECORD_STATUS.REMOVED) { // 不存在或者已删除的,不处理 @@ -228,7 +233,7 @@ export class DownloadTaskService { configKey: 'SERVER_LOCAL_CONFIG', file, pathPrefix: 'exportfile', - keepOriginFilename: true, + filename: taskInfo.filename, }); const curStatus = { diff --git a/server/src/modules/survey/services/surveyMeta.service.ts b/server/src/modules/survey/services/surveyMeta.service.ts index e44d3b52..b61106d7 100644 --- a/server/src/modules/survey/services/surveyMeta.service.ts +++ b/server/src/modules/survey/services/surveyMeta.service.ts @@ -6,14 +6,14 @@ import { RECORD_STATUS } from 'src/enums'; import { ObjectId } from 'mongodb'; import { HttpException } from 'src/exceptions/httpException'; import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; -import { XiaojuSurveyPluginManager } from 'src/securityPlugin/pluginManager'; +import { PluginManager } from 'src/securityPlugin/pluginManager'; @Injectable() export class SurveyMetaService { constructor( @InjectRepository(SurveyMeta) private readonly surveyRepository: MongoRepository, - private readonly pluginManager: XiaojuSurveyPluginManager, + private readonly pluginManager: PluginManager, ) {} async getNewSurveyPath(): Promise { diff --git a/server/src/modules/surveyResponse/__test/surveyResponse.controller.spec.ts b/server/src/modules/surveyResponse/__test/surveyResponse.controller.spec.ts index 6533d6c7..eca1cfea 100644 --- a/server/src/modules/surveyResponse/__test/surveyResponse.controller.spec.ts +++ b/server/src/modules/surveyResponse/__test/surveyResponse.controller.spec.ts @@ -13,14 +13,14 @@ import { ClientEncryptService } from '../services/clientEncrypt.service'; import { MessagePushingTaskService } from 'src/modules/message/services/messagePushingTask.service'; import { PluginManagerProvider } from 'src/securityPlugin/pluginManager.provider'; -import { XiaojuSurveyPluginManager } from 'src/securityPlugin/pluginManager'; +import { PluginManager } from 'src/securityPlugin/pluginManager'; import { HttpException } from 'src/exceptions/httpException'; import { SurveyNotFoundException } from 'src/exceptions/surveyNotFoundException'; import { ResponseSecurityPlugin } from 'src/securityPlugin/responseSecurityPlugin'; import { RECORD_STATUS } from 'src/enums'; import { SurveyResponse } from 'src/models/surveyResponse.entity'; -import { XiaojuSurveyLogger } from 'src/logger'; +import { Logger } from 'src/logger'; import { ResponseSchema } from 'src/models/responseSchema.entity'; import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; import { UserService } from 'src/modules/auth/services/user.service'; @@ -122,7 +122,7 @@ describe('SurveyResponseController', () => { }, }, { - provide: XiaojuSurveyLogger, + provide: Logger, useValue: { error: jest.fn(), info: jest.fn(), @@ -153,8 +153,8 @@ describe('SurveyResponseController', () => { clientEncryptService = module.get(ClientEncryptService); - const pluginManager = module.get( - XiaojuSurveyPluginManager, + const pluginManager = module.get( + PluginManager, ); pluginManager.registerPlugin( new ResponseSecurityPlugin('dataAesEncryptSecretKey'), diff --git a/server/src/modules/surveyResponse/controllers/responseSchema.controller.ts b/server/src/modules/surveyResponse/controllers/responseSchema.controller.ts index 5d8bd2e3..b6070c22 100644 --- a/server/src/modules/surveyResponse/controllers/responseSchema.controller.ts +++ b/server/src/modules/surveyResponse/controllers/responseSchema.controller.ts @@ -13,7 +13,7 @@ import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; import { RECORD_STATUS } from 'src/enums'; import { ApiTags } from '@nestjs/swagger'; import Joi from 'joi'; -import { XiaojuSurveyLogger } from 'src/logger'; +import { Logger } from 'src/logger'; import { SurveyNotFoundException } from 'src/exceptions/surveyNotFoundException'; import { WhitelistType } from 'src/interfaces/survey'; import { UserService } from 'src/modules/auth/services/user.service'; @@ -24,7 +24,7 @@ import { WorkspaceMemberService } from 'src/modules/workspace/services/workspace export class ResponseSchemaController { constructor( private readonly responseSchemaService: ResponseSchemaService, - private readonly logger: XiaojuSurveyLogger, + private readonly logger: Logger, private readonly userService: UserService, private readonly workspaceMemberService: WorkspaceMemberService, ) {} diff --git a/server/src/modules/surveyResponse/controllers/surveyResponse.controller.ts b/server/src/modules/surveyResponse/controllers/surveyResponse.controller.ts index 7f1a3ba3..f40aa9c0 100644 --- a/server/src/modules/surveyResponse/controllers/surveyResponse.controller.ts +++ b/server/src/modules/surveyResponse/controllers/surveyResponse.controller.ts @@ -18,7 +18,7 @@ import * as forge from 'node-forge'; import { ApiTags } from '@nestjs/swagger'; import { CounterService } from '../services/counter.service'; -import { XiaojuSurveyLogger } from 'src/logger'; +import { Logger } from 'src/logger'; import { WhitelistType } from 'src/interfaces/survey'; import { UserService } from 'src/modules/auth/services/user.service'; import { WorkspaceMemberService } from 'src/modules/workspace/services/workspaceMember.service'; @@ -40,7 +40,7 @@ export class SurveyResponseController { private readonly clientEncryptService: ClientEncryptService, private readonly messagePushingTaskService: MessagePushingTaskService, private readonly counterService: CounterService, - private readonly logger: XiaojuSurveyLogger, + private readonly logger: Logger, // private readonly redisService: RedisService, private readonly userService: UserService, private readonly workspaceMemberService: WorkspaceMemberService, diff --git a/server/src/modules/workspace/_test/workspace.controller.spec.ts b/server/src/modules/workspace/_test/workspace.controller.spec.ts index aed91a3b..9431b6ce 100644 --- a/server/src/modules/workspace/_test/workspace.controller.spec.ts +++ b/server/src/modules/workspace/_test/workspace.controller.spec.ts @@ -10,7 +10,7 @@ import { Workspace } from 'src/models/workspace.entity'; import { WorkspaceMember } from 'src/models/workspaceMember.entity'; import { UserService } from 'src/modules/auth/services/user.service'; import { SurveyMetaService } from 'src/modules/survey/services/surveyMeta.service'; -import { XiaojuSurveyLogger } from 'src/logger'; +import { Logger } from 'src/logger'; import { User } from 'src/models/user.entity'; jest.mock('src/guards/authentication.guard'); @@ -65,7 +65,7 @@ describe('WorkspaceController', () => { }, }, { - provide: XiaojuSurveyLogger, + provide: Logger, useValue: { info: jest.fn(), error: jest.fn(), diff --git a/server/src/modules/workspace/controllers/workspace.controller.ts b/server/src/modules/workspace/controllers/workspace.controller.ts index d74c9878..7279c13f 100644 --- a/server/src/modules/workspace/controllers/workspace.controller.ts +++ b/server/src/modules/workspace/controllers/workspace.controller.ts @@ -31,7 +31,7 @@ import { import { splitMembers } from '../utils/splitMember'; import { UserService } from 'src/modules/auth/services/user.service'; import { SurveyMetaService } from 'src/modules/survey/services/surveyMeta.service'; -import { XiaojuSurveyLogger } from 'src/logger'; +import { Logger } from 'src/logger'; import { GetWorkspaceListDto } from '../dto/getWorkspaceList.dto'; import { WorkspaceMember } from 'src/models/workspaceMember.entity'; import { Workspace } from 'src/models/workspace.entity'; @@ -46,7 +46,7 @@ export class WorkspaceController { private readonly workspaceMemberService: WorkspaceMemberService, private readonly userService: UserService, private readonly surveyMetaService: SurveyMetaService, - private readonly logger: XiaojuSurveyLogger, + private readonly logger: Logger, ) {} @Get('getRoleList') diff --git a/server/src/securityPlugin/interface.ts b/server/src/securityPlugin/interface.ts index b402f704..4a693b3e 100644 --- a/server/src/securityPlugin/interface.ts +++ b/server/src/securityPlugin/interface.ts @@ -1,6 +1,6 @@ export interface XiaojuSurveyPlugin { - beforeResponseDataCreate?(responseData); + encryptResponseData?(responseData); afterResponseFind?(responseData); - desensitiveData?(data: Record); + maskData?(data: Record); genSurveyPath?(); } diff --git a/server/src/securityPlugin/pluginManager.provider.ts b/server/src/securityPlugin/pluginManager.provider.ts index 4f13047a..05f1ef8b 100644 --- a/server/src/securityPlugin/pluginManager.provider.ts +++ b/server/src/securityPlugin/pluginManager.provider.ts @@ -1,9 +1,9 @@ import xiaojuSurveyPluginManager, { - XiaojuSurveyPluginManager, + PluginManager, } from './pluginManager'; import { Provider } from '@nestjs/common'; export const PluginManagerProvider: Provider = { - provide: XiaojuSurveyPluginManager, + provide: PluginManager, useValue: xiaojuSurveyPluginManager, }; diff --git a/server/src/securityPlugin/pluginManager.ts b/server/src/securityPlugin/pluginManager.ts index 67d5e2fe..2b05f133 100644 --- a/server/src/securityPlugin/pluginManager.ts +++ b/server/src/securityPlugin/pluginManager.ts @@ -1,12 +1,12 @@ import { XiaojuSurveyPlugin } from './interface'; type AllowHooks = - | 'beforeResponseDataCreate' - | 'afterResponseDataReaded' - | 'desensitiveData' + | 'encryptResponseData' + | 'decryptResponseData' + | 'maskData' | 'genSurveyPath'; -export class XiaojuSurveyPluginManager { +export class PluginManager { private plugins: Array = []; // 注册插件 registerPlugin(...plugins: Array) { @@ -23,4 +23,4 @@ export class XiaojuSurveyPluginManager { } } -export default new XiaojuSurveyPluginManager(); +export default new PluginManager(); diff --git a/server/src/securityPlugin/responseSecurityPlugin/index.ts b/server/src/securityPlugin/responseSecurityPlugin/index.ts index 65cf1a69..e218b208 100644 --- a/server/src/securityPlugin/responseSecurityPlugin/index.ts +++ b/server/src/securityPlugin/responseSecurityPlugin/index.ts @@ -4,12 +4,12 @@ import { decryptData, encryptData, isDataSensitive, - desensitiveData, + maskData, } from './utils'; export class ResponseSecurityPlugin implements XiaojuSurveyPlugin { constructor(private readonly secretKey: string) {} - beforeResponseDataCreate(responseData: SurveyResponse) { + encryptResponseData(responseData: SurveyResponse) { const secretKeys = []; if (responseData.data) { for (const key in responseData.data) { @@ -39,7 +39,7 @@ export class ResponseSecurityPlugin implements XiaojuSurveyPlugin { responseData.secretKeys = secretKeys; } - afterResponseDataReaded(responseData: SurveyResponse) { + decryptResponseData(responseData: SurveyResponse) { const secretKeys = responseData.secretKeys; if (Array.isArray(secretKeys) && secretKeys.length > 0) { for (const key of secretKeys) { @@ -57,10 +57,10 @@ export class ResponseSecurityPlugin implements XiaojuSurveyPlugin { responseData.secretKeys = []; } - desensitiveData(data: Record) { + maskData(data: Record) { Object.keys(data).forEach((key) => { if (isDataSensitive(data[key])) { - data[key] = desensitiveData(data[key]); + data[key] = maskData(data[key]); } }); } diff --git a/server/src/securityPlugin/responseSecurityPlugin/utils.ts b/server/src/securityPlugin/responseSecurityPlugin/utils.ts index d2960e0d..dca43bdc 100644 --- a/server/src/securityPlugin/responseSecurityPlugin/utils.ts +++ b/server/src/securityPlugin/responseSecurityPlugin/utils.ts @@ -50,7 +50,7 @@ export const decryptData = (data, { secretKey }) => { return CryptoJS.AES.decrypt(data, secretKey).toString(CryptoJS.enc.Utf8); }; -export const desensitiveData = (data: string): string => { +export const maskData = (data: string): string => { if (!isString(data)) { return '*'; } diff --git a/web/package.json b/web/package.json index b310089a..883a8bed 100644 --- a/web/package.json +++ b/web/package.json @@ -23,7 +23,7 @@ "clipboard": "^2.0.11", "crypto-js": "^4.2.0", "echarts": "^5.5.0", - "element-plus": "^2.8.1", + "element-plus": "^2.8.3", "lodash-es": "^4.17.21", "moment": "^2.29.4", "nanoid": "^5.0.7", diff --git a/web/src/management/App.vue b/web/src/management/App.vue index c26a6762..eb5beb6f 100644 --- a/web/src/management/App.vue +++ b/web/src/management/App.vue @@ -4,13 +4,10 @@ + + \ No newline at end of file diff --git a/web/src/management/main.js b/web/src/management/main.js index aa44a4fe..1a0618b0 100644 --- a/web/src/management/main.js +++ b/web/src/management/main.js @@ -6,6 +6,10 @@ import safeHtml from './directive/safeHtml' import App from './App.vue' import router from './router' +import moment from 'moment' +import 'moment/locale/zh-cn' +moment.locale('zh-cn') + const pinia = createPinia() const app = createApp(App) diff --git a/web/src/management/pages/analysis/pages/DataTablePage.vue b/web/src/management/pages/analysis/pages/DataTablePage.vue index 3ab915ff..b298457a 100644 --- a/web/src/management/pages/analysis/pages/DataTablePage.vue +++ b/web/src/management/pages/analysis/pages/DataTablePage.vue @@ -4,7 +4,7 @@