Skip to content

Commit

Permalink
feat: 修改验收问题
Browse files Browse the repository at this point in the history
  • Loading branch information
luch1994 committed Sep 10, 2024
1 parent 42ea516 commit 6a98e22
Show file tree
Hide file tree
Showing 18 changed files with 207 additions and 96 deletions.
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
22 changes: 17 additions & 5 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
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 Down Expand Up @@ -244,11 +252,13 @@ export class SurveyResponseController {
);
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,
);
return {
code: EXCEPTION_CODE.RESPONSE_OVER_LIMIT,
data: {
field,
optionHash: option.hash,
},
};
}
}
}
Expand Down
42 changes: 21 additions & 21 deletions server/src/utils/xss.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,53 @@
import xss from 'xss'
import xss from 'xss';

const myxss = new (xss as any).FilterXSS({
onIgnoreTagAttr(tag, name, value) {
if (name === 'style' || name === 'class') {
return `${name}="${value}"`
return `${name}="${value}"`;
}
return undefined
return undefined;
},
onIgnoreTag(tag, html) {
// <xxx>过滤为空,否则不过滤为空
var re1 = new RegExp('<.+?>', 'g')
const re1 = new RegExp('<.+?>', 'g');
if (re1.test(html)) {
return ''
return '';
} else {
return html
return html;
}
}
})
},
});

export const cleanRichTextWithMediaTag = (text) => {
if (!text) {
return text === 0 ? 0 : ''
return text === 0 ? 0 : '';
}
const html = transformHtmlTag(text)
.replace(/<img([\w\W]+?)\/>/g, '[图片]')
.replace(/<video.*\/video>/g, '[视频]')
const content = html.replace(/<[^<>]+>/g, '').replace(/&nbsp;/g, '')
.replace(/<video.*\/video>/g, '[视频]');
const content = html.replace(/<[^<>]+>/g, '').replace(/&nbsp;/g, '');

return content
}
return content;
};

export function escapeHtml(html) {
return html.replace(/</g, '&lt;').replace(/>/g, '&gt;')
return html.replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
export const transformHtmlTag = (html) => {
if (!html) return ''
if (typeof html !== 'string') return html + ''
if (!html) return '';
if (typeof html !== 'string') return html + '';
return html
.replace(html ? /&(?!#?\w+;)/g : /&/g, '&amp;')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
.replace(/\\\n/g, '\\n')
.replace(/\\\n/g, '\\n');
//.replace(/&nbsp;/g, "")
}
};

const filterXSSClone = myxss.process.bind(myxss)
const filterXSSClone = myxss.process.bind(myxss);

export const filterXSS = (html) => filterXSSClone(transformHtmlTag(html))
export const filterXSS = (html) => filterXSSClone(transformHtmlTag(html));

export const escapeFilterXSS = (html) => escapeHtml(filterXSS(html))
export const escapeFilterXSS = (html) => escapeHtml(filterXSS(html));
13 changes: 11 additions & 2 deletions web/src/management/pages/analysis/components/DataTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
<script setup>
import { ref } from 'vue'
import ImagePreview from './ImagePreview.vue'
import { cleanRichTextWithMediaTag } from '@/common/xss'
const props = defineProps({
tableData: {
Expand All @@ -78,8 +79,16 @@ const popoverVirtualRef = ref()
const popoverContent = ref('')
const getContent = (content) => {
// const content = cleanRichText(value)
return content === 0 ? 0 : content || '未知'
if (Array.isArray(content)) {
return content.map(item => getContent(item)).join(',');
}
if (content === null || content === undefined) {
return ''
}
if (typeof content !== 'string') {
content = content + ''
}
return cleanRichTextWithMediaTag(content) || '未知'
}
const setPopoverContent = (content) => {
popoverContent.value = content
Expand Down
19 changes: 16 additions & 3 deletions web/src/management/pages/analysis/pages/DataTablePage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,22 @@
v-model="downloadDialogVisible"
title="导出确认"
width="500"
style="padding: 40px;"
>
<el-form :model="downloadForm">
<el-form :model="downloadForm" label-width="100px" label-position="left" >
<el-form-item label="导出内容">
<el-radio-group v-model="downloadForm.isDesensitive">
<el-radio :value="true">脱敏数据</el-radio>
<el-radio value="Venue">原回收数据</el-radio>
<el-radio :value="false">原回收数据</el-radio>
</el-radio-group>
</el-form-item>
<div class="download-tips">
<div>注:</div>
<div>
<p>推荐优先下载脱敏数据,如手机号:1***3。</p>
<p>原回收数据可能存在敏感信息,请谨慎下载。</p>
</div>
</div>
</el-form>
<template #footer>
<div class="dialog-footer">
Expand Down Expand Up @@ -163,9 +171,9 @@ const confirmDownload = async () => {
const createRes = await createDownloadSurveyResponseTask({ surveyId: route.params.id, isDesensitive: downloadForm.isDesensitive })
dataTableState.downloadDialogVisible = false
if (createRes.code === 200) {
ElMessage.success(`下载文件计算中,可前往“下载中心”查看`)
try {
const taskInfo = await checkIsTaskFinished(createRes.data.taskId)
console.log(taskInfo)
if (taskInfo.url) {
window.open(taskInfo.url)
ElMessage.success("导出成功")
Expand Down Expand Up @@ -217,6 +225,11 @@ const checkIsTaskFinished = (taskId) => {
overflow: hidden;
}
.download-tips {
display: flex;
color: #ec4e29;
}
.menus {
margin-bottom: 20px;
}
Expand Down
4 changes: 2 additions & 2 deletions web/src/management/pages/downloadTask/TaskList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const handleLogout = () => {
const activeIndex = ref('2')
</script>

<style>
<style lang="scss" scoped>
.question-list-root {
height: 100%;
background-color: #f6f7f9;
Expand Down Expand Up @@ -94,7 +94,7 @@ const activeIndex = ref('2')
}
}
.table-container {
padding: 20px;
margin-top: 20px;
display: flex;
justify-content: center;
width: 100%; /* 确保容器宽度为100% */
Expand Down
Loading

0 comments on commit 6a98e22

Please sign in to comment.