Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature]: 修改验收问题 #421

Merged
merged 1 commit into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions server/.env
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
XIAOJU_SURVEY_MONGO_DB_NAME= xiaojuSurvey
XIAOJU_SURVEY_MONGO_URL= # mongodb://localhost:27017 # 建议设置强密码
XIAOJU_SURVEY_MONGO_DB_NAME=xiaojuSurvey
XIAOJU_SURVEY_MONGO_URL= # mongodb://127.0.0.1:27017 # 建议设置强密码
XIAOJU_SURVEY_MONGO_AUTH_SOURCE= # admin

XIAOJU_SURVEY_REDIS_HOST=
Expand Down
4 changes: 3 additions & 1 deletion server/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,6 @@ yarn.lock
!.vscode/launch.json
!.vscode/extensions.json

tmp
tmp
exportfile
userUpload
3 changes: 3 additions & 0 deletions server/src/models/session.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ export class Session extends BaseEntity {

@Column()
surveyId: string;

@Column()
userId: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,16 @@ export class DownloadTaskController {
async downloadList(
@Query()
queryInfo: GetDownloadTaskListDto,
@Request() req,
) {
const { value, error } = GetDownloadTaskListDto.validate(queryInfo);
if (error) {
this.logger.error(error.message);
throw new HttpException('参数有误', EXCEPTION_CODE.PARAMETER_ERROR);
}
const { ownerId, pageIndex, pageSize } = value;
const { pageIndex, pageSize } = value;
const { total, list } = await this.downloadTaskService.getDownloadTaskList({
ownerId,
ownerId: req.user._id.toString(),
pageIndex,
pageSize,
});
Expand Down
7 changes: 6 additions & 1 deletion server/src/modules/survey/controllers/session.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export class SessionController {
reqBody: {
surveyId: string;
},
@Request()
req,
) {
const { value, error } = Joi.object({
surveyId: Joi.string().required(),
Expand All @@ -50,7 +52,10 @@ export class SessionController {
}

const surveyId = value.surveyId;
const session = await this.sessionService.create({ surveyId });
const session = await this.sessionService.create({
surveyId,
userId: req.user._id.toString(),
});

return {
code: 200,
Expand Down
27 changes: 17 additions & 10 deletions server/src/modules/survey/controllers/survey.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { WorkspaceGuard } from 'src/guards/workspace.guard';
import { PERMISSION as WORKSPACE_PERMISSION } from 'src/enums/workspace';
import { SessionService } from '../services/session.service';
import { MemberType, WhitelistType } from 'src/interfaces/survey';
import { UserService } from 'src/modules/auth/services/user.service';

@ApiTags('survey')
@Controller('/api/survey')
Expand All @@ -47,6 +48,7 @@ export class SurveyController {
private readonly logger: XiaojuSurveyLogger,
private readonly counterService: CounterService,
private readonly sessionService: SessionService,
private readonly userService: UserService,
) {}

@Get('/getBannerData')
Expand Down Expand Up @@ -146,11 +148,21 @@ export class SurveyController {
if (latestEditingOne && latestEditingOne._id.toString() !== sessionId) {
const curSession = await this.sessionService.findOne(sessionId);
if (curSession.createDate <= latestEditingOne.updateDate) {
// 在当前用户打开之后,有人保存过了
throw new HttpException(
'当前问卷已在其它页面开启编辑',
EXCEPTION_CODE.SURVEY_SAVE_CONFLICT,
);
// 在当前用户打开之后,被其他页面保存过了
const isSameOperator =
latestEditingOne.userId === req.user._id.toString();
let preOperator;
if (!isSameOperator) {
preOperator = await this.userService.getUserById(
latestEditingOne.userId,
);
}
return {
code: EXCEPTION_CODE.SURVEY_SAVE_CONFLICT,
errmsg: isSameOperator
? '当前问卷已在其它页面开启编辑,刷新以获取最新内容'
: `当前问卷已由 ${preOperator.username} 编辑,刷新以获取最新内容`,
};
}
}
await this.sessionService.updateSessionToEditing({ sessionId, surveyId });
Expand Down Expand Up @@ -331,11 +343,6 @@ export class SurveyController {
pageId: surveyId,
});

await this.counterService.createCounters({
surveyPath: surveyMeta.surveyPath,
dataList: surveyConf.code.dataConf.dataList,
});

await this.surveyHistoryService.addHistory({
surveyId,
schema: surveyConf.code,
Expand Down
14 changes: 6 additions & 8 deletions server/src/modules/survey/services/downloadTask.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { load } from 'cheerio';
import { get } from 'lodash';
import { FileService } from 'src/modules/file/services/file.service';
import { XiaojuSurveyLogger } from 'src/logger';
import moment from 'moment';

@Injectable()
export class DownloadTaskService {
Expand Down Expand Up @@ -41,6 +42,7 @@ export class DownloadTaskService {
operatorId: string;
params: any;
}) {
const filename = `${responseSchema.title}-${params.isDesensitive ? '脱敏' : '原'}回收数据-${moment().format('YYYYMMDDHHmmss')}.xlsx`;
const downloadTask = this.downloadTaskRepository.create({
surveyId,
surveyPath: responseSchema.surveyPath,
Expand All @@ -50,6 +52,7 @@ export class DownloadTaskService {
...params,
title: responseSchema.title,
},
filename,
});
await this.downloadTaskRepository.save(downloadTask);
return downloadTask._id.toString();
Expand All @@ -65,7 +68,7 @@ export class DownloadTaskService {
pageSize: number;
}) {
const where = {
onwer: ownerId,
ownerId,
'curStatus.status': {
$ne: RECORD_STATUS.REMOVED,
},
Expand Down Expand Up @@ -209,16 +212,12 @@ export class DownloadTaskService {
{ name: 'sheet1', data: xlsxData, options: {} },
]);

const isDesensitive = taskInfo.params?.isDesensitive;

const originalname = `${taskInfo.params.title}-${isDesensitive ? '脱敏' : '原'}回收数据.xlsx`;

const file: Express.Multer.File = {
fieldname: 'file',
originalname: originalname,
originalname: taskInfo.filename,
encoding: '7bit',
mimetype: 'application/octet-stream',
filename: originalname,
filename: taskInfo.filename,
size: buffer.length,
buffer: buffer,
stream: null,
Expand Down Expand Up @@ -246,7 +245,6 @@ export class DownloadTaskService {
$set: {
curStatus,
url,
filename: originalname,
fileKey: key,
fileSize: buffer.length,
},
Expand Down
3 changes: 2 additions & 1 deletion server/src/modules/survey/services/session.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ export class SessionService {
private readonly sessionRepository: MongoRepository<Session>,
) {}

create({ surveyId }) {
create({ surveyId, userId }) {
const session = this.sessionRepository.create({
surveyId,
userId,
});
return this.sessionRepository.save(session);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Controller, Post, Body, HttpCode } from '@nestjs/common';
import { HttpException } from 'src/exceptions/httpException';
import { SurveyNotFoundException } from 'src/exceptions/surveyNotFoundException';
import { checkSign } from 'src/utils/checkSign';
import { cleanRichTextWithMediaTag } from 'src/utils/xss'
import { ENCRYPT_TYPE } from 'src/enums/encrypt';
import { EXCEPTION_CODE } from 'src/enums/exceptionCode';
import { getPushingData } from 'src/utils/messagePushing';
Expand All @@ -23,6 +22,14 @@ import { XiaojuSurveyLogger } 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';
import { QUESTION_TYPE } from 'src/enums/question';

const optionQuestionType: Array<string> = [
QUESTION_TYPE.RADIO,
QUESTION_TYPE.CHECKBOX,
QUESTION_TYPE.BINARY_CHOICE,
QUESTION_TYPE.VOTE,
];

@ApiTags('surveyResponse')
@Controller('/api/surveyResponse')
Expand Down Expand Up @@ -207,6 +214,7 @@ export class SurveyResponseController {
const optionTextAndId = dataList
.filter((questionItem) => {
return (
optionQuestionType.includes(questionItem.type) &&
Array.isArray(questionItem.options) &&
questionItem.options.length > 0 &&
decryptedData[questionItem.field]
Expand All @@ -222,59 +230,65 @@ export class SurveyResponseController {
return pre;
}, {});

// 使用redis作为锁,校验选项配额
const surveyId = responseSchema.pageId;
const lockKey = `locks:optionSelectedCount:${surveyId}`;
const lock = await this.redisService.lockResource(lockKey, 1000);
this.logger.info(`lockKey: ${lockKey}`);
try {
const successParams = [];
for (const field in decryptedData) {
const value = decryptedData[field];
const values = Array.isArray(value) ? value : [value];
if (field in optionTextAndId) {
const optionCountData = await this.counterService.get({
key: field,
surveyPath,
type: 'option',
});
const optionCountData =
(await this.counterService.get({
key: field,
surveyPath,
type: 'option',
})) || {};

//遍历选项hash值
for (const val of values) {
const option = optionTextAndId[field].find(
(opt) => opt['hash'] === val,
);
const quota = parseInt(option['quota']);
if (quota !== 0 && quota <= optionCountData[val]) {
const item = dataList.find((item) => item['field'] === field);
throw new HttpException(
`【${cleanRichTextWithMediaTag(item['title'])}】中的【${cleanRichTextWithMediaTag(option['text'])}】所选人数已达到上限,请重新选择`,
EXCEPTION_CODE.RESPONSE_OVER_LIMIT,
);
if (
quota &&
optionCountData?.[val] &&
quota <= optionCountData[val]
) {
return {
code: EXCEPTION_CODE.RESPONSE_OVER_LIMIT,
data: {
field,
optionHash: option.hash,
},
};
}
if (!optionCountData[val]) {
optionCountData[val] = 0;
}
optionCountData[val]++;
}
}
}

for (const field in decryptedData) {
const value = decryptedData[field];
const values = Array.isArray(value) ? value : [value];
if (field in optionTextAndId) {
const optionCountData = await this.counterService.get({
if (!optionCountData['total']) {
optionCountData['total'] = 1;
} else {
optionCountData['total']++;
}
successParams.push({
key: field,
surveyPath,
type: 'option',
data: optionCountData,
});
for (const val of values) {
optionCountData[val]++;
this.counterService.set({
key: field,
surveyPath,
type: 'option',
data: optionCountData,
});
}
optionCountData['total']++;
}
}
// 校验通过后统一更新
await Promise.all(
successParams.map((item) => this.counterService.set(item)),
);
} catch (error) {
this.logger.error(error.message);
throw error;
Expand Down
21 changes: 0 additions & 21 deletions server/src/modules/surveyResponse/services/counter.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,25 +65,4 @@ export class CounterService {
return pre;
}, {});
}

async createCounters({ surveyPath, dataList }) {
const optionList = dataList.filter((questionItem) => {
return (
Array.isArray(questionItem.options) && questionItem.options.length > 0
);
});
optionList.forEach((option) => {
const data = {};
option.options.forEach((option) => {
data[option.hash] = 0;
});
data['total'] = 0;
this.set({
surveyPath,
key: option.field,
type: 'option',
data: data,
});
});
}
}
Loading
Loading