From e9f5fb3e3d237e09310d24365ff83696755ccf0c Mon Sep 17 00:00:00 2001 From: luch1994 <1097650398@qq.com> Date: Tue, 10 Sep 2024 15:19:43 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=AE=E6=94=B9=E9=AA=8C=E6=94=B6?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/.env | 4 +- server/.gitignore | 4 +- server/src/models/session.entity.ts | 3 ++ .../controllers/downloadTask.controller.ts | 5 +- .../survey/controllers/session.controller.ts | 7 ++- .../survey/controllers/survey.controller.ts | 22 ++++++-- .../survey/services/downloadTask.service.ts | 2 +- .../survey/services/session.service.ts | 3 +- .../controllers/surveyResponse.controller.ts | 22 +++++--- server/src/utils/xss.ts | 42 ++++++++-------- .../pages/analysis/components/DataTable.vue | 13 ++++- .../pages/analysis/pages/DataTablePage.vue | 17 ++++++- .../components/DownloadTaskList.vue | 13 +++-- .../modules/contentModule/PublishPanel.vue | 50 +++++++++++++++---- .../edit/modules/contentModule/SavePanel.vue | 12 +++-- web/src/management/pages/list/index.vue | 2 +- web/src/management/router/index.ts | 4 +- 17 files changed, 160 insertions(+), 65 deletions(-) diff --git a/server/.env b/server/.env index 3e4e9e70..b2286da4 100644 --- a/server/.env +++ b/server/.env @@ -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= diff --git a/server/.gitignore b/server/.gitignore index 6309f8c1..93515c51 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -38,4 +38,6 @@ yarn.lock !.vscode/launch.json !.vscode/extensions.json -tmp \ No newline at end of file +tmp +exportfile +userUpload \ No newline at end of file diff --git a/server/src/models/session.entity.ts b/server/src/models/session.entity.ts index c7e3f485..bee82cc1 100644 --- a/server/src/models/session.entity.ts +++ b/server/src/models/session.entity.ts @@ -12,4 +12,7 @@ export class Session extends BaseEntity { @Column() surveyId: string; + + @Column() + userId: string; } diff --git a/server/src/modules/survey/controllers/downloadTask.controller.ts b/server/src/modules/survey/controllers/downloadTask.controller.ts index f9001112..23ac42cb 100644 --- a/server/src/modules/survey/controllers/downloadTask.controller.ts +++ b/server/src/modules/survey/controllers/downloadTask.controller.ts @@ -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, }); diff --git a/server/src/modules/survey/controllers/session.controller.ts b/server/src/modules/survey/controllers/session.controller.ts index 4b4455e4..33b87a46 100644 --- a/server/src/modules/survey/controllers/session.controller.ts +++ b/server/src/modules/survey/controllers/session.controller.ts @@ -39,6 +39,8 @@ export class SessionController { reqBody: { surveyId: string; }, + @Request() + req, ) { const { value, error } = Joi.object({ surveyId: Joi.string().required(), @@ -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, diff --git a/server/src/modules/survey/controllers/survey.controller.ts b/server/src/modules/survey/controllers/survey.controller.ts index e97b21ca..d13c4578 100644 --- a/server/src/modules/survey/controllers/survey.controller.ts +++ b/server/src/modules/survey/controllers/survey.controller.ts @@ -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') @@ -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') @@ -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 }); diff --git a/server/src/modules/survey/services/downloadTask.service.ts b/server/src/modules/survey/services/downloadTask.service.ts index 2a3e0bba..ef914c61 100644 --- a/server/src/modules/survey/services/downloadTask.service.ts +++ b/server/src/modules/survey/services/downloadTask.service.ts @@ -65,7 +65,7 @@ export class DownloadTaskService { pageSize: number; }) { const where = { - onwer: ownerId, + ownerId, 'curStatus.status': { $ne: RECORD_STATUS.REMOVED, }, diff --git a/server/src/modules/survey/services/session.service.ts b/server/src/modules/survey/services/session.service.ts index f6a78134..e70fd3da 100644 --- a/server/src/modules/survey/services/session.service.ts +++ b/server/src/modules/survey/services/session.service.ts @@ -12,9 +12,10 @@ export class SessionService { private readonly sessionRepository: MongoRepository, ) {} - create({ surveyId }) { + create({ surveyId, userId }) { const session = this.sessionRepository.create({ surveyId, + userId, }); return this.sessionRepository.save(session); } diff --git a/server/src/modules/surveyResponse/controllers/surveyResponse.controller.ts b/server/src/modules/surveyResponse/controllers/surveyResponse.controller.ts index fb2bcf04..cc5f7f9f 100644 --- a/server/src/modules/surveyResponse/controllers/surveyResponse.controller.ts +++ b/server/src/modules/surveyResponse/controllers/surveyResponse.controller.ts @@ -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'; @@ -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 = [ + QUESTION_TYPE.RADIO, + QUESTION_TYPE.CHECKBOX, + QUESTION_TYPE.BINARY_CHOICE, + QUESTION_TYPE.VOTE, +]; @ApiTags('surveyResponse') @Controller('/api/surveyResponse') @@ -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] @@ -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, + }, + }; } } } diff --git a/server/src/utils/xss.ts b/server/src/utils/xss.ts index 7da9f2bf..b7ea7ee5 100644 --- a/server/src/utils/xss.ts +++ b/server/src/utils/xss.ts @@ -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) { // 过滤为空,否则不过滤为空 - 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(//g, '[图片]') - .replace(//g, '[视频]') - const content = html.replace(/<[^<>]+>/g, '').replace(/ /g, '') + .replace(//g, '[视频]'); + const content = html.replace(/<[^<>]+>/g, '').replace(/ /g, ''); - return content -} + return content; +}; export function escapeHtml(html) { - return html.replace(//g, '>') + return html.replace(//g, '>'); } 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, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, "'") - .replace(/\\\n/g, '\\n') + .replace(/\\\n/g, '\\n'); //.replace(/ /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)); diff --git a/web/src/management/pages/analysis/components/DataTable.vue b/web/src/management/pages/analysis/components/DataTable.vue index e6835d64..437315a9 100644 --- a/web/src/management/pages/analysis/components/DataTable.vue +++ b/web/src/management/pages/analysis/components/DataTable.vue @@ -60,6 +60,7 @@ diff --git a/web/src/management/router/index.ts b/web/src/management/router/index.ts index 12b131ab..be8cba54 100644 --- a/web/src/management/router/index.ts +++ b/web/src/management/router/index.ts @@ -27,8 +27,8 @@ const routes: RouteRecordRaw[] = [ } }, { - path: '/survey/downloadTask/', - name: 'download', + path: '/downloadTask/', + name: 'downloadTask', component: () => import('../pages/downloadTask/TaskList.vue'), meta: { needLogin: true