feat: 修改导出和登录状态检测代码CR问题 (#431)
This commit is contained in:
parent
cdb8b6532a
commit
54a86e1501
@ -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=
|
||||
|
@ -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<string>('XIAOJU_SURVEY_LOGGER_FILENAME'),
|
||||
});
|
||||
}
|
||||
|
@ -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 };
|
68
server/src/guards/__test/session.guard.spec.ts
Normal file
68
server/src/guards/__test/session.guard.spec.ts
Normal file
@ -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(),
|
||||
);
|
||||
});
|
||||
});
|
@ -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 }) {
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -19,7 +19,11 @@ export class DownloadTask extends BaseEntity {
|
||||
|
||||
// 任务创建人
|
||||
@Column()
|
||||
ownerId: string;
|
||||
creatorId: string;
|
||||
|
||||
// 任务创建人
|
||||
@Column()
|
||||
creator: string;
|
||||
|
||||
// 文件名
|
||||
@Column()
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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>(CollaboratorController);
|
||||
collaboratorService = module.get<CollaboratorService>(CollaboratorService);
|
||||
logger = module.get<XiaojuSurveyLogger>(XiaojuSurveyLogger);
|
||||
logger = module.get<Logger>(Logger);
|
||||
userService = module.get<UserService>(UserService);
|
||||
surveyMetaService = module.get<SurveyMetaService>(SurveyMetaService);
|
||||
workspaceMemberServie = module.get<WorkspaceMemberService>(
|
||||
|
@ -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<Collaborator>;
|
||||
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<MongoRepository<Collaborator>>(
|
||||
getRepositoryToken(Collaborator),
|
||||
);
|
||||
logger = module.get<XiaojuSurveyLogger>(XiaojuSurveyLogger);
|
||||
logger = module.get<Logger>(Logger);
|
||||
});
|
||||
|
||||
describe('create', () => {
|
||||
|
@ -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>(
|
||||
ResponseSchemaService,
|
||||
);
|
||||
pluginManager = module.get<XiaojuSurveyPluginManager>(
|
||||
XiaojuSurveyPluginManager,
|
||||
);
|
||||
logger = module.get<XiaojuSurveyLogger>(XiaojuSurveyLogger);
|
||||
pluginManager = module.get<PluginManager>(PluginManager);
|
||||
logger = module.get<Logger>(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,
|
||||
},
|
||||
|
@ -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<MongoRepository<SurveyResponse>>(
|
||||
getRepositoryToken(SurveyResponse),
|
||||
);
|
||||
const manager = module.get<XiaojuSurveyPluginManager>(
|
||||
XiaojuSurveyPluginManager,
|
||||
);
|
||||
const manager = module.get<PluginManager>(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<SurveyResponse> = [
|
||||
{
|
||||
|
259
server/src/modules/survey/__test/downloadTask.controller.spec.ts
Normal file
259
server/src/modules/survey/__test/downloadTask.controller.spec.ts
Normal file
@ -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>(DownloadTaskController);
|
||||
responseSchemaService = module.get<ResponseSchemaService>(
|
||||
ResponseSchemaService,
|
||||
);
|
||||
downloadTaskService = module.get<DownloadTaskService>(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),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
192
server/src/modules/survey/__test/downloadTask.service.spec.ts
Normal file
192
server/src/modules/survey/__test/downloadTask.service.spec.ts
Normal file
@ -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<DownloadTask>;
|
||||
|
||||
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>(DownloadTaskService);
|
||||
downloadTaskRepository = module.get<MongoRepository<DownloadTask>>(
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
87
server/src/modules/survey/__test/session.controller.spec.ts
Normal file
87
server/src/modules/survey/__test/session.controller.spec.ts
Normal file
@ -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<SessionService>;
|
||||
let logger: jest.Mocked<Logger>;
|
||||
|
||||
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>(SessionController);
|
||||
sessionService = module.get<jest.Mocked<SessionService>>(SessionService);
|
||||
logger = module.get<jest.Mocked<Logger>>(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',
|
||||
});
|
||||
});
|
||||
});
|
@ -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>(
|
||||
ResponseSchemaService,
|
||||
);
|
||||
contentSecurityService = module.get<ContentSecurityService>(
|
||||
ContentSecurityService,
|
||||
);
|
||||
surveyHistoryService =
|
||||
module.get<SurveyHistoryService>(SurveyHistoryService);
|
||||
sessionService = module.get<SessionService>(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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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(),
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -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<SurveyMeta>;
|
||||
let pluginManager: XiaojuSurveyPluginManager;
|
||||
let pluginManager: PluginManager;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
@ -37,8 +37,8 @@ describe('SurveyMetaService', () => {
|
||||
surveyRepository = module.get<MongoRepository<SurveyMeta>>(
|
||||
getRepositoryToken(SurveyMeta),
|
||||
);
|
||||
pluginManager = module.get<XiaojuSurveyPluginManager>(
|
||||
XiaojuSurveyPluginManager,
|
||||
pluginManager = module.get<PluginManager>(
|
||||
PluginManager,
|
||||
);
|
||||
pluginManager.registerPlugin(new SurveyUtilPlugin());
|
||||
});
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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<string, any> = {
|
||||
@ -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('没有权限');
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
) {}
|
||||
|
@ -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')
|
||||
|
@ -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,
|
||||
) {}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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<Collaborator>,
|
||||
private readonly logger: XiaojuSurveyLogger,
|
||||
private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
async create({ surveyId, userId, permissions }) {
|
||||
|
@ -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<any> = [];
|
||||
private static isExecuting: boolean = false;
|
||||
static taskList: Array<any> = [];
|
||||
static isExecuting: boolean = false;
|
||||
|
||||
constructor(
|
||||
@InjectRepository(DownloadTask)
|
||||
@ -28,26 +28,29 @@ export class DownloadTaskService {
|
||||
private readonly surveyResponseRepository: MongoRepository<SurveyResponse>,
|
||||
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 = {
|
||||
|
@ -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<SurveyMeta>,
|
||||
private readonly pluginManager: XiaojuSurveyPluginManager,
|
||||
private readonly pluginManager: PluginManager,
|
||||
) {}
|
||||
|
||||
async getNewSurveyPath(): Promise<string> {
|
||||
|
@ -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>(ClientEncryptService);
|
||||
|
||||
const pluginManager = module.get<XiaojuSurveyPluginManager>(
|
||||
XiaojuSurveyPluginManager,
|
||||
const pluginManager = module.get<PluginManager>(
|
||||
PluginManager,
|
||||
);
|
||||
pluginManager.registerPlugin(
|
||||
new ResponseSecurityPlugin('dataAesEncryptSecretKey'),
|
||||
|
@ -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,
|
||||
) {}
|
||||
|
@ -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,
|
||||
|
@ -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(),
|
||||
|
@ -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')
|
||||
|
@ -1,6 +1,6 @@
|
||||
export interface XiaojuSurveyPlugin {
|
||||
beforeResponseDataCreate?(responseData);
|
||||
encryptResponseData?(responseData);
|
||||
afterResponseFind?(responseData);
|
||||
desensitiveData?(data: Record<string, any>);
|
||||
maskData?(data: Record<string, any>);
|
||||
genSurveyPath?();
|
||||
}
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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<XiaojuSurveyPlugin> = [];
|
||||
// 注册插件
|
||||
registerPlugin(...plugins: Array<XiaojuSurveyPlugin>) {
|
||||
@ -23,4 +23,4 @@ export class XiaojuSurveyPluginManager {
|
||||
}
|
||||
}
|
||||
|
||||
export default new XiaojuSurveyPluginManager();
|
||||
export default new PluginManager();
|
||||
|
@ -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<string, any>) {
|
||||
maskData(data: Record<string, any>) {
|
||||
Object.keys(data).forEach((key) => {
|
||||
if (isDataSensitive(data[key])) {
|
||||
data[key] = desensitiveData(data[key]);
|
||||
data[key] = maskData(data[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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 '*';
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -4,13 +4,10 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { watch, onBeforeUnmount } from 'vue'
|
||||
import { get as _get } from 'lodash-es'
|
||||
import { useUserStore } from '@/management/stores/user'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessageBox, ElMessage, type Action } from 'element-plus'
|
||||
|
||||
// 这里不需要自动跳转登录页面,所以单独引入axios
|
||||
import axios from 'axios'
|
||||
import { checkIsTokenValid } from '@/management/api/auth';
|
||||
|
||||
const userStore = useUserStore()
|
||||
const router = useRouter()
|
||||
@ -32,16 +29,9 @@ const showConfirmBox = () => {
|
||||
|
||||
const checkAuth = async () => {
|
||||
try {
|
||||
const token = _get(userStore, 'userInfo.token')
|
||||
|
||||
const res = await axios({
|
||||
url: '/api/user/getUserInfo',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
if (res.data.code !== 200) {
|
||||
showConfirmBox()
|
||||
const res: Record<string, any> = await checkIsTokenValid()
|
||||
if (res.code !== 200 || !res.data) {
|
||||
showConfirmBox();
|
||||
} else {
|
||||
timer = setTimeout(
|
||||
() => {
|
||||
|
@ -19,3 +19,7 @@ export const getPasswordStrength = (password) => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const checkIsTokenValid = () => {
|
||||
return axios.get('/auth/verifyToken');
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import axios from './base'
|
||||
|
||||
export const createDownloadSurveyResponseTask = ({ surveyId, isDesensitive }) => {
|
||||
export const createDownloadTask = ({ surveyId, isMasked }) => {
|
||||
return axios.post('/downloadTask/createTask', {
|
||||
surveyId,
|
||||
isDesensitive
|
||||
isMasked
|
||||
})
|
||||
}
|
||||
|
||||
|
91
web/src/management/components/TopNav.vue
Normal file
91
web/src/management/components/TopNav.vue
Normal file
@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<div class="top-nav">
|
||||
<div class="left">
|
||||
<img class="logo-img" src="/imgs/Logo.webp" alt="logo" />
|
||||
<el-menu router default-active-index="survey" class="el-menu-demo" mode="horizontal">
|
||||
<el-menu-item index="survey" >
|
||||
<router-link :to="{ name: 'survey' }">问卷列表</router-link>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="download">
|
||||
<router-link :to="{ name: 'download' }">下载中心</router-link>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
<div class="login-info">
|
||||
您好,{{ userInfo?.username }}
|
||||
<img class="login-info-img" src="/imgs/avatar.webp" />
|
||||
<span class="logout" @click="handleLogout">退出</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useUserStore } from '@/management/stores/user'
|
||||
import { computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
|
||||
const userStore = useUserStore()
|
||||
const userInfo = computed(() => {
|
||||
return userStore.userInfo
|
||||
})
|
||||
|
||||
const handleLogout = () => {
|
||||
userStore.logout()
|
||||
router.replace({ name: 'login' })
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.top-nav {
|
||||
background: #fff;
|
||||
color: #4a4c5b;
|
||||
padding: 0 20px;
|
||||
height: 56px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.04);
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: calc(100% - 200px);
|
||||
.logo-img {
|
||||
width: 90px;
|
||||
height: fit-content;
|
||||
padding-right: 20px;
|
||||
}
|
||||
.el-menu {
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
border: none !important;
|
||||
:deep(.el-menu-item, .is-active) {
|
||||
border: none !important;
|
||||
}
|
||||
.router-link-active {
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
.login-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.login-info-img {
|
||||
margin-left: 10px;
|
||||
height: 30px;
|
||||
margin-top: -6px;
|
||||
}
|
||||
|
||||
.logout {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
cursor: pointer;
|
||||
color: #faa600;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -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)
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
<div class="menus">
|
||||
<el-button type="primary" :loading="isDownloading" @click="onDownload">导出全部数据</el-button>
|
||||
<el-switch
|
||||
class="desensitive-switch"
|
||||
class="desensitize-switch"
|
||||
:model-value="isShowOriginData"
|
||||
active-text="是否展示原数据"
|
||||
@input="onIsShowOriginChange"
|
||||
@ -31,7 +31,7 @@
|
||||
<el-dialog v-model="downloadDialogVisible" title="导出确认" width="500" style="padding: 40px">
|
||||
<el-form :model="downloadForm" label-width="100px" label-position="left">
|
||||
<el-form-item label="导出内容">
|
||||
<el-radio-group v-model="downloadForm.isDesensitive">
|
||||
<el-radio-group v-model="downloadForm.isMasked">
|
||||
<el-radio :value="true">脱敏数据</el-radio>
|
||||
<el-radio :value="false">原回收数据</el-radio>
|
||||
</el-radio-group>
|
||||
@ -63,7 +63,7 @@ import EmptyIndex from '@/management/components/EmptyIndex.vue'
|
||||
import { getRecycleList } from '@/management/api/analysis'
|
||||
import { noDataConfig } from '@/management/config/analysisConfig'
|
||||
import DataTable from '../components/DataTable.vue'
|
||||
import { createDownloadSurveyResponseTask, getDownloadTask } from '@/management/api/downloadTask'
|
||||
import { createDownloadTask, getDownloadTask } from '@/management/api/downloadTask'
|
||||
|
||||
const dataTableState = reactive({
|
||||
mainTableLoading: false,
|
||||
@ -78,8 +78,8 @@ const dataTableState = reactive({
|
||||
isDownloading: false,
|
||||
downloadDialogVisible: false,
|
||||
downloadForm: {
|
||||
isDesensitive: true
|
||||
}
|
||||
isMasked: true,
|
||||
},
|
||||
})
|
||||
|
||||
const { mainTableLoading, tableData, isShowOriginData, downloadDialogVisible, isDownloading } =
|
||||
@ -137,7 +137,7 @@ const init = async () => {
|
||||
const res = await getRecycleList({
|
||||
page: dataTableState.currentPage,
|
||||
surveyId: route.params.id,
|
||||
isDesensitive: !dataTableState.tmpIsShowOriginData // 发起请求的时候,isShowOriginData还没改变,暂存了一个字段
|
||||
isMasked: !dataTableState.tmpIsShowOriginData // 发起请求的时候,isShowOriginData还没改变,暂存了一个字段
|
||||
})
|
||||
|
||||
if (res.code === 200) {
|
||||
@ -162,10 +162,7 @@ const confirmDownload = async () => {
|
||||
}
|
||||
try {
|
||||
isDownloading.value = true
|
||||
const createRes = await createDownloadSurveyResponseTask({
|
||||
surveyId: route.params.id,
|
||||
isDesensitive: downloadForm.isDesensitive
|
||||
})
|
||||
const createRes = await createDownloadTask({ surveyId: route.params.id, isMasked: downloadForm.isMasked })
|
||||
dataTableState.downloadDialogVisible = false
|
||||
if (createRes.code === 200) {
|
||||
ElMessage.success(`下载文件计算中,可前往“下载中心”查看`)
|
||||
@ -237,7 +234,7 @@ const checkIsTaskFinished = (taskId) => {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.desensitive-switch {
|
||||
.desensitize-switch {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
|
27
web/src/management/pages/download/DownloadPage.vue
Normal file
27
web/src/management/pages/download/DownloadPage.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div class="question-list-root">
|
||||
<TopNav></TopNav>
|
||||
<div class="table-container">
|
||||
<DownloadTaskList></DownloadTaskList>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import TopNav from '@/management/components/TopNav.vue';
|
||||
import DownloadTaskList from './components/DownloadTaskList.vue'
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.question-list-root {
|
||||
height: 100%;
|
||||
background-color: #f6f7f9;
|
||||
.table-container {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%; /* 确保容器宽度为100% */
|
||||
}
|
||||
}
|
||||
</style>
|
@ -57,12 +57,6 @@ import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { deleteDownloadTask, getDownloadTaskList } from '@/management/api/downloadTask'
|
||||
import { CODE_MAP } from '@/management/api/base'
|
||||
|
||||
import moment from 'moment'
|
||||
// 引入中文
|
||||
import 'moment/locale/zh-cn'
|
||||
// 设置中文
|
||||
moment.locale('zh-cn')
|
||||
|
||||
const loading = ref(false)
|
||||
const pageSize = ref(10)
|
||||
const total = ref(0)
|
@ -1,103 +0,0 @@
|
||||
<template>
|
||||
<div class="question-list-root">
|
||||
<div class="top-nav">
|
||||
<div class="left">
|
||||
<img class="logo-img" src="/imgs/Logo.webp" alt="logo" />
|
||||
<el-menu :default-active="activeIndex" class="el-menu-demo" mode="horizontal">
|
||||
<el-menu-item index="1" @click="handleSurvey">问卷列表</el-menu-item>
|
||||
<el-menu-item index="2">下载中心</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
<div class="login-info">
|
||||
您好,{{ userInfo?.username }}
|
||||
<img class="login-info-img" src="/imgs/avatar.webp" />
|
||||
<span class="logout" @click="handleLogout">退出</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<DownloadTaskList></DownloadTaskList>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { useUserStore } from '@/management/stores/user'
|
||||
import { useRouter } from 'vue-router'
|
||||
import DownloadTaskList from './components/DownloadTaskList.vue'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const router = useRouter()
|
||||
const userInfo = computed(() => {
|
||||
return userStore.userInfo
|
||||
})
|
||||
|
||||
const handleSurvey = () => {
|
||||
router.push('/survey')
|
||||
}
|
||||
const handleLogout = () => {
|
||||
userStore.logout()
|
||||
router.replace({ name: 'login' })
|
||||
}
|
||||
|
||||
const activeIndex = ref('2')
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.question-list-root {
|
||||
height: 100%;
|
||||
background-color: #f6f7f9;
|
||||
.top-nav {
|
||||
background: #fff;
|
||||
color: #4a4c5b;
|
||||
padding: 0 20px;
|
||||
height: 56px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.04);
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: calc(100% - 200px);
|
||||
.logo-img {
|
||||
width: 90px;
|
||||
height: fit-content;
|
||||
padding-right: 20px;
|
||||
}
|
||||
.el-menu {
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
border: none !important;
|
||||
:deep(.el-menu-item, .is-active) {
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.login-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.login-info-img {
|
||||
margin-left: 10px;
|
||||
height: 30px;
|
||||
margin-top: -6px;
|
||||
}
|
||||
|
||||
.logout {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
cursor: pointer;
|
||||
color: #faa600;
|
||||
}
|
||||
}
|
||||
.table-container {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%; /* 确保容器宽度为100% */
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,19 +1,6 @@
|
||||
<template>
|
||||
<div class="question-list-root">
|
||||
<div class="top-nav">
|
||||
<div class="left">
|
||||
<img class="logo-img" src="/imgs/Logo.webp" alt="logo" />
|
||||
<el-menu :default-active="activeIndex" class="el-menu-demo" mode="horizontal">
|
||||
<el-menu-item index="1">问卷列表</el-menu-item>
|
||||
<el-menu-item index="2" @click="handleDownload">下载中心</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
<div class="login-info">
|
||||
您好,{{ userInfo?.username }}
|
||||
<img class="login-info-img" src="/imgs/avatar.webp" />
|
||||
<span class="logout" @click="handleLogout">退出</span>
|
||||
</div>
|
||||
</div>
|
||||
<TopNav></TopNav>
|
||||
<div class="content-wrap">
|
||||
<SliderBar :menus="spaceMenus" @select="handleSpaceSelect" />
|
||||
<div class="list-content">
|
||||
@ -80,6 +67,7 @@ import BaseList from './components/BaseList.vue'
|
||||
import SpaceList from './components/SpaceList.vue'
|
||||
import SliderBar from './components/SliderBar.vue'
|
||||
import SpaceModify from './components/SpaceModify.vue'
|
||||
import TopNav from '@/management/components/TopNav.vue'
|
||||
import { SpaceType } from '@/management/utils/types/workSpace'
|
||||
import { useUserStore } from '@/management/stores/user'
|
||||
import { useWorkSpaceStore } from '@/management/stores/workSpace'
|
||||
@ -93,16 +81,12 @@ const { surveyList, surveyTotal } = storeToRefs(surveyListStore)
|
||||
const { spaceMenus, workSpaceId, spaceType, workSpaceList, workSpaceListTotal } =
|
||||
storeToRefs(workSpaceStore)
|
||||
const router = useRouter()
|
||||
const userInfo = computed(() => {
|
||||
return userStore.userInfo
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const spaceListRef = ref<any>(null)
|
||||
const spaceLoading = ref(false)
|
||||
|
||||
const activeIndex = ref('1')
|
||||
|
||||
const fetchSpaceList = async (params?: any) => {
|
||||
spaceLoading.value = true
|
||||
@ -181,67 +165,13 @@ const onSpaceCreate = () => {
|
||||
const onCreate = () => {
|
||||
router.push('/create')
|
||||
}
|
||||
const handleLogout = () => {
|
||||
userStore.logout()
|
||||
router.replace({ name: 'login' })
|
||||
}
|
||||
// 下载页面
|
||||
const handleDownload = () => {
|
||||
router.push({ name: 'download' })
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.question-list-root {
|
||||
height: 100%;
|
||||
background-color: #f6f7f9;
|
||||
|
||||
.top-nav {
|
||||
background: #fff;
|
||||
color: #4a4c5b;
|
||||
padding: 0 20px;
|
||||
height: 56px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.04);
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: calc(100% - 200px);
|
||||
.logo-img {
|
||||
width: 90px;
|
||||
height: fit-content;
|
||||
padding-right: 20px;
|
||||
}
|
||||
.el-menu {
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
border: none !important;
|
||||
:deep(.el-menu-item, .is-active) {
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.login-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.login-info-img {
|
||||
margin-left: 10px;
|
||||
height: 30px;
|
||||
margin-top: -6px;
|
||||
}
|
||||
|
||||
.logout {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
cursor: pointer;
|
||||
color: #faa600;
|
||||
}
|
||||
}
|
||||
.content-wrap {
|
||||
position: relative;
|
||||
height: calc(100% - 56px);
|
||||
|
@ -32,7 +32,7 @@ const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: '/download',
|
||||
name: 'download',
|
||||
component: () => import('../pages/downloadTask/TaskList.vue'),
|
||||
component: () => import('../pages/download/DownloadPage.vue'),
|
||||
meta: {
|
||||
needLogin: true
|
||||
}
|
||||
|
@ -22,10 +22,8 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import moment from 'moment'
|
||||
import 'moment/locale/zh-cn'
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||
import { FORM_CHANGE_EVENT_KEY } from '@/materials/setters/constant'
|
||||
moment.locale('zh-cn')
|
||||
|
||||
interface Props {
|
||||
formConfig: any
|
||||
|
@ -19,12 +19,9 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import moment from 'moment'
|
||||
import 'moment/locale/zh-cn'
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||
import { FORM_CHANGE_EVENT_KEY } from '@/materials/setters/constant'
|
||||
|
||||
moment.locale('zh-cn')
|
||||
|
||||
interface Props {
|
||||
formConfig: any
|
||||
}
|
||||
|
@ -3,9 +3,6 @@ import { useRouter } from 'vue-router'
|
||||
import { defineStore } from 'pinia'
|
||||
import { pick } from 'lodash-es'
|
||||
import moment from 'moment'
|
||||
// 引入中文
|
||||
import 'moment/locale/zh-cn'
|
||||
// 设置中文
|
||||
|
||||
import { isMobile as isInMobile } from '@/render/utils/index'
|
||||
|
||||
@ -16,7 +13,6 @@ import { useErrorInfo } from '@/render/stores/errorInfo'
|
||||
import adapter from '../adapter'
|
||||
import { RuleMatch } from '@/common/logicEngine/RulesMatch'
|
||||
|
||||
moment.locale('zh-cn')
|
||||
/**
|
||||
* CODE_MAP不从management引入,在dev阶段,会导致B端 router被加载,进而导致C端路由被添加 baseUrl: /management
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user