[Feature]: 服务端新增eslint (#35)
1. 服务端新增eslint 2. 修复所有的lint问题 3. 优化部署,把build命令放到docker构建中
This commit is contained in:
parent
3ec86cbe73
commit
08f3bb0578
@ -21,12 +21,11 @@ COPY . /xiaoju-survey
|
||||
RUN npm config set registry https://registry.npmjs.org/
|
||||
|
||||
# 安装项目依赖
|
||||
RUN cd /xiaoju-survey/web && npm install
|
||||
RUN cd /xiaoju-survey/server && npm install
|
||||
RUN cd /xiaoju-survey/web && npm install && npm run build
|
||||
|
||||
# 构建项目,并把产物推送到服务公共目录
|
||||
RUN cd /xiaoju-survey/web && npm run build
|
||||
RUN cd /xiaoju-survey && cp -af ./web/dist/* ./server/src/apps/ui/public/
|
||||
RUN cd /xiaoju-survey/server && npm install && npm run build
|
||||
|
||||
RUN cd /xiaoju-survey && mkdir -p ./build/apps/ui/public/ && cp -af ./web/dist/* ./server/build/apps/ui/public/
|
||||
|
||||
# 暴露端口 需要跟server的port一致
|
||||
EXPOSE 3000
|
||||
|
42
server/.eslintrc.json
Normal file
42
server/.eslintrc.json
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"indent": [
|
||||
"error",
|
||||
2
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"space-in-parens": ["error", "never"],
|
||||
"key-spacing": ["error", { "mode": "strict" }],
|
||||
"comma-spacing": ["error", { "before": false, "after": true }],
|
||||
"arrow-spacing": ["error", { "before": true, "after": true }],
|
||||
"space-before-blocks": 2,
|
||||
"object-curly-spacing": ["error", "always"]
|
||||
}
|
||||
}
|
@ -5,9 +5,9 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start:stable": "npm run build && SERVER_ENV=stable node ./build/index.js",
|
||||
"start:preonline": "npm run build && SERVER_ENV=preonline node ./build/index.js",
|
||||
"start:online": "npm run build && SERVER_ENV=online node ./build/index.js",
|
||||
"start:stable": "SERVER_ENV=stable node ./build/index.js",
|
||||
"start:preonline": "SERVER_ENV=preonline node ./build/index.js",
|
||||
"start:online": "SERVER_ENV=online node ./build/index.js",
|
||||
"start": "npm run start:online",
|
||||
"local": "npx ts-node scripts/run-local.ts",
|
||||
"dev": "npx ts-node-dev ./src/index.ts"
|
||||
@ -17,7 +17,11 @@
|
||||
"@types/koa": "^2.13.8",
|
||||
"@types/koa-bodyparser": "^4.3.10",
|
||||
"@types/koa-router": "^7.4.4",
|
||||
"@types/koa-static": "^4.0.4",
|
||||
"@typescript-eslint/eslint-plugin": "^6.15.0",
|
||||
"@typescript-eslint/parser": "^6.15.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.56.0",
|
||||
"mongodb-memory-server": "^9.0.1",
|
||||
"nodemon": "^2.0.20",
|
||||
"ts-node": "^10.9.2",
|
||||
@ -26,12 +30,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"crypto-js": "^4.2.0",
|
||||
"glob": "^10.3.10",
|
||||
"joi": "^17.9.2",
|
||||
"jsonwebtoken": "^9.0.1",
|
||||
"koa": "^2.14.2",
|
||||
"koa-bodyparser": "^4.4.1",
|
||||
"koa-pino-logger": "^4.0.0",
|
||||
"koa-router": "^12.0.0",
|
||||
"koa-static": "^4.0.3",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.29.4",
|
||||
"mongodb": "^5.7.0",
|
||||
|
@ -1,10 +1,10 @@
|
||||
const config = {
|
||||
mongo: {
|
||||
url: process.env.xiaojuSurveyMongoUrl || 'mongodb://localhost:27017',
|
||||
dbName: 'xiaojuSurvey'
|
||||
}
|
||||
}
|
||||
mongo: {
|
||||
url: process.env.xiaojuSurveyMongoUrl || 'mongodb://localhost:27017',
|
||||
dbName: 'xiaojuSurvey'
|
||||
}
|
||||
};
|
||||
|
||||
export function getConfig() {
|
||||
return config
|
||||
return config;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { getConfig } from '../config/index'
|
||||
import { getConfig } from '../config/index';
|
||||
|
||||
import MongoService from '../../../utils/mongoService'
|
||||
import MongoService from '../../../utils/mongoService';
|
||||
|
||||
const config = getConfig()
|
||||
const config = getConfig();
|
||||
|
||||
export const mongo = new MongoService({ url: config.mongo.url, dbName: config.mongo.dbName })
|
||||
export const mongo = new MongoService({ url: config.mongo.url, dbName: config.mongo.dbName });
|
@ -1,16 +1,17 @@
|
||||
import { SurveyServer } from '../../decorator'
|
||||
import { securityService } from './service/securityService'
|
||||
import { SurveyApp, SurveyServer } from '../../decorator';
|
||||
import { securityService } from './service/securityService';
|
||||
|
||||
@SurveyApp('/api/security')
|
||||
export default class Security {
|
||||
@SurveyServer({ type: 'rpc' })
|
||||
async isHitKeys({ params, context }: { params: any, context: any }) {
|
||||
async isHitKeys({ params, context }) {
|
||||
const data = securityService.isHitKeys({
|
||||
content: params.content,
|
||||
dictType: params.dictType,
|
||||
})
|
||||
});
|
||||
return {
|
||||
result: data,
|
||||
context, // 上下文主要是传递调用方信息使用,比如traceid
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
import { mongo } from '../db/mongo'
|
||||
import { DICT_TYPE } from '../../../types'
|
||||
import { participle } from '../utils/index'
|
||||
import { mongo } from '../db/mongo';
|
||||
import { DICT_TYPE } from '../../../types';
|
||||
import { participle } from '../utils/index';
|
||||
|
||||
class SecurityService {
|
||||
async isHitKeys({ content, dictType }: { content: string, dictType: DICT_TYPE }) {
|
||||
const securityDictModel = await mongo.getCollection({ collectionName: 'securityDict' })
|
||||
const keywordList = participle({ content })
|
||||
const hitCount = await securityDictModel.countDocuments({ keyword: { $in: keywordList }, type: dictType })
|
||||
return hitCount > 0
|
||||
}
|
||||
async isHitKeys({ content, dictType }: { content: string, dictType: DICT_TYPE }) {
|
||||
const securityDictModel = await mongo.getCollection({ collectionName: 'securityDict' });
|
||||
const keywordList = participle({ content });
|
||||
const hitCount = await securityDictModel.countDocuments({ keyword: { $in: keywordList }, type: dictType });
|
||||
return hitCount > 0;
|
||||
}
|
||||
}
|
||||
|
||||
export const securityService = new SecurityService()
|
||||
export const securityService = new SecurityService();
|
@ -1,16 +1,16 @@
|
||||
|
||||
export function participle({ content, minLen, maxLen }: { content: string, minLen?: number, maxLen?: number }) {
|
||||
const keys: Array<string> = []
|
||||
minLen = minLen || 2
|
||||
maxLen = maxLen || 13
|
||||
for (let i = 0; i < content.length; i++) {
|
||||
let tempStr = content[i]
|
||||
for (let j = 1; j < maxLen && i + j < content.length; j++) {
|
||||
tempStr += content[i + j]
|
||||
if (j >= minLen - 1) {
|
||||
keys.push(tempStr)
|
||||
}
|
||||
}
|
||||
const keys: Array<string> = [];
|
||||
minLen = minLen || 2;
|
||||
maxLen = maxLen || 13;
|
||||
for (let i = 0; i < content.length; i++) {
|
||||
let tempStr = content[i];
|
||||
for (let j = 1; j < maxLen && i + j < content.length; j++) {
|
||||
tempStr += content[i + j];
|
||||
if (j >= minLen - 1) {
|
||||
keys.push(tempStr);
|
||||
}
|
||||
}
|
||||
return keys
|
||||
}
|
||||
return keys;
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
const config = {
|
||||
mongo: {
|
||||
url: process.env.xiaojuSurveyMongoUrl || 'mongodb://localhost:27017',
|
||||
dbName: 'xiaojuSurvey',
|
||||
}
|
||||
}
|
||||
mongo: {
|
||||
url: process.env.xiaojuSurveyMongoUrl || 'mongodb://localhost:27017',
|
||||
dbName: 'xiaojuSurvey',
|
||||
}
|
||||
};
|
||||
|
||||
export function getConfig() {
|
||||
return config
|
||||
return config;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { getConfig } from '../config/index'
|
||||
import MongoService from '../../../utils/mongoService'
|
||||
import { getConfig } from '../config/index';
|
||||
import MongoService from '../../../utils/mongoService';
|
||||
|
||||
const config = getConfig()
|
||||
const config = getConfig();
|
||||
|
||||
export const mongo = new MongoService({ url: config.mongo.url, dbName: config.mongo.dbName })
|
||||
export const mongo = new MongoService({ url: config.mongo.url, dbName: config.mongo.dbName });
|
@ -1,184 +1,185 @@
|
||||
import { SurveyServer } from '../../decorator'
|
||||
import { Request, Response } from 'koa'
|
||||
import { surveyService } from './service/surveyService'
|
||||
import { userService } from './service/userService'
|
||||
import { surveyHistoryService } from './service/surveyHistoryService'
|
||||
import { HISTORY_TYPE } from '../../types/index'
|
||||
import { getValidateValue } from './utils/index'
|
||||
import * as Joi from 'joi'
|
||||
import { SurveyApp, SurveyServer } from '../../decorator';
|
||||
import { surveyService } from './service/surveyService';
|
||||
import { userService } from './service/userService';
|
||||
import { surveyHistoryService } from './service/surveyHistoryService';
|
||||
import { HISTORY_TYPE } from '../../types/index';
|
||||
import { getValidateValue } from './utils/index';
|
||||
import * as Joi from 'joi';
|
||||
|
||||
@SurveyApp('/api/surveyManage')
|
||||
export default class SurveyManage {
|
||||
@SurveyServer({type:'http',method:'get',routerName:'/getBannerData'})
|
||||
async getBannerData({req,res}:{req:Request, res:Response}) {
|
||||
const data = await surveyService.getBannerData()
|
||||
return {
|
||||
code:200,
|
||||
data,
|
||||
}
|
||||
}
|
||||
|
||||
@SurveyServer({type:'http',method:'post',routerName:'/add'})
|
||||
async add({req,res}:{req:Request, res:Response}) {
|
||||
const params = getValidateValue(Joi.object({
|
||||
remark: Joi.string().required(),
|
||||
questionType: Joi.string().required(),
|
||||
title: Joi.string().required(),
|
||||
}).validate(req.body,{allowUnknown: true}));
|
||||
params.userData = await userService.checkLogin({req})
|
||||
const addRes = await surveyService.add(params)
|
||||
return {
|
||||
code:200,
|
||||
data: {
|
||||
id: addRes.pageId,
|
||||
},
|
||||
}
|
||||
}
|
||||
@SurveyServer({ type: 'http', method: 'get', routerName: '/getBannerData' })
|
||||
async getBannerData() {
|
||||
const data = await surveyService.getBannerData();
|
||||
return {
|
||||
code: 200,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
@SurveyServer({type:'http',method:'post',routerName:'/update'})
|
||||
async update({req,res}:{req:Request, res:Response}) {
|
||||
const surveyParams = getValidateValue(Joi.object({
|
||||
surveyId: Joi.string().required(),
|
||||
remark: Joi.string().required(),
|
||||
title: Joi.string().required(),
|
||||
}).validate(req.body,{allowUnknown: true}));
|
||||
const userData = await userService.checkLogin({req})
|
||||
surveyParams.userData = userData
|
||||
const data = await surveyService.update(surveyParams)
|
||||
return {
|
||||
code:200,
|
||||
data,
|
||||
}
|
||||
}
|
||||
@SurveyServer({ type: 'http', method: 'post', routerName: '/add' })
|
||||
async add({ req }) {
|
||||
const params = getValidateValue(Joi.object({
|
||||
remark: Joi.string().required(),
|
||||
questionType: Joi.string().required(),
|
||||
title: Joi.string().required(),
|
||||
}).validate(req.body, { allowUnknown: true }));
|
||||
params.userData = await userService.checkLogin({ req });
|
||||
const addRes = await surveyService.add(params);
|
||||
return {
|
||||
code: 200,
|
||||
data: {
|
||||
id: addRes.pageId,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@SurveyServer({type:'http',method:'post',routerName:'/delete'})
|
||||
async delete({req,res}:{req:Request, res:Response}) {
|
||||
const surveyParams = getValidateValue(Joi.object({
|
||||
surveyId: Joi.string().required(),
|
||||
}).validate(req.body,{allowUnknown: true}));
|
||||
const userData = await userService.checkLogin({req})
|
||||
surveyParams.userData = userData
|
||||
const data = await surveyService.delete(surveyParams)
|
||||
return {
|
||||
code:200,
|
||||
data,
|
||||
}
|
||||
}
|
||||
@SurveyServer({type:'http',method:'get',routerName:'/list'})
|
||||
async list({req,res}:{req:Request, res:Response}) {
|
||||
const condition = getValidateValue(Joi.object({
|
||||
curPage: Joi.number().default(1),
|
||||
pageSize: Joi.number().default(10),
|
||||
}).validate(req.query,{allowUnknown: true}));
|
||||
const userData = await userService.checkLogin({req})
|
||||
const listRes = await surveyService.list({
|
||||
pageNum:condition.curPage,
|
||||
pageSize:condition.pageSize,
|
||||
userData
|
||||
})
|
||||
return {
|
||||
code:200,
|
||||
data: listRes,
|
||||
}
|
||||
}
|
||||
@SurveyServer({type:'http',method:'post',routerName:'/saveConf'})
|
||||
async saveConf({req,res}:{req:Request, res:Response}) {
|
||||
const surveyData = getValidateValue(Joi.object({
|
||||
surveyId: Joi.string().required(),
|
||||
configData: Joi.object().required(),
|
||||
}).validate(req.body,{allowUnknown: true}));
|
||||
const userData = await userService.checkLogin({req})
|
||||
// 保存数据
|
||||
const saveRes = await surveyService.saveConf(surveyData);
|
||||
// 保存历史
|
||||
const historyRes = await surveyHistoryService.addHistory({
|
||||
surveyId:surveyData.surveyId,
|
||||
configData:surveyData.configData,
|
||||
type:HISTORY_TYPE.dailyHis,
|
||||
userData
|
||||
});
|
||||
return {
|
||||
code:200,
|
||||
data:{
|
||||
saveRes,
|
||||
historyRes
|
||||
}
|
||||
}
|
||||
}
|
||||
@SurveyServer({ type: 'http', method: 'post', routerName: '/update' })
|
||||
async update({ req }) {
|
||||
const surveyParams = getValidateValue(Joi.object({
|
||||
surveyId: Joi.string().required(),
|
||||
remark: Joi.string().required(),
|
||||
title: Joi.string().required(),
|
||||
}).validate(req.body, { allowUnknown: true }));
|
||||
const userData = await userService.checkLogin({ req });
|
||||
surveyParams.userData = userData;
|
||||
const data = await surveyService.update(surveyParams);
|
||||
return {
|
||||
code: 200,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
@SurveyServer({type:'http',method:'get',routerName:'/get'})
|
||||
async get({req,res}:{req:Request, res:Response}) {
|
||||
const params = getValidateValue(Joi.object({
|
||||
surveyId: Joi.string().required(),
|
||||
}).validate(req.query,{allowUnknown: true}));
|
||||
const userData = await userService.checkLogin({req})
|
||||
const data = await surveyService.get({
|
||||
surveyId:params.surveyId,
|
||||
userData
|
||||
})
|
||||
return {
|
||||
code:200,
|
||||
data,
|
||||
}
|
||||
}
|
||||
|
||||
@SurveyServer({type:'http',method:'get',routerName:'/getHistoryList'})
|
||||
async getHistoryList({req,res}:{req:Request, res:Response}) {
|
||||
const historyParams = getValidateValue(Joi.object({
|
||||
surveyId: Joi.string().required(),
|
||||
historyType: Joi.string().required(),
|
||||
}).validate(req.query,{allowUnknown: true}));
|
||||
const data = await surveyHistoryService.getHistoryList(historyParams)
|
||||
return {
|
||||
code:200,
|
||||
data
|
||||
@SurveyServer({ type: 'http', method: 'post', routerName: '/delete' })
|
||||
async delete({ req }) {
|
||||
const surveyParams = getValidateValue(Joi.object({
|
||||
surveyId: Joi.string().required(),
|
||||
}).validate(req.body, { allowUnknown: true }));
|
||||
const userData = await userService.checkLogin({ req });
|
||||
surveyParams.userData = userData;
|
||||
const data = await surveyService.delete(surveyParams);
|
||||
return {
|
||||
code: 200,
|
||||
data,
|
||||
};
|
||||
}
|
||||
@SurveyServer({ type: 'http', method: 'get', routerName: '/list' })
|
||||
async list({ req }) {
|
||||
const condition = getValidateValue(Joi.object({
|
||||
curPage: Joi.number().default(1),
|
||||
pageSize: Joi.number().default(10),
|
||||
}).validate(req.query, { allowUnknown: true }));
|
||||
const userData = await userService.checkLogin({ req });
|
||||
const listRes = await surveyService.list({
|
||||
pageNum: condition.curPage,
|
||||
pageSize: condition.pageSize,
|
||||
userData
|
||||
});
|
||||
return {
|
||||
code: 200,
|
||||
data: listRes,
|
||||
};
|
||||
}
|
||||
@SurveyServer({ type: 'http', method: 'post', routerName: '/saveConf' })
|
||||
async saveConf({ req }) {
|
||||
const surveyData = getValidateValue(Joi.object({
|
||||
surveyId: Joi.string().required(),
|
||||
configData: Joi.object().required(),
|
||||
}).validate(req.body, { allowUnknown: true }));
|
||||
const userData = await userService.checkLogin({ req });
|
||||
// 保存数据
|
||||
const saveRes = await surveyService.saveConf(surveyData);
|
||||
// 保存历史
|
||||
const historyRes = await surveyHistoryService.addHistory({
|
||||
surveyId: surveyData.surveyId,
|
||||
configData: surveyData.configData,
|
||||
type: HISTORY_TYPE.dailyHis,
|
||||
userData
|
||||
});
|
||||
return {
|
||||
code: 200,
|
||||
data: {
|
||||
saveRes,
|
||||
historyRes
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SurveyServer({type:'http',method:'post',routerName:'/publish'})
|
||||
async publish({req,res}:{req:Request, res:Response}) {
|
||||
const surveyParams = getValidateValue(Joi.object({
|
||||
surveyId: Joi.string().required(),
|
||||
}).validate(req.body,{allowUnknown: true}));
|
||||
// 鉴权
|
||||
const userData = await userService.checkLogin({req})
|
||||
// 发布
|
||||
surveyParams.userData = userData
|
||||
const surveyData = await surveyService.publish(surveyParams)
|
||||
// 保存历史
|
||||
const historyRes = await surveyHistoryService.addHistory({
|
||||
surveyId:surveyData.surveyConfRes.pageId,
|
||||
configData:surveyData.surveyConfRes.code,
|
||||
type:HISTORY_TYPE.publishHis,
|
||||
userData
|
||||
});
|
||||
return {
|
||||
code:200,
|
||||
data:{
|
||||
...surveyData,
|
||||
historyRes
|
||||
}
|
||||
}
|
||||
}
|
||||
@SurveyServer({ type: 'http', method: 'get', routerName: '/get' })
|
||||
async get({ req }) {
|
||||
const params = getValidateValue(Joi.object({
|
||||
surveyId: Joi.string().required(),
|
||||
}).validate(req.query, { allowUnknown: true }));
|
||||
const userData = await userService.checkLogin({ req });
|
||||
const data = await surveyService.get({
|
||||
surveyId: params.surveyId,
|
||||
userData
|
||||
});
|
||||
return {
|
||||
code: 200,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
@SurveyServer({type:'http',method:'get',routerName:'/data'})
|
||||
async data({req,res}:{req:Request, res:Response}) {
|
||||
const surveyParams = getValidateValue(Joi.object({
|
||||
surveyId: Joi.string().required(),
|
||||
isShowSecret:Joi.boolean().default(true), // 默认true就是需要脱敏
|
||||
page: Joi.number().default(1),
|
||||
pageSize: Joi.number().default(10),
|
||||
}).validate(req.query,{allowUnknown: true}));
|
||||
const userData = await userService.checkLogin({req})
|
||||
const data = await surveyService.data({
|
||||
userData,
|
||||
surveyId:surveyParams.surveyId,
|
||||
isShowSecret:surveyParams.isShowSecret,
|
||||
pageNum:surveyParams.page,
|
||||
pageSize:surveyParams.pageSize,
|
||||
})
|
||||
return {
|
||||
code:200,
|
||||
data
|
||||
@SurveyServer({ type: 'http', method: 'get', routerName: '/getHistoryList' })
|
||||
async getHistoryList({ req }) {
|
||||
const historyParams = getValidateValue(Joi.object({
|
||||
surveyId: Joi.string().required(),
|
||||
historyType: Joi.string().required(),
|
||||
}).validate(req.query, { allowUnknown: true }));
|
||||
const data = await surveyHistoryService.getHistoryList(historyParams);
|
||||
return {
|
||||
code: 200,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
@SurveyServer({ type: 'http', method: 'post', routerName: '/publish' })
|
||||
async publish({ req }) {
|
||||
const surveyParams = getValidateValue(Joi.object({
|
||||
surveyId: Joi.string().required(),
|
||||
}).validate(req.body, { allowUnknown: true }));
|
||||
// 鉴权
|
||||
const userData = await userService.checkLogin({ req });
|
||||
// 发布
|
||||
surveyParams.userData = userData;
|
||||
const surveyData = await surveyService.publish(surveyParams);
|
||||
// 保存历史
|
||||
const historyRes = await surveyHistoryService.addHistory({
|
||||
surveyId: surveyData.surveyConfRes.pageId,
|
||||
configData: surveyData.surveyConfRes.code,
|
||||
type: HISTORY_TYPE.publishHis,
|
||||
userData
|
||||
});
|
||||
return {
|
||||
code: 200,
|
||||
data: {
|
||||
...surveyData,
|
||||
historyRes
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SurveyServer({ type: 'http', method: 'get', routerName: '/data' })
|
||||
async data({ req }) {
|
||||
const surveyParams = getValidateValue(Joi.object({
|
||||
surveyId: Joi.string().required(),
|
||||
isShowSecret: Joi.boolean().default(true), // 默认true就是需要脱敏
|
||||
page: Joi.number().default(1),
|
||||
pageSize: Joi.number().default(10),
|
||||
}).validate(req.query, { allowUnknown: true }));
|
||||
const userData = await userService.checkLogin({ req });
|
||||
const data = await surveyService.data({
|
||||
userData,
|
||||
surveyId: surveyParams.surveyId,
|
||||
isShowSecret: surveyParams.isShowSecret,
|
||||
pageNum: surveyParams.page,
|
||||
pageSize: surveyParams.pageSize,
|
||||
});
|
||||
return {
|
||||
code: 200,
|
||||
data
|
||||
};
|
||||
}
|
||||
}
|
@ -1,37 +1,37 @@
|
||||
import { mongo } from '../db/mongo'
|
||||
import { getStatusObject } from '../utils/index'
|
||||
import { SURVEY_STATUS, HISTORY_TYPE, UserType } from '../../../types/index'
|
||||
import { mongo } from '../db/mongo';
|
||||
import { getStatusObject } from '../utils/index';
|
||||
import { SURVEY_STATUS, HISTORY_TYPE, UserType } from '../../../types/index';
|
||||
|
||||
class SurveyHistoryService {
|
||||
async addHistory(surveyData: { surveyId: string, configData: any, type: HISTORY_TYPE, userData: UserType }) {
|
||||
const surveyHistory = await mongo.getCollection({ collectionName: 'surveyHistory' });
|
||||
const surveyHistoryRes = await surveyHistory.insertOne({
|
||||
curStatus: getStatusObject({ status: SURVEY_STATUS.new }),
|
||||
pageId: surveyData.surveyId,
|
||||
type: surveyData.type,
|
||||
code: {
|
||||
data: surveyData.configData
|
||||
},
|
||||
createDate: Date.now(),
|
||||
operator: {
|
||||
_id: surveyData.userData._id,
|
||||
username: surveyData.userData.username,
|
||||
}
|
||||
})
|
||||
return surveyHistoryRes
|
||||
}
|
||||
async addHistory(surveyData: { surveyId: string, configData: unknown, type: HISTORY_TYPE, userData: UserType }) {
|
||||
const surveyHistory = await mongo.getCollection({ collectionName: 'surveyHistory' });
|
||||
const surveyHistoryRes = await surveyHistory.insertOne({
|
||||
curStatus: getStatusObject({ status: SURVEY_STATUS.new }),
|
||||
pageId: surveyData.surveyId,
|
||||
type: surveyData.type,
|
||||
code: {
|
||||
data: surveyData.configData
|
||||
},
|
||||
createDate: Date.now(),
|
||||
operator: {
|
||||
_id: surveyData.userData._id,
|
||||
username: surveyData.userData.username,
|
||||
}
|
||||
});
|
||||
return surveyHistoryRes;
|
||||
}
|
||||
|
||||
async getHistoryList(historyParams: { surveyId: string, historyType: HISTORY_TYPE }) {
|
||||
const surveyHistory = await mongo.getCollection({ collectionName: 'surveyHistory' });
|
||||
const surveyHistoryListRes = await surveyHistory.find({
|
||||
pageId: historyParams.surveyId,
|
||||
type: historyParams.historyType,
|
||||
})
|
||||
.sort({ createDate: -1 })
|
||||
.limit(100)
|
||||
.toArray()
|
||||
return mongo.convertId2StringByList(surveyHistoryListRes)
|
||||
}
|
||||
async getHistoryList(historyParams: { surveyId: string, historyType: HISTORY_TYPE }) {
|
||||
const surveyHistory = await mongo.getCollection({ collectionName: 'surveyHistory' });
|
||||
const surveyHistoryListRes = await surveyHistory.find({
|
||||
pageId: historyParams.surveyId,
|
||||
type: historyParams.historyType,
|
||||
})
|
||||
.sort({ createDate: -1 })
|
||||
.limit(100)
|
||||
.toArray();
|
||||
return mongo.convertId2StringByList(surveyHistoryListRes);
|
||||
}
|
||||
}
|
||||
|
||||
export const surveyHistoryService = new SurveyHistoryService()
|
||||
export const surveyHistoryService = new SurveyHistoryService();
|
@ -1,351 +1,356 @@
|
||||
import { mongo } from '../db/mongo'
|
||||
import { rpcInvote } from '../../../rpc'
|
||||
import { SURVEY_STATUS, QUESTION_TYPE, CommonError, UserType, DICT_TYPE } from '../../../types/index'
|
||||
import { getStatusObject, genSurveyPath, getMapByKey, hanleSensitiveDate } from '../utils/index'
|
||||
import * as path from "path";
|
||||
import * as _ from "lodash";
|
||||
import * as moment from "moment";
|
||||
import { mongo } from '../db/mongo';
|
||||
import { rpcInvote } from '../../../rpc';
|
||||
import { SURVEY_STATUS, QUESTION_TYPE, CommonError, UserType, DICT_TYPE } from '../../../types/index';
|
||||
import { getStatusObject, genSurveyPath, hanleSensitiveDate } from '../utils/index';
|
||||
import * as path from 'path';
|
||||
import * as _ from 'lodash';
|
||||
import * as moment from 'moment';
|
||||
import { getFile } from '../utils/index';
|
||||
import { DataItem } from '../../../types/survey';
|
||||
|
||||
class SurveyService {
|
||||
async checkSecurity({ content, dictType }: { content: string, dictType: DICT_TYPE }) {
|
||||
const rpcResult = await rpcInvote<any, { result: boolean }>('security.isHitKeys', {
|
||||
params: { content, dictType },
|
||||
context: {}
|
||||
})
|
||||
return rpcResult.result
|
||||
}
|
||||
async checkSecurity({ content, dictType }: { content: string, dictType: DICT_TYPE }) {
|
||||
const rpcResult = await rpcInvote<unknown, { result: boolean }>('security.isHitKeys', {
|
||||
params: { content, dictType },
|
||||
context: {}
|
||||
});
|
||||
return rpcResult.result;
|
||||
}
|
||||
|
||||
async getBannerData() {
|
||||
const bannerConfPath = path.resolve(__dirname, "../template/banner/index.json");
|
||||
return require(bannerConfPath)
|
||||
}
|
||||
async getBannerData() {
|
||||
const bannerConfPath = path.resolve(__dirname, '../template/banner/index.json');
|
||||
return require(bannerConfPath);
|
||||
}
|
||||
|
||||
async getCodeData({
|
||||
questionType,
|
||||
}: { questionType: QUESTION_TYPE }): Promise<any> {
|
||||
const baseConfPath = path.resolve(__dirname, "../template/surveyTemplate/templateBase.json");
|
||||
const templateConfPath = path.resolve(
|
||||
__dirname,
|
||||
`../template/surveyTemplate/survey/${questionType}.json`,
|
||||
);
|
||||
const baseConf = _.cloneDeep(require(baseConfPath))
|
||||
const templateConf = _.cloneDeep(require(templateConfPath))
|
||||
const codeData = _.merge(baseConf, templateConf);
|
||||
const nowMoment = moment()
|
||||
codeData.baseConf.begTime = nowMoment.format("YYYY-MM-DD HH:mm:ss")
|
||||
codeData.baseConf.endTime = nowMoment.add(10, 'years').format("YYYY-MM-DD HH:mm:ss")
|
||||
return codeData;
|
||||
}
|
||||
async getNewSurveyPath() {
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const surveyPath = genSurveyPath()
|
||||
const surveyPathCount = await surveyMeta.countDocuments({ surveyPath })
|
||||
if (surveyPathCount > 0) { return await this.getNewSurveyPath() }
|
||||
return surveyPath
|
||||
}
|
||||
async getCodeData({
|
||||
questionType,
|
||||
}: { questionType: QUESTION_TYPE }): Promise<unknown> {
|
||||
const baseConfPath = path.resolve(__dirname, '../template/surveyTemplate/templateBase.json');
|
||||
const templateConfPath = path.resolve(
|
||||
__dirname,
|
||||
`../template/surveyTemplate/survey/${questionType}.json`,
|
||||
);
|
||||
|
||||
async add(surveyMetaInfo: { remark: string, questionType: QUESTION_TYPE, title: string, userData: UserType }) {
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const now = Date.now()
|
||||
const surveyPath = await this.getNewSurveyPath()
|
||||
const surveyMetaRes = await surveyMeta.insertOne({
|
||||
surveyPath,
|
||||
remark: surveyMetaInfo.remark,
|
||||
questionType: surveyMetaInfo.questionType,
|
||||
title: surveyMetaInfo.title,
|
||||
creator: surveyMetaInfo.userData.username,
|
||||
owner: surveyMetaInfo.userData.username,
|
||||
curStatus: getStatusObject({ status: SURVEY_STATUS.new }),
|
||||
createDate: now,
|
||||
updateDate: now,
|
||||
})
|
||||
const pageId = surveyMetaRes.insertedId.toString()
|
||||
const surveyConf = await mongo.getCollection({ collectionName: 'surveyConf' });
|
||||
const surveyConfRes = await surveyConf.insertOne({
|
||||
pageId,
|
||||
pageType: surveyMetaInfo.questionType,
|
||||
curStatus: getStatusObject({ status: SURVEY_STATUS.new }),
|
||||
code: await this.getCodeData({
|
||||
questionType: surveyMetaInfo.questionType,
|
||||
})
|
||||
})
|
||||
return {
|
||||
pageId,
|
||||
surveyMetaRes,
|
||||
surveyConfRes
|
||||
const [baseConfStr, templateConfStr] = await Promise.all([getFile(baseConfPath), getFile(templateConfPath)]);
|
||||
|
||||
const baseConf = JSON.parse(baseConfStr);
|
||||
const templateConf = JSON.parse(templateConfStr);
|
||||
const codeData = _.merge(baseConf, templateConf);
|
||||
const nowMoment = moment();
|
||||
codeData.baseConf.begTime = nowMoment.format('YYYY-MM-DD HH:mm:ss');
|
||||
codeData.baseConf.endTime = nowMoment.add(10, 'years').format('YYYY-MM-DD HH:mm:ss');
|
||||
return codeData;
|
||||
}
|
||||
async getNewSurveyPath() {
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const surveyPath = genSurveyPath();
|
||||
const surveyPathCount = await surveyMeta.countDocuments({ surveyPath });
|
||||
if (surveyPathCount > 0) { return await this.getNewSurveyPath(); }
|
||||
return surveyPath;
|
||||
}
|
||||
|
||||
async add(surveyMetaInfo: { remark: string, questionType: QUESTION_TYPE, title: string, userData: UserType }) {
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const now = Date.now();
|
||||
const surveyPath = await this.getNewSurveyPath();
|
||||
const surveyMetaRes = await surveyMeta.insertOne({
|
||||
surveyPath,
|
||||
remark: surveyMetaInfo.remark,
|
||||
questionType: surveyMetaInfo.questionType,
|
||||
title: surveyMetaInfo.title,
|
||||
creator: surveyMetaInfo.userData.username,
|
||||
owner: surveyMetaInfo.userData.username,
|
||||
curStatus: getStatusObject({ status: SURVEY_STATUS.new }),
|
||||
createDate: now,
|
||||
updateDate: now,
|
||||
});
|
||||
const pageId = surveyMetaRes.insertedId.toString();
|
||||
const surveyConf = await mongo.getCollection({ collectionName: 'surveyConf' });
|
||||
const surveyConfRes = await surveyConf.insertOne({
|
||||
pageId,
|
||||
pageType: surveyMetaInfo.questionType,
|
||||
curStatus: getStatusObject({ status: SURVEY_STATUS.new }),
|
||||
code: await this.getCodeData({
|
||||
questionType: surveyMetaInfo.questionType,
|
||||
})
|
||||
});
|
||||
return {
|
||||
pageId,
|
||||
surveyMetaRes,
|
||||
surveyConfRes
|
||||
};
|
||||
}
|
||||
|
||||
async update(surveyParams: { surveyId: string, remark: string, title: string, userData: UserType }) {
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const _id = mongo.getObjectIdByStr(surveyParams.surveyId);
|
||||
const surveyMetaUpdateRes = await surveyMeta.updateOne({
|
||||
_id,
|
||||
owner: surveyParams.userData.username,
|
||||
}, [{
|
||||
$set: {
|
||||
remark: surveyParams.remark,
|
||||
title: surveyParams.title,
|
||||
updateDate: Date.now(),
|
||||
}
|
||||
}, {
|
||||
$set: {
|
||||
'curStatus': {
|
||||
$cond: {
|
||||
if: {
|
||||
$eq: ['$curStatus.status', 'new']
|
||||
},
|
||||
then: '$curStatus',
|
||||
else: getStatusObject({ status: SURVEY_STATUS.editing })
|
||||
}
|
||||
}
|
||||
}
|
||||
}]);
|
||||
if (surveyMetaUpdateRes.matchedCount < 1) {
|
||||
throw new CommonError('更新问卷信息失败,问卷不存在或您不是该问卷所有者');
|
||||
}
|
||||
return {
|
||||
surveyMetaUpdateRes
|
||||
};
|
||||
}
|
||||
|
||||
async update(surveyParams: { surveyId: string, remark: string, title: string, userData: UserType }) {
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const _id = mongo.getObjectIdByStr(surveyParams.surveyId)
|
||||
const surveyMetaUpdateRes = await surveyMeta.updateOne({
|
||||
_id,
|
||||
owner: surveyParams.userData.username,
|
||||
}, [{
|
||||
$set: {
|
||||
remark: surveyParams.remark,
|
||||
title: surveyParams.title,
|
||||
updateDate: Date.now(),
|
||||
}
|
||||
}, {
|
||||
$set: {
|
||||
"curStatus": {
|
||||
$cond: {
|
||||
if: {
|
||||
$eq: ["$curStatus.status", "new"]
|
||||
},
|
||||
then: "$curStatus",
|
||||
else: getStatusObject({ status: SURVEY_STATUS.editing })
|
||||
}
|
||||
}
|
||||
}
|
||||
}])
|
||||
if (surveyMetaUpdateRes.matchedCount < 1) {
|
||||
throw new CommonError("更新问卷信息失败,问卷不存在或您不是该问卷所有者")
|
||||
}
|
||||
return {
|
||||
surveyMetaUpdateRes
|
||||
}
|
||||
async delete(surveyParams: { surveyId: string, userData: UserType }) {
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const _id = mongo.getObjectIdByStr(surveyParams.surveyId);
|
||||
const surveyMetaDeleteRes = await surveyMeta.deleteOne({
|
||||
_id,
|
||||
owner: surveyParams.userData.username,
|
||||
});
|
||||
if (surveyMetaDeleteRes.deletedCount < 1) {
|
||||
throw new CommonError('删除问卷失败,问卷已被删除或您不是该问卷所有者');
|
||||
}
|
||||
return {
|
||||
surveyMetaDeleteRes
|
||||
};
|
||||
}
|
||||
|
||||
async delete(surveyParams: { surveyId: string, userData: UserType }) {
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const _id = mongo.getObjectIdByStr(surveyParams.surveyId)
|
||||
const surveyMetaDeleteRes = await surveyMeta.deleteOne({
|
||||
_id,
|
||||
owner: surveyParams.userData.username,
|
||||
})
|
||||
if (surveyMetaDeleteRes.deletedCount < 1) {
|
||||
throw new CommonError("删除问卷失败,问卷已被删除或您不是该问卷所有者")
|
||||
}
|
||||
return {
|
||||
surveyMetaDeleteRes
|
||||
}
|
||||
}
|
||||
async list(condition: { pageNum: number, pageSize: number, userData: UserType }) {
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const cond = {
|
||||
owner: condition.userData.username
|
||||
};
|
||||
const data = await surveyMeta.find(cond)
|
||||
.sort({ createDate: -1 })
|
||||
.limit(condition.pageSize)
|
||||
.skip((condition.pageNum - 1) * condition.pageSize)
|
||||
.toArray();
|
||||
const count = await surveyMeta.countDocuments(cond);
|
||||
return { data: mongo.convertId2StringByList(data), count };
|
||||
}
|
||||
|
||||
async list(condition: { pageNum: number, pageSize: number, userData: UserType }) {
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const cond = {
|
||||
owner: condition.userData.username
|
||||
getListHeadByDataList(dataList) {
|
||||
const listHead = dataList.map(surveyItem => {
|
||||
let othersCode;
|
||||
if (surveyItem.type === 'radio-star') {
|
||||
const rangeConfigKeys = Object.keys(surveyItem.rangeConfig);
|
||||
if (rangeConfigKeys.length > 0) {
|
||||
othersCode = [{ code: `${surveyItem.field}_custom`, option: '填写理由' }];
|
||||
}
|
||||
const data = await surveyMeta.find(cond)
|
||||
.sort({ createDate: -1 })
|
||||
.limit(condition.pageSize)
|
||||
.skip((condition.pageNum - 1) * condition.pageSize)
|
||||
.toArray()
|
||||
const count = await surveyMeta.countDocuments(cond);
|
||||
return { data: mongo.convertId2StringByList(data), count }
|
||||
}
|
||||
|
||||
getListHeadByDataList(dataList) {
|
||||
const listHead = dataList.map(surveyItem => {
|
||||
let othersCode;
|
||||
if (surveyItem.type === 'radio-star') {
|
||||
const rangeConfigKeys = Object.keys(surveyItem.rangeConfig)
|
||||
if (rangeConfigKeys.length > 0) {
|
||||
othersCode = [{ code: `${surveyItem.field}_custom`, option: "填写理由" }]
|
||||
}
|
||||
} else {
|
||||
othersCode = (surveyItem.options || [])
|
||||
.filter(optionItem => optionItem.othersKey)
|
||||
.map((optionItem) => {
|
||||
return {
|
||||
code: optionItem.othersKey,
|
||||
option: optionItem.text
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
othersCode = (surveyItem.options || [])
|
||||
.filter(optionItem => optionItem.othersKey)
|
||||
.map((optionItem) => {
|
||||
return {
|
||||
field: surveyItem.field,
|
||||
title: surveyItem.title,
|
||||
type: surveyItem.type,
|
||||
othersCode
|
||||
}
|
||||
})
|
||||
listHead.push({
|
||||
field: 'difTime',
|
||||
title: '答题耗时(秒)',
|
||||
type: 'text',
|
||||
})
|
||||
listHead.push({
|
||||
field: 'createDate',
|
||||
title: '提交时间',
|
||||
type: 'text',
|
||||
})
|
||||
return listHead
|
||||
code: optionItem.othersKey,
|
||||
option: optionItem.text
|
||||
};
|
||||
});
|
||||
}
|
||||
return {
|
||||
field: surveyItem.field,
|
||||
title: surveyItem.title,
|
||||
type: surveyItem.type,
|
||||
othersCode
|
||||
};
|
||||
});
|
||||
listHead.push({
|
||||
field: 'difTime',
|
||||
title: '答题耗时(秒)',
|
||||
type: 'text',
|
||||
});
|
||||
listHead.push({
|
||||
field: 'createDate',
|
||||
title: '提交时间',
|
||||
type: 'text',
|
||||
});
|
||||
return listHead;
|
||||
}
|
||||
|
||||
async data(condition: { userData: UserType, surveyId: string, pageNum: number, pageSize: number, isShowSecret: boolean }) {
|
||||
const surveyObjectId = mongo.getObjectIdByStr(condition.surveyId);
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const surveyMetaData = await surveyMeta.findOne({ _id: surveyObjectId });
|
||||
if (surveyMetaData.owner !== condition.userData.username) {
|
||||
throw new CommonError('问卷回收数据列表仅所有人才能打开');
|
||||
}
|
||||
const surveyPublish = await mongo.getCollection({ collectionName: 'surveyPublish' });
|
||||
const publishConf = await surveyPublish.findOne({ pageId: condition.surveyId });
|
||||
const dataList = publishConf?.code?.dataConf?.dataList || [];
|
||||
const listHead = this.getListHeadByDataList(dataList);
|
||||
const dataListMap = _.keyBy(dataList, 'field');
|
||||
const surveySubmit = await mongo.getCollection({ collectionName: 'surveySubmit' });
|
||||
const surveySubmitData = await surveySubmit.find({ pageId: condition.surveyId })
|
||||
.sort({ createDate: -1 })
|
||||
.limit(condition.pageSize)
|
||||
.skip((condition.pageNum - 1) * condition.pageSize)
|
||||
.toArray();
|
||||
const listBody = surveySubmitData.map((surveySubmitResList) => {
|
||||
const data = surveySubmitResList.data;
|
||||
const dataKeys = Object.keys(data);
|
||||
for (const itemKey of dataKeys) {
|
||||
if (typeof itemKey !== 'string') { continue; }
|
||||
if (itemKey.indexOf('data') !== 0) { continue; }
|
||||
const itemConfigKey = itemKey.split('_')[0];
|
||||
const itemConfig: DataItem = dataListMap[itemConfigKey];
|
||||
// 题目删除会出现,数据列表报错
|
||||
if (!itemConfig) { continue; }
|
||||
const doSecretData = (data) => {
|
||||
if (condition.isShowSecret) {
|
||||
return hanleSensitiveDate(data);
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
};
|
||||
data[itemKey] = doSecretData(data[itemKey]);
|
||||
// 处理选项
|
||||
if (itemConfig.type === 'radio-star' && !data[`${itemConfigKey}_custom`]) {
|
||||
data[`${itemConfigKey}_custom`] = data[`${itemConfigKey}_${data[itemConfigKey]}`];
|
||||
}
|
||||
if (!itemConfig?.options?.length) { continue; }
|
||||
const options = itemConfig.options;
|
||||
const optionsMap = _.keyBy(options, 'hash');
|
||||
const getText = e => doSecretData(optionsMap?.[e]?.text || e);
|
||||
if (Array.isArray(data[itemKey])) {
|
||||
data[itemKey] = data[itemKey].map(getText);
|
||||
} else {
|
||||
data[itemKey] = getText(data[itemKey]);
|
||||
}
|
||||
}
|
||||
return {
|
||||
...data,
|
||||
difTime: (surveySubmitResList.difTime / 1000).toFixed(2),
|
||||
createDate: moment(surveySubmitResList.createDate).format('YYYY-MM-DD HH:mm:ss')
|
||||
};
|
||||
});
|
||||
const total = await surveySubmit.countDocuments({ pageId: condition.surveyId });
|
||||
return {
|
||||
total,
|
||||
listHead,
|
||||
listBody
|
||||
};
|
||||
}
|
||||
|
||||
async data(condition: { userData: UserType, surveyId: string, pageNum: number, pageSize: number, isShowSecret: boolean }) {
|
||||
const surveyObjectId = mongo.getObjectIdByStr(condition.surveyId)
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const surveyMetaData = await surveyMeta.findOne({ _id: surveyObjectId })
|
||||
if (surveyMetaData.owner !== condition.userData.username) {
|
||||
throw new CommonError("问卷回收数据列表仅所有人才能打开")
|
||||
}
|
||||
const surveyPublish = await mongo.getCollection({ collectionName: 'surveyPublish' });
|
||||
const publishConf = await surveyPublish.findOne({ pageId: condition.surveyId })
|
||||
const dataList = publishConf?.code?.dataConf?.dataList || []
|
||||
const listHead = this.getListHeadByDataList(dataList)
|
||||
const dataListMap = getMapByKey({ data: dataList, key: 'field' })
|
||||
const surveySubmit = await mongo.getCollection({ collectionName: 'surveySubmit' });
|
||||
const surveySubmitData = await surveySubmit.find({ pageId: condition.surveyId })
|
||||
.sort({ createDate: -1 })
|
||||
.limit(condition.pageSize)
|
||||
.skip((condition.pageNum - 1) * condition.pageSize)
|
||||
.toArray()
|
||||
const listBody = surveySubmitData.map((surveySubmitResList) => {
|
||||
const data = surveySubmitResList.data
|
||||
const dataKeys = Object.keys(data)
|
||||
for (const itemKey of dataKeys) {
|
||||
if (typeof itemKey !== 'string') { continue }
|
||||
if (itemKey.indexOf("data") !== 0) { continue }
|
||||
const itemConfigKey = itemKey.split("_")[0];
|
||||
const itemConfig = dataListMap[itemConfigKey];
|
||||
// 题目删除会出现,数据列表报错
|
||||
if (!itemConfig) { continue }
|
||||
const doSecretData = (data) => {
|
||||
if (itemConfig.isSecret && condition.isShowSecret) {
|
||||
return hanleSensitiveDate(data)
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
data[itemKey] = doSecretData(data[itemKey])
|
||||
// 处理选项
|
||||
if (itemConfig.type === 'radio-star' && !data[`${itemConfigKey}_custom`]) {
|
||||
data[`${itemConfigKey}_custom`] = data[`${itemConfigKey}_${data[itemConfigKey]}`]
|
||||
}
|
||||
if (!itemConfig?.options?.length) { continue }
|
||||
const options = itemConfig.options
|
||||
const optionsMap = getMapByKey({ data: options, key: 'hash' })
|
||||
const getText = e => doSecretData(optionsMap?.[e]?.text || e);
|
||||
if (Array.isArray(data[itemKey])) {
|
||||
data[itemKey] = data[itemKey].map(getText)
|
||||
} else {
|
||||
data[itemKey] = getText(data[itemKey])
|
||||
}
|
||||
}
|
||||
return {
|
||||
...data,
|
||||
difTime: (surveySubmitResList.difTime / 1000).toFixed(2),
|
||||
createDate: moment(surveySubmitResList.createDate).format("YYYY-MM-DD HH:mm:ss")
|
||||
}
|
||||
})
|
||||
const total = await surveySubmit.countDocuments({ pageId: condition.surveyId });
|
||||
return {
|
||||
total,
|
||||
listHead,
|
||||
listBody
|
||||
}
|
||||
|
||||
async get({ surveyId, userData }: { surveyId: string, userData: UserType }) {
|
||||
const surveyObjectId = mongo.getObjectIdByStr(surveyId);
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const surveyMetaData = await surveyMeta.findOne({ _id: surveyObjectId });
|
||||
if (!surveyMetaData) {
|
||||
throw new CommonError('问卷不存在或已被删除');
|
||||
}
|
||||
|
||||
|
||||
async get({ surveyId, userData }: { surveyId: string, userData: UserType }) {
|
||||
const surveyObjectId = mongo.getObjectIdByStr(surveyId)
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const surveyMetaData = await surveyMeta.findOne({ _id: surveyObjectId })
|
||||
if (!surveyMetaData) {
|
||||
throw new CommonError("问卷不存在或已被删除")
|
||||
}
|
||||
if (surveyMetaData.owner !== userData.username) {
|
||||
throw new CommonError("问卷仅所有人才能打开")
|
||||
}
|
||||
const surveyMetaRes = mongo.convertId2StringByDoc(surveyMetaData)
|
||||
const surveyConf = await mongo.getCollection({ collectionName: 'surveyConf' });
|
||||
const surveyConfData = await surveyConf.findOne({ pageId: surveyId })
|
||||
if (!surveyConfData) {
|
||||
throw new CommonError("问卷配置不存在或已被删除")
|
||||
}
|
||||
const surveyConfRes = mongo.convertId2StringByDoc(surveyConfData)
|
||||
return {
|
||||
surveyMetaRes,
|
||||
surveyConfRes
|
||||
}
|
||||
if (surveyMetaData.owner !== userData.username) {
|
||||
throw new CommonError('问卷仅所有人才能打开');
|
||||
}
|
||||
|
||||
|
||||
async saveConf(surveyData: { surveyId: string, configData: any }) {
|
||||
const surveyConf = await mongo.getCollection({ collectionName: 'surveyConf' });
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const saveRes = await surveyConf.updateOne({
|
||||
pageId: surveyData.surveyId
|
||||
}, {
|
||||
$set: {
|
||||
code: surveyData.configData,
|
||||
}
|
||||
})
|
||||
const _id = mongo.getObjectIdByStr(surveyData.surveyId)
|
||||
surveyMeta.updateOne({
|
||||
_id,
|
||||
}, [{
|
||||
$set: {
|
||||
"curStatus": {
|
||||
$cond: {
|
||||
if: {
|
||||
$eq: ["$curStatus.status", "new"]
|
||||
},
|
||||
then: "$curStatus",
|
||||
else: getStatusObject({ status: SURVEY_STATUS.editing })
|
||||
}
|
||||
}
|
||||
}
|
||||
}])
|
||||
return saveRes
|
||||
const surveyMetaRes = mongo.convertId2StringByDoc(surveyMetaData);
|
||||
const surveyConf = await mongo.getCollection({ collectionName: 'surveyConf' });
|
||||
const surveyConfData = await surveyConf.findOne({ pageId: surveyId });
|
||||
if (!surveyConfData) {
|
||||
throw new CommonError('问卷配置不存在或已被删除');
|
||||
}
|
||||
const surveyConfRes = mongo.convertId2StringByDoc(surveyConfData);
|
||||
return {
|
||||
surveyMetaRes,
|
||||
surveyConfRes
|
||||
};
|
||||
}
|
||||
|
||||
async publish({ surveyId, userData }: { surveyId: string, userData: UserType }) {
|
||||
const surveyObjectId = mongo.getObjectIdByStr(surveyId)
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const surveyConf = await mongo.getCollection({ collectionName: 'surveyConf' });
|
||||
const surveyMetaRes = await surveyMeta.findOne({ _id: surveyObjectId })
|
||||
if (!surveyMetaRes) {
|
||||
throw new CommonError("问卷不存在或已被删除,无法发布")
|
||||
}
|
||||
if (surveyMetaRes.owner !== userData.username) {
|
||||
throw new CommonError("只有问卷的所有者才能发布该问卷")
|
||||
}
|
||||
const surveyConfRes = await surveyConf.findOne({ pageId: surveyId })
|
||||
if (!surveyConfRes) {
|
||||
throw new CommonError("问卷配置不存在或已被删除,无法发布")
|
||||
}
|
||||
const surveyPublish = await mongo.getCollection({ collectionName: 'surveyPublish' });
|
||||
// 清除id存储发布
|
||||
delete surveyConfRes._id;
|
||||
surveyConfRes.title = surveyMetaRes.title
|
||||
surveyConfRes.curStatus = surveyMetaRes.curStatus
|
||||
surveyConfRes.surveyPath = surveyMetaRes.surveyPath
|
||||
const dataList = surveyConfRes?.code?.dataConf?.dataList || []
|
||||
for (const data of dataList) {
|
||||
const isDangerKey = await this.checkSecurity({ content: data.title, dictType: DICT_TYPE.danger })
|
||||
if (isDangerKey) {
|
||||
throw new CommonError("问卷存在非法关键字,不允许发布")
|
||||
}
|
||||
const isSecretKey = await this.checkSecurity({ content: data.title, dictType: DICT_TYPE.secret })
|
||||
if (isSecretKey) {
|
||||
data.isSecret = true
|
||||
}
|
||||
}
|
||||
const publishRes = await surveyPublish.updateOne({
|
||||
pageId: surveyId
|
||||
}, {
|
||||
$set: surveyConfRes
|
||||
}, {
|
||||
upsert: true //如果不存在则插入
|
||||
});
|
||||
const updateMetaRes = await surveyMeta.updateOne({
|
||||
_id: surveyObjectId
|
||||
}, {
|
||||
$set: {
|
||||
curStatus: getStatusObject({ status: SURVEY_STATUS.published }),
|
||||
}
|
||||
})
|
||||
return {
|
||||
updateMetaRes,
|
||||
surveyConfRes,
|
||||
publishRes
|
||||
|
||||
async saveConf(surveyData: { surveyId: string, configData: unknown }) {
|
||||
const surveyConf = await mongo.getCollection({ collectionName: 'surveyConf' });
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const saveRes = await surveyConf.updateOne({
|
||||
pageId: surveyData.surveyId
|
||||
}, {
|
||||
$set: {
|
||||
code: surveyData.configData,
|
||||
}
|
||||
});
|
||||
const _id = mongo.getObjectIdByStr(surveyData.surveyId);
|
||||
surveyMeta.updateOne({
|
||||
_id,
|
||||
}, [{
|
||||
$set: {
|
||||
'curStatus': {
|
||||
$cond: {
|
||||
if: {
|
||||
$eq: ['$curStatus.status', 'new']
|
||||
},
|
||||
then: '$curStatus',
|
||||
else: getStatusObject({ status: SURVEY_STATUS.editing })
|
||||
}
|
||||
}
|
||||
}
|
||||
}]);
|
||||
return saveRes;
|
||||
}
|
||||
|
||||
async publish({ surveyId, userData }: { surveyId: string, userData: UserType }) {
|
||||
const surveyObjectId = mongo.getObjectIdByStr(surveyId);
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const surveyConf = await mongo.getCollection({ collectionName: 'surveyConf' });
|
||||
const surveyMetaRes = await surveyMeta.findOne({ _id: surveyObjectId });
|
||||
if (!surveyMetaRes) {
|
||||
throw new CommonError('问卷不存在或已被删除,无法发布');
|
||||
}
|
||||
if (surveyMetaRes.owner !== userData.username) {
|
||||
throw new CommonError('只有问卷的所有者才能发布该问卷');
|
||||
}
|
||||
const surveyConfRes = await surveyConf.findOne({ pageId: surveyId });
|
||||
if (!surveyConfRes) {
|
||||
throw new CommonError('问卷配置不存在或已被删除,无法发布');
|
||||
}
|
||||
const surveyPublish = await mongo.getCollection({ collectionName: 'surveyPublish' });
|
||||
// 清除id存储发布
|
||||
delete surveyConfRes._id;
|
||||
surveyConfRes.title = surveyMetaRes.title;
|
||||
surveyConfRes.curStatus = surveyMetaRes.curStatus;
|
||||
surveyConfRes.surveyPath = surveyMetaRes.surveyPath;
|
||||
const dataList = surveyConfRes?.code?.dataConf?.dataList || [];
|
||||
for (const data of dataList) {
|
||||
const isDangerKey = await this.checkSecurity({ content: data.title, dictType: DICT_TYPE.danger });
|
||||
if (isDangerKey) {
|
||||
throw new CommonError('问卷存在非法关键字,不允许发布');
|
||||
}
|
||||
const isSecretKey = await this.checkSecurity({ content: data.title, dictType: DICT_TYPE.secret });
|
||||
if (isSecretKey) {
|
||||
data.isSecret = true;
|
||||
}
|
||||
}
|
||||
const publishRes = await surveyPublish.updateOne({
|
||||
pageId: surveyId
|
||||
}, {
|
||||
$set: surveyConfRes
|
||||
}, {
|
||||
upsert: true //如果不存在则插入
|
||||
});
|
||||
const updateMetaRes = await surveyMeta.updateOne({
|
||||
_id: surveyObjectId
|
||||
}, {
|
||||
$set: {
|
||||
curStatus: getStatusObject({ status: SURVEY_STATUS.published }),
|
||||
}
|
||||
});
|
||||
return {
|
||||
updateMetaRes,
|
||||
surveyConfRes,
|
||||
publishRes
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const surveyService = new SurveyService()
|
||||
export const surveyService = new SurveyService();
|
@ -1,19 +1,19 @@
|
||||
import { rpcInvote } from '../../../rpc'
|
||||
import { Request } from 'koa'
|
||||
import { UserType, CommonError } from '../../../types/index'
|
||||
import { rpcInvote } from '../../../rpc';
|
||||
import { Request } from 'koa';
|
||||
import { UserType, CommonError } from '../../../types/index';
|
||||
|
||||
class UserService {
|
||||
async checkLogin({ req }: { req: Request }) {
|
||||
if (!req.headers['authorization']) {
|
||||
throw new CommonError('请先登录', 403)
|
||||
}
|
||||
const token = (String(req.headers['authorization']) || '').replace("Bearer ", "")
|
||||
const rpcResult = await rpcInvote<any, { result: UserType }>('user.getUserByToken', {
|
||||
params: { token },
|
||||
context: req
|
||||
})
|
||||
return rpcResult.result
|
||||
async checkLogin({ req }: { req: Request }) {
|
||||
if (!req.headers['authorization']) {
|
||||
throw new CommonError('请先登录', 403);
|
||||
}
|
||||
const token = (String(req.headers['authorization']) || '').replace('Bearer ', '');
|
||||
const rpcResult = await rpcInvote<unknown, { result: UserType }>('user.getUserByToken', {
|
||||
params: { token },
|
||||
context: req
|
||||
});
|
||||
return rpcResult.result;
|
||||
}
|
||||
}
|
||||
|
||||
export const userService = new UserService()
|
||||
export const userService = new UserService();
|
@ -40,7 +40,6 @@
|
||||
"addressType": 3,
|
||||
"isAuto": false,
|
||||
"urlKey": "",
|
||||
"othersKeyMap": {},
|
||||
"textRange": {
|
||||
"min": {
|
||||
"placeholder": "0",
|
||||
@ -103,7 +102,6 @@
|
||||
"exclude": false,
|
||||
"addressType": 3,
|
||||
"isAuto": false,
|
||||
"othersKeyMap": {},
|
||||
"textRange": {
|
||||
"min": {
|
||||
"placeholder": "0",
|
||||
|
@ -68,7 +68,6 @@
|
||||
"valid": "",
|
||||
"title": "标题1",
|
||||
"answer": "",
|
||||
"othersKeyMap": {},
|
||||
"textRange": {
|
||||
"min": {
|
||||
"placeholder": "0",
|
||||
@ -118,7 +117,6 @@
|
||||
"type": "radio-star",
|
||||
"title": "标题2",
|
||||
"answer": "",
|
||||
"othersKeyMap": {},
|
||||
"options": [],
|
||||
"textRange": {
|
||||
"min": {
|
||||
|
@ -48,7 +48,6 @@
|
||||
"jumpTo": "",
|
||||
"startDate": "",
|
||||
"endDate": "",
|
||||
"othersKeyMap": {},
|
||||
"textRange": {
|
||||
"min": {
|
||||
"placeholder": "0",
|
||||
@ -134,7 +133,6 @@
|
||||
},
|
||||
"startDate": "",
|
||||
"endDate": "",
|
||||
"othersKeyMap": {},
|
||||
"textRange": {
|
||||
"min": {
|
||||
"placeholder": "0",
|
||||
|
@ -64,7 +64,6 @@
|
||||
"addressType": 3,
|
||||
"isAuto": false,
|
||||
"urlKey": "",
|
||||
"othersKeyMap": {},
|
||||
"textRange": {
|
||||
"min": {
|
||||
"placeholder": "0",
|
||||
@ -122,7 +121,6 @@
|
||||
},
|
||||
"star": 5,
|
||||
"addressType": 3,
|
||||
"othersKeyMap": {},
|
||||
"textRange": {
|
||||
"min": {
|
||||
"placeholder": "0",
|
||||
|
@ -1,37 +1,37 @@
|
||||
const base58KeysObject = {
|
||||
"1":0,"2":1,"3":2,"4":3,"5":4,"6":5,"7":6,"8":7,"9":8,
|
||||
"A":9,"B":10,"C":11,"D":12,"E":13,"F":14,"G":15,"H":16,"J":17,
|
||||
"K":18,"L":19,"M":20,"N":21,"P":22,"Q":23,"R":24,"S":25,"T":26,
|
||||
"U":27,"V":28,"W":29,"X":30,"Y":31,"Z":32,
|
||||
"a":33,"b":34,"c":35,"d":36,"e":37,"f":38,"g":39,"h":40,"i":41,"j":42,
|
||||
"k":43,"m":44,"n":45,"o":46,"p":47,"q":48,"r":49,"s":50,"t":51,
|
||||
"u":52,"v":53,"w":54,"x":55,"y":56,"z":57
|
||||
'1': 0, '2': 1, '3': 2, '4': 3, '5': 4, '6': 5, '7': 6, '8': 7, '9': 8,
|
||||
'A': 9, 'B': 10, 'C': 11, 'D': 12, 'E': 13, 'F': 14, 'G': 15, 'H': 16, 'J': 17,
|
||||
'K': 18, 'L': 19, 'M': 20, 'N': 21, 'P': 22, 'Q': 23, 'R': 24, 'S': 25, 'T': 26,
|
||||
'U': 27, 'V': 28, 'W': 29, 'X': 30, 'Y': 31, 'Z': 32,
|
||||
'a': 33, 'b': 34, 'c': 35, 'd': 36, 'e': 37, 'f': 38, 'g': 39, 'h': 40, 'i': 41, 'j': 42,
|
||||
'k': 43, 'm': 44, 'n': 45, 'o': 46, 'p': 47, 'q': 48, 'r': 49, 's': 50, 't': 51,
|
||||
'u': 52, 'v': 53, 'w': 54, 'x': 55, 'y': 56, 'z': 57
|
||||
};
|
||||
const base58Keys = Object.keys(base58KeysObject);
|
||||
const base58Len = 58n;
|
||||
|
||||
export function hex2Base58(hexNum:string):string
|
||||
{
|
||||
let base58NumArray =[];
|
||||
let bigHexNumber = BigInt(`0x${hexNum}`);
|
||||
while (bigHexNumber>=58n)
|
||||
{
|
||||
base58NumArray.unshift(base58Keys[(bigHexNumber % base58Len).toString()]);
|
||||
bigHexNumber = bigHexNumber / base58Len;
|
||||
}
|
||||
base58NumArray.unshift(base58Keys[bigHexNumber.toString()]);
|
||||
return base58NumArray.join('');
|
||||
const base58NumArray =[];
|
||||
let bigHexNumber = BigInt(`0x${hexNum}`);
|
||||
while (bigHexNumber>=58n)
|
||||
{
|
||||
base58NumArray.unshift(base58Keys[(bigHexNumber % base58Len).toString()]);
|
||||
bigHexNumber = bigHexNumber / base58Len;
|
||||
}
|
||||
base58NumArray.unshift(base58Keys[bigHexNumber.toString()]);
|
||||
return base58NumArray.join('');
|
||||
}
|
||||
|
||||
export function base582Hex(base58Num:string):string
|
||||
{
|
||||
let base58NumArray =base58Num.split('');
|
||||
let big58Number = 0n;
|
||||
let len = base58NumArray.length;
|
||||
for(let i = 1;i<=len;i++)
|
||||
{
|
||||
let big58NumberTemp = BigInt(base58KeysObject[base58NumArray[len-i]])*(base58Len** BigInt(i-1));
|
||||
big58Number += big58NumberTemp;
|
||||
}
|
||||
return big58Number.toString(16);
|
||||
const base58NumArray =base58Num.split('');
|
||||
let big58Number = 0n;
|
||||
const len = base58NumArray.length;
|
||||
for(let i = 1;i<=len;i++)
|
||||
{
|
||||
const big58NumberTemp = BigInt(base58KeysObject[base58NumArray[len-i]])*(base58Len** BigInt(i-1));
|
||||
big58Number += big58NumberTemp;
|
||||
}
|
||||
return big58Number.toString(16);
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
import { SURVEY_STATUS, CommonError } from '../../../types/index'
|
||||
import { hex2Base58 } from './base58'
|
||||
import * as Joi from 'joi'
|
||||
import { SURVEY_STATUS, CommonError } from '../../../types/index';
|
||||
import { hex2Base58 } from './base58';
|
||||
import * as Joi from 'joi';
|
||||
import * as fs from 'fs';
|
||||
|
||||
export function getStatusObject({status}:{status:SURVEY_STATUS}) {
|
||||
export function getStatusObject({ status }: { status: SURVEY_STATUS }) {
|
||||
return {
|
||||
status,
|
||||
id: status,
|
||||
@ -10,39 +11,51 @@ export function getStatusObject({status}:{status:SURVEY_STATUS}) {
|
||||
};
|
||||
}
|
||||
|
||||
export function getValidateValue<T=any>(validationResult:Joi.ValidationResult<T>):T {
|
||||
if(validationResult.error) {
|
||||
throw new CommonError(validationResult.error.details.map(e=>e.message).join())
|
||||
export function getValidateValue<T = unknown>(validationResult: Joi.ValidationResult<T>): T {
|
||||
if (validationResult.error) {
|
||||
throw new CommonError(validationResult.error.details.map(e => e.message).join());
|
||||
}
|
||||
return validationResult.value;
|
||||
}
|
||||
|
||||
export function genSurveyPath() {
|
||||
return hex2Base58(process.hrtime.bigint().toString(16))
|
||||
return hex2Base58(process.hrtime.bigint().toString(16));
|
||||
}
|
||||
|
||||
export function getMapByKey({data,key}:{data:Array<any>,key:string}) {
|
||||
const datamap = {}
|
||||
for(const item of data) {
|
||||
datamap[item[key]] = item
|
||||
}
|
||||
return datamap
|
||||
}
|
||||
|
||||
export function hanleSensitiveDate(value:string = ''): string {
|
||||
export function hanleSensitiveDate(value: string = ''): string {
|
||||
if (!value) {
|
||||
return '*'
|
||||
return '*';
|
||||
}
|
||||
|
||||
let str = '' + value
|
||||
let str = '' + value;
|
||||
if (str.length === 1) {
|
||||
str = '*'
|
||||
str = '*';
|
||||
}
|
||||
if (str.length === 2) {
|
||||
str = str[0] + '*'
|
||||
str = str[0] + '*';
|
||||
}
|
||||
if (str.length >= 3) {
|
||||
str = str[0] + '***' + str.slice(str.length - 1)
|
||||
str = str[0] + '***' + str.slice(str.length - 1);
|
||||
}
|
||||
return str
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
export const getFile = function(path, { encoding }: { encoding } = { encoding: 'utf-8' }): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
fs.stat(path, err => {
|
||||
if (!err) {
|
||||
fs.readFile(path, { encoding }, (err, data) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(data.toString());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
};
|
@ -1,17 +1,17 @@
|
||||
const config = {
|
||||
mongo:{
|
||||
url: process.env.xiaojuSurveyMongoUrl ||'mongodb://localhost:27017',
|
||||
dbName:'xiaojuSurvey',
|
||||
},
|
||||
session:{
|
||||
expireTime: parseInt(process.env.xiaojuSurveySessionExpireTime) || 8*3600*1000
|
||||
},
|
||||
encrypt:{
|
||||
type: process.env.xiaojuSurveyEncryptType ||'aes',
|
||||
aesCodelength: parseInt(process.env.xiaojuSurveyAesCodelength) || 10 //aes密钥长度
|
||||
}
|
||||
}
|
||||
mongo: {
|
||||
url: process.env.xiaojuSurveyMongoUrl ||'mongodb://localhost:27017',
|
||||
dbName: 'xiaojuSurvey',
|
||||
},
|
||||
session: {
|
||||
expireTime: parseInt(process.env.xiaojuSurveySessionExpireTime) || 8*3600*1000
|
||||
},
|
||||
encrypt: {
|
||||
type: process.env.xiaojuSurveyEncryptType ||'aes',
|
||||
aesCodelength: parseInt(process.env.xiaojuSurveyAesCodelength) || 10 //aes密钥长度
|
||||
}
|
||||
};
|
||||
|
||||
export function getConfig() {
|
||||
return config
|
||||
return config;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { getConfig } from '../config/index'
|
||||
import MongoService from '../../../utils/mongoService'
|
||||
import { getConfig } from '../config/index';
|
||||
import MongoService from '../../../utils/mongoService';
|
||||
|
||||
const config = getConfig()
|
||||
const config = getConfig();
|
||||
|
||||
export const mongo = new MongoService({ url: config.mongo.url, dbName: config.mongo.dbName })
|
||||
export const mongo = new MongoService({ url: config.mongo.url, dbName: config.mongo.dbName });
|
@ -1,52 +1,52 @@
|
||||
import { SurveyServer } from '../../decorator'
|
||||
import { Request, Response } from 'koa'
|
||||
import { surveySubmitService } from './service/surveySubmitService'
|
||||
import { surveyPublishService } from './service/surveyPublishService'
|
||||
import { getValidateValue } from './utils/index'
|
||||
import { checkSign } from './utils/checkSign'
|
||||
import * as Joi from 'joi'
|
||||
import { SurveyApp, SurveyServer } from '../../decorator';
|
||||
import { surveySubmitService } from './service/surveySubmitService';
|
||||
import { surveyPublishService } from './service/surveyPublishService';
|
||||
import { getValidateValue } from './utils/index';
|
||||
import { checkSign } from './utils/checkSign';
|
||||
import * as Joi from 'joi';
|
||||
|
||||
@SurveyApp('/api/surveyPublish')
|
||||
export default class SurveyPublish {
|
||||
// 获取发布配置
|
||||
@SurveyServer({ type: 'http', method: 'get', routerName: '/getSurveyPublish' })
|
||||
async getSurveyPublish({ req, res }: { req: Request, res: Response }) {
|
||||
async getSurveyPublish({ req }) {
|
||||
const surveySubmitData = getValidateValue(Joi.object({
|
||||
surveyPath: Joi.string().required(),
|
||||
}).validate(req.query, { allowUnknown: true }));
|
||||
const data = await surveyPublishService.get(surveySubmitData)
|
||||
const data = await surveyPublishService.get(surveySubmitData);
|
||||
return {
|
||||
code: 200,
|
||||
data: data.surveyPublishRes,
|
||||
}
|
||||
};
|
||||
}
|
||||
// 获取投票
|
||||
@SurveyServer({ type: 'http', method: 'get', routerName: '/queryVote' })
|
||||
async queryVote({ req, res }: { req: Request, res: Response }) {
|
||||
async queryVote({ req }) {
|
||||
const params = getValidateValue(Joi.object({
|
||||
surveyPath: Joi.string().required(),
|
||||
voteKeyList: Joi.string().required(),
|
||||
}).validate(req.query, { allowUnknown: true }));
|
||||
params.voteKeyList = params.voteKeyList.split(',')
|
||||
const data = await surveyPublishService.queryVote(params)
|
||||
params.voteKeyList = params.voteKeyList.split(',');
|
||||
const data = await surveyPublishService.queryVote(params);
|
||||
return {
|
||||
code: 200,
|
||||
data: data,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SurveyServer({ type: 'http', method: 'get', routerName: '/getEncryptInfo' })
|
||||
async getEncryptInfo({ req, res }: { req: Request, res: Response }) {
|
||||
const data = await surveySubmitService.getEncryptInfo()
|
||||
async getEncryptInfo() {
|
||||
const data = await surveySubmitService.getEncryptInfo();
|
||||
return {
|
||||
code: 200,
|
||||
data: data,
|
||||
}
|
||||
};
|
||||
}
|
||||
// 提交问卷
|
||||
@SurveyServer({ type: 'http', method: 'post', routerName: '/submit' })
|
||||
async submit({ req, res }: { req: Request, res: Response }) {
|
||||
async submit({ req }) {
|
||||
// 检查签名
|
||||
checkSign(req.body)
|
||||
checkSign(req.body);
|
||||
// 校验参数
|
||||
const surveySubmitData = getValidateValue(Joi.object({
|
||||
surveyPath: Joi.string().required(),
|
||||
@ -54,10 +54,10 @@ export default class SurveyPublish {
|
||||
encryptType: Joi.string(),
|
||||
sessionId: Joi.string(),
|
||||
}).validate(req.body, { allowUnknown: true }));
|
||||
await surveySubmitService.submit({ surveySubmitData })
|
||||
await surveySubmitService.submit({ surveySubmitData });
|
||||
return {
|
||||
code: 200,
|
||||
msg: "提交成功",
|
||||
}
|
||||
msg: '提交成功',
|
||||
};
|
||||
}
|
||||
}
|
@ -1,56 +1,66 @@
|
||||
import { mongo } from '../db/mongo'
|
||||
|
||||
import { mongo } from '../db/mongo';
|
||||
import { KeyStore } from '../../../types/keyStore';
|
||||
// 该服务用于模拟redis
|
||||
class SurveyKeyStoreService {
|
||||
getKeyStoreResult(surveyKeyStoreData: Array<any>) {
|
||||
const surveyKeyStoreReult = {}
|
||||
for (const surveyKeyStoreItem of surveyKeyStoreData) {
|
||||
surveyKeyStoreReult[surveyKeyStoreItem.key] = surveyKeyStoreItem.data
|
||||
}
|
||||
return surveyKeyStoreReult
|
||||
getKeyStoreResult(surveyKeyStoreData: Array<KeyStore>) {
|
||||
const surveyKeyStoreReult = {};
|
||||
for (const surveyKeyStoreItem of surveyKeyStoreData) {
|
||||
surveyKeyStoreReult[surveyKeyStoreItem.key] = surveyKeyStoreItem.data;
|
||||
}
|
||||
return surveyKeyStoreReult;
|
||||
}
|
||||
|
||||
async set({ surveyPath, key, data, type }) {
|
||||
const surveyKeyStore = await mongo.getCollection({ collectionName: 'surveyKeyStore' });
|
||||
const setResult = await surveyKeyStore.updateOne({
|
||||
key,
|
||||
surveyPath,
|
||||
type
|
||||
}, {
|
||||
$set: {
|
||||
key,
|
||||
surveyPath,
|
||||
type,
|
||||
data,
|
||||
createDate: Date.now(),
|
||||
updateDate: Date.now(),
|
||||
}
|
||||
}, {
|
||||
upsert: true //如果不存在则插入
|
||||
})
|
||||
return setResult
|
||||
}
|
||||
async set({ surveyPath, key, data, type }) {
|
||||
const surveyKeyStore = await mongo.getCollection({ collectionName: 'surveyKeyStore' });
|
||||
const setResult = await surveyKeyStore.updateOne({
|
||||
key,
|
||||
surveyPath,
|
||||
type
|
||||
}, {
|
||||
$set: {
|
||||
key,
|
||||
surveyPath,
|
||||
type,
|
||||
data,
|
||||
createDate: Date.now(),
|
||||
updateDate: Date.now(),
|
||||
}
|
||||
}, {
|
||||
upsert: true //如果不存在则插入
|
||||
});
|
||||
return setResult;
|
||||
}
|
||||
|
||||
async get({ surveyPath, key, type }) {
|
||||
const surveyKeyStore = await mongo.getCollection({ collectionName: 'surveyKeyStore' });
|
||||
const surveyKeyStoreData = await surveyKeyStore.findOne({
|
||||
key,
|
||||
surveyPath,
|
||||
type
|
||||
})
|
||||
return surveyKeyStoreData?.data
|
||||
}
|
||||
async get({ surveyPath, key, type }) {
|
||||
const surveyKeyStore = await mongo.getCollection({ collectionName: 'surveyKeyStore' });
|
||||
const surveyKeyStoreData = await surveyKeyStore.findOne({
|
||||
key,
|
||||
surveyPath,
|
||||
type
|
||||
});
|
||||
return surveyKeyStoreData?.data;
|
||||
}
|
||||
|
||||
async getAll({ surveyPath, keyList, type }) {
|
||||
const surveyKeyStore = await mongo.getCollection({ collectionName: 'surveyKeyStore' });
|
||||
const surveyKeyStoreData = await surveyKeyStore.find({
|
||||
key: { $in: keyList },
|
||||
surveyPath,
|
||||
type
|
||||
}).toArray()
|
||||
return this.getKeyStoreResult(surveyKeyStoreData)
|
||||
}
|
||||
async getAll({ surveyPath, keyList, type }) {
|
||||
const surveyKeyStore = await mongo.getCollection({ collectionName: 'surveyKeyStore' });
|
||||
const res = await surveyKeyStore.find({
|
||||
key: { $in: keyList },
|
||||
surveyPath,
|
||||
type
|
||||
}).toArray();
|
||||
const surveyKeyStoreData : Array<KeyStore> = res.map(doc => {
|
||||
return {
|
||||
key: doc.key,
|
||||
surveyPath: doc.surveyPath,
|
||||
type: doc.type,
|
||||
data: doc.data,
|
||||
createDate: doc.createDate,
|
||||
updateDate: doc.updateDate,
|
||||
};
|
||||
});
|
||||
return this.getKeyStoreResult(surveyKeyStoreData);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const surveyKeyStoreService = new SurveyKeyStoreService()
|
||||
export const surveyKeyStoreService = new SurveyKeyStoreService();
|
@ -1,31 +1,31 @@
|
||||
import { mongo } from '../db/mongo'
|
||||
import { surveyKeyStoreService } from './surveyKeyStoreService'
|
||||
import { CommonError } from '../../../types/index'
|
||||
import { mongo } from '../db/mongo';
|
||||
import { surveyKeyStoreService } from './surveyKeyStoreService';
|
||||
import { CommonError } from '../../../types/index';
|
||||
|
||||
class SurveyPublishService {
|
||||
async get({ surveyPath }: { surveyPath: string }) {
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const surveyMetaData = await surveyMeta.findOne({ surveyPath })
|
||||
if (!surveyMetaData) {
|
||||
throw new CommonError('该问卷已不存在')
|
||||
}
|
||||
const surveyMetaRes = mongo.convertId2StringByDoc(surveyMetaData)
|
||||
const surveyPublish = await mongo.getCollection({ collectionName: 'surveyPublish' });
|
||||
const surveyPublishData = await surveyPublish.findOne({ pageId: surveyMetaRes._id.toString() }, { sort: { createDate: -1 } })
|
||||
if (!surveyPublishData) {
|
||||
throw new CommonError('该问卷未发布')
|
||||
}
|
||||
const surveyPublishRes = mongo.convertId2StringByDoc(surveyPublishData)
|
||||
return {
|
||||
surveyMetaRes,
|
||||
surveyPublishRes
|
||||
}
|
||||
async get({ surveyPath }: { surveyPath: string }) {
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const surveyMetaData = await surveyMeta.findOne({ surveyPath });
|
||||
if (!surveyMetaData) {
|
||||
throw new CommonError('该问卷已不存在');
|
||||
}
|
||||
const surveyMetaRes = mongo.convertId2StringByDoc(surveyMetaData);
|
||||
const surveyPublish = await mongo.getCollection({ collectionName: 'surveyPublish' });
|
||||
const surveyPublishData = await surveyPublish.findOne({ pageId: surveyMetaRes._id.toString() }, { sort: { createDate: -1 } });
|
||||
if (!surveyPublishData) {
|
||||
throw new CommonError('该问卷未发布');
|
||||
}
|
||||
const surveyPublishRes = mongo.convertId2StringByDoc(surveyPublishData);
|
||||
return {
|
||||
surveyMetaRes,
|
||||
surveyPublishRes
|
||||
};
|
||||
}
|
||||
|
||||
async queryVote({ surveyPath, voteKeyList }: { surveyPath: string, voteKeyList: Array<string> }) {
|
||||
return await surveyKeyStoreService.getAll({ surveyPath, keyList: voteKeyList, type: 'vote' })
|
||||
}
|
||||
async queryVote({ surveyPath, voteKeyList }: { surveyPath: string, voteKeyList: Array<string> }) {
|
||||
return await surveyKeyStoreService.getAll({ surveyPath, keyList: voteKeyList, type: 'vote' });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const surveyPublishService = new SurveyPublishService()
|
||||
export const surveyPublishService = new SurveyPublishService();
|
@ -1,130 +1,132 @@
|
||||
import { mongo } from '../db/mongo'
|
||||
import { getStatusObject, getMapByKey, randomCode } from '../utils/index'
|
||||
import { SURVEY_STATUS, CommonError } from '../../../types/index'
|
||||
import { surveyKeyStoreService } from './surveyKeyStoreService'
|
||||
import { getConfig } from '../config/index'
|
||||
import * as CryptoJS from 'crypto-js'
|
||||
import { mongo } from '../db/mongo';
|
||||
import { getStatusObject, randomCode } from '../utils/index';
|
||||
import { SURVEY_STATUS, CommonError } from '../../../types/index';
|
||||
import { surveyKeyStoreService } from './surveyKeyStoreService';
|
||||
import { getConfig } from '../config/index';
|
||||
import * as CryptoJS from 'crypto-js';
|
||||
import * as aes from 'crypto-js/aes';
|
||||
import * as moment from 'moment'
|
||||
const config = getConfig()
|
||||
import * as moment from 'moment';
|
||||
import { keyBy } from 'lodash';
|
||||
|
||||
const config = getConfig();
|
||||
|
||||
class SurveySubmitService {
|
||||
|
||||
async addSessionData(data) {
|
||||
const surveySession = await mongo.getCollection({ collectionName: 'surveySession' });
|
||||
const surveySessionRes = await surveySession.insertOne({
|
||||
data,
|
||||
expireDate: Date.now() + config.session.expireTime
|
||||
})
|
||||
return {
|
||||
sessionId: surveySessionRes.insertedId.toString(),
|
||||
...data
|
||||
}
|
||||
}
|
||||
async addSessionData(data) {
|
||||
const surveySession = await mongo.getCollection({ collectionName: 'surveySession' });
|
||||
const surveySessionRes = await surveySession.insertOne({
|
||||
data,
|
||||
expireDate: Date.now() + config.session.expireTime
|
||||
});
|
||||
return {
|
||||
sessionId: surveySessionRes.insertedId.toString(),
|
||||
...data
|
||||
};
|
||||
}
|
||||
|
||||
async getSessionData(sessionId) {
|
||||
const surveySession = await mongo.getCollection({ collectionName: 'surveySession' });
|
||||
const sessionObjectId = mongo.getObjectIdByStr(sessionId)
|
||||
const surveySessionRes = await surveySession.findOne({ _id: sessionObjectId })
|
||||
await surveySession.deleteMany({ expireDate: { $lt: Date.now() } })
|
||||
return { sessionId, data: surveySessionRes.data }
|
||||
}
|
||||
async getSessionData(sessionId) {
|
||||
const surveySession = await mongo.getCollection({ collectionName: 'surveySession' });
|
||||
const sessionObjectId = mongo.getObjectIdByStr(sessionId);
|
||||
const surveySessionRes = await surveySession.findOne({ _id: sessionObjectId });
|
||||
await surveySession.deleteMany({ expireDate: { $lt: Date.now() } });
|
||||
return { sessionId, data: surveySessionRes.data };
|
||||
}
|
||||
|
||||
async getEncryptInfo() {
|
||||
const encryptType = config.encrypt.type
|
||||
let data = {}
|
||||
if (encryptType === 'aes') {
|
||||
data = await this.addSessionData({
|
||||
code: randomCode(config.encrypt.aesCodelength)
|
||||
})
|
||||
}
|
||||
return {
|
||||
encryptType,
|
||||
data
|
||||
}
|
||||
async getEncryptInfo() {
|
||||
const encryptType = config.encrypt.type;
|
||||
let data = {};
|
||||
if (encryptType === 'aes') {
|
||||
data = await this.addSessionData({
|
||||
code: randomCode(config.encrypt.aesCodelength)
|
||||
});
|
||||
}
|
||||
return {
|
||||
encryptType,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
async submit({ surveySubmitData }: { surveySubmitData: any }) {
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const surveyMetaRes = mongo.convertId2StringByDoc(
|
||||
await surveyMeta.findOne({ surveyPath: surveySubmitData.surveyPath })
|
||||
)
|
||||
if (!surveyMetaRes) {
|
||||
throw new CommonError('该问卷已不存在,无法提交')
|
||||
}
|
||||
const pageId = surveyMetaRes._id.toString()
|
||||
const surveyPublish = await mongo.getCollection({ collectionName: 'surveyPublish' });
|
||||
const publishConf = await surveyPublish.findOne({ pageId })
|
||||
const surveySubmit = await mongo.getCollection({ collectionName: 'surveySubmit' });
|
||||
if (surveySubmitData.encryptType === 'base64') {
|
||||
surveySubmitData.data = JSON.parse(decodeURIComponent(Buffer.from(surveySubmitData.data, "base64").toString()))
|
||||
} else if (surveySubmitData.encryptType === 'aes') {
|
||||
const sessionData = await this.getSessionData(surveySubmitData.sessionId)
|
||||
surveySubmitData.data = JSON.parse(decodeURIComponent(aes.decrypt(surveySubmitData.data, sessionData.data.code).toString(CryptoJS.enc.Utf8)))
|
||||
} else {
|
||||
surveySubmitData.data = JSON.parse(surveySubmitData.data)
|
||||
}
|
||||
// 提交时间限制
|
||||
const begTime = publishConf?.code?.baseConf?.begTime || 0
|
||||
const endTime = publishConf?.code?.baseConf?.endTime || 0
|
||||
if (begTime && endTime) {
|
||||
const nowStamp = Date.now()
|
||||
const begTimeStamp = new Date(begTime).getTime()
|
||||
const endTimeStamp = new Date(endTime).getTime()
|
||||
if (nowStamp < begTimeStamp || nowStamp > endTimeStamp) {
|
||||
throw new CommonError('不在答题有效期内')
|
||||
}
|
||||
}
|
||||
// 提交时间段限制
|
||||
const answerBegTime = publishConf?.code?.baseConf?.answerBegTime || "00:00:00"
|
||||
const answerEndTime = publishConf?.code?.baseConf?.answerEndTime || "23:59:59"
|
||||
if (answerBegTime && answerEndTime) {
|
||||
const nowStamp = Date.now()
|
||||
const ymdString = moment().format('YYYY-MM-DD');
|
||||
const answerBegTimeStamp = new Date(`${ymdString} ${answerBegTime}`).getTime()
|
||||
const answerEndTimeStamp = new Date(`${ymdString} ${answerEndTime}`).getTime()
|
||||
if (nowStamp < answerBegTimeStamp || nowStamp > answerEndTimeStamp) {
|
||||
throw new CommonError('不在答题时段内')
|
||||
}
|
||||
}
|
||||
// 提交总数限制
|
||||
const tLimit = publishConf?.code?.baseConf?.tLimit || 0
|
||||
if (tLimit > 0) {
|
||||
// 提升性能可以使用redis
|
||||
let nowSubmitCount = await surveySubmit.countDocuments({ surveyPath: surveySubmitData.surveyPath }) || 0
|
||||
if (nowSubmitCount >= tLimit) {
|
||||
throw new CommonError('超出提交总数限制')
|
||||
}
|
||||
}
|
||||
// 投票信息保存
|
||||
const dataList = publishConf?.code?.dataConf?.dataList || []
|
||||
const dataListMap = getMapByKey({ data: dataList, key: 'field' })
|
||||
const surveySubmitDataKeys = Object.keys(surveySubmitData.data)
|
||||
for (const field of surveySubmitDataKeys) {
|
||||
const configData = dataListMap[field]
|
||||
if (configData && /vote/.exec(configData.type)) {
|
||||
const voteData = (await surveyKeyStoreService.get({ surveyPath: surveySubmitData.surveyPath, key: field, type: 'vote' })) || { total: 0 }
|
||||
const fields = Array.isArray(surveySubmitData.data[field]) ? surveySubmitData.data[field] : [surveySubmitData.data[field]]
|
||||
for (const field of fields) {
|
||||
voteData.total++;
|
||||
if (!voteData[field]) {
|
||||
voteData[field] = 1
|
||||
} else {
|
||||
voteData[field]++;
|
||||
}
|
||||
}
|
||||
await surveyKeyStoreService.set({ surveyPath: surveySubmitData.surveyPath, key: field, data: voteData, type: 'vote' })
|
||||
}
|
||||
}
|
||||
// 提交问卷
|
||||
const surveySubmitRes = await surveySubmit.insertOne({
|
||||
...surveySubmitData,
|
||||
pageId,
|
||||
curStatus: getStatusObject({ status: SURVEY_STATUS.new }),
|
||||
createDate: Date.now()
|
||||
})
|
||||
return surveySubmitRes
|
||||
async submit({ surveySubmitData }) {
|
||||
const surveyMeta = await mongo.getCollection({ collectionName: 'surveyMeta' });
|
||||
const surveyMetaRes = mongo.convertId2StringByDoc(
|
||||
await surveyMeta.findOne({ surveyPath: surveySubmitData.surveyPath })
|
||||
);
|
||||
if (!surveyMetaRes) {
|
||||
throw new CommonError('该问卷已不存在,无法提交');
|
||||
}
|
||||
const pageId = surveyMetaRes._id.toString();
|
||||
const surveyPublish = await mongo.getCollection({ collectionName: 'surveyPublish' });
|
||||
const publishConf = await surveyPublish.findOne({ pageId });
|
||||
const surveySubmit = await mongo.getCollection({ collectionName: 'surveySubmit' });
|
||||
if (surveySubmitData.encryptType === 'base64') {
|
||||
surveySubmitData.data = JSON.parse(decodeURIComponent(Buffer.from(surveySubmitData.data, 'base64').toString()));
|
||||
} else if (surveySubmitData.encryptType === 'aes') {
|
||||
const sessionData = await this.getSessionData(surveySubmitData.sessionId);
|
||||
surveySubmitData.data = JSON.parse(decodeURIComponent(aes.decrypt(surveySubmitData.data, sessionData.data.code).toString(CryptoJS.enc.Utf8)));
|
||||
} else {
|
||||
surveySubmitData.data = JSON.parse(surveySubmitData.data);
|
||||
}
|
||||
// 提交时间限制
|
||||
const begTime = publishConf?.code?.baseConf?.begTime || 0;
|
||||
const endTime = publishConf?.code?.baseConf?.endTime || 0;
|
||||
if (begTime && endTime) {
|
||||
const nowStamp = Date.now();
|
||||
const begTimeStamp = new Date(begTime).getTime();
|
||||
const endTimeStamp = new Date(endTime).getTime();
|
||||
if (nowStamp < begTimeStamp || nowStamp > endTimeStamp) {
|
||||
throw new CommonError('不在答题有效期内');
|
||||
}
|
||||
}
|
||||
// 提交时间段限制
|
||||
const answerBegTime = publishConf?.code?.baseConf?.answerBegTime || '00:00:00';
|
||||
const answerEndTime = publishConf?.code?.baseConf?.answerEndTime || '23:59:59';
|
||||
if (answerBegTime && answerEndTime) {
|
||||
const nowStamp = Date.now();
|
||||
const ymdString = moment().format('YYYY-MM-DD');
|
||||
const answerBegTimeStamp = new Date(`${ymdString} ${answerBegTime}`).getTime();
|
||||
const answerEndTimeStamp = new Date(`${ymdString} ${answerEndTime}`).getTime();
|
||||
if (nowStamp < answerBegTimeStamp || nowStamp > answerEndTimeStamp) {
|
||||
throw new CommonError('不在答题时段内');
|
||||
}
|
||||
}
|
||||
// 提交总数限制
|
||||
const tLimit = publishConf?.code?.baseConf?.tLimit || 0;
|
||||
if (tLimit > 0) {
|
||||
// 提升性能可以使用redis
|
||||
const nowSubmitCount = await surveySubmit.countDocuments({ surveyPath: surveySubmitData.surveyPath }) || 0;
|
||||
if (nowSubmitCount >= tLimit) {
|
||||
throw new CommonError('超出提交总数限制');
|
||||
}
|
||||
}
|
||||
// 投票信息保存
|
||||
const dataList = publishConf?.code?.dataConf?.dataList || [];
|
||||
const dataListMap = keyBy(dataList, 'field');
|
||||
const surveySubmitDataKeys = Object.keys(surveySubmitData.data);
|
||||
for (const field of surveySubmitDataKeys) {
|
||||
const configData = dataListMap[field];
|
||||
if (configData && /vote/.exec(configData.type)) {
|
||||
const voteData = (await surveyKeyStoreService.get({ surveyPath: surveySubmitData.surveyPath, key: field, type: 'vote' })) || { total: 0 };
|
||||
voteData.total++;
|
||||
const fields = Array.isArray(surveySubmitData.data[field]) ? surveySubmitData.data[field] : [surveySubmitData.data[field]];
|
||||
for (const field of fields) {
|
||||
if (!voteData[field]) {
|
||||
voteData[field] = 1;
|
||||
} else {
|
||||
voteData[field]++;
|
||||
}
|
||||
}
|
||||
await surveyKeyStoreService.set({ surveyPath: surveySubmitData.surveyPath, key: field, data: voteData, type: 'vote' });
|
||||
}
|
||||
}
|
||||
// 提交问卷
|
||||
const surveySubmitRes = await surveySubmit.insertOne({
|
||||
...surveySubmitData,
|
||||
pageId,
|
||||
curStatus: getStatusObject({ status: SURVEY_STATUS.new }),
|
||||
createDate: Date.now()
|
||||
});
|
||||
return surveySubmitRes;
|
||||
}
|
||||
}
|
||||
|
||||
export const surveySubmitService = new SurveySubmitService()
|
||||
export const surveySubmitService = new SurveySubmitService();
|
@ -1,48 +1,48 @@
|
||||
import {
|
||||
createHash
|
||||
createHash
|
||||
} from 'crypto';
|
||||
import { CommonError } from '../../../types/index'
|
||||
import { CommonError } from '../../../types/index';
|
||||
|
||||
const hash256 = (text) => {
|
||||
return createHash('sha256').update(text).digest('hex')
|
||||
}
|
||||
return createHash('sha256').update(text).digest('hex');
|
||||
};
|
||||
const undefinedToString = (data) => {
|
||||
const res = {}
|
||||
for (const key in data) {
|
||||
if (data[key] === undefined) {
|
||||
res[key] = ''
|
||||
} else {
|
||||
res[key] = data[key]
|
||||
}
|
||||
const res = {};
|
||||
for (const key in data) {
|
||||
if (data[key] === undefined) {
|
||||
res[key] = '';
|
||||
} else {
|
||||
res[key] = data[key];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
const getSignByData = (sourceData, ts) => {
|
||||
const data = undefinedToString(sourceData)
|
||||
const keysArr = Object.keys(data)
|
||||
keysArr.sort()
|
||||
let signArr = keysArr.map(key => {
|
||||
if (typeof data[key] === 'string') {
|
||||
return `${key}=${encodeURIComponent(data[key])}`
|
||||
}
|
||||
return `${key}=${JSON.stringify(data[key])}`
|
||||
})
|
||||
const sign = hash256(signArr.join('') + ts)
|
||||
return `${sign}`
|
||||
}
|
||||
const getSignByData = (sourceData, ts) => {
|
||||
const data = undefinedToString(sourceData);
|
||||
const keysArr = Object.keys(data);
|
||||
keysArr.sort();
|
||||
const signArr = keysArr.map(key => {
|
||||
if (typeof data[key] === 'string') {
|
||||
return `${key}=${encodeURIComponent(data[key])}`;
|
||||
}
|
||||
return `${key}=${JSON.stringify(data[key])}`;
|
||||
});
|
||||
const sign = hash256(signArr.join('') + ts);
|
||||
return `${sign}`;
|
||||
};
|
||||
|
||||
export const checkSign = (sourceData) =>{
|
||||
const sign = sourceData.sign
|
||||
if(!sign) {
|
||||
throw new CommonError('请求签名不存在')
|
||||
}
|
||||
delete sourceData.sign
|
||||
const [inSign, ts] = sign.split('.');
|
||||
const realSign = getSignByData(sourceData, ts)
|
||||
if(inSign!==realSign) {
|
||||
throw new CommonError('请求签名异常')
|
||||
}
|
||||
return true;
|
||||
export const checkSign = (sourceData) => {
|
||||
const sign = sourceData.sign;
|
||||
if(!sign) {
|
||||
throw new CommonError('请求签名不存在');
|
||||
}
|
||||
delete sourceData.sign;
|
||||
const [inSign, ts] = sign.split('.');
|
||||
const realSign = getSignByData(sourceData, ts);
|
||||
if(inSign!==realSign) {
|
||||
throw new CommonError('请求签名异常');
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
@ -1,32 +1,24 @@
|
||||
import { SURVEY_STATUS, CommonError } from '../../../types/index'
|
||||
import * as Joi from 'joi'
|
||||
import { SURVEY_STATUS, CommonError } from '../../../types/index';
|
||||
import * as Joi from 'joi';
|
||||
|
||||
export function getMapByKey({data,key}:{data:Array<any>,key:string}) {
|
||||
const datamap = {}
|
||||
for(const item of data) {
|
||||
datamap[item[key]] = item
|
||||
}
|
||||
return datamap
|
||||
}
|
||||
|
||||
export function getStatusObject({status}:{status:SURVEY_STATUS}) {
|
||||
export function getStatusObject({ status }: { status: SURVEY_STATUS }) {
|
||||
return {
|
||||
status,
|
||||
id: status,
|
||||
date: Date.now(),
|
||||
};
|
||||
}
|
||||
export function getValidateValue<T=any>(validationResult:Joi.ValidationResult<T>):T {
|
||||
if(validationResult.error) {
|
||||
throw new CommonError(validationResult.error.details.map(e=>e.message).join())
|
||||
export function getValidateValue<T = unknown>(validationResult: Joi.ValidationResult<T>): T {
|
||||
if (validationResult.error) {
|
||||
throw new CommonError(validationResult.error.details.map(e => e.message).join());
|
||||
}
|
||||
return validationResult.value;
|
||||
}
|
||||
|
||||
export function randomCode(length) {
|
||||
let charList:Array<string> = []
|
||||
for(let i=0;i<length;i++) {
|
||||
charList.push(Math.floor(Math.random()*16).toString(16))
|
||||
const charList: Array<string> = [];
|
||||
for (let i = 0; i < length; i++) {
|
||||
charList.push(Math.floor(Math.random() * 16).toString(16));
|
||||
}
|
||||
return charList.join('')
|
||||
return charList.join('');
|
||||
}
|
@ -1,29 +1,25 @@
|
||||
import { SurveyServer } from '../../decorator'
|
||||
import { Request, Response } from 'koa'
|
||||
import { createReadStream } from 'fs'
|
||||
import * as path from 'path'
|
||||
import { SurveyApp, SurveyServer } from '../../decorator';
|
||||
import { createReadStream } from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
@SurveyApp('')
|
||||
export default class UI {
|
||||
@SurveyServer({ type: 'http', method: 'get', routerName: '/render/(.*)' })
|
||||
async render({ req, res }: {req:Request, res:Response}) {
|
||||
@SurveyServer({ type: 'http', method: 'get', routerName: '/render/(.*)' })
|
||||
async render({ res }) {
|
||||
const filePath = path.join(__dirname, 'public', 'render.html');
|
||||
res.type = path.extname(filePath)
|
||||
return createReadStream(filePath)
|
||||
res.type = path.extname(filePath);
|
||||
return createReadStream(filePath);
|
||||
}
|
||||
@SurveyServer({ type: 'http', method: 'get', routerName: '/management/(.*)' })
|
||||
async management({ req, res }: {req:Request, res:Response}) {
|
||||
@SurveyServer({ type: 'http', method: 'get', routerName: '/management/(.*)' })
|
||||
async management({ res }) {
|
||||
const filePath = path.join(__dirname, 'public', 'management.html');
|
||||
res.type = path.extname(filePath)
|
||||
return createReadStream(filePath)
|
||||
res.type = path.extname(filePath);
|
||||
return createReadStream(filePath);
|
||||
}
|
||||
@SurveyServer({ type: 'http', method: 'get', routerName: '/(.*)' })
|
||||
async index({ req, res }: {req:Request, res:Response}) {
|
||||
let reqPath = req.path;
|
||||
if (req.path === '/') {
|
||||
reqPath = '/management.html'
|
||||
}
|
||||
const filePath = path.join(__dirname, 'public', reqPath);
|
||||
res.type = path.extname(filePath)
|
||||
return createReadStream(filePath)
|
||||
@SurveyServer({ type: 'http', method: 'get', routerName: '/' })
|
||||
async index({ res }) {
|
||||
const filePath = path.join(__dirname, 'public', 'management.html');
|
||||
res.type = path.extname(filePath);
|
||||
return createReadStream(filePath);
|
||||
}
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
const config = {
|
||||
mongo:{
|
||||
url: process.env.xiaojuSurveyMongoUrl ||'mongodb://localhost:27017',
|
||||
dbName:'xiaojuSurvey',
|
||||
},
|
||||
jwt:{
|
||||
secret: process.env.xiaojuSurveyJwtSecret || 'xiaojuSurveyJwtSecret',
|
||||
expiresIn: process.env.xiaojuSurveyJwtExpiresIn || '8h',
|
||||
}
|
||||
}
|
||||
mongo: {
|
||||
url: process.env.xiaojuSurveyMongoUrl ||'mongodb://localhost:27017',
|
||||
dbName: 'xiaojuSurvey',
|
||||
},
|
||||
jwt: {
|
||||
secret: process.env.xiaojuSurveyJwtSecret || 'xiaojuSurveyJwtSecret',
|
||||
expiresIn: process.env.xiaojuSurveyJwtExpiresIn || '8h',
|
||||
}
|
||||
};
|
||||
|
||||
export function getConfig() {
|
||||
return config
|
||||
return config;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { getConfig } from '../config/index'
|
||||
import MongoService from '../../../utils/mongoService'
|
||||
import { getConfig } from '../config/index';
|
||||
import MongoService from '../../../utils/mongoService';
|
||||
|
||||
const config = getConfig()
|
||||
const config = getConfig();
|
||||
|
||||
export const mongo = new MongoService({ url: config.mongo.url, dbName: config.mongo.dbName })
|
||||
export const mongo = new MongoService({ url: config.mongo.url, dbName: config.mongo.dbName });
|
||||
|
@ -1,83 +1,85 @@
|
||||
import { SurveyServer } from '../../decorator'
|
||||
import { Request, Response } from 'koa'
|
||||
import * as Joi from 'joi'
|
||||
import { userService } from './service/userService'
|
||||
import { captchaService } from './service/captchaService'
|
||||
import { getValidateValue } from './utils/index'
|
||||
import { SurveyApp, SurveyServer } from '../../decorator';
|
||||
import { Request, Response } from 'koa';
|
||||
import * as Joi from 'joi';
|
||||
import { userService } from './service/userService';
|
||||
import { captchaService } from './service/captchaService';
|
||||
import { getValidateValue } from './utils/index';
|
||||
|
||||
import { CommonError } from '../../types/index'
|
||||
import { CommonError } from '../../types/index';
|
||||
|
||||
@SurveyApp('/api/user')
|
||||
export default class User {
|
||||
@SurveyServer({ type: 'http', method: 'post', routerName: '/register' })
|
||||
async register({ req, res }: { req: Request, res: Response }) {
|
||||
const userInfo = getValidateValue(Joi.object({
|
||||
username: Joi.string().required(),
|
||||
password: Joi.string().required(),
|
||||
captchaId: Joi.string().required(),
|
||||
captcha: Joi.string().required(),
|
||||
}).validate(req.body, { allowUnknown: true }));
|
||||
const isCorrect = await captchaService.checkCaptchaIsCorrect({ captcha: userInfo.captcha, id: userInfo.captchaId })
|
||||
if (!isCorrect) {
|
||||
throw new CommonError('验证码不正确')
|
||||
}
|
||||
const userRegisterRes = await userService.register({
|
||||
username: userInfo.username,
|
||||
password: userInfo.password,
|
||||
})
|
||||
// 删除验证码
|
||||
captchaService.deleteCaptcha({ id: userInfo.captchaId })
|
||||
return {
|
||||
code: 200,
|
||||
data: userRegisterRes,
|
||||
}
|
||||
@SurveyServer({ type: 'http', method: 'post', routerName: '/register' })
|
||||
async register({ req }: { req: Request, res: Response }) {
|
||||
const userInfo = getValidateValue(Joi.object({
|
||||
username: Joi.string().required(),
|
||||
password: Joi.string().required(),
|
||||
captchaId: Joi.string().required(),
|
||||
captcha: Joi.string().required(),
|
||||
}).validate(req.body, { allowUnknown: true }));
|
||||
const isCorrect = await captchaService.checkCaptchaIsCorrect({ captcha: userInfo.captcha, id: userInfo.captchaId });
|
||||
if (!isCorrect) {
|
||||
throw new CommonError('验证码不正确');
|
||||
}
|
||||
const userRegisterRes = await userService.register({
|
||||
username: userInfo.username,
|
||||
password: userInfo.password,
|
||||
});
|
||||
// 删除验证码
|
||||
captchaService.deleteCaptcha({ id: userInfo.captchaId });
|
||||
return {
|
||||
code: 200,
|
||||
data: userRegisterRes,
|
||||
};
|
||||
}
|
||||
|
||||
@SurveyServer({ type: 'http', method: 'post', routerName: '/login' })
|
||||
async login({ req, res }: { req: Request, res: Response }) {
|
||||
const userInfo = getValidateValue(Joi.object({
|
||||
username: Joi.string().required(),
|
||||
password: Joi.string().required(),
|
||||
captchaId: Joi.string().required(),
|
||||
captcha: Joi.string().required(),
|
||||
}).validate(req.body, { allowUnknown: true }));
|
||||
const isCorrect = await captchaService.checkCaptchaIsCorrect({ captcha: userInfo.captcha, id: userInfo.captchaId })
|
||||
if (!isCorrect) {
|
||||
throw new CommonError('验证码不正确')
|
||||
}
|
||||
const data = await userService.login({
|
||||
username: userInfo.username,
|
||||
password: userInfo.password,
|
||||
})
|
||||
// 删除验证码
|
||||
captchaService.deleteCaptcha({ id: userInfo.captchaId })
|
||||
return {
|
||||
code: 200,
|
||||
data,
|
||||
}
|
||||
@SurveyServer({ type: 'http', method: 'post', routerName: '/login' })
|
||||
async login({ req }: { req: Request, res: Response }) {
|
||||
const userInfo = getValidateValue(Joi.object({
|
||||
username: Joi.string().required(),
|
||||
password: Joi.string().required(),
|
||||
captchaId: Joi.string().required(),
|
||||
captcha: Joi.string().required(),
|
||||
}).validate(req.body, { allowUnknown: true }));
|
||||
const isCorrect = await captchaService.checkCaptchaIsCorrect({ captcha: userInfo.captcha, id: userInfo.captchaId });
|
||||
if (!isCorrect) {
|
||||
throw new CommonError('验证码不正确');
|
||||
}
|
||||
const data = await userService.login({
|
||||
username: userInfo.username,
|
||||
password: userInfo.password,
|
||||
});
|
||||
// 删除验证码
|
||||
captchaService.deleteCaptcha({ id: userInfo.captchaId });
|
||||
return {
|
||||
code: 200,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
@SurveyServer({ type: 'rpc' })
|
||||
async getUserByToken({ params, context }: { params: any, context: any }) {
|
||||
const data = await userService.getUserByToken({ token: params.token })
|
||||
return {
|
||||
result: data,
|
||||
context, // 上下文主要是传递调用方信息使用,比如traceid
|
||||
}
|
||||
}
|
||||
@SurveyServer({ type: 'rpc' })
|
||||
async getUserByToken({ params, context }) {
|
||||
const data = await userService.getUserByToken({ token: params.token });
|
||||
return {
|
||||
result: data,
|
||||
context, // 上下文主要是传递调用方信息使用,比如traceid
|
||||
};
|
||||
}
|
||||
|
||||
@SurveyServer({ type: 'http', method: 'post', routerName: '/captcha' })
|
||||
async refreshCaptcha({ req }) {
|
||||
const captchaData = captchaService.createCaptcha()
|
||||
const res = await captchaService.addCaptchaData({ text: captchaData.text })
|
||||
if (req.body && req.body.captchaId) {
|
||||
// 删除验证码
|
||||
captchaService.deleteCaptcha({ id: req.body.captchaId })
|
||||
}
|
||||
return {
|
||||
code: 200,
|
||||
data: {
|
||||
id: res.insertedId,
|
||||
img: captchaData.data,
|
||||
},
|
||||
}
|
||||
@SurveyServer({ type: 'http', method: 'post', routerName: '/captcha' })
|
||||
async refreshCaptcha({ req }) {
|
||||
const captchaData = captchaService.createCaptcha();
|
||||
const res = await captchaService.addCaptchaData({ text: captchaData.text });
|
||||
if (req.body && req.body.captchaId) {
|
||||
// 删除验证码
|
||||
captchaService.deleteCaptcha({ id: req.body.captchaId });
|
||||
}
|
||||
return {
|
||||
code: 200,
|
||||
data: {
|
||||
id: res.insertedId,
|
||||
img: captchaData.data,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { mongo } from '../db/mongo'
|
||||
import { create } from 'svg-captcha'
|
||||
import { mongo } from '../db/mongo';
|
||||
import { create } from 'svg-captcha';
|
||||
class CaptchaService {
|
||||
|
||||
createCaptcha() {
|
||||
@ -9,7 +9,7 @@ class CaptchaService {
|
||||
noise: 3, // 干扰线数量
|
||||
color: true, // 启用彩色
|
||||
background: '#f0f0f0', // 背景色
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
async addCaptchaData({ text }) {
|
||||
@ -30,11 +30,11 @@ class CaptchaService {
|
||||
|
||||
async deleteCaptcha({ id }) {
|
||||
const captchaDb = await mongo.getCollection({ collectionName: 'captcha' });
|
||||
const _id = mongo.getObjectIdByStr(id)
|
||||
const _id = mongo.getObjectIdByStr(id);
|
||||
await captchaDb.deleteOne({
|
||||
_id
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const captchaService = new CaptchaService()
|
||||
export const captchaService = new CaptchaService();
|
||||
|
@ -1,81 +1,81 @@
|
||||
import {
|
||||
verify as jwtVerify,
|
||||
sign as jwtSign
|
||||
verify as jwtVerify,
|
||||
sign as jwtSign
|
||||
} from 'jsonwebtoken';
|
||||
import {
|
||||
createHash
|
||||
createHash
|
||||
} from 'crypto';
|
||||
import { mongo } from '../db/mongo'
|
||||
import { getStatusObject } from '../utils/index'
|
||||
import { SURVEY_STATUS, CommonError } from '../../../types/index'
|
||||
import { getConfig } from '../config/index'
|
||||
const config = getConfig()
|
||||
import { mongo } from '../db/mongo';
|
||||
import { getStatusObject } from '../utils/index';
|
||||
import { SURVEY_STATUS, CommonError } from '../../../types/index';
|
||||
import { getConfig } from '../config/index';
|
||||
const config = getConfig();
|
||||
|
||||
|
||||
class UserService {
|
||||
|
||||
hash256(text) {
|
||||
return createHash('sha256').update(text).digest('hex')
|
||||
}
|
||||
hash256(text) {
|
||||
return createHash('sha256').update(text).digest('hex');
|
||||
}
|
||||
|
||||
|
||||
getToken(userInfo) {
|
||||
return jwtSign(userInfo, config.jwt.secret, { expiresIn: config.jwt.expiresIn });
|
||||
}
|
||||
getToken(userInfo) {
|
||||
return jwtSign(userInfo, config.jwt.secret, { expiresIn: config.jwt.expiresIn });
|
||||
}
|
||||
|
||||
async register(userInfo: { username: string, password: string }) {
|
||||
const user = await mongo.getCollection({ collectionName: 'user' });
|
||||
const userRes = await user.findOne({
|
||||
username: userInfo.username,
|
||||
})
|
||||
if (userRes) {
|
||||
throw new CommonError('该用户已存在')
|
||||
}
|
||||
const userInsertRes = await user.insertOne({
|
||||
username: userInfo.username,
|
||||
password: this.hash256(userInfo.password),
|
||||
curStatus: getStatusObject({ status: SURVEY_STATUS.new }),
|
||||
createDate: Date.now()
|
||||
})
|
||||
const token = this.getToken({
|
||||
_id: userInsertRes.insertedId.toString(),
|
||||
username: userInfo.username
|
||||
});
|
||||
return { userInsertRes, token, username: userInfo.username }
|
||||
async register(userInfo: { username: string, password: string }) {
|
||||
const user = await mongo.getCollection({ collectionName: 'user' });
|
||||
const userRes = await user.findOne({
|
||||
username: userInfo.username,
|
||||
});
|
||||
if (userRes) {
|
||||
throw new CommonError('该用户已存在');
|
||||
}
|
||||
const userInsertRes = await user.insertOne({
|
||||
username: userInfo.username,
|
||||
password: this.hash256(userInfo.password),
|
||||
curStatus: getStatusObject({ status: SURVEY_STATUS.new }),
|
||||
createDate: Date.now()
|
||||
});
|
||||
const token = this.getToken({
|
||||
_id: userInsertRes.insertedId.toString(),
|
||||
username: userInfo.username
|
||||
});
|
||||
return { userInsertRes, token, username: userInfo.username };
|
||||
}
|
||||
|
||||
async login(userInfo: { username: string, password: string }) {
|
||||
const user = await mongo.getCollection({ collectionName: 'user' });
|
||||
const userRes = await user.findOne({
|
||||
username: userInfo.username,
|
||||
password: this.hash256(userInfo.password),
|
||||
})
|
||||
if (!userRes) {
|
||||
throw new CommonError('用户名或密码错误')
|
||||
}
|
||||
const token = this.getToken({
|
||||
_id: userRes._id.toString(),
|
||||
username: userInfo.username
|
||||
});
|
||||
return { token, username: userInfo.username }
|
||||
async login(userInfo: { username: string, password: string }) {
|
||||
const user = await mongo.getCollection({ collectionName: 'user' });
|
||||
const userRes = await user.findOne({
|
||||
username: userInfo.username,
|
||||
password: this.hash256(userInfo.password),
|
||||
});
|
||||
if (!userRes) {
|
||||
throw new CommonError('用户名或密码错误');
|
||||
}
|
||||
const token = this.getToken({
|
||||
_id: userRes._id.toString(),
|
||||
username: userInfo.username
|
||||
});
|
||||
return { token, username: userInfo.username };
|
||||
}
|
||||
|
||||
async getUserByToken(tokenInfo: { token: string }) {
|
||||
let userInfo;
|
||||
try {
|
||||
userInfo = jwtVerify(tokenInfo.token, config.jwt.secret)
|
||||
} catch (err) {
|
||||
throw new CommonError('用户凭证无效或已过期', 403)
|
||||
}
|
||||
const user = await mongo.getCollection({ collectionName: 'user' });
|
||||
const userRes = await user.findOne({
|
||||
_id: mongo.getObjectIdByStr(userInfo._id),
|
||||
})
|
||||
if (!userRes) {
|
||||
throw new CommonError('用户已不存在')
|
||||
}
|
||||
return mongo.convertId2StringByDoc(userRes);
|
||||
async getUserByToken(tokenInfo: { token: string }) {
|
||||
let userInfo;
|
||||
try {
|
||||
userInfo = jwtVerify(tokenInfo.token, config.jwt.secret);
|
||||
} catch (err) {
|
||||
throw new CommonError('用户凭证无效或已过期', 403);
|
||||
}
|
||||
const user = await mongo.getCollection({ collectionName: 'user' });
|
||||
const userRes = await user.findOne({
|
||||
_id: mongo.getObjectIdByStr(userInfo._id),
|
||||
});
|
||||
if (!userRes) {
|
||||
throw new CommonError('用户已不存在');
|
||||
}
|
||||
return mongo.convertId2StringByDoc(userRes);
|
||||
}
|
||||
}
|
||||
|
||||
export const userService = new UserService()
|
||||
export const userService = new UserService();
|
@ -1,5 +1,5 @@
|
||||
import { SURVEY_STATUS, CommonError } from '../../../types/index'
|
||||
import * as Joi from 'joi'
|
||||
import { SURVEY_STATUS, CommonError } from '../../../types/index';
|
||||
import * as Joi from 'joi';
|
||||
export function getStatusObject({ status }: { status: SURVEY_STATUS }) {
|
||||
return {
|
||||
status,
|
||||
@ -7,9 +7,9 @@ export function getStatusObject({ status }: { status: SURVEY_STATUS }) {
|
||||
date: Date.now(),
|
||||
};
|
||||
}
|
||||
export function getValidateValue<T = any>(validationResult: Joi.ValidationResult<T>): T {
|
||||
export function getValidateValue<T = unknown>(validationResult: Joi.ValidationResult<T>): T {
|
||||
if (validationResult.error) {
|
||||
throw new CommonError(validationResult.error.details.map(e => e.message).join())
|
||||
throw new CommonError(validationResult.error.details.map(e => e.message).join());
|
||||
}
|
||||
return validationResult.value;
|
||||
}
|
@ -1,19 +1,29 @@
|
||||
type ServerType = 'http' | 'websocket' | 'rpc'
|
||||
export interface ServerValue {
|
||||
export interface RouterOptions {
|
||||
type: ServerType,
|
||||
method: 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options';
|
||||
routerName: string;
|
||||
method?: 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options';
|
||||
routerName?: string;
|
||||
}
|
||||
|
||||
export const SurveyServerConfigKey = Symbol('appConfig') // vm环境和worker环境上下文不一致导致不能使用Symbol
|
||||
export function SurveyServer(surveyServer) {
|
||||
return function (target: any, propertyKey: string, _descriptor: PropertyDescriptor) {
|
||||
if (!target[SurveyServerConfigKey]) {
|
||||
target[SurveyServerConfigKey] = new Map<string, ServerValue>()
|
||||
export const surveyServerKey = Symbol('surveyServer'); // vm环境和worker环境上下文不一致导致不能使用Symbol
|
||||
export const surveyAppKey = Symbol('surveyApp');
|
||||
|
||||
export function SurveyApp(routerName) {
|
||||
return (target: unknown) => {
|
||||
if (!target[surveyAppKey]) {
|
||||
target[surveyAppKey] = routerName;
|
||||
}
|
||||
target[SurveyServerConfigKey].set(
|
||||
};
|
||||
}
|
||||
|
||||
export function SurveyServer(options: RouterOptions) {
|
||||
return function(target: unknown, propertyKey: string) {
|
||||
if (!target[surveyServerKey]) {
|
||||
target[surveyServerKey] = new Map<string, RouterOptions>();
|
||||
}
|
||||
target[surveyServerKey].set(
|
||||
propertyKey,
|
||||
surveyServer
|
||||
)
|
||||
}
|
||||
options
|
||||
);
|
||||
};
|
||||
}
|
@ -1,21 +1,30 @@
|
||||
import * as os from 'os'
|
||||
import * as os from 'os';
|
||||
import * as Koa from 'koa';
|
||||
import * as KoaBodyparser from 'koa-bodyparser'
|
||||
import * as logger from 'koa-pino-logger'
|
||||
import { getRouter } from './router'
|
||||
import { outputCatch } from './middleware/outputCatch'
|
||||
import * as KoaBodyparser from 'koa-bodyparser';
|
||||
import * as KoaStatic from 'koa-static';
|
||||
import * as logger from 'koa-pino-logger';
|
||||
import { initRouter } from './router';
|
||||
import { outputCatch } from './middleware/outputCatch';
|
||||
import * as path from 'path';
|
||||
|
||||
const app = new Koa();
|
||||
app.use(outputCatch({ showErrorStack: true }))
|
||||
app.use(logger())
|
||||
app.use(KoaBodyparser({
|
||||
async function main() {
|
||||
const app = new Koa();
|
||||
app.use(outputCatch({ showErrorStack: true }));
|
||||
app.use(logger());
|
||||
app.use(KoaBodyparser({
|
||||
formLimit: '30mb',
|
||||
jsonLimit: '30mb',
|
||||
textLimit: '30mb',
|
||||
xmlLimit: '30mb',
|
||||
}))
|
||||
}));
|
||||
|
||||
app.use(getRouter().routes())
|
||||
const port = process.env.PORT || 3000
|
||||
app.listen(port)
|
||||
process.stdout.write(`${os.EOL}server run: http://127.0.0.1:${port} ${os.EOL}`)
|
||||
await initRouter(app);
|
||||
|
||||
app.use(KoaStatic(path.join(__dirname, './apps/ui/public')));
|
||||
|
||||
const port = process.env.PORT || 3000;
|
||||
app.listen(port);
|
||||
process.stdout.write(`${os.EOL}server run: http://127.0.0.1:${port} ${os.EOL}`);
|
||||
}
|
||||
|
||||
main();
|
||||
|
@ -1,13 +1,13 @@
|
||||
export function outputCatch({ showErrorStack }: { showErrorStack: boolean }) {
|
||||
return async function (ctx, next) {
|
||||
try {
|
||||
await next()
|
||||
} catch (err) {
|
||||
const outputData = { ...err }
|
||||
if (showErrorStack) {
|
||||
outputData.stack = err.stack;
|
||||
}
|
||||
return ctx.body = outputData
|
||||
}
|
||||
return async function(ctx, next) {
|
||||
try {
|
||||
await next();
|
||||
} catch (err) {
|
||||
const outputData = { ...err };
|
||||
if (showErrorStack) {
|
||||
outputData.stack = err.stack;
|
||||
}
|
||||
return ctx.body = outputData;
|
||||
}
|
||||
};
|
||||
}
|
21
server/src/registry.ts
Normal file
21
server/src/registry.ts
Normal file
@ -0,0 +1,21 @@
|
||||
class AppRegistry {
|
||||
private apps: Record<string, unknown>;
|
||||
|
||||
constructor() {
|
||||
this.apps = {};
|
||||
}
|
||||
|
||||
registerApp(name: string, service: unknown) {
|
||||
if (!this.apps[name]) {
|
||||
this.apps[name] = service;
|
||||
}
|
||||
}
|
||||
|
||||
getApp(name: string) {
|
||||
return this.apps[name];
|
||||
}
|
||||
}
|
||||
|
||||
const appRegistry = new AppRegistry();
|
||||
|
||||
export default appRegistry;
|
@ -1,39 +1,37 @@
|
||||
import * as Router from 'koa-router';
|
||||
import { Context } from 'koa'
|
||||
import { SurveyServerConfigKey } from './decorator';
|
||||
|
||||
import Ui from './apps/ui/index'
|
||||
import SurveyManage from './apps/surveyManage/index'
|
||||
import SurveyPublish from './apps/surveyPublish/index'
|
||||
import User from './apps/user/index'
|
||||
import { Context } from 'koa';
|
||||
import { RouterOptions, surveyAppKey, surveyServerKey } from './decorator';
|
||||
import { glob } from 'glob';
|
||||
import * as path from 'path';
|
||||
import appRegistry from './registry';
|
||||
|
||||
|
||||
function loadAppRouter(app, appRouter) {
|
||||
const appServerConfigMap = app[SurveyServerConfigKey]
|
||||
for (const [serveName, serveValue] of appServerConfigMap) {
|
||||
const middleware = async (ctx: Context) => {
|
||||
const data = await app[serveName]({ req: ctx.request, res: ctx.response })
|
||||
return ctx.body = data
|
||||
}
|
||||
const method = serveValue.method || 'all'
|
||||
const routerName = serveValue.routerName || `/${serveName}`
|
||||
appRouter[method](routerName, middleware)
|
||||
export async function initRouter(app) {
|
||||
const rootRouter = new Router();
|
||||
const entries = await glob(path.join(__dirname, './apps/*/index.{ts,js}'));
|
||||
|
||||
for (const entry of entries) {
|
||||
const module = await import(entry);
|
||||
const instance = new module.default();
|
||||
|
||||
const moduleRouter = new Router();
|
||||
|
||||
const serverConfig: Map<string, RouterOptions> = instance[surveyServerKey];
|
||||
|
||||
for (const [serverName, serverValue] of serverConfig.entries()) {
|
||||
if (serverValue.routerName) {
|
||||
const method = serverValue.method || 'get';
|
||||
moduleRouter[method](serverValue.routerName, async (ctx: Context, next) => {
|
||||
const ret = await instance[serverName]({ req: ctx.request, res: ctx.response }, next);
|
||||
ctx.body = ret;
|
||||
});
|
||||
}
|
||||
}
|
||||
return appRouter
|
||||
}
|
||||
rootRouter.use(module.default[surveyAppKey], moduleRouter.routes());
|
||||
|
||||
export function getRouter() {
|
||||
const rootRouter = new Router()
|
||||
const apiAppMap = {
|
||||
surveyManage: new SurveyManage(),
|
||||
surveyPublish: new SurveyPublish(),
|
||||
user: new User(),
|
||||
}
|
||||
for (const [apiAppName, apiApp] of Object.entries(apiAppMap)) {
|
||||
const appRouter = new Router()
|
||||
loadAppRouter(apiApp, appRouter)
|
||||
rootRouter.use(`/api/${apiAppName}`, appRouter.routes(), rootRouter.allowedMethods())
|
||||
}
|
||||
loadAppRouter(new Ui(), rootRouter)
|
||||
return rootRouter
|
||||
appRegistry.registerApp(instance.constructor.name.toLowerCase(), instance);
|
||||
|
||||
}
|
||||
// console.log(rootRouter);
|
||||
app.use(rootRouter.routes());
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
export async function rpcInvote<P, R>(appServerName: string, params: P): Promise<R> {
|
||||
const appServerNameData = /^(\w+)\.(\w+)$/.exec(appServerName);
|
||||
if (!appServerNameData) {
|
||||
throw new Error('rpc调用必须按照app.function名方式填写,app和function名称只支持数字字母下划线')
|
||||
}
|
||||
const appName = appServerNameData[1];
|
||||
const serverName = appServerNameData[2];
|
||||
const App = require(`./apps/${appName}/index`).default
|
||||
const instance = new App()
|
||||
return await instance[serverName](params)
|
||||
import appRegistry from './registry';
|
||||
|
||||
export function rpcInvote<P, R>(appServerName: string, params: P): Promise<R> {
|
||||
const appServerNameData = /^(\w+)\.(\w+)$/.exec(appServerName);
|
||||
if (!appServerNameData) {
|
||||
throw new Error('rpc调用必须按照app.function名方式填写,app和function名称只支持数字字母下划线');
|
||||
}
|
||||
const appName = appServerNameData[1];
|
||||
const serverName = appServerNameData[2];
|
||||
const instance = appRegistry.getApp(appName.toLowerCase());
|
||||
return instance[serverName](params);
|
||||
}
|
@ -1,43 +1,53 @@
|
||||
export enum DICT_TYPE {
|
||||
danger = "danger",
|
||||
secret = "secret"
|
||||
danger = 'danger',
|
||||
secret = 'secret'
|
||||
}
|
||||
|
||||
export enum SURVEY_STATUS {
|
||||
new = "new",
|
||||
editing = "editing",
|
||||
pausing = "pausing",
|
||||
published = "published",
|
||||
removed = "removed"
|
||||
new = 'new',
|
||||
editing = 'editing',
|
||||
pausing = 'pausing',
|
||||
published = 'published',
|
||||
removed = 'removed'
|
||||
}
|
||||
|
||||
export enum QUESTION_TYPE {
|
||||
enps = "enps",
|
||||
nps = "nps",
|
||||
question = "question", //通用问卷
|
||||
register = "register", //报名
|
||||
vote = "vote" //投票
|
||||
enps = 'enps',
|
||||
nps = 'nps',
|
||||
question = 'question', //通用问卷
|
||||
register = 'register', //报名
|
||||
vote = 'vote' //投票
|
||||
}
|
||||
|
||||
export enum HISTORY_TYPE {
|
||||
dailyHis = "dailyHis", //保存历史
|
||||
publishHis = "publishHis" //发布历史
|
||||
dailyHis = 'dailyHis', //保存历史
|
||||
publishHis = 'publishHis' //发布历史
|
||||
}
|
||||
|
||||
export interface StatusObj {
|
||||
id: string;
|
||||
status: string;
|
||||
date: number;
|
||||
}
|
||||
|
||||
export interface UserType {
|
||||
_id: string;
|
||||
username: string;
|
||||
password: string;
|
||||
curStatus: any;
|
||||
curStatus: StatusObj;
|
||||
createDate: number;
|
||||
}
|
||||
|
||||
export class CommonError extends Error {
|
||||
code: number
|
||||
errmsg: number
|
||||
constructor(msg, code = 500) {
|
||||
super(msg)
|
||||
this.errmsg = msg
|
||||
this.code = code
|
||||
}
|
||||
code: number;
|
||||
errmsg: number;
|
||||
constructor(msg, code = 500) {
|
||||
super(msg);
|
||||
this.errmsg = msg;
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
export interface AnyType {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
8
server/src/types/keyStore.ts
Normal file
8
server/src/types/keyStore.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export interface KeyStore {
|
||||
key: string;
|
||||
surveyPath: string;
|
||||
type: string;
|
||||
data: unknown;
|
||||
createDate: number;
|
||||
updateDate: number;
|
||||
}
|
120
server/src/types/survey.ts
Normal file
120
server/src/types/survey.ts
Normal file
@ -0,0 +1,120 @@
|
||||
export interface TitleConfig {
|
||||
mainTitle: string;
|
||||
subTitle: string;
|
||||
}
|
||||
|
||||
export interface BannerConfig {
|
||||
bgImage: string;
|
||||
videoLink: string;
|
||||
postImg: string;
|
||||
}
|
||||
|
||||
export interface BannerConf {
|
||||
titleConfig: TitleConfig;
|
||||
bannerConfig: BannerConfig;
|
||||
}
|
||||
|
||||
export interface TimeStep {
|
||||
hour: number;
|
||||
min: number;
|
||||
}
|
||||
|
||||
export interface NPS {
|
||||
leftText: string;
|
||||
rightText: string;
|
||||
}
|
||||
|
||||
export interface TextRange {
|
||||
min: {
|
||||
placeholder: string;
|
||||
value: number;
|
||||
};
|
||||
max: {
|
||||
placeholder: string;
|
||||
value: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface DataItem {
|
||||
isRequired: boolean;
|
||||
showIndex: boolean;
|
||||
showType: boolean;
|
||||
showSpliter: boolean;
|
||||
type: string;
|
||||
valid: string;
|
||||
field: string;
|
||||
title: string;
|
||||
placeholder: string;
|
||||
randomSort: boolean;
|
||||
checked: boolean;
|
||||
minNum: string;
|
||||
maxNum: string;
|
||||
maxPhotos: number;
|
||||
star: number;
|
||||
timeStep: TimeStep;
|
||||
nps: NPS;
|
||||
placeholderDesc: string;
|
||||
addressType: number;
|
||||
isAuto: boolean;
|
||||
urlKey: string;
|
||||
textRange: TextRange;
|
||||
options?: Option[];
|
||||
importKey?: string;
|
||||
importData?: string;
|
||||
cOption?: string;
|
||||
cOptions?: string[];
|
||||
exclude?: boolean;
|
||||
}
|
||||
|
||||
export interface Option {
|
||||
text: string;
|
||||
imageUrl: string;
|
||||
others: boolean;
|
||||
mustOthers: boolean;
|
||||
othersKey: string;
|
||||
placeholderDesc: string;
|
||||
hash: string;
|
||||
}
|
||||
|
||||
export interface DataConf {
|
||||
dataList: DataItem[];
|
||||
}
|
||||
|
||||
export interface ConfirmAgain {
|
||||
is_again: boolean;
|
||||
again_text: string;
|
||||
}
|
||||
|
||||
export interface MsgContent {
|
||||
msg_200: string;
|
||||
msg_9001: string;
|
||||
msg_9002: string;
|
||||
msg_9003: string;
|
||||
msg_9004: string;
|
||||
}
|
||||
|
||||
export interface SubmitConf {
|
||||
submitTitle: string;
|
||||
confirmAgain: ConfirmAgain;
|
||||
msgContent: MsgContent;
|
||||
}
|
||||
|
||||
export interface BaseConf {
|
||||
begTime: string;
|
||||
endTime: string;
|
||||
tLimit: string;
|
||||
language: string;
|
||||
}
|
||||
|
||||
export interface SkinConf {
|
||||
skinColor: string;
|
||||
inputBgColor: string;
|
||||
}
|
||||
|
||||
export interface ParsedData {
|
||||
bannerConf: BannerConf;
|
||||
dataConf: DataConf;
|
||||
submitConf: SubmitConf;
|
||||
baseConf: BaseConf;
|
||||
skinConf: SkinConf;
|
||||
}
|
@ -1,50 +1,55 @@
|
||||
import { Collection, MongoClient, ObjectId } from 'mongodb'
|
||||
import { CommonError } from '../types'
|
||||
import { Collection, MongoClient, ObjectId } from 'mongodb';
|
||||
import { CommonError } from '../types';
|
||||
|
||||
|
||||
class MongoService {
|
||||
isInit: boolean
|
||||
client: MongoClient
|
||||
dbName: string
|
||||
constructor({ url, dbName }) {
|
||||
this.client = new MongoClient(url);
|
||||
this.dbName = dbName;
|
||||
}
|
||||
|
||||
async getCollection({ collectionName }): Promise<Collection> {
|
||||
try {
|
||||
// 设置一个6秒的计时器
|
||||
const timeoutPromise = new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
reject(new Error('连接超时'));
|
||||
}, 6000); // 6秒超时
|
||||
});
|
||||
await Promise.race([this.client.connect(), timeoutPromise])
|
||||
} catch (error) {
|
||||
throw new CommonError('数据库连接错误:' + error.message)
|
||||
}
|
||||
|
||||
try {
|
||||
return this.client.db(this.dbName).collection(collectionName)
|
||||
} catch (error) {
|
||||
throw new CommonError(`get collection ${collectionName} error`)
|
||||
}
|
||||
}
|
||||
|
||||
convertId2StringByDoc(doc: any): any {
|
||||
doc._id = doc._id.toString()
|
||||
return doc
|
||||
}
|
||||
|
||||
convertId2StringByList(list: Array<any>): Array<any> {
|
||||
return list.map(e => {
|
||||
return this.convertId2StringByDoc(e)
|
||||
})
|
||||
}
|
||||
|
||||
getObjectIdByStr(str: string) {
|
||||
return new ObjectId(str)
|
||||
}
|
||||
// 定义一个通用类型,表示具有 _id 字段的对象
|
||||
interface ObjectWithId {
|
||||
_id: ObjectId;
|
||||
// 其他可能的属性...
|
||||
}
|
||||
|
||||
export default MongoService
|
||||
class MongoService {
|
||||
isInit: boolean;
|
||||
client: MongoClient;
|
||||
dbName: string;
|
||||
constructor({ url, dbName }) {
|
||||
this.client = new MongoClient(url);
|
||||
this.dbName = dbName;
|
||||
}
|
||||
|
||||
async getCollection({ collectionName }): Promise<Collection> {
|
||||
try {
|
||||
// 设置一个6秒的计时器
|
||||
const timeoutPromise = new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
reject(new Error('连接超时'));
|
||||
}, 6000); // 6秒超时
|
||||
});
|
||||
await Promise.race([this.client.connect(), timeoutPromise]);
|
||||
} catch (error) {
|
||||
throw new CommonError('数据库连接错误:' + error.message);
|
||||
}
|
||||
|
||||
try {
|
||||
return this.client.db(this.dbName).collection(collectionName);
|
||||
} catch (error) {
|
||||
throw new CommonError(`get collection ${collectionName} error`);
|
||||
}
|
||||
}
|
||||
|
||||
convertId2StringByDoc<T extends ObjectWithId>(doc: T): T {
|
||||
return { ...doc, _id: doc._id.toString() };
|
||||
}
|
||||
|
||||
convertId2StringByList<T extends ObjectWithId>(list: Array<T>): Array<T> {
|
||||
return list.map(e => {
|
||||
return this.convertId2StringByDoc(e);
|
||||
});
|
||||
}
|
||||
|
||||
getObjectIdByStr(str: string) {
|
||||
return new ObjectId(str);
|
||||
}
|
||||
}
|
||||
|
||||
export default MongoService;
|
@ -59,11 +59,10 @@ export const noListDataConfig = {
|
||||
img: '/imgs/icons/list-empty.png',
|
||||
};
|
||||
|
||||
|
||||
export const statusMaps = {
|
||||
new: '未发布',
|
||||
editing: '修改中',
|
||||
published: '已发布',
|
||||
removed: '',
|
||||
pausing: '',
|
||||
};
|
||||
};
|
||||
|
@ -11,7 +11,8 @@ const routes = [
|
||||
{
|
||||
path: '/survey',
|
||||
name: 'survey',
|
||||
component: () => import(/* webpackChunkName: "survey" */'../pages/list/index.vue'),
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "survey" */ '../pages/list/index.vue'),
|
||||
meta: {
|
||||
needLogin: true,
|
||||
title: '问卷列表',
|
||||
@ -23,7 +24,8 @@ const routes = [
|
||||
meta: {
|
||||
needLogin: true,
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "editPage" */'../pages/edit/index.vue'),
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "editPage" */ '../pages/edit/index.vue'),
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
@ -31,7 +33,10 @@ const routes = [
|
||||
meta: {
|
||||
needLogin: true,
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "QuestionEditIndex" */'../pages/edit/pages/edit.vue'),
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "QuestionEditIndex" */ '../pages/edit/pages/edit.vue'
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'setting',
|
||||
@ -39,7 +44,10 @@ const routes = [
|
||||
meta: {
|
||||
needLogin: true,
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "QuestionEditSetting" */'../pages/edit/pages/setting.vue'),
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "QuestionEditSetting" */ '../pages/edit/pages/setting.vue'
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'resultConfig',
|
||||
@ -47,7 +55,10 @@ const routes = [
|
||||
meta: {
|
||||
needLogin: true,
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "QuestionEditResultConfig" */'../pages/edit/pages/resultConfig.vue'),
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "QuestionEditResultConfig" */ '../pages/edit/pages/resultConfig.vue'
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -57,7 +68,10 @@ const routes = [
|
||||
meta: {
|
||||
needLogin: true,
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "analysisPage" */'../pages/analysis/index.vue'),
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "analysisPage" */ '../pages/analysis/index.vue'
|
||||
),
|
||||
},
|
||||
{
|
||||
path: '/survey/:id/publishResult',
|
||||
@ -65,7 +79,10 @@ const routes = [
|
||||
meta: {
|
||||
needLogin: true,
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "publishResultPage" */'../pages/publishResult/index.vue'),
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "publishResultPage" */ '../pages/publishResult/index.vue'
|
||||
),
|
||||
},
|
||||
{
|
||||
path: '/create',
|
||||
@ -74,13 +91,15 @@ const routes = [
|
||||
needLogin: true,
|
||||
title: '创建问卷',
|
||||
},
|
||||
component: () => import(/* webpackChunkName: "create" */'../pages/create/index.vue'),
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "create" */ '../pages/create/index.vue'),
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
title: '登陆',
|
||||
component: () => import(/* webpackChunkName: "login" */'../pages/login/index.vue'),
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "login" */ '../pages/login/index.vue'),
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { forEach as _forEach, trim as _trim } from 'lodash';
|
||||
import { forEach as _forEach, trim as _trim } from 'lodash';
|
||||
import { escapeFilterXSS } from '@/common/xss';
|
||||
|
||||
// 获取选项的hash
|
||||
|
@ -1,4 +1,10 @@
|
||||
import { forEach as _forEach, get as _get, isArray as _isArray, keys as _keys, set as _set } from 'lodash';
|
||||
import {
|
||||
forEach as _forEach,
|
||||
get as _get,
|
||||
isArray as _isArray,
|
||||
keys as _keys,
|
||||
set as _set,
|
||||
} from 'lodash';
|
||||
|
||||
const regexpMap = {
|
||||
nd: /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/,
|
||||
@ -261,7 +267,7 @@ export default function (questionConfig) {
|
||||
}
|
||||
} else {
|
||||
_forEach(options, (item) => {
|
||||
const othersKey = `${field}_${item.hash}`
|
||||
const othersKey = `${field}_${item.hash}`;
|
||||
const { mustOthers } = item;
|
||||
if (mustOthers) {
|
||||
_set(validMap, othersKey, generateValidArr(true, ''));
|
||||
|
@ -1,7 +1,7 @@
|
||||
import confirm from '../../components/confirm.vue';
|
||||
import alert from '../../components/alert.vue';
|
||||
|
||||
import { isFunction as _isFunction} from 'lodash';
|
||||
import { isFunction as _isFunction } from 'lodash';
|
||||
|
||||
export default {
|
||||
install(Vue) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import moment from 'moment';
|
||||
// 引入中文
|
||||
import 'moment/locale/zh-cn'
|
||||
import 'moment/locale/zh-cn';
|
||||
// 设置中文
|
||||
moment.locale('zh-cn');
|
||||
import adapter from '../adapter';
|
||||
|
@ -1,8 +1,7 @@
|
||||
const { defineConfig } = require('@vue/cli-service');
|
||||
const Webpack = require('webpack')
|
||||
const Webpack = require('webpack');
|
||||
// 分析打包时间
|
||||
const SpeedMeasureWebpackPlugin = require('speed-measure-webpack-plugin')
|
||||
|
||||
const SpeedMeasureWebpackPlugin = require('speed-measure-webpack-plugin');
|
||||
|
||||
module.exports = defineConfig({
|
||||
transpileDependencies: true,
|
||||
@ -31,7 +30,7 @@ module.exports = defineConfig({
|
||||
devServer: {
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:3000',
|
||||
target: 'http://127.0.0.1:3000',
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
@ -47,19 +46,23 @@ module.exports = defineConfig({
|
||||
open: true,
|
||||
},
|
||||
configureWebpack: {
|
||||
plugins: [new Webpack.IgnorePlugin({resourceRegExp: /^\.\/locale$/, contextRegExp: /moment$/ })],
|
||||
plugins: [
|
||||
new Webpack.IgnorePlugin({
|
||||
resourceRegExp: /^\.\/locale$/,
|
||||
contextRegExp: /moment$/,
|
||||
}),
|
||||
],
|
||||
},
|
||||
chainWebpack: (config) => {
|
||||
config.module
|
||||
.rule('js')
|
||||
.test(/\.jsx?$/)
|
||||
.exclude
|
||||
.add(/node_modules/)
|
||||
.end()
|
||||
.exclude.add(/node_modules/)
|
||||
.end()
|
||||
.use('babel-loader')
|
||||
.loader('babel-loader')
|
||||
.end();
|
||||
|
||||
|
||||
config.optimization.splitChunks({
|
||||
cacheGroups: {
|
||||
setterWidgets: {
|
||||
@ -82,41 +85,40 @@ module.exports = defineConfig({
|
||||
element: {
|
||||
name: 'chunk-element-ui',
|
||||
test: /[\\/]node_modules[\\/]element-ui[\\/]/,
|
||||
chunks: "all",
|
||||
chunks: 'all',
|
||||
priority: 3,
|
||||
reuseExistingChunk: true,
|
||||
enforce: true
|
||||
enforce: true,
|
||||
},
|
||||
moment: {
|
||||
name: 'chunk-moment',
|
||||
test: /[\\/]node_modules[\\/]moment[\\/]/,
|
||||
chunks: "all",
|
||||
chunks: 'all',
|
||||
priority: 3,
|
||||
reuseExistingChunk: true,
|
||||
enforce: true
|
||||
enforce: true,
|
||||
},
|
||||
'@wangeditor': {
|
||||
name: 'chunk-wangeditor',
|
||||
test: /[\\/]node_modules[\\/]@wangeditor[\\/]/,
|
||||
chunks: "all",
|
||||
chunks: 'all',
|
||||
priority: 3,
|
||||
reuseExistingChunk: true,
|
||||
enforce: true
|
||||
enforce: true,
|
||||
},
|
||||
common: {
|
||||
//抽取所有入口页面都需要的公共chunk
|
||||
name: "chunk-common",
|
||||
chunks: "initial",
|
||||
name: 'chunk-common',
|
||||
chunks: 'initial',
|
||||
minChunks: 2,
|
||||
maxInitialRequests: 5,
|
||||
minSize: 0,
|
||||
priority: 1,
|
||||
reuseExistingChunk: true,
|
||||
enforce: true
|
||||
}
|
||||
enforce: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
config.plugin('speed')
|
||||
.use(SpeedMeasureWebpackPlugin)
|
||||
config.plugin('speed').use(SpeedMeasureWebpackPlugin);
|
||||
},
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user