feat:新增暂停功能 (#416)

* feat:新增暂停功能
This commit is contained in:
chaorenluo 2024-09-11 16:19:55 +08:00 committed by GitHub
parent 43001a12c7
commit 0b4e1fa13b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
39 changed files with 549 additions and 189 deletions

View File

@ -14,6 +14,7 @@ import { AuthModule } from './modules/auth/auth.module';
import { MessageModule } from './modules/message/message.module'; import { MessageModule } from './modules/message/message.module';
import { FileModule } from './modules/file/file.module'; import { FileModule } from './modules/file/file.module';
import { WorkspaceModule } from './modules/workspace/workspace.module'; import { WorkspaceModule } from './modules/workspace/workspace.module';
import { UpgradeModule } from './modules/upgrade/upgrade.module';
import { join } from 'path'; import { join } from 'path';
@ -100,6 +101,7 @@ import { Logger } from './logger';
MessageModule, MessageModule,
FileModule, FileModule,
WorkspaceModule, WorkspaceModule,
UpgradeModule,
], ],
controllers: [AppController], controllers: [AppController],
providers: [ providers: [

View File

@ -20,6 +20,7 @@ export enum EXCEPTION_CODE {
RESPONSE_OVER_LIMIT = 9003, // 超出限制 RESPONSE_OVER_LIMIT = 9003, // 超出限制
RESPONSE_SCHEMA_REMOVED = 9004, // 问卷已删除 RESPONSE_SCHEMA_REMOVED = 9004, // 问卷已删除
RESPONSE_DATA_DECRYPT_ERROR = 9005, // 问卷已删除 RESPONSE_DATA_DECRYPT_ERROR = 9005, // 问卷已删除
RESPONSE_PAUSING = 9006, // 问卷已暂停
UPLOAD_FILE_ERROR = 5001, // 上传文件错误 UPLOAD_FILE_ERROR = 5001, // 上传文件错误
} }

View File

@ -1,9 +1,14 @@
// 状态类型 // 状态类型
export enum RECORD_STATUS { export enum RECORD_STATUS {
NEW = 'new', // 新建 NEW = 'new', // 新建 | 未发布
PUBLISHED = 'published', // 发布
CLOSE = 'close', // 关闭
}
export const enum RECORD_SUB_STATUS {
DEFAULT = '', // 默认
EDITING = 'editing', // 编辑 EDITING = 'editing', // 编辑
PAUSING = 'pausing', // 暂停 PAUSING = 'pausing', // 暂停
PUBLISHED = 'published', // 发布
REMOVED = 'removed', // 删除 REMOVED = 'removed', // 删除
FORCE_REMOVED = 'forceRemoved', // 从回收站删除 FORCE_REMOVED = 'forceRemoved', // 从回收站删除
} }

View File

@ -1,6 +1,6 @@
import { Column, ObjectIdColumn, BeforeInsert, BeforeUpdate } from 'typeorm'; import { Column, ObjectIdColumn, BeforeInsert, BeforeUpdate } from 'typeorm';
import { ObjectId } from 'mongodb'; import { ObjectId } from 'mongodb';
import { RECORD_STATUS } from '../enums'; import { RECORD_STATUS, RECORD_SUB_STATUS } from '../enums';
export class BaseEntity { export class BaseEntity {
@ObjectIdColumn() @ObjectIdColumn()
@ -12,9 +12,15 @@ export class BaseEntity {
date: number; date: number;
}; };
@Column()
subStatus: {
status: RECORD_SUB_STATUS;
date: number;
};
@Column() @Column()
statusList: Array<{ statusList: Array<{
status: RECORD_STATUS; status: RECORD_STATUS | RECORD_SUB_STATUS;
date: number; date: number;
}>; }>;
@ -32,6 +38,10 @@ export class BaseEntity {
this.curStatus = curStatus; this.curStatus = curStatus;
this.statusList = [curStatus]; this.statusList = [curStatus];
} }
if (!this.subStatus) {
const subStatus = { status: RECORD_SUB_STATUS.DEFAULT, date: now };
this.subStatus = subStatus;
}
this.createDate = now; this.createDate = now;
this.updateDate = now; this.updateDate = now;
} }

View File

@ -5,7 +5,7 @@ import { UserService } from '../services/user.service';
import { User } from 'src/models/user.entity'; import { User } from 'src/models/user.entity';
import { HttpException } from 'src/exceptions/httpException'; import { HttpException } from 'src/exceptions/httpException';
import { hash256 } from 'src/utils/hash256'; import { hash256 } from 'src/utils/hash256';
import { RECORD_STATUS } from 'src/enums'; import { RECORD_SUB_STATUS } from 'src/enums';
import { ObjectId } from 'mongodb'; import { ObjectId } from 'mongodb';
describe('UserService', () => { describe('UserService', () => {
@ -145,7 +145,7 @@ describe('UserService', () => {
expect(userRepository.findOne).toHaveBeenCalledWith({ expect(userRepository.findOne).toHaveBeenCalledWith({
where: { where: {
username: username, username: username,
'curStatus.status': { $ne: RECORD_STATUS.REMOVED }, 'subStatus.status': { $ne: RECORD_SUB_STATUS.REMOVED },
}, },
}); });
expect(user).toEqual(userInfo); expect(user).toEqual(userInfo);
@ -163,7 +163,7 @@ describe('UserService', () => {
expect(findOneSpy).toHaveBeenCalledWith({ expect(findOneSpy).toHaveBeenCalledWith({
where: { where: {
username: username, username: username,
'curStatus.status': { $ne: RECORD_STATUS.REMOVED }, 'subStatus.status': { $ne: RECORD_SUB_STATUS.REMOVED },
}, },
}); });
expect(user).toBe(null); expect(user).toBe(null);
@ -184,7 +184,7 @@ describe('UserService', () => {
expect(userRepository.findOne).toHaveBeenCalledWith({ expect(userRepository.findOne).toHaveBeenCalledWith({
where: { where: {
_id: new ObjectId(id), _id: new ObjectId(id),
'curStatus.status': { $ne: RECORD_STATUS.REMOVED }, 'subStatus.status': { $ne: RECORD_SUB_STATUS.REMOVED },
}, },
}); });
expect(user).toEqual(userInfo); expect(user).toEqual(userInfo);
@ -202,7 +202,7 @@ describe('UserService', () => {
expect(findOneSpy).toHaveBeenCalledWith({ expect(findOneSpy).toHaveBeenCalledWith({
where: { where: {
_id: new ObjectId(id), _id: new ObjectId(id),
'curStatus.status': { $ne: RECORD_STATUS.REMOVED }, 'subStatus.status': { $ne: RECORD_SUB_STATUS.REMOVED },
}, },
}); });
expect(user).toBe(null); expect(user).toBe(null);
@ -228,7 +228,7 @@ describe('UserService', () => {
expect(userRepository.find).toHaveBeenCalledWith({ expect(userRepository.find).toHaveBeenCalledWith({
where: { where: {
username: new RegExp(username), username: new RegExp(username),
'curStatus.status': { $ne: RECORD_STATUS.REMOVED }, 'subStatus.status': { $ne: RECORD_SUB_STATUS.REMOVED },
}, },
skip: 0, skip: 0,
take: 10, take: 10,
@ -263,7 +263,7 @@ describe('UserService', () => {
_id: { _id: {
$in: idList.map((id) => new ObjectId(id)), $in: idList.map((id) => new ObjectId(id)),
}, },
'curStatus.status': { $ne: RECORD_STATUS.REMOVED }, 'subStatus.status': { $ne: RECORD_SUB_STATUS.REMOVED },
}, },
select: ['_id', 'username', 'createDate'], select: ['_id', 'username', 'createDate'],
}); });

View File

@ -5,7 +5,7 @@ import { User } from 'src/models/user.entity';
import { HttpException } from 'src/exceptions/httpException'; import { HttpException } from 'src/exceptions/httpException';
import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; import { EXCEPTION_CODE } from 'src/enums/exceptionCode';
import { hash256 } from 'src/utils/hash256'; import { hash256 } from 'src/utils/hash256';
import { RECORD_STATUS } from 'src/enums'; import { RECORD_SUB_STATUS } from 'src/enums';
import { ObjectId } from 'mongodb'; import { ObjectId } from 'mongodb';
@Injectable() @Injectable()
@ -53,8 +53,8 @@ export class UserService {
const user = await this.userRepository.findOne({ const user = await this.userRepository.findOne({
where: { where: {
username: username, username: username,
'curStatus.status': { 'subStatus.status': {
$ne: RECORD_STATUS.REMOVED, $ne: RECORD_SUB_STATUS.REMOVED,
}, },
}, },
}); });
@ -66,8 +66,8 @@ export class UserService {
const user = await this.userRepository.findOne({ const user = await this.userRepository.findOne({
where: { where: {
_id: new ObjectId(id), _id: new ObjectId(id),
'curStatus.status': { 'subStatus.status': {
$ne: RECORD_STATUS.REMOVED, $ne: RECORD_SUB_STATUS.REMOVED,
}, },
}, },
}); });
@ -79,8 +79,8 @@ export class UserService {
const list = await this.userRepository.find({ const list = await this.userRepository.find({
where: { where: {
username: new RegExp(username), username: new RegExp(username),
'curStatus.status': { 'subStatus.status': {
$ne: RECORD_STATUS.REMOVED, $ne: RECORD_SUB_STATUS.REMOVED,
}, },
}, },
skip, skip,
@ -96,8 +96,8 @@ export class UserService {
_id: { _id: {
$in: idList.map((item) => new ObjectId(item)), $in: idList.map((item) => new ObjectId(item)),
}, },
'curStatus.status': { 'subStatus.status': {
$ne: RECORD_STATUS.REMOVED, $ne: RECORD_SUB_STATUS.REMOVED,
}, },
}, },
select: ['_id', 'username', 'createDate'], select: ['_id', 'username', 'createDate'],

View File

@ -10,7 +10,7 @@ import { MessagePushingLogService } from '../services/messagePushingLog.service'
import { CreateMessagePushingTaskDto } from '../dto/createMessagePushingTask.dto'; import { CreateMessagePushingTaskDto } from '../dto/createMessagePushingTask.dto';
import { UpdateMessagePushingTaskDto } from '../dto/updateMessagePushingTask.dto'; import { UpdateMessagePushingTaskDto } from '../dto/updateMessagePushingTask.dto';
import { RECORD_STATUS } from 'src/enums'; import { RECORD_SUB_STATUS } from 'src/enums';
import { MESSAGE_PUSHING_TYPE } from 'src/enums/messagePushing'; import { MESSAGE_PUSHING_TYPE } from 'src/enums/messagePushing';
import { MESSAGE_PUSHING_HOOK } from 'src/enums/messagePushing'; import { MESSAGE_PUSHING_HOOK } from 'src/enums/messagePushing';
import { MessagePushingTask } from 'src/models/messagePushingTask.entity'; import { MessagePushingTask } from 'src/models/messagePushingTask.entity';
@ -121,7 +121,7 @@ describe('MessagePushingTaskService', () => {
ownerId: mockOwnerId, ownerId: mockOwnerId,
surveys: { $all: [surveyId] }, surveys: { $all: [surveyId] },
triggerHook: hook, triggerHook: hook,
'curStatus.status': { $ne: RECORD_STATUS.REMOVED }, 'subStatus.status': { $ne: RECORD_SUB_STATUS.REMOVED },
}, },
}); });
}); });
@ -146,7 +146,7 @@ describe('MessagePushingTaskService', () => {
where: { where: {
ownerId: mockOwnerId, ownerId: mockOwnerId,
_id: new ObjectId(taskId), _id: new ObjectId(taskId),
'curStatus.status': { $ne: RECORD_STATUS.REMOVED }, 'subStatus.status': { $ne: RECORD_SUB_STATUS.REMOVED },
}, },
}); });
}); });
@ -161,8 +161,8 @@ describe('MessagePushingTaskService', () => {
pushAddress: 'http://update.example.com', pushAddress: 'http://update.example.com',
triggerHook: MESSAGE_PUSHING_HOOK.RESPONSE_INSERTED, triggerHook: MESSAGE_PUSHING_HOOK.RESPONSE_INSERTED,
surveys: ['new survey id'], surveys: ['new survey id'],
curStatus: { subStatus: {
status: RECORD_STATUS.EDITING, status: RECORD_SUB_STATUS.EDITING,
date: Date.now(), date: Date.now(),
}, },
}; };
@ -211,18 +211,18 @@ describe('MessagePushingTaskService', () => {
{ {
ownerId: mockOwnerId, ownerId: mockOwnerId,
_id: new ObjectId(taskId), _id: new ObjectId(taskId),
'curStatus.status': { $ne: RECORD_STATUS.REMOVED }, 'subStatus.status': { $ne: RECORD_SUB_STATUS.REMOVED },
}, },
{ {
$set: { $set: {
curStatus: { subStatus: {
status: RECORD_STATUS.REMOVED, status: RECORD_SUB_STATUS.REMOVED,
date: expect.any(Number), date: expect.any(Number),
}, },
}, },
$push: { $push: {
statusList: { statusList: {
status: RECORD_STATUS.REMOVED, status: RECORD_SUB_STATUS.REMOVED,
date: expect.any(Number), date: expect.any(Number),
}, },
}, },

View File

@ -1,5 +1,5 @@
import { ApiProperty } from '@nestjs/swagger'; import { ApiProperty } from '@nestjs/swagger';
import { RECORD_STATUS } from 'src/enums'; import { RECORD_STATUS, RECORD_SUB_STATUS } from 'src/enums';
import { import {
MESSAGE_PUSHING_TYPE, MESSAGE_PUSHING_TYPE,
MESSAGE_PUSHING_HOOK, MESSAGE_PUSHING_HOOK,
@ -26,4 +26,9 @@ export class UpdateMessagePushingTaskDto {
status: RECORD_STATUS; status: RECORD_STATUS;
date: number; date: number;
}; };
@ApiProperty({ description: '任务子状态', required: false })
subStatus?: {
status: RECORD_SUB_STATUS;
date: number;
};
} }

View File

@ -6,7 +6,7 @@ import { MESSAGE_PUSHING_HOOK } from 'src/enums/messagePushing';
import { CreateMessagePushingTaskDto } from '../dto/createMessagePushingTask.dto'; import { CreateMessagePushingTaskDto } from '../dto/createMessagePushingTask.dto';
import { UpdateMessagePushingTaskDto } from '../dto/updateMessagePushingTask.dto'; import { UpdateMessagePushingTaskDto } from '../dto/updateMessagePushingTask.dto';
import { ObjectId } from 'mongodb'; import { ObjectId } from 'mongodb';
import { RECORD_STATUS } from 'src/enums'; import { RECORD_SUB_STATUS } from 'src/enums';
import { MESSAGE_PUSHING_TYPE } from 'src/enums/messagePushing'; import { MESSAGE_PUSHING_TYPE } from 'src/enums/messagePushing';
import { MessagePushingLogService } from './messagePushingLog.service'; import { MessagePushingLogService } from './messagePushingLog.service';
import { httpPost } from 'src/utils/request'; import { httpPost } from 'src/utils/request';
@ -44,8 +44,8 @@ export class MessagePushingTaskService {
ownerId?: string; ownerId?: string;
}): Promise<MessagePushingTask[]> { }): Promise<MessagePushingTask[]> {
const where: Record<string, any> = { const where: Record<string, any> = {
'curStatus.status': { 'subStatus.status': {
$ne: RECORD_STATUS.REMOVED, $ne: RECORD_SUB_STATUS.REMOVED,
}, },
}; };
if (surveyId) { if (surveyId) {
@ -75,8 +75,8 @@ export class MessagePushingTaskService {
where: { where: {
ownerId, ownerId,
_id: new ObjectId(id), _id: new ObjectId(id),
'curStatus.status': { 'subStatus.status': {
$ne: RECORD_STATUS.REMOVED, $ne: RECORD_SUB_STATUS.REMOVED,
}, },
}, },
}); });
@ -103,26 +103,25 @@ export class MessagePushingTaskService {
const updatedTask = Object.assign(existingTask, updateData); const updatedTask = Object.assign(existingTask, updateData);
return await this.messagePushingTaskRepository.save(updatedTask); return await this.messagePushingTaskRepository.save(updatedTask);
} }
async remove({ id, ownerId }: { id: string; ownerId: string }) { async remove({ id, ownerId }: { id: string; ownerId: string }) {
const curStatus = { const subStatus = {
status: RECORD_STATUS.REMOVED, status: RECORD_SUB_STATUS.REMOVED,
date: Date.now(), date: Date.now(),
}; };
return this.messagePushingTaskRepository.updateOne( return this.messagePushingTaskRepository.updateOne(
{ {
ownerId, ownerId,
_id: new ObjectId(id), _id: new ObjectId(id),
'curStatus.status': { 'subStatus.status': {
$ne: RECORD_STATUS.REMOVED, $ne: RECORD_SUB_STATUS.REMOVED,
}, },
}, },
{ {
$set: { $set: {
curStatus, subStatus,
}, },
$push: { $push: {
statusList: curStatus as never, statusList: subStatus as never,
}, },
}, },
); );

View File

@ -116,6 +116,9 @@ describe('SurveyMetaController', () => {
curStatus: { curStatus: {
date: date, date: date,
}, },
subStatus: {
date: date,
},
}, },
], ],
}); });
@ -132,10 +135,12 @@ describe('SurveyMetaController', () => {
createDate: expect.stringMatching( createDate: expect.stringMatching(
/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/, /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/,
), ),
updateDate: expect.stringMatching( curStatus: expect.objectContaining({
date: expect.stringMatching(
/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/, /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/,
), ),
curStatus: expect.objectContaining({ }),
subStatus: expect.objectContaining({
date: expect.stringMatching( date: expect.stringMatching(
/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/, /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/,
), ),

View File

@ -4,7 +4,7 @@ import { MongoRepository } from 'typeorm';
import { SurveyMeta } from 'src/models/surveyMeta.entity'; import { SurveyMeta } from 'src/models/surveyMeta.entity';
import { PluginManagerProvider } from 'src/securityPlugin/pluginManager.provider'; import { PluginManagerProvider } from 'src/securityPlugin/pluginManager.provider';
import { XiaojuSurveyPluginManager } from 'src/securityPlugin/pluginManager'; import { XiaojuSurveyPluginManager } from 'src/securityPlugin/pluginManager';
import { RECORD_STATUS } from 'src/enums'; import { RECORD_STATUS, RECORD_SUB_STATUS } from 'src/enums';
import { getRepositoryToken } from '@nestjs/typeorm'; import { getRepositoryToken } from '@nestjs/typeorm';
import { HttpException } from 'src/exceptions/httpException'; import { HttpException } from 'src/exceptions/httpException';
import { SurveyUtilPlugin } from 'src/securityPlugin/surveyUtilPlugin'; import { SurveyUtilPlugin } from 'src/securityPlugin/surveyUtilPlugin';
@ -100,14 +100,18 @@ describe('SurveyMetaService', () => {
it('should edit a survey meta and return it if in NEW or EDITING status', async () => { it('should edit a survey meta and return it if in NEW or EDITING status', async () => {
const survey = new SurveyMeta(); const survey = new SurveyMeta();
survey.curStatus = { status: RECORD_STATUS.PUBLISHED, date: Date.now() }; survey.curStatus = { status: RECORD_STATUS.PUBLISHED, date: Date.now() };
survey.subStatus = {
status: RECORD_SUB_STATUS.DEFAULT,
date: Date.now(),
};
survey.statusList = []; survey.statusList = [];
jest.spyOn(surveyRepository, 'save').mockResolvedValue(survey); jest.spyOn(surveyRepository, 'save').mockResolvedValue(survey);
const result = await service.editSurveyMeta(survey); const result = await service.editSurveyMeta(survey);
expect(survey.curStatus.status).toEqual(RECORD_STATUS.EDITING); expect(survey.subStatus.status).toEqual(RECORD_SUB_STATUS.EDITING);
expect(survey.statusList.length).toBe(1); expect(survey.statusList.length).toBe(1);
expect(survey.statusList[0].status).toEqual(RECORD_STATUS.EDITING); expect(survey.statusList[0].status).toEqual(RECORD_SUB_STATUS.EDITING);
expect(surveyRepository.save).toHaveBeenCalledWith(survey); expect(surveyRepository.save).toHaveBeenCalledWith(survey);
expect(result).toEqual(survey); expect(result).toEqual(survey);
}); });
@ -118,6 +122,10 @@ describe('SurveyMetaService', () => {
// 准备假的SurveyMeta对象 // 准备假的SurveyMeta对象
const survey = new SurveyMeta(); const survey = new SurveyMeta();
survey.curStatus = { status: RECORD_STATUS.NEW, date: Date.now() }; survey.curStatus = { status: RECORD_STATUS.NEW, date: Date.now() };
survey.subStatus = {
status: RECORD_SUB_STATUS.DEFAULT,
date: Date.now(),
};
survey.statusList = []; survey.statusList = [];
// 模拟save方法 // 模拟save方法
@ -128,9 +136,9 @@ describe('SurveyMetaService', () => {
// 验证结果 // 验证结果
expect(result).toBe(survey); expect(result).toBe(survey);
expect(survey.curStatus.status).toBe(RECORD_STATUS.REMOVED); expect(survey.subStatus.status).toBe(RECORD_SUB_STATUS.REMOVED);
expect(survey.statusList.length).toBe(1); expect(survey.statusList.length).toBe(1);
expect(survey.statusList[0].status).toBe(RECORD_STATUS.REMOVED); expect(survey.statusList[0].status).toBe(RECORD_SUB_STATUS.REMOVED);
expect(surveyRepository.save).toHaveBeenCalledTimes(1); expect(surveyRepository.save).toHaveBeenCalledTimes(1);
expect(surveyRepository.save).toHaveBeenCalledWith(survey); expect(surveyRepository.save).toHaveBeenCalledWith(survey);
}); });
@ -138,7 +146,10 @@ describe('SurveyMetaService', () => {
it('should throw exception when survey is already removed', async () => { it('should throw exception when survey is already removed', async () => {
// 准备假的SurveyMeta对象其状态已设置为REMOVED // 准备假的SurveyMeta对象其状态已设置为REMOVED
const survey = new SurveyMeta(); const survey = new SurveyMeta();
survey.curStatus = { status: RECORD_STATUS.REMOVED, date: Date.now() }; survey.subStatus = {
status: RECORD_SUB_STATUS.REMOVED,
date: Date.now(),
};
// 调用要测试的方法并期待异常 // 调用要测试的方法并期待异常
await expect(service.deleteSurveyMeta(survey)).rejects.toThrow( await expect(service.deleteSurveyMeta(survey)).rejects.toThrow(
@ -195,6 +206,10 @@ describe('SurveyMetaService', () => {
status: RECORD_STATUS.PUBLISHED, status: RECORD_STATUS.PUBLISHED,
date: expect.any(Number), date: expect.any(Number),
}, },
subStatus: {
status: RECORD_SUB_STATUS.DEFAULT,
date: expect.any(Number),
},
} as unknown as SurveyMeta; } as unknown as SurveyMeta;
jest.spyOn(surveyRepository, 'save').mockResolvedValue(savedSurveyMeta); jest.spyOn(surveyRepository, 'save').mockResolvedValue(savedSurveyMeta);

View File

@ -175,6 +175,25 @@ export class SurveyController {
}; };
} }
@HttpCode(200)
@Post('/pausingSurvey')
@UseGuards(SurveyGuard)
@SetMetadata('surveyId', 'body.surveyId')
@SetMetadata('surveyPermission', [SURVEY_PERMISSION.SURVEY_CONF_MANAGE])
@UseGuards(Authentication)
async pausingSurvey(@Request() req) {
const surveyMeta = req.surveyMeta;
await this.surveyMetaService.pausingSurveyMeta(surveyMeta);
await this.responseSchemaService.pausingResponseSchema({
surveyPath: surveyMeta.surveyPath,
});
return {
code: 200,
};
}
@Get('/getSurvey') @Get('/getSurvey')
@HttpCode(200) @HttpCode(200)
@UseGuards(SurveyGuard) @UseGuards(SurveyGuard)

View File

@ -130,8 +130,8 @@ export class SurveyMetaController {
item.surveyType = item.questionType || 'normal'; item.surveyType = item.questionType || 'normal';
} }
item.createDate = moment(item.createDate).format(fmt); item.createDate = moment(item.createDate).format(fmt);
item.updateDate = moment(item.updateDate).format(fmt);
item.curStatus.date = moment(item.curStatus.date).format(fmt); item.curStatus.date = moment(item.curStatus.date).format(fmt);
item.subStatus.date = moment(item.subStatus.date).format(fmt);
const surveyId = item._id.toString(); const surveyId = item._id.toString();
if (cooperSurveyIdMap[surveyId]) { if (cooperSurveyIdMap[surveyId]) {
item.isCollaborated = true; item.isCollaborated = true;

View File

@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { MongoRepository } from 'typeorm'; import { MongoRepository } from 'typeorm';
import { SurveyResponse } from 'src/models/surveyResponse.entity'; import { SurveyResponse } from 'src/models/surveyResponse.entity';
import { RECORD_SUB_STATUS } from 'src/enums';
import moment from 'moment'; import moment from 'moment';
import { keyBy } from 'lodash'; import { keyBy } from 'lodash';
@ -34,8 +35,8 @@ export class DataStatisticService {
const dataListMap = keyBy(dataList, 'field'); const dataListMap = keyBy(dataList, 'field');
const where = { const where = {
pageId: surveyId, pageId: surveyId,
'curStatus.status': { 'subStatus.status': {
$ne: 'removed', $ne: RECORD_SUB_STATUS.REMOVED,
}, },
}; };
const [surveyResponseList, total] = const [surveyResponseList, total] =
@ -124,8 +125,8 @@ export class DataStatisticService {
{ {
$match: { $match: {
pageId: surveyId, pageId: surveyId,
'curStatus.status': { 'subStatus.status': {
$ne: 'removed', $ne: RECORD_SUB_STATUS.REMOVED,
}, },
}, },
}, },

View File

@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { MongoRepository, FindOptionsOrder } from 'typeorm'; import { MongoRepository, FindOptionsOrder } from 'typeorm';
import { SurveyMeta } from 'src/models/surveyMeta.entity'; import { SurveyMeta } from 'src/models/surveyMeta.entity';
import { RECORD_STATUS } from 'src/enums'; import { RECORD_STATUS, RECORD_SUB_STATUS } from 'src/enums';
import { ObjectId } from 'mongodb'; import { ObjectId } from 'mongodb';
import { HttpException } from 'src/exceptions/httpException'; import { HttpException } from 'src/exceptions/httpException';
import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; import { EXCEPTION_CODE } from 'src/enums/exceptionCode';
@ -75,33 +75,58 @@ export class SurveyMetaService {
return await this.surveyRepository.save(newSurvey); return await this.surveyRepository.save(newSurvey);
} }
async pausingSurveyMeta(survey: SurveyMeta) {
if (
survey.curStatus.status !== RECORD_STATUS.PUBLISHED ||
(survey?.subStatus?.status &&
survey?.subStatus?.status != RECORD_SUB_STATUS.EDITING)
) {
throw new HttpException(
'问卷不能暂停',
EXCEPTION_CODE.SURVEY_STATUS_TRANSFORM_ERROR,
);
}
const subCurStatus = {
status: RECORD_SUB_STATUS.PAUSING,
date: Date.now(),
};
survey.subStatus = subCurStatus;
survey.curStatus.status = RECORD_STATUS.PUBLISHED;
if (Array.isArray(survey.statusList)) {
survey.statusList.push(subCurStatus);
} else {
survey.statusList = [subCurStatus];
}
return this.surveyRepository.save(survey);
}
async editSurveyMeta(survey: SurveyMeta) { async editSurveyMeta(survey: SurveyMeta) {
if ( if (
survey.curStatus.status !== RECORD_STATUS.NEW && survey.curStatus.status !== RECORD_STATUS.NEW &&
survey.curStatus.status !== RECORD_STATUS.EDITING survey.subStatus.status !== RECORD_SUB_STATUS.EDITING
) { ) {
const newStatus = { const newStatus = {
status: RECORD_STATUS.EDITING, status: RECORD_SUB_STATUS.EDITING,
date: Date.now(), date: Date.now(),
}; };
survey.curStatus = newStatus; survey.subStatus = newStatus;
survey.statusList.push(newStatus); survey.statusList.push(newStatus);
} }
return this.surveyRepository.save(survey); return this.surveyRepository.save(survey);
} }
async deleteSurveyMeta(survey: SurveyMeta) { async deleteSurveyMeta(survey: SurveyMeta) {
if (survey.curStatus.status === RECORD_STATUS.REMOVED) { if (survey.subStatus.status === RECORD_SUB_STATUS.REMOVED) {
throw new HttpException( throw new HttpException(
'问卷已删除,不能重复删除', '问卷已删除,不能重复删除',
EXCEPTION_CODE.SURVEY_STATUS_TRANSFORM_ERROR, EXCEPTION_CODE.SURVEY_STATUS_TRANSFORM_ERROR,
); );
} }
const newStatusInfo = { const newStatusInfo = {
status: RECORD_STATUS.REMOVED, status: RECORD_SUB_STATUS.REMOVED,
date: Date.now(), date: Date.now(),
}; };
survey.curStatus = newStatusInfo; survey.subStatus = newStatusInfo;
if (Array.isArray(survey.statusList)) { if (Array.isArray(survey.statusList)) {
survey.statusList.push(newStatusInfo); survey.statusList.push(newStatusInfo);
} else { } else {
@ -127,12 +152,15 @@ export class SurveyMetaService {
const query: Record<string, any> = Object.assign( const query: Record<string, any> = Object.assign(
{}, {},
{ {
'curStatus.status': { 'subStatus.status': {
$ne: 'removed', $ne: RECORD_SUB_STATUS.REMOVED,
}, },
}, },
condition.filter, condition.filter,
); );
if (condition.filter['curStatus.status']) {
query['subStatus.status'] = RECORD_SUB_STATUS.DEFAULT;
}
if (workspaceId) { if (workspaceId) {
query.workspaceId = workspaceId; query.workspaceId = workspaceId;
} else { } else {
@ -162,12 +190,23 @@ export class SurveyMetaService {
: ({ : ({
createDate: -1, createDate: -1,
} as FindOptionsOrder<SurveyMeta>); } as FindOptionsOrder<SurveyMeta>);
const [data, count] = await this.surveyRepository.findAndCount({ const [data, count] = await this.surveyRepository.findAndCount({
where: query, where: query,
skip, skip,
take: pageSize, take: pageSize,
order, order,
select: [
'_id',
'title',
'remark',
'surveyType',
'curStatus',
'subStatus',
'createDate',
'owner',
'ownerId',
'workspaceId',
],
}); });
return { data, count }; return { data, count };
} catch (error) { } catch (error) {
@ -181,6 +220,10 @@ export class SurveyMetaService {
date: Date.now(), date: Date.now(),
}; };
surveyMeta.curStatus = curStatus; surveyMeta.curStatus = curStatus;
surveyMeta.subStatus = {
status: RECORD_SUB_STATUS.DEFAULT,
date: Date.now(),
};
if (Array.isArray(surveyMeta.statusList)) { if (Array.isArray(surveyMeta.statusList)) {
surveyMeta.statusList.push(curStatus); surveyMeta.statusList.push(curStatus);
} else { } else {
@ -192,8 +235,8 @@ export class SurveyMetaService {
async countSurveyMetaByWorkspaceId({ workspaceId }) { async countSurveyMetaByWorkspaceId({ workspaceId }) {
const total = await this.surveyRepository.count({ const total = await this.surveyRepository.count({
workspaceId, workspaceId,
'curStatus.status': { 'subStatus.status': {
$ne: RECORD_STATUS.REMOVED, $ne: RECORD_SUB_STATUS.REMOVED,
}, },
}); });
return total; return total;

View File

@ -3,7 +3,7 @@ import { MongoRepository } from 'typeorm';
import { ClientEncryptService } from '../services/clientEncrypt.service'; import { ClientEncryptService } from '../services/clientEncrypt.service';
import { ClientEncrypt } from 'src/models/clientEncrypt.entity'; import { ClientEncrypt } from 'src/models/clientEncrypt.entity';
import { ENCRYPT_TYPE } from 'src/enums/encrypt'; import { ENCRYPT_TYPE } from 'src/enums/encrypt';
import { RECORD_STATUS } from 'src/enums'; import { RECORD_SUB_STATUS } from 'src/enums';
import { ObjectId } from 'mongodb'; import { ObjectId } from 'mongodb';
import { getRepositoryToken } from '@nestjs/typeorm'; import { getRepositoryToken } from '@nestjs/typeorm';
@ -88,8 +88,8 @@ describe('ClientEncryptService', () => {
expect(repository.findOne).toHaveBeenCalledWith({ expect(repository.findOne).toHaveBeenCalledWith({
where: { where: {
_id: new ObjectId(id), _id: new ObjectId(id),
'curStatus.status': { 'subStatus.status': {
$ne: RECORD_STATUS.REMOVED, $ne: RECORD_SUB_STATUS.REMOVED,
}, },
}, },
}); });

View File

@ -1,5 +1,5 @@
import { ResponseSchema } from 'src/models/responseSchema.entity'; import { ResponseSchema } from 'src/models/responseSchema.entity';
import { RECORD_STATUS } from 'src/enums'; import { RECORD_STATUS, RECORD_SUB_STATUS } from 'src/enums';
import { ObjectId } from 'mongodb'; import { ObjectId } from 'mongodb';
export const mockResponseSchema: ResponseSchema = { export const mockResponseSchema: ResponseSchema = {
@ -8,6 +8,10 @@ export const mockResponseSchema: ResponseSchema = {
status: RECORD_STATUS.PUBLISHED, status: RECORD_STATUS.PUBLISHED,
date: 1710399368439, date: 1710399368439,
}, },
subStatus: {
status: RECORD_SUB_STATUS.DEFAULT,
date: 1710399368439,
},
statusList: [ statusList: [
{ {
status: RECORD_STATUS.PUBLISHED, status: RECORD_STATUS.PUBLISHED,

View File

@ -3,7 +3,7 @@ import { ResponseSchemaController } from '../controllers/responseSchema.controll
import { ResponseSchemaService } from '../services/responseScheme.service'; import { ResponseSchemaService } from '../services/responseScheme.service';
import { HttpException } from 'src/exceptions/httpException'; import { HttpException } from 'src/exceptions/httpException';
import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; import { EXCEPTION_CODE } from 'src/enums/exceptionCode';
import { RECORD_STATUS } from 'src/enums'; import { RECORD_STATUS, RECORD_SUB_STATUS } from 'src/enums';
import { ResponseSchema } from 'src/models/responseSchema.entity'; import { ResponseSchema } from 'src/models/responseSchema.entity';
import { Logger } from 'src/logger'; import { Logger } from 'src/logger';
@ -69,6 +69,7 @@ describe('ResponseSchemaController', () => {
const mockResponseSchema = { const mockResponseSchema = {
surveyPath: 'testSurveyPath', surveyPath: 'testSurveyPath',
curStatus: { status: RECORD_STATUS.PUBLISHED, date: Date.now() }, curStatus: { status: RECORD_STATUS.PUBLISHED, date: Date.now() },
subStatus: { status: RECORD_SUB_STATUS.DEFAULT, date: Date.now() },
} as ResponseSchema; } as ResponseSchema;
jest jest
@ -97,7 +98,7 @@ describe('ResponseSchemaController', () => {
jest jest
.spyOn(responseSchemaService, 'getResponseSchemaByPath') .spyOn(responseSchemaService, 'getResponseSchemaByPath')
.mockResolvedValue({ .mockResolvedValue({
curStatus: { status: RECORD_STATUS.REMOVED }, subStatus: { status: RECORD_SUB_STATUS.REMOVED },
} as ResponseSchema); } as ResponseSchema);
await expect(controller.getSchema(mockQueryInfo)).rejects.toThrow( await expect(controller.getSchema(mockQueryInfo)).rejects.toThrow(
@ -125,6 +126,9 @@ describe('ResponseSchemaController', () => {
curStatus: { curStatus: {
status: 'published', status: 'published',
}, },
subStatus: {
status: '',
},
code: { code: {
baseConf: { baseConf: {
passwordSwitch: true, passwordSwitch: true,
@ -149,6 +153,9 @@ describe('ResponseSchemaController', () => {
curStatus: { curStatus: {
status: 'published', status: 'published',
}, },
subStatus: {
status: '',
},
code: { code: {
baseConf: { baseConf: {
passwordSwitch: true, passwordSwitch: true,
@ -172,6 +179,9 @@ describe('ResponseSchemaController', () => {
curStatus: { curStatus: {
status: 'published', status: 'published',
}, },
subStatus: {
status: '',
},
code: { code: {
baseConf: { baseConf: {
passwordSwitch: true, passwordSwitch: true,
@ -200,6 +210,9 @@ describe('ResponseSchemaController', () => {
curStatus: { curStatus: {
status: 'published', status: 'published',
}, },
subStatus: {
status: '',
},
code: { code: {
baseConf: { baseConf: {
passwordSwitch: true, passwordSwitch: true,
@ -228,6 +241,9 @@ describe('ResponseSchemaController', () => {
curStatus: { curStatus: {
status: 'published', status: 'published',
}, },
subStatus: {
status: '',
},
code: { code: {
baseConf: { baseConf: {
passwordSwitch: true, passwordSwitch: true,

View File

@ -18,7 +18,7 @@ import { HttpException } from 'src/exceptions/httpException';
import { SurveyNotFoundException } from 'src/exceptions/surveyNotFoundException'; import { SurveyNotFoundException } from 'src/exceptions/surveyNotFoundException';
import { ResponseSecurityPlugin } from 'src/securityPlugin/responseSecurityPlugin'; import { ResponseSecurityPlugin } from 'src/securityPlugin/responseSecurityPlugin';
import { RECORD_STATUS } from 'src/enums'; import { RECORD_STATUS, RECORD_SUB_STATUS } from 'src/enums';
import { SurveyResponse } from 'src/models/surveyResponse.entity'; import { SurveyResponse } from 'src/models/surveyResponse.entity';
import { Logger } from 'src/logger'; import { Logger } from 'src/logger';
import { ResponseSchema } from 'src/models/responseSchema.entity'; import { ResponseSchema } from 'src/models/responseSchema.entity';
@ -335,6 +335,9 @@ describe('SurveyResponseController', () => {
curStatus: { curStatus: {
status: RECORD_STATUS.PUBLISHED, status: RECORD_STATUS.PUBLISHED,
}, },
subStatus: {
status: RECORD_SUB_STATUS.DEFAULT,
},
code: { code: {
baseConf: { baseConf: {
passwordSwitch: true, passwordSwitch: true,

View File

@ -77,7 +77,7 @@ describe('SurveyResponseService', () => {
expect(surveyResponseRepository.count).toHaveBeenCalledWith({ expect(surveyResponseRepository.count).toHaveBeenCalledWith({
where: { where: {
surveyPath, surveyPath,
'curStatus.status': { $ne: 'removed' }, 'subStatus.status': { $ne: 'removed' },
}, },
}); });
}); });

View File

@ -10,7 +10,7 @@ import {
import { ResponseSchemaService } from '../services/responseScheme.service'; import { ResponseSchemaService } from '../services/responseScheme.service';
import { HttpException } from 'src/exceptions/httpException'; import { HttpException } from 'src/exceptions/httpException';
import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; import { EXCEPTION_CODE } from 'src/enums/exceptionCode';
import { RECORD_STATUS } from 'src/enums'; import { RECORD_SUB_STATUS } from 'src/enums';
import { ApiTags } from '@nestjs/swagger'; import { ApiTags } from '@nestjs/swagger';
import Joi from 'joi'; import Joi from 'joi';
import { Logger } from 'src/logger'; import { Logger } from 'src/logger';
@ -46,7 +46,7 @@ export class ResponseSchemaController {
); );
if ( if (
!responseSchema || !responseSchema ||
responseSchema.curStatus.status === RECORD_STATUS.REMOVED responseSchema.subStatus.status === RECORD_SUB_STATUS.REMOVED
) { ) {
throw new HttpException( throw new HttpException(
'问卷已删除', '问卷已删除',
@ -82,7 +82,7 @@ export class ResponseSchemaController {
// 问卷信息 // 问卷信息
const schema = const schema =
await this.responseSchemaService.getResponseSchemaByPath(surveyPath); await this.responseSchemaService.getResponseSchemaByPath(surveyPath);
if (!schema || schema.curStatus.status === 'removed') { if (!schema || schema.subStatus.status === RECORD_SUB_STATUS.REMOVED) {
throw new SurveyNotFoundException('该问卷不存在,无法提交'); throw new SurveyNotFoundException('该问卷不存在,无法提交');
} }

View File

@ -5,6 +5,7 @@ import { checkSign } from 'src/utils/checkSign';
import { ENCRYPT_TYPE } from 'src/enums/encrypt'; import { ENCRYPT_TYPE } from 'src/enums/encrypt';
import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; import { EXCEPTION_CODE } from 'src/enums/exceptionCode';
import { getPushingData } from 'src/utils/messagePushing'; import { getPushingData } from 'src/utils/messagePushing';
import { RECORD_SUB_STATUS } from 'src/enums';
import { ResponseSchemaService } from '../services/responseScheme.service'; import { ResponseSchemaService } from '../services/responseScheme.service';
import { CounterService } from '../services/counter.service'; import { CounterService } from '../services/counter.service';
@ -73,9 +74,18 @@ export class SurveyResponseController {
// 查询schema // 查询schema
const responseSchema = const responseSchema =
await this.responseSchemaService.getResponseSchemaByPath(surveyPath); await this.responseSchemaService.getResponseSchemaByPath(surveyPath);
if (!responseSchema || responseSchema.curStatus.status === 'removed') { if (
!responseSchema ||
responseSchema.subStatus.status === RECORD_SUB_STATUS.REMOVED
) {
throw new SurveyNotFoundException('该问卷不存在,无法提交'); throw new SurveyNotFoundException('该问卷不存在,无法提交');
} }
if (responseSchema?.subStatus?.status === RECORD_SUB_STATUS.PAUSING) {
throw new HttpException(
'该问卷已暂停,无法提交',
EXCEPTION_CODE.RESPONSE_PAUSING,
);
}
// 白名单的verifyId校验 // 白名单的verifyId校验
const baseConf = responseSchema.code.baseConf; const baseConf = responseSchema.code.baseConf;

View File

@ -4,7 +4,7 @@ import { MongoRepository } from 'typeorm';
import { ClientEncrypt } from 'src/models/clientEncrypt.entity'; import { ClientEncrypt } from 'src/models/clientEncrypt.entity';
import { ENCRYPT_TYPE } from 'src/enums/encrypt'; import { ENCRYPT_TYPE } from 'src/enums/encrypt';
import { ObjectId } from 'mongodb'; import { ObjectId } from 'mongodb';
import { RECORD_STATUS } from 'src/enums'; import { RECORD_SUB_STATUS } from 'src/enums';
@Injectable() @Injectable()
export class ClientEncryptService { export class ClientEncryptService {
@ -38,8 +38,8 @@ export class ClientEncryptService {
return this.clientEncryptRepository.findOne({ return this.clientEncryptRepository.findOne({
where: { where: {
_id: new ObjectId(id), _id: new ObjectId(id),
'curStatus.status': { 'subStatus.status': {
$ne: RECORD_STATUS.REMOVED, $ne: RECORD_SUB_STATUS.REMOVED,
}, },
}, },
}); });
@ -52,8 +52,8 @@ export class ClientEncryptService {
}, },
{ {
$set: { $set: {
curStatus: { subStatus: {
status: RECORD_STATUS.REMOVED, status: RECORD_SUB_STATUS.REMOVED,
date: Date.now(), date: Date.now(),
}, },
}, },

View File

@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { MongoRepository } from 'typeorm'; import { MongoRepository } from 'typeorm';
import { ResponseSchema } from 'src/models/responseSchema.entity'; import { ResponseSchema } from 'src/models/responseSchema.entity';
import { RECORD_STATUS } from 'src/enums'; import { RECORD_STATUS, RECORD_SUB_STATUS } from 'src/enums';
@Injectable() @Injectable()
export class ResponseSchemaService { export class ResponseSchemaService {
@ -23,12 +23,20 @@ export class ResponseSchemaService {
status: RECORD_STATUS.PUBLISHED, status: RECORD_STATUS.PUBLISHED,
date: Date.now(), date: Date.now(),
}; };
clientSurvey.subStatus = {
status: RECORD_SUB_STATUS.DEFAULT,
date: Date.now(),
};
return this.responseSchemaRepository.save(clientSurvey); return this.responseSchemaRepository.save(clientSurvey);
} else { } else {
const curStatus = { const curStatus = {
status: RECORD_STATUS.PUBLISHED, status: RECORD_STATUS.PUBLISHED,
date: Date.now(), date: Date.now(),
}; };
const subStatus = {
status: RECORD_SUB_STATUS.DEFAULT,
date: Date.now(),
};
const newClientSurvey = this.responseSchemaRepository.create({ const newClientSurvey = this.responseSchemaRepository.create({
title, title,
surveyPath, surveyPath,
@ -36,6 +44,7 @@ export class ResponseSchemaService {
pageId, pageId,
curStatus, curStatus,
statusList: [curStatus], statusList: [curStatus],
subStatus,
}); });
return this.responseSchemaRepository.save(newClientSurvey); return this.responseSchemaRepository.save(newClientSurvey);
} }
@ -53,6 +62,26 @@ export class ResponseSchemaService {
}); });
} }
async pausingResponseSchema({ surveyPath }) {
const responseSchema = await this.responseSchemaRepository.findOne({
where: { surveyPath },
});
if (responseSchema) {
const subStatus = {
status: RECORD_SUB_STATUS.PAUSING,
date: Date.now(),
};
responseSchema.subStatus = subStatus;
responseSchema.curStatus.status = RECORD_STATUS.PUBLISHED;
if (Array.isArray(responseSchema.statusList)) {
responseSchema.statusList.push(subStatus);
} else {
responseSchema.statusList = [subStatus];
}
return this.responseSchemaRepository.save(responseSchema);
}
}
async deleteResponseSchema({ surveyPath }) { async deleteResponseSchema({ surveyPath }) {
const responseSchema = await this.responseSchemaRepository.findOne({ const responseSchema = await this.responseSchemaRepository.findOne({
where: { surveyPath }, where: { surveyPath },

View File

@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { MongoRepository } from 'typeorm'; import { MongoRepository } from 'typeorm';
import { SurveyResponse } from 'src/models/surveyResponse.entity'; import { SurveyResponse } from 'src/models/surveyResponse.entity';
import { RECORD_SUB_STATUS } from 'src/enums';
@Injectable() @Injectable()
export class SurveyResponseService { export class SurveyResponseService {
constructor( constructor(
@ -38,8 +39,8 @@ export class SurveyResponseService {
const count = await this.surveyResponseRepository.count({ const count = await this.surveyResponseRepository.count({
where: { where: {
surveyPath, surveyPath,
'curStatus.status': { 'subStatus.status': {
$ne: 'removed', $ne: RECORD_SUB_STATUS.REMOVED,
}, },
}, },
}); });

View File

@ -0,0 +1,18 @@
import { Controller, Get, HttpCode } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { UpgradeService } from '../services/upgrade.service';
@ApiTags('survey')
@Controller('/api/upgrade')
export class UpgradeController {
constructor(private readonly upgradeService: UpgradeService) {}
@Get('/subStatus')
@HttpCode(200)
async upgradeSubStatus() {
await this.upgradeService.upgradeSubStatus();
return {
code: 200,
};
}
}

View File

@ -0,0 +1,74 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { MongoRepository } from 'typeorm';
import { SurveyMeta } from 'src/models/surveyMeta.entity';
import { ResponseSchema } from 'src/models/ResponseSchema.entity';
import { RECORD_STATUS, RECORD_SUB_STATUS } from 'src/enums';
@Injectable()
export class UpgradeService {
constructor(
@InjectRepository(SurveyMeta)
private readonly SurveyMeta: MongoRepository<SurveyMeta>,
@InjectRepository(ResponseSchema)
private readonly ResponseSchema: MongoRepository<ResponseSchema>,
) {}
async upgradeSubStatus() {
const surveyMetaList = await this.SurveyMeta.find();
const responseSchemaList = await this.ResponseSchema.find();
const callBack = (v: SurveyMeta | ResponseSchema) => {
// 将主状态的REMOVEDEDITING刷到子状态
// 主状态查一下历史数据删除前最近的状态是“新建”or“已发布
if (
v.curStatus.status == (RECORD_SUB_STATUS.REMOVED as any) ||
v.curStatus.status == (RECORD_SUB_STATUS.EDITING as any)
) {
const subStatus = {
status: v.curStatus.status,
date: v.curStatus.date,
};
v.subStatus = subStatus as any;
console.log('subStatus', subStatus);
if (v.curStatus.status == (RECORD_SUB_STATUS.EDITING as any)) {
v.curStatus.status = RECORD_STATUS.PUBLISHED;
}
if (v.curStatus.status == (RECORD_SUB_STATUS.REMOVED as any)) {
for (let index = v.statusList.length; index > 0; index--) {
const item = v.statusList[index];
if (
item?.status == RECORD_STATUS.PUBLISHED ||
item?.status == RECORD_STATUS.NEW
) {
v.curStatus.status = item.status;
break;
}
}
}
return v;
}
if (
v.curStatus.status == RECORD_STATUS.PUBLISHED ||
v.curStatus.status == RECORD_STATUS.NEW
) {
const subStatus = {
status: RECORD_SUB_STATUS.DEFAULT,
date: v.statusList[0].date,
};
v.subStatus = subStatus;
}
return v;
};
surveyMetaList.map(async (v) => {
const item = callBack(v);
await this.SurveyMeta.save(item);
});
responseSchemaList.map(async (v) => {
const item = callBack(v);
await this.ResponseSchema.save(item);
});
}
}

View File

@ -0,0 +1,39 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule } from '@nestjs/config';
import { MessageModule } from '../message/message.module';
import { UpgradeService } from './services/upgrade.service';
import { ResponseSchema } from 'src/models/responseSchema.entity';
import { Counter } from 'src/models/counter.entity';
import { SurveyResponse } from 'src/models/surveyResponse.entity';
import { ClientEncrypt } from 'src/models/clientEncrypt.entity';
import { Logger } from 'src/logger';
import { SurveyMeta } from 'src/models/surveyMeta.entity';
import { UpgradeController } from './controllers/upgrade.controller';
import { AuthModule } from '../auth/auth.module';
import { WorkspaceModule } from '../workspace/workspace.module';
@Module({
imports: [
TypeOrmModule.forFeature([
ResponseSchema,
Counter,
SurveyResponse,
ClientEncrypt,
SurveyMeta,
]),
ConfigModule,
MessageModule,
AuthModule,
WorkspaceModule,
],
controllers: [UpgradeController],
providers: [UpgradeService, Logger],
exports: [UpgradeService],
})
export class UpgradeModule {}

View File

@ -242,11 +242,15 @@ describe('WorkspaceController', () => {
describe('getWorkspaceAndMember', () => { describe('getWorkspaceAndMember', () => {
it('should return a list of workspaces and members for the user', async () => { it('should return a list of workspaces and members for the user', async () => {
const req = { user: { _id: new ObjectId() } };
const workspaceId = new ObjectId();
const userId = new ObjectId(); const userId = new ObjectId();
const memberList = [{ workspaceId, userId: userId }]; jest.spyOn(userService, 'getUserListByIds').mockResolvedValue([
{
_id: userId,
},
] as Array<User>);
const req = { user: { _id: userId } };
const workspaceId = new ObjectId();
const memberList = [{ workspaceId, userId }];
const workspaces = [{ _id: workspaceId, name: 'Test Workspace' }]; const workspaces = [{ _id: workspaceId, name: 'Test Workspace' }];
const userList = [{ _id: userId, username: 'Test User' }]; const userList = [{ _id: userId, username: 'Test User' }];

View File

@ -6,7 +6,7 @@ import { Workspace } from 'src/models/workspace.entity';
import { SurveyMeta } from 'src/models/surveyMeta.entity'; import { SurveyMeta } from 'src/models/surveyMeta.entity';
import { ObjectId } from 'mongodb'; import { ObjectId } from 'mongodb';
import { RECORD_STATUS } from 'src/enums'; import { RECORD_SUB_STATUS } from 'src/enums';
interface FindAllByIdWithPaginationParams { interface FindAllByIdWithPaginationParams {
workspaceIdList: string[]; workspaceIdList: string[];
@ -56,8 +56,8 @@ export class WorkspaceService {
_id: { _id: {
$in: workspaceIdList.map((item) => new ObjectId(item)), $in: workspaceIdList.map((item) => new ObjectId(item)),
}, },
'curStatus.status': { 'subStatus.status': {
$ne: RECORD_STATUS.REMOVED, $ne: RECORD_SUB_STATUS.REMOVED,
}, },
}; };
@ -91,8 +91,8 @@ export class WorkspaceService {
_id: { _id: {
$in: workspaceIdList.map((m) => new ObjectId(m)), $in: workspaceIdList.map((m) => new ObjectId(m)),
}, },
'curStatus.status': { 'subStatus.status': {
$ne: RECORD_STATUS.REMOVED, $ne: RECORD_SUB_STATUS.REMOVED,
}, },
}; };
if (name) { if (name) {
@ -115,7 +115,7 @@ export class WorkspaceService {
async delete(id: string) { async delete(id: string) {
const newStatus = { const newStatus = {
status: RECORD_STATUS.REMOVED, status: RECORD_SUB_STATUS.REMOVED,
date: Date.now(), date: Date.now(),
}; };
const workspaceRes = await this.workspaceRepository.updateOne( const workspaceRes = await this.workspaceRepository.updateOne(
@ -124,7 +124,7 @@ export class WorkspaceService {
}, },
{ {
$set: { $set: {
curStatus: newStatus, subStatus: newStatus,
}, },
$push: { $push: {
statusList: newStatus as never, statusList: newStatus as never,
@ -137,7 +137,7 @@ export class WorkspaceService {
}, },
{ {
$set: { $set: {
curStatus: newStatus, subStatus: newStatus,
}, },
$push: { $push: {
statusList: newStatus as never, statusList: newStatus as never,
@ -155,8 +155,8 @@ export class WorkspaceService {
return await this.workspaceRepository.find({ return await this.workspaceRepository.find({
where: { where: {
ownerId: userId, ownerId: userId,
'curStatus.status': { 'subStatus.status': {
$ne: RECORD_STATUS.REMOVED, $ne: RECORD_SUB_STATUS.REMOVED,
}, },
}, },
order: { order: {

View File

@ -3,7 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
import { MongoRepository } from 'typeorm'; import { MongoRepository } from 'typeorm';
import { WorkspaceMember } from 'src/models/workspaceMember.entity'; import { WorkspaceMember } from 'src/models/workspaceMember.entity';
import { ObjectId } from 'mongodb'; import { ObjectId } from 'mongodb';
import { RECORD_STATUS } from 'src/enums'; import { RECORD_SUB_STATUS } from 'src/enums';
@Injectable() @Injectable()
export class WorkspaceMemberService { export class WorkspaceMemberService {
@ -94,8 +94,8 @@ export class WorkspaceMemberService {
return this.workspaceMemberRepository.find({ return this.workspaceMemberRepository.find({
where: { where: {
workspaceId, workspaceId,
'curStatus.status': { 'subStatus.status': {
$ne: RECORD_STATUS.REMOVED, $ne: RECORD_SUB_STATUS.REMOVED,
}, },
}, },
select: ['_id', 'createDate', 'curStatus', 'role', 'userId'], select: ['_id', 'createDate', 'curStatus', 'role', 'userId'],
@ -135,8 +135,8 @@ export class WorkspaceMemberService {
async countByWorkspaceId({ workspaceId }) { async countByWorkspaceId({ workspaceId }) {
return this.workspaceMemberRepository.count({ return this.workspaceMemberRepository.count({
workspaceId, workspaceId,
'curStatus.status': { 'subStatus.status': {
$ne: RECORD_STATUS.REMOVED, $ne: RECORD_SUB_STATUS.REMOVED,
}, },
}); });
} }

View File

@ -20,6 +20,7 @@ export function getFilter(filterList: Array<FilterItem>) {
'remark', 'remark',
'surveyType', 'surveyType',
'curStatus.status', 'curStatus.status',
'subStatus.status',
]; ];
return filterList.reduce( return filterList.reduce(
(preItem, curItem) => { (preItem, curItem) => {

View File

@ -52,3 +52,13 @@ export const deleteSurvey = (surveyId) => {
export const updateSurvey = (data) => { export const updateSurvey = (data) => {
return axios.post('/survey/updateMeta', data) return axios.post('/survey/updateMeta', data)
} }
export const pausingSurvey= (surveyId) => {
return axios.post('/survey/pausingSurvey', {
surveyId
})
}
export const upgradeSubStatus = () => {
return axios.get('/upgrade/subStatus')
}

View File

@ -66,7 +66,7 @@ export const fieldConfig = {
}, },
updateDate: { updateDate: {
title: '更新时间', title: '更新时间',
key: 'curStatus.date', key: 'subStatus.date',
minWidth: 200 minWidth: 200
}, },
createDate: { createDate: {
@ -98,13 +98,36 @@ export const noSearchDataConfig = {
img: '/imgs/icons/list-empty.webp' img: '/imgs/icons/list-empty.webp'
} }
export const statusMaps = { export const curStatus = {
new: '未发布', new: {
editing: '修改中', value: 'new',
published: '已发布', label: '未发布'
removed: '', },
pausing: '' published: {
value: 'published',
label: '已发布'
} }
}
// 子状态
export const subStatus = {
editing: {
label: '修改中',
value: 'editing'
},
pausing: {
label: '暂停中',
value: 'pausing'
}
}
export const statusMaps = {
...Object.fromEntries(Object.keys(curStatus).map(key => ([key, curStatus[key].label]))),
...Object.fromEntries(Object.keys(subStatus).map(key => ([key, subStatus[key].label])))
}
export const curStatusKey = 'curStatus.status';
export const subStatusKey = 'subStatus.status';
// 问卷类型 // 问卷类型
export const surveyTypeSelect = { export const surveyTypeSelect = {
@ -146,29 +169,21 @@ export const curStatusSelect = {
value: '', value: '',
label: '全部状态' label: '全部状态'
}, },
{ curStatus.new,
value: 'new', curStatus.published,
label: '未发布' subStatus.editing,
}, subStatus.pausing
{
value: 'published',
label: '已发布'
},
{
value: 'editing',
label: '修改中'
}
], ],
default: '' default: ''
} }
export const selectOptionsDict = Object.freeze({ export const selectOptionsDict = Object.freeze({
surveyType: surveyTypeSelect, surveyType: surveyTypeSelect,
'curStatus.status': curStatusSelect status: curStatusSelect,
}) })
export const buttonOptionsDict = Object.freeze({ export const buttonOptionsDict = Object.freeze({
'curStatus.date': { 'subStatus.date': {
label: '更新时间', label: '更新时间',
icons: [ icons: [
{ {

View File

@ -42,6 +42,7 @@
@row-click="onRowClick" @row-click="onRowClick"
> >
<el-table-column column-key="space" width="20" /> <el-table-column column-key="space" width="20" />
<el-table-column <el-table-column
v-for="field in fieldList" v-for="field in fieldList"
:key="field.key" :key="field.key"
@ -123,7 +124,7 @@ import EmptyIndex from '@/management/components/EmptyIndex.vue'
import CooperModify from '@/management/components/CooperModify/ModifyDialog.vue' import CooperModify from '@/management/components/CooperModify/ModifyDialog.vue'
import { CODE_MAP } from '@/management/api/base' import { CODE_MAP } from '@/management/api/base'
import { QOP_MAP } from '@/management/utils/constant.ts' import { QOP_MAP } from '@/management/utils/constant.ts'
import { deleteSurvey } from '@/management/api/survey' import { deleteSurvey,pausingSurvey } from '@/management/api/survey'
import { useWorkSpaceStore } from '@/management/stores/workSpace' import { useWorkSpaceStore } from '@/management/stores/workSpace'
import { useSurveyListStore } from '@/management/stores/surveyList' import { useSurveyListStore } from '@/management/stores/surveyList'
import ModifyDialog from './ModifyDialog.vue' import ModifyDialog from './ModifyDialog.vue'
@ -140,7 +141,9 @@ import {
noListDataConfig, noListDataConfig,
noSearchDataConfig, noSearchDataConfig,
selectOptionsDict, selectOptionsDict,
buttonOptionsDict buttonOptionsDict,
curStatus,
subStatus
} from '@/management/config/listConfig' } from '@/management/config/listConfig'
const surveyListStore = useSurveyListStore() const surveyListStore = useSurveyListStore()
@ -197,42 +200,12 @@ const dataList = computed(() => {
return data.value.map((item) => { return data.value.map((item) => {
return { return {
...item, ...item,
'curStatus.date': item.curStatus.date 'curStatus.date': item.curStatus.date,
'subStatus.date': item.subStatus.date
} }
}) })
}) })
const filter = computed(() => {
return [
{
comparator: '',
condition: [
{
field: 'title',
value: searchVal.value,
comparator: '$regex'
}
]
},
{
comparator: '',
condition: [
{
field: 'curStatus.status',
value: selectValueMap.value['curStatus.status']
}
]
},
{
comparator: '',
condition: [
{
field: 'surveyType',
value: selectValueMap.value.surveyType
}
]
}
]
})
const order = computed(() => { const order = computed(() => {
const formatOrder = Object.entries(buttonValueMap.value) const formatOrder = Object.entries(buttonValueMap.value)
.filter(([, effectValue]) => effectValue) .filter(([, effectValue]) => effectValue)
@ -245,14 +218,8 @@ const order = computed(() => {
}) })
const onRefresh = async () => { const onRefresh = async () => {
const filterString = JSON.stringify(
filter.value.filter((item) => {
return item.condition[0].value
})
)
let params = { let params = {
curPage: currentPage.value, curPage: currentPage.value,
filter: filterString,
order: order.value order: order.value
} }
if (workSpaceId.value) { if (workSpaceId.value) {
@ -286,6 +253,10 @@ const getToolConfig = (row) => {
key: 'release', key: 'release',
label: '投放' label: '投放'
}, },
{
key: subStatus.pausing.value,
label: '暂停'
},
{ {
key: 'cooper', key: 'cooper',
label: '协作' label: '协作'
@ -306,6 +277,10 @@ const getToolConfig = (row) => {
if (row.currentPermissions.includes(SurveyPermissions.SurveyManage)) { if (row.currentPermissions.includes(SurveyPermissions.SurveyManage)) {
// //
funcList.push( funcList.push(
{
key: subStatus.pausing.value,
label: '暂停'
},
{ {
key: QOP_MAP.EDIT, key: QOP_MAP.EDIT,
label: '修改' label: '修改'
@ -339,7 +314,12 @@ const getToolConfig = (row) => {
permissionsBtn.splice(-1) permissionsBtn.splice(-1)
funcList = permissionsBtn funcList = permissionsBtn
} }
const order = ['edit', 'analysis', 'release', 'delete', 'copy', 'cooper'] const order = ['edit', 'analysis', 'release', 'pausing', 'delete', 'copy', 'cooper']
if((row.curStatus.status !== curStatus.published.value && row.curStatus.status !== subStatus.editing.value) || (row.subStatus.status &&
row.subStatus.status != subStatus.editing.value)){
order.splice(3, 1)
funcList = funcList.filter(item => item.key !== subStatus.pausing.value)
}
const result = funcList.sort((a, b) => order.indexOf(a.key) - order.indexOf(b.key)) const result = funcList.sort((a, b) => order.indexOf(a.key) - order.indexOf(b.key))
return result return result
@ -374,6 +354,9 @@ const handleClick = (key, data) => {
case 'cooper': case 'cooper':
onCooper(data) onCooper(data)
return return
case 'pausing':
onPausing(data)
return
default: default:
return return
} }
@ -398,6 +381,26 @@ const onDelete = async (row) => {
ElMessage.error(res.errmsg || '删除失败') ElMessage.error(res.errmsg || '删除失败')
} }
} }
const onPausing = async (row) => {
try {
await ElMessageBox.confirm('“暂停回收”后问卷将不能填写,是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
} catch (error) {
console.log('取消暂停')
return
}
const res = await pausingSurvey(row._id)
if (res.code === CODE_MAP.SUCCESS) {
ElMessage.success('暂停成功')
onRefresh()
} else {
ElMessage.error(res.errmsg || '暂停失败')
}
}
const handleCurrentChange = (current) => { const handleCurrentChange = (current) => {
currentPage.value = current currentPage.value = current
onRefresh() onRefresh()

View File

@ -1,23 +1,23 @@
<template> <template>
<div :class="['list-state', 'list-state-' + value.curStatus?.status]"> <div :class="['list-state', 'list-state-' + status]">
<span class="list-state-badge" /> <span class="list-state-badge" />
<span>{{ statusMaps[value.curStatus?.status] }}</span> <span>{{ statusMaps[status] }}</span>
</div> </div>
</template> </template>
<script> <script setup>
import { computed } from 'vue'
import { statusMaps } from '@/management/config/listConfig' import { statusMaps } from '@/management/config/listConfig'
export default { const props = defineProps({
name: 'StateModule', value: {
props: { type: Object,
value: Object default: () => ({})
},
data() {
return {
statusMaps
}
}
} }
})
const status = computed(() => {
const {curStatus,subStatus } = props.value;
return subStatus?.status || curStatus?.status;
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -30,6 +30,12 @@ export default {
background: $normal-color; background: $normal-color;
} }
} }
&-pausing{
color: #EB505C;
.list-state-badge {
background: #EB505C;
}
}
&-published { &-published {
color: #0dbd93; color: #0dbd93;
.list-state-badge { .list-state-badge {

View File

@ -9,15 +9,28 @@ import { getSurveyList as getSurveyListReq } from '@/management/api/survey'
import { useWorkSpaceStore } from './workSpace' import { useWorkSpaceStore } from './workSpace'
import {
curStatus,
subStatus,
curStatusKey,
subStatusKey
} from '@/management/config/listConfig'
const verdictStatus = (status:never) => {
if(curStatus[status]) return curStatusKey
if (subStatus[status]) return subStatusKey
return curStatusKey
}
function useSearchSurvey() { function useSearchSurvey() {
const searchVal = ref('') const searchVal = ref('')
const selectValueMap = ref<Record<string, any>>({ const selectValueMap = ref<Record<string, any>>({
surveyType: '', surveyType: '',
'curStatus.status': '' 'status': '',
}) })
const buttonValueMap = ref<Record<string, any>>({ const buttonValueMap = ref<Record<string, any>>({
'curStatus.date': '', 'subStatus.date': '',
createDate: -1 createDate: -1
}) })
@ -37,8 +50,8 @@ function useSearchSurvey() {
comparator: '', comparator: '',
condition: [ condition: [
{ {
field: 'curStatus.status', field: verdictStatus( selectValueMap.value['status'] as never),
value: selectValueMap.value['curStatus.status'] value: selectValueMap.value['status']
} }
] ]
}, },
@ -67,13 +80,13 @@ function useSearchSurvey() {
function resetSelectValueMap() { function resetSelectValueMap() {
selectValueMap.value = { selectValueMap.value = {
surveyType: '', surveyType: '',
'curStatus.status': '' 'status': ''
} }
} }
function resetButtonValueMap() { function resetButtonValueMap() {
buttonValueMap.value = { buttonValueMap.value = {
'curStatus.date': '', 'subStatus.date': '',
createDate: -1 createDate: -1
} }
} }

View File

@ -39,3 +39,4 @@ export const validate = ({ surveyPath, password, whitelist }) => {
whitelist whitelist
}) })
} }

View File

@ -64,6 +64,13 @@ watch(
} }
) )
const checkStatus = (data: any) => {
const alert = useCommandComponent(AlertDialog)
if (data?.subStatus?.status == 'pausing') {
alert({ title:'问卷已暂停回收' })
}
}
const getDetail = async (surveyPath: string) => { const getDetail = async (surveyPath: string) => {
const alert = useCommandComponent(AlertDialog) const alert = useCommandComponent(AlertDialog)
@ -73,6 +80,7 @@ const getDetail = async (surveyPath: string) => {
loadData(res, surveyPath) loadData(res, surveyPath)
} else { } else {
const res: any = await getPublishedSurveyInfo({ surveyPath }) const res: any = await getPublishedSurveyInfo({ surveyPath })
checkStatus(res.data)
loadData(res, surveyPath) loadData(res, surveyPath)
surveyStore.getEncryptInfo() surveyStore.getEncryptInfo()
} }