├── .gitignore ├── LICENSE ├── README.md ├── client ├── .babelrc ├── .editorconfig ├── .gitignore ├── .postcssrc.js ├── README.md ├── build │ ├── build.js │ ├── check-versions.js │ ├── logo.png │ ├── utils.js │ ├── vue-loader.conf.js │ ├── webpack.base.conf.js │ ├── webpack.dev.conf.js │ └── webpack.prod.conf.js ├── config │ ├── dev.env.js │ ├── index.js │ └── prod.env.js ├── dist │ ├── index.html │ └── static │ │ ├── css │ │ ├── app.28f2284184a8d1356fab6b9af38dc00b.css │ │ ├── app.28f2284184a8d1356fab6b9af38dc00b.css.map │ │ └── main.css │ │ ├── fonts │ │ ├── element-icons.535877f.woff │ │ └── element-icons.732389d.ttf │ │ ├── img │ │ └── logo.39aabf3.png │ │ ├── js │ │ ├── app.a775ce576d6ca3078d44.js │ │ ├── app.a775ce576d6ca3078d44.js.map │ │ ├── axios.js │ │ ├── base64.js │ │ ├── config.js │ │ ├── httpRequest.js │ │ ├── main.js │ │ ├── manifest.2ae2e69a05c33dfc65f8.js │ │ ├── manifest.2ae2e69a05c33dfc65f8.js.map │ │ ├── vendor.50affbea1a3146e9f2e5.js │ │ └── vendor.50affbea1a3146e9f2e5.js.map │ │ └── student.xlsx ├── index.html ├── package.json ├── src │ ├── App.vue │ ├── components │ │ ├── admin │ │ │ ├── Main.vue │ │ │ ├── assets │ │ │ │ └── girl.png │ │ │ ├── common │ │ │ │ ├── Left.vue │ │ │ │ ├── ShowQuestion.vue │ │ │ │ ├── ShowQuestionGroup.vue │ │ │ │ ├── Top.vue │ │ │ │ └── config.js │ │ │ └── pages │ │ │ │ ├── course │ │ │ │ ├── Main.vue │ │ │ │ ├── Q_A │ │ │ │ │ ├── CurrentQuestionAnswer.vue │ │ │ │ │ ├── QuestionAnswerHistory.vue │ │ │ │ │ └── common │ │ │ │ │ │ ├── Badge.vue │ │ │ │ │ │ ├── MessageDetail.vue │ │ │ │ │ │ ├── MessageList.vue │ │ │ │ │ │ └── StudentBadge.vue │ │ │ │ ├── announcement │ │ │ │ │ ├── AddAnnouncement.vue │ │ │ │ │ └── OverView.vue │ │ │ │ ├── base │ │ │ │ │ ├── Base.vue │ │ │ │ │ ├── CourseIntro.vue │ │ │ │ │ ├── CoursewareManage.vue │ │ │ │ │ ├── ExamType.vue │ │ │ │ │ ├── FirstCourse.vue │ │ │ │ │ ├── ReferenceBook.vue │ │ │ │ │ ├── Section.vue │ │ │ │ │ ├── TeachPlan.vue │ │ │ │ │ └── TeacherIntro.vue │ │ │ │ ├── questionBank │ │ │ │ │ ├── AddChoiceQuestion.vue │ │ │ │ │ ├── AddGapFilling.vue │ │ │ │ │ ├── AddProgram.vue │ │ │ │ │ ├── OrganizeQuestion.vue │ │ │ │ │ ├── Overview.vue │ │ │ │ │ ├── QuestionGroupOverview.vue │ │ │ │ │ └── common │ │ │ │ │ │ ├── ChosePoint.vue │ │ │ │ │ │ ├── QuestionGroupTable.vue │ │ │ │ │ │ ├── QuestionList.vue │ │ │ │ │ │ └── QuestionTable.vue │ │ │ │ ├── statistics │ │ │ │ │ └── StudentStatistics.vue │ │ │ │ ├── student │ │ │ │ │ ├── AddManyStudent.vue │ │ │ │ │ ├── AddNode.vue │ │ │ │ │ ├── AddSingleStudent.vue │ │ │ │ │ ├── ChoseClass.vue │ │ │ │ │ ├── EditStudent.vue │ │ │ │ │ ├── OverView.vue │ │ │ │ │ └── ShowClass.vue │ │ │ │ └── test │ │ │ │ │ ├── AddExam.vue │ │ │ │ │ ├── AddHomework.vue │ │ │ │ │ ├── OverView.vue │ │ │ │ │ └── common │ │ │ │ │ ├── ChoseClass.vue │ │ │ │ │ ├── ChoseExamQuestion.vue │ │ │ │ │ ├── ChoseHomeworkQuestion.vue │ │ │ │ │ ├── TestDetail.vue │ │ │ │ │ └── TestDetailCommon │ │ │ │ │ ├── CodeCheck.vue │ │ │ │ │ ├── KnowledgePontPercent.vue │ │ │ │ │ ├── QuestionRightPercent.vue │ │ │ │ │ └── ScoreTable.vue │ │ │ │ └── manage │ │ │ │ ├── AnnoumcementManage.vue │ │ │ │ ├── CourseManage.vue │ │ │ │ ├── EditCourse.vue │ │ │ │ ├── EditUser.vue │ │ │ │ ├── PersonInfo.vue │ │ │ │ ├── Safe.vue │ │ │ │ ├── TotalStatistic.vue │ │ │ │ └── UserManage.vue │ │ ├── client │ │ │ ├── Main.vue │ │ │ ├── assets │ │ │ │ ├── logo.jpg │ │ │ │ └── logo.png │ │ │ ├── common │ │ │ │ ├── Badge.vue │ │ │ │ ├── DoQuestion.vue │ │ │ │ ├── DoTest.vue │ │ │ │ ├── StudentBadge.vue │ │ │ │ └── Top.vue │ │ │ └── pages │ │ │ │ ├── Announcement.vue │ │ │ │ ├── ClientMain.vue │ │ │ │ ├── Home.vue │ │ │ │ ├── courseInfo │ │ │ │ ├── CourseIntro.vue │ │ │ │ ├── Courseware.vue │ │ │ │ ├── ExamType.vue │ │ │ │ ├── FirstCourse.vue │ │ │ │ ├── ReferenceBook.vue │ │ │ │ ├── TeachPlan.vue │ │ │ │ └── TeacherIntro.vue │ │ │ │ ├── exam │ │ │ │ ├── CurrentExam.vue │ │ │ │ ├── ExamDetail.vue │ │ │ │ └── LastExam.vue │ │ │ │ ├── knowledgePoint │ │ │ │ ├── History.vue │ │ │ │ ├── KnowledgePointView.vue │ │ │ │ └── KnowledgeTestDetail.vue │ │ │ │ ├── questionAnswer │ │ │ │ ├── CurrentQuestionAnswer.vue │ │ │ │ ├── NewQuestionAnswer.vue │ │ │ │ ├── QuestionAnswerHistory.vue │ │ │ │ └── common │ │ │ │ │ ├── MessageDetail.vue │ │ │ │ │ └── MessageList.vue │ │ │ │ ├── rankList │ │ │ │ ├── CoverageList.vue │ │ │ │ └── EvaluateList.vue │ │ │ │ ├── studentInfo │ │ │ │ └── StudentInfo.vue │ │ │ │ └── work │ │ │ │ ├── CurrentWork.vue │ │ │ │ ├── LastWork.vue │ │ │ │ └── WorkDetail.vue │ │ ├── common │ │ │ ├── Breadcrumb.vue │ │ │ ├── Editor.vue │ │ │ └── ShowText.vue │ │ └── public │ │ │ ├── 404.vue │ │ │ ├── About.vue │ │ │ ├── Index.vue │ │ │ ├── Login.vue │ │ │ ├── Main.vue │ │ │ ├── ResetPass.vue │ │ │ ├── assets │ │ │ └── background.jpg │ │ │ └── test.vue │ ├── main.js │ └── router │ │ ├── admin.js │ │ ├── client.js │ │ ├── index.js │ │ └── public.js └── static │ ├── .gitkeep │ ├── css │ └── main.css │ ├── js │ ├── axios.js │ ├── base64.js │ ├── config.js │ ├── httpRequest.js │ └── main.js │ └── student.xlsx ├── docker-compose.yml ├── files ├── Dockerfile ├── front │ └── nginx.conf ├── mysql │ ├── data │ │ └── .gitkeep │ ├── init.sql │ └── my.cnf ├── redis │ ├── data │ │ └── .gitkeep │ └── redis.conf └── sim.tar ├── start.sh └── teachSystem ├── .gitignore ├── api ├── announcement.js ├── announcementHelper.js ├── badge.js ├── class.js ├── classHelper.js ├── course.js ├── courseHelper.js ├── courseware.js ├── grade.js ├── knowledgePoint.js ├── public.js ├── questionAnswer.js ├── questionGroup.js ├── questionGroupHelper.js ├── questionLibrary.js ├── questionLibraryHelper.js ├── section.js ├── test.js ├── testHelper.js ├── user.js └── userHelper.js ├── authority ├── dbAuth.js ├── index.js ├── readme.md └── static │ ├── css │ └── main.css │ ├── js │ ├── axios.js │ ├── main.js │ └── vue.js │ └── main.html ├── config ├── config.js └── judgeConfig.js ├── dao ├── announcementDao.js ├── badgeDao.js ├── badgeHelper.js ├── classDao.js ├── courseDao.js ├── coursewareDao.js ├── gradeDao.js ├── knowledgePointDao.js ├── publicDao.js ├── questionAnswerDao.js ├── questionGroupDao.js ├── questionLibraryDao.js ├── sectionDao.js ├── studentClassDao.js ├── testDao.js ├── testHelper.js └── userDao.js ├── files ├── badgeIcon │ ├── badge_10.png │ ├── badge_2.png │ ├── badge_5.png │ ├── badge_6.png │ ├── badge_7.png │ ├── badge_8.png │ └── badge_9.png ├── code │ └── .gitkeep ├── course │ └── .gitkeep ├── imgs │ └── .gitkeep ├── temp │ └── uploads │ │ └── .gitkeep ├── tests │ └── .gitkeep └── userIcon │ └── default.jpg ├── index.js ├── log ├── log.js ├── operate │ └── .gitkeep ├── responseTime │ └── .gitkeep └── visit │ └── .gitkeep ├── middleware ├── auth.js ├── controllerEngine.js ├── handleError.js └── init.js ├── package.json ├── test └── courseDao.test.js └── util ├── ErrorHelper.js ├── MailHelper.js ├── MysqlHelper.js ├── ObjectOperate.js ├── RedisHelper.js ├── RequestHelper.js ├── StringHelper.js ├── TextVerify.js └── TimeHelper.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | 7 | # Editor directories and files 8 | .idea 9 | .vscode 10 | *.suo 11 | *.ntvs* 12 | *.njsproj 13 | *.sln 14 | */node_modules/ 15 | *.log 16 | **.package-lock.json 17 | yarn.lock 18 | timezone 19 | 20 | files/mysql/data/* 21 | !files/mysql/data/.gitkeep 22 | files/redis/data/* 23 | !files/redis/data/.gitkeep 24 | files/front/log/* 25 | !files/front/data/.gitkeep 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 概述 2 | 3 | 本仓库用于存放毕业设计代码,同时也给想要使用node.js,vue.js构建系统的同志做个参考。。。 4 | 5 | 编程题判题使用的是青岛大学的判题核心,[https://github.com/QingdaoU/JudgeServer](https://github.com/QingdaoU/JudgeServer) 6 | 7 | 代码判重使用SIM实现,[地址](https://dickgrune.com/Programs/similarity_tester/) 8 | 9 | demo地址:[ali.tapme.top:8008](http://ali.tapme.top:8008) 管理员账户密码:123456/123456 10 | 11 | # 安装 12 | 13 |   使用docker一键部署。需要先设置mysql 密码和判题核心的密钥环境变量.然后执行start.sh即可。 14 | 15 | 注意下面只是临时设置环境变量,终端关闭就会失效。 16 | 17 | ```bash 18 | export MYSQL_PASS=123456 19 | #export JUDGE_TOKEN=12345678 20 | chmod +x start.sh 21 | ./start.sh 22 | ``` 23 | 部署完毕后访问:部署机器ip:8089。 24 | 25 | **PS:如果docker pull 镜像速度很慢,请给docker设置国内加速地址。** 26 | 27 | ## 前端技术栈如下 28 | 29 | - vue.js 30 | - element.ui 31 | - axios 32 | 33 | ## 后端技术栈如下 34 | 35 | - node.js 36 | - koa 37 | - jwt 38 | - mysql 39 | - redis 40 | -------------------------------------------------------------------------------- /client/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-vue-jsx", "transform-runtime"] 12 | } 13 | -------------------------------------------------------------------------------- /client/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | 7 | # Editor directories and files 8 | .idea 9 | .vscode 10 | *.suo 11 | *.ntvs* 12 | *.njsproj 13 | *.sln 14 | package-lock.json 15 | -------------------------------------------------------------------------------- /client/.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # client 2 | 3 | > A Vue.js project 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | npm install 10 | 11 | # serve with hot reload at localhost:8080 12 | npm run dev 13 | 14 | # build for production with minification 15 | npm run build 16 | 17 | # build for production and view the bundle analyzer report 18 | npm run build --report 19 | ``` 20 | 21 | For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). 22 | -------------------------------------------------------------------------------- /client/build/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | process.env.NODE_ENV = 'production' 5 | 6 | const ora = require('ora') 7 | const rm = require('rimraf') 8 | const path = require('path') 9 | const chalk = require('chalk') 10 | const webpack = require('webpack') 11 | const config = require('../config') 12 | const webpackConfig = require('./webpack.prod.conf') 13 | 14 | const spinner = ora('building for production...') 15 | spinner.start() 16 | 17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 18 | if (err) throw err 19 | webpack(webpackConfig, (err, stats) => { 20 | spinner.stop() 21 | if (err) throw err 22 | process.stdout.write(stats.toString({ 23 | colors: true, 24 | modules: false, 25 | children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. 26 | chunks: false, 27 | chunkModules: false 28 | }) + '\n\n') 29 | 30 | if (stats.hasErrors()) { 31 | console.log(chalk.red(' Build failed with errors.\n')) 32 | process.exit(1) 33 | } 34 | 35 | console.log(chalk.cyan(' Build complete.\n')) 36 | console.log(chalk.yellow( 37 | ' Tip: built files are meant to be served over an HTTP server.\n' + 38 | ' Opening index.html over file:// won\'t work.\n' 39 | )) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /client/build/check-versions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const chalk = require('chalk') 3 | const semver = require('semver') 4 | const packageConfig = require('../package.json') 5 | const shell = require('shelljs') 6 | 7 | function exec (cmd) { 8 | return require('child_process').execSync(cmd).toString().trim() 9 | } 10 | 11 | const versionRequirements = [ 12 | { 13 | name: 'node', 14 | currentVersion: semver.clean(process.version), 15 | versionRequirement: packageConfig.engines.node 16 | } 17 | ] 18 | 19 | if (shell.which('npm')) { 20 | versionRequirements.push({ 21 | name: 'npm', 22 | currentVersion: exec('npm --version'), 23 | versionRequirement: packageConfig.engines.npm 24 | }) 25 | } 26 | 27 | module.exports = function () { 28 | const warnings = [] 29 | 30 | for (let i = 0; i < versionRequirements.length; i++) { 31 | const mod = versionRequirements[i] 32 | 33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 34 | warnings.push(mod.name + ': ' + 35 | chalk.red(mod.currentVersion) + ' should be ' + 36 | chalk.green(mod.versionRequirement) 37 | ) 38 | } 39 | } 40 | 41 | if (warnings.length) { 42 | console.log('') 43 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 44 | console.log() 45 | 46 | for (let i = 0; i < warnings.length; i++) { 47 | const warning = warnings[i] 48 | console.log(' ' + warning) 49 | } 50 | 51 | console.log() 52 | process.exit(1) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /client/build/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/build/logo.png -------------------------------------------------------------------------------- /client/build/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const config = require('../config') 4 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 5 | const packageConfig = require('../package.json') 6 | 7 | exports.assetsPath = function (_path) { 8 | const assetsSubDirectory = process.env.NODE_ENV === 'production' 9 | ? config.build.assetsSubDirectory 10 | : config.dev.assetsSubDirectory 11 | 12 | return path.posix.join(assetsSubDirectory, _path) 13 | } 14 | 15 | exports.cssLoaders = function (options) { 16 | options = options || {} 17 | 18 | const cssLoader = { 19 | loader: 'css-loader', 20 | options: { 21 | sourceMap: options.sourceMap 22 | } 23 | } 24 | 25 | const postcssLoader = { 26 | loader: 'postcss-loader', 27 | options: { 28 | sourceMap: options.sourceMap 29 | } 30 | } 31 | 32 | // generate loader string to be used with extract text plugin 33 | function generateLoaders (loader, loaderOptions) { 34 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] 35 | 36 | if (loader) { 37 | loaders.push({ 38 | loader: loader + '-loader', 39 | options: Object.assign({}, loaderOptions, { 40 | sourceMap: options.sourceMap 41 | }) 42 | }) 43 | } 44 | 45 | // Extract CSS when that option is specified 46 | // (which is the case during production build) 47 | if (options.extract) { 48 | return ExtractTextPlugin.extract({ 49 | use: loaders, 50 | fallback: 'vue-style-loader' 51 | }) 52 | } else { 53 | return ['vue-style-loader'].concat(loaders) 54 | } 55 | } 56 | 57 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 58 | return { 59 | css: generateLoaders(), 60 | postcss: generateLoaders(), 61 | less: generateLoaders('less'), 62 | sass: generateLoaders('sass', { indentedSyntax: true }), 63 | scss: generateLoaders('sass'), 64 | stylus: generateLoaders('stylus'), 65 | styl: generateLoaders('stylus') 66 | } 67 | } 68 | 69 | // Generate loaders for standalone style files (outside of .vue) 70 | exports.styleLoaders = function (options) { 71 | const output = [] 72 | const loaders = exports.cssLoaders(options) 73 | 74 | for (const extension in loaders) { 75 | const loader = loaders[extension] 76 | output.push({ 77 | test: new RegExp('\\.' + extension + '$'), 78 | use: loader 79 | }) 80 | } 81 | 82 | return output 83 | } 84 | 85 | exports.createNotifierCallback = () => { 86 | const notifier = require('node-notifier') 87 | 88 | return (severity, errors) => { 89 | if (severity !== 'error') return 90 | 91 | const error = errors[0] 92 | const filename = error.file && error.file.split('!').pop() 93 | 94 | notifier.notify({ 95 | title: packageConfig.name, 96 | message: severity + ': ' + error.name, 97 | subtitle: filename || '', 98 | icon: path.join(__dirname, 'logo.png') 99 | }) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /client/build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const config = require('../config') 4 | const isProduction = process.env.NODE_ENV === 'production' 5 | const sourceMapEnabled = isProduction 6 | ? config.build.productionSourceMap 7 | : config.dev.cssSourceMap 8 | 9 | module.exports = { 10 | loaders: utils.cssLoaders({ 11 | sourceMap: sourceMapEnabled, 12 | extract: isProduction 13 | }), 14 | cssSourceMap: sourceMapEnabled, 15 | cacheBusting: config.dev.cacheBusting, 16 | transformToRequire: { 17 | video: ['src', 'poster'], 18 | source: 'src', 19 | img: 'src', 20 | image: 'xlink:href' 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /client/build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const config = require('../config') 5 | const vueLoaderConfig = require('./vue-loader.conf') 6 | 7 | function resolve (dir) { 8 | return path.join(__dirname, '..', dir) 9 | } 10 | 11 | 12 | 13 | module.exports = { 14 | context: path.resolve(__dirname, '../'), 15 | entry: { 16 | app: './src/main.js' 17 | }, 18 | output: { 19 | path: config.build.assetsRoot, 20 | filename: '[name].js', 21 | publicPath: process.env.NODE_ENV === 'production' 22 | ? config.build.assetsPublicPath 23 | : config.dev.assetsPublicPath 24 | }, 25 | resolve: { 26 | extensions: ['.js', '.vue', '.json'], 27 | alias: { 28 | 'vue$': 'vue/dist/vue.esm.js', 29 | '@': resolve('src'), 30 | } 31 | }, 32 | module: { 33 | rules: [ 34 | { 35 | test: /\.vue$/, 36 | loader: 'vue-loader', 37 | options: vueLoaderConfig 38 | }, 39 | { 40 | test: /\.js$/, 41 | loader: 'babel-loader', 42 | include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] 43 | }, 44 | { 45 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 46 | loader: 'url-loader', 47 | options: { 48 | limit: 10000, 49 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 50 | } 51 | }, 52 | { 53 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 54 | loader: 'url-loader', 55 | options: { 56 | limit: 10000, 57 | name: utils.assetsPath('media/[name].[hash:7].[ext]') 58 | } 59 | }, 60 | { 61 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 62 | loader: 'url-loader', 63 | options: { 64 | limit: 10000, 65 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 66 | } 67 | } 68 | ] 69 | }, 70 | node: { 71 | // prevent webpack from injecting useless setImmediate polyfill because Vue 72 | // source contains it (although only uses it if it's native). 73 | setImmediate: false, 74 | // prevent webpack from injecting mocks to Node native modules 75 | // that does not make sense for the client 76 | dgram: 'empty', 77 | fs: 'empty', 78 | net: 'empty', 79 | tls: 'empty', 80 | child_process: 'empty' 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /client/config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"' 7 | }) 8 | -------------------------------------------------------------------------------- /client/config/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // Template version: 1.3.1 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require("path"); 6 | 7 | module.exports = { 8 | dev: { 9 | // Paths 10 | assetsSubDirectory: "static", 11 | assetsPublicPath: "/", 12 | proxyTable: { 13 | "/api/v1": { 14 | target: "http://localhost:8082", 15 | changeOrigin: true 16 | }, 17 | "/files": { 18 | target: "http://localhost:8082", 19 | changeOrigin: true, 20 | pathRewrite: { 21 | "^/files": "" 22 | } 23 | } 24 | }, 25 | 26 | // Various Dev Server settings 27 | host: "localhost", // can be overwritten by process.env.HOST 28 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 29 | autoOpenBrowser: false, 30 | errorOverlay: true, 31 | notifyOnErrors: true, 32 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 33 | 34 | /** 35 | * Source Maps 36 | */ 37 | 38 | // https://webpack.js.org/configuration/devtool/#development 39 | devtool: "cheap-module-eval-source-map", 40 | 41 | // If you have problems debugging vue-files in devtools, 42 | // set this to false - it *may* help 43 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 44 | cacheBusting: true, 45 | 46 | cssSourceMap: true 47 | }, 48 | 49 | build: { 50 | // Template for index.html 51 | index: path.resolve(__dirname, "../dist/index.html"), 52 | 53 | // Paths 54 | assetsRoot: path.resolve(__dirname, "../dist"), 55 | assetsSubDirectory: "static", 56 | assetsPublicPath: "/", 57 | 58 | /** 59 | * Source Maps 60 | */ 61 | 62 | productionSourceMap: true, 63 | // https://webpack.js.org/configuration/devtool/#production 64 | devtool: "#source-map", 65 | 66 | // Gzip off by default as many popular static hosts such as 67 | // Surge or Netlify already gzip all static assets for you. 68 | // Before setting to `true`, make sure to: 69 | // npm install --save-dev compression-webpack-plugin 70 | productionGzip: false, 71 | productionGzipExtensions: ["js", "css"], 72 | 73 | // Run the build command with an extra argument to 74 | // View the bundle analyzer report after build finishes: 75 | // `npm run build --report` 76 | // Set to `true` or `false` to always turn it on or off 77 | bundleAnalyzerReport: process.env.npm_config_report 78 | } 79 | }; 80 | -------------------------------------------------------------------------------- /client/config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /client/dist/index.html: -------------------------------------------------------------------------------- 1 | 教学辅助系统
-------------------------------------------------------------------------------- /client/dist/static/fonts/element-icons.535877f.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/dist/static/fonts/element-icons.535877f.woff -------------------------------------------------------------------------------- /client/dist/static/fonts/element-icons.732389d.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/dist/static/fonts/element-icons.732389d.ttf -------------------------------------------------------------------------------- /client/dist/static/img/logo.39aabf3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/dist/static/img/logo.39aabf3.png -------------------------------------------------------------------------------- /client/dist/static/js/config.js: -------------------------------------------------------------------------------- 1 | window.actionUrl = "/api/v1"; 2 | -------------------------------------------------------------------------------- /client/dist/static/js/main.js: -------------------------------------------------------------------------------- 1 | function clearInfo() { 2 | window.token = undefined; 3 | localStorage.removeItem('token'); 4 | localStorage.removeItem('userInfo'); 5 | } 6 | 7 | function clearClientInfo(){ 8 | window.clientToken = undefined; 9 | localStorage.removeItem('clientToken'); 10 | localStorage.removeItem('clientUserInfo'); 11 | } 12 | 13 | function getToken() { 14 | if (window.token == undefined || window.token==null) 15 | window.token = localStorage.getItem('token'); 16 | return window.token; 17 | } 18 | 19 | function getClientToken() { 20 | if (window.clientToken == undefined || window.clientToken==null) 21 | window.clientToken = localStorage.getItem('clientToken'); 22 | return window.clientToken; 23 | } 24 | 25 | function getUserInfo() { 26 | return JSON.parse(localStorage.getItem('userInfo')); 27 | } 28 | 29 | function getClientUserInfo() { 30 | return JSON.parse(localStorage.getItem('clientUserInfo')); 31 | } 32 | 33 | function alertConfirm(text) { 34 | return vm.$confirm(text, '提示', { 35 | confirmButtonText: "确定", 36 | cancelButtonText: "取消" 37 | }); 38 | } 39 | 40 | function alertMessage(message, type) { 41 | vm.$message({ 42 | message, 43 | type, 44 | center: true, 45 | duration:2000 46 | }); 47 | } 48 | 49 | function alertNotify(title, message, type) { 50 | vm.$notify({ 51 | title, 52 | message, 53 | type, 54 | duration:2000 55 | }) 56 | } 57 | -------------------------------------------------------------------------------- /client/dist/static/js/manifest.2ae2e69a05c33dfc65f8.js: -------------------------------------------------------------------------------- 1 | !function(r){var n=window.webpackJsonp;window.webpackJsonp=function(e,u,c){for(var f,i,p,a=0,l=[];a 2 | 3 | 4 | 5 | 6 | 7 | 教学辅助系统 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "fxb", 6 | "private": true, 7 | "scripts": { 8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", 9 | "start": "npm run dev", 10 | "build": "node build/build.js" 11 | }, 12 | "dependencies": { 13 | "element-ui": "^2.2.2", 14 | "moment": "^2.21.0", 15 | "vue": "^2.5.2", 16 | "vue-router": "^3.0.1", 17 | "wangeditor": "^3.1.0", 18 | "xss": "^0.3.7" 19 | }, 20 | "devDependencies": { 21 | "autoprefixer": "^7.1.2", 22 | "babel-core": "^6.22.1", 23 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 24 | "babel-loader": "^7.1.1", 25 | "babel-plugin-syntax-jsx": "^6.18.0", 26 | "babel-plugin-transform-runtime": "^6.22.0", 27 | "babel-plugin-transform-vue-jsx": "^3.5.0", 28 | "babel-preset-env": "^1.3.2", 29 | "babel-preset-stage-2": "^6.22.0", 30 | "chalk": "^2.0.1", 31 | "copy-webpack-plugin": "^4.0.1", 32 | "css-loader": "^0.28.0", 33 | "extract-text-webpack-plugin": "^3.0.0", 34 | "file-loader": "^1.1.4", 35 | "friendly-errors-webpack-plugin": "^1.6.1", 36 | "html-webpack-plugin": "^2.30.1", 37 | "node-notifier": "^5.1.2", 38 | "optimize-css-assets-webpack-plugin": "^3.2.0", 39 | "ora": "^1.2.0", 40 | "portfinder": "^1.0.13", 41 | "postcss-import": "^11.0.0", 42 | "postcss-loader": "^2.0.8", 43 | "postcss-url": "^7.2.1", 44 | "rimraf": "^2.6.0", 45 | "semver": "^5.3.0", 46 | "shelljs": "^0.7.6", 47 | "uglifyjs-webpack-plugin": "^1.1.1", 48 | "url-loader": "^0.5.8", 49 | "vue-loader": "^13.3.0", 50 | "vue-style-loader": "^3.0.1", 51 | "vue-template-compiler": "^2.5.2", 52 | "webpack": "^3.6.0", 53 | "webpack-bundle-analyzer": "^2.9.0", 54 | "webpack-dev-server": "^2.9.1", 55 | "webpack-merge": "^4.1.0" 56 | }, 57 | "engines": { 58 | "node": ">= 6.0.0", 59 | "npm": ">= 3.0.0" 60 | }, 61 | "browserslist": [ 62 | "> 1%", 63 | "last 2 versions", 64 | "not ie <= 8" 65 | ] 66 | } 67 | -------------------------------------------------------------------------------- /client/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 29 | -------------------------------------------------------------------------------- /client/src/components/admin/Main.vue: -------------------------------------------------------------------------------- 1 | 20 | 61 | 62 | 91 | 92 | -------------------------------------------------------------------------------- /client/src/components/admin/assets/girl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/src/components/admin/assets/girl.png -------------------------------------------------------------------------------- /client/src/components/admin/common/Left.vue: -------------------------------------------------------------------------------- 1 | 34 | 65 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /client/src/components/admin/common/ShowQuestion.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 61 | 62 | 75 | -------------------------------------------------------------------------------- /client/src/components/admin/common/ShowQuestionGroup.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 46 | 47 | 50 | -------------------------------------------------------------------------------- /client/src/components/admin/common/config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | person_space: [{ 3 | index: "person_space", 4 | name: "个人资料", 5 | }, { 6 | index: "person_space/safe", 7 | name: "安全设置", 8 | data: [{ 9 | index: "person_space/safe/change_email", 10 | name: "修改邮箱" 11 | }, { 12 | index: "person_space/safe/change_password", 13 | name: "修改密码" 14 | }] 15 | }], 16 | manage: [{ 17 | index: 'manage/course_manage', 18 | name: '课程管理' 19 | }, { 20 | index: 'manage/user_manage', 21 | name: '用户管理' 22 | }], 23 | student: [{ 24 | index: 'student', 25 | name: '学生概览' 26 | }, { 27 | index: 'student/add', 28 | name: '录入学生', 29 | data: [{ 30 | index: 'student/add_one', 31 | name: "单个录入" 32 | }, { 33 | index: 'student/add_many', 34 | name: "批量录入" 35 | }] 36 | }], 37 | announcement: [{ 38 | index: 'announcement', 39 | name: '公告概览' 40 | }, { 41 | index: 'announcement/add', 42 | name: '新增公告' 43 | }], 44 | base:[{ 45 | index:'base', 46 | name:'课程简介' 47 | },{ 48 | index:'base/teacher_intro', 49 | name:'教师简介', 50 | },{ 51 | index:'base/first_course', 52 | name:'先导课程' 53 | },{ 54 | index:'base/teach_plan', 55 | name:'教学计划' 56 | },{ 57 | index:'base/exam_type', 58 | name:'考试方式' 59 | },{ 60 | index:'base/reference_book', 61 | name:'参考书目' 62 | },{ 63 | index:'base/section', 64 | name:'章节管理' 65 | },{ 66 | index:'base/courseware_manage', 67 | name:'课件管理' 68 | }], 69 | question_bank:[{ 70 | index:'question_bank', 71 | name:'题库总览' 72 | },{ 73 | index:'add', 74 | name:'录入题目', 75 | data:[{ 76 | index:'question_bank/add/choice_question', 77 | name:'选择题' 78 | },{ 79 | index:'question_bank/add/gap_filling', 80 | name:'填空题' 81 | },{ 82 | index:'question_bank/add/program', 83 | name:'编程题' 84 | }] 85 | },{ 86 | index:'question_bank/question_group_overview', 87 | name:'题组总览' 88 | },{ 89 | index:'question_bank/organize_question', 90 | name:"组织题目" 91 | }], 92 | test:[{ 93 | index:'test', 94 | name:"测试总览" 95 | },{ 96 | index:'test/add_homework', 97 | name:"发布作业" 98 | },{ 99 | index:'test/add_exam', 100 | name:"发布考试" 101 | }], 102 | statistics:[ 103 | { 104 | index:'statistics', 105 | name:'总览' 106 | }, 107 | ], 108 | //答疑 109 | Q_A:[ 110 | { 111 | index:'Q_A', 112 | name:"当前答疑" 113 | } 114 | ], 115 | //主页统计 116 | admin:[ 117 | { 118 | index:'overview', 119 | name:'总览' 120 | } 121 | ] 122 | } 123 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/Main.vue: -------------------------------------------------------------------------------- 1 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/Q_A/CurrentQuestionAnswer.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 61 | 62 | 64 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/Q_A/QuestionAnswerHistory.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 41 | 42 | 45 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/Q_A/common/Badge.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 51 | 52 | 71 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/Q_A/common/MessageList.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 63 | 64 | 84 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/Q_A/common/StudentBadge.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 46 | 47 | 53 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/announcement/AddAnnouncement.vue: -------------------------------------------------------------------------------- 1 | 22 | 55 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/base/Base.vue: -------------------------------------------------------------------------------- 1 | 9 | 43 | 50 | 51 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/base/CourseIntro.vue: -------------------------------------------------------------------------------- 1 | 4 | 13 | 14 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/base/CoursewareManage.vue: -------------------------------------------------------------------------------- 1 | 24 | 85 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/base/ExamType.vue: -------------------------------------------------------------------------------- 1 | 4 | 13 | 14 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/base/FirstCourse.vue: -------------------------------------------------------------------------------- 1 | 4 | 13 | 14 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/base/ReferenceBook.vue: -------------------------------------------------------------------------------- 1 | 4 | 13 | 14 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/base/TeachPlan.vue: -------------------------------------------------------------------------------- 1 | 4 | 13 | 14 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/base/TeacherIntro.vue: -------------------------------------------------------------------------------- 1 | 4 | 13 | 14 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/questionBank/Overview.vue: -------------------------------------------------------------------------------- 1 | 6 | 60 | 63 | 64 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/questionBank/QuestionGroupOverview.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 26 | 27 | 30 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/questionBank/common/ChosePoint.vue: -------------------------------------------------------------------------------- 1 | 5 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/questionBank/common/QuestionList.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 30 | 31 | 34 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/statistics/StudentStatistics.vue: -------------------------------------------------------------------------------- 1 | 20 | 103 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/student/AddManyStudent.vue: -------------------------------------------------------------------------------- 1 | 28 | 67 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/student/AddNode.vue: -------------------------------------------------------------------------------- 1 | 16 | 50 | 57 | 58 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/student/AddSingleStudent.vue: -------------------------------------------------------------------------------- 1 | 23 | 65 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/student/ChoseClass.vue: -------------------------------------------------------------------------------- 1 | 5 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/student/EditStudent.vue: -------------------------------------------------------------------------------- 1 | 23 | 54 | 60 | 61 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/student/ShowClass.vue: -------------------------------------------------------------------------------- 1 | 28 | 91 | 93 | 94 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/test/common/ChoseClass.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 50 | 51 | 54 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/test/common/TestDetailCommon/KnowledgePontPercent.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/test/common/TestDetailCommon/QuestionRightPercent.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/course/test/common/TestDetailCommon/ScoreTable.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 57 | 58 | 61 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/manage/AnnoumcementManage.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/manage/EditUser.vue: -------------------------------------------------------------------------------- 1 | 31 | 85 | 94 | 95 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/manage/Safe.vue: -------------------------------------------------------------------------------- 1 | 31 | 85 | 96 | -------------------------------------------------------------------------------- /client/src/components/admin/pages/manage/TotalStatistic.vue: -------------------------------------------------------------------------------- 1 | 18 | 114 | 119 | 120 | -------------------------------------------------------------------------------- /client/src/components/client/Main.vue: -------------------------------------------------------------------------------- 1 | 14 | 52 | 53 | 78 | 79 | -------------------------------------------------------------------------------- /client/src/components/client/assets/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/src/components/client/assets/logo.jpg -------------------------------------------------------------------------------- /client/src/components/client/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/src/components/client/assets/logo.png -------------------------------------------------------------------------------- /client/src/components/client/common/Badge.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 57 | 58 | 77 | -------------------------------------------------------------------------------- /client/src/components/client/common/DoQuestion.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 47 | 48 | 70 | -------------------------------------------------------------------------------- /client/src/components/client/common/StudentBadge.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 46 | 47 | 53 | -------------------------------------------------------------------------------- /client/src/components/client/pages/Announcement.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 61 | 62 | 80 | -------------------------------------------------------------------------------- /client/src/components/client/pages/ClientMain.vue: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /client/src/components/client/pages/Home.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 62 | 63 | 73 | -------------------------------------------------------------------------------- /client/src/components/client/pages/courseInfo/CourseIntro.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | 24 | 27 | -------------------------------------------------------------------------------- /client/src/components/client/pages/courseInfo/Courseware.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 44 | -------------------------------------------------------------------------------- /client/src/components/client/pages/courseInfo/ExamType.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | 24 | 27 | -------------------------------------------------------------------------------- /client/src/components/client/pages/courseInfo/FirstCourse.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | 24 | 27 | -------------------------------------------------------------------------------- /client/src/components/client/pages/courseInfo/ReferenceBook.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | 24 | 27 | -------------------------------------------------------------------------------- /client/src/components/client/pages/courseInfo/TeachPlan.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | 24 | 27 | -------------------------------------------------------------------------------- /client/src/components/client/pages/courseInfo/TeacherIntro.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | 24 | 27 | -------------------------------------------------------------------------------- /client/src/components/client/pages/exam/CurrentExam.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 57 | 58 | 61 | -------------------------------------------------------------------------------- /client/src/components/client/pages/exam/ExamDetail.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 26 | 27 | -------------------------------------------------------------------------------- /client/src/components/client/pages/exam/LastExam.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 63 | 64 | 67 | -------------------------------------------------------------------------------- /client/src/components/client/pages/knowledgePoint/History.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 69 | 70 | 73 | -------------------------------------------------------------------------------- /client/src/components/client/pages/knowledgePoint/KnowledgeTestDetail.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 17 | 18 | 21 | -------------------------------------------------------------------------------- /client/src/components/client/pages/questionAnswer/CurrentQuestionAnswer.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 61 | 62 | 64 | -------------------------------------------------------------------------------- /client/src/components/client/pages/questionAnswer/QuestionAnswerHistory.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /client/src/components/client/pages/questionAnswer/common/MessageList.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 63 | 64 | 84 | -------------------------------------------------------------------------------- /client/src/components/client/pages/rankList/CoverageList.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 63 | 64 | 67 | -------------------------------------------------------------------------------- /client/src/components/client/pages/rankList/EvaluateList.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 62 | 63 | 66 | -------------------------------------------------------------------------------- /client/src/components/client/pages/work/CurrentWork.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 53 | 54 | 57 | -------------------------------------------------------------------------------- /client/src/components/client/pages/work/LastWork.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 63 | 64 | 67 | -------------------------------------------------------------------------------- /client/src/components/client/pages/work/WorkDetail.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 25 | 31 | 32 | -------------------------------------------------------------------------------- /client/src/components/common/Breadcrumb.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 31 | 32 | 35 | -------------------------------------------------------------------------------- /client/src/components/common/Editor.vue: -------------------------------------------------------------------------------- 1 | 8 | 78 | 88 | 89 | -------------------------------------------------------------------------------- /client/src/components/common/ShowText.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /client/src/components/public/404.vue: -------------------------------------------------------------------------------- 1 | 8 | 18 | 24 | -------------------------------------------------------------------------------- /client/src/components/public/About.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 18 | 19 | 30 | -------------------------------------------------------------------------------- /client/src/components/public/Index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | 18 | 27 | -------------------------------------------------------------------------------- /client/src/components/public/Main.vue: -------------------------------------------------------------------------------- 1 | 9 | 17 | 31 | -------------------------------------------------------------------------------- /client/src/components/public/assets/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/src/components/public/assets/background.jpg -------------------------------------------------------------------------------- /client/src/components/public/test.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/src/components/public/test.vue -------------------------------------------------------------------------------- /client/src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import App from './App' 5 | import router from './router' 6 | import ElementUI from 'element-ui' 7 | import moment from 'moment' 8 | import 'element-ui/lib/theme-chalk/index.css' 9 | //全局注册组件 10 | import Editor from './components/common/Editor' 11 | import ShowText from './components/common/ShowText' 12 | import Breadcrumb from './components/common/Breadcrumb' 13 | Vue.component('Editor',Editor); 14 | Vue.component('ShowText',ShowText); 15 | Vue.component('Breadcrumb',Breadcrumb); 16 | 17 | Object.defineProperty(Vue.prototype,'$moment',{value:moment}); 18 | Vue.config.productionTip = false 19 | Vue.use(ElementUI); 20 | 21 | window.bus = new Vue(); 22 | 23 | window.vm = new Vue({ 24 | el: '#app', 25 | router, 26 | components: { 27 | App 28 | }, 29 | template: '' 30 | }) 31 | console.log('vue示例构建完毕'); 32 | -------------------------------------------------------------------------------- /client/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import Public from './public' 4 | import Admin from './admin' 5 | import Client from './client' 6 | import NotFound from '@/components/public/404' 7 | import About from '@/components/public/About' 8 | import IndexPage from '@/components/public/Index' 9 | Vue.use(Router) 10 | 11 | export default new Router({ 12 | mode: 'history', 13 | base: '/app/', 14 | routes: [{ 15 | path: '', 16 | name: 'indexPage', 17 | component: IndexPage 18 | }, 19 | { 20 | path: '/about', 21 | name: 'about', 22 | component: About 23 | }, 24 | Admin, 25 | Client, 26 | Public, 27 | { 28 | path: '*', 29 | name: "NotFound", 30 | component: NotFound 31 | } 32 | ] 33 | }) 34 | -------------------------------------------------------------------------------- /client/src/router/public.js: -------------------------------------------------------------------------------- 1 | import Public from '@/components/public/Main' 2 | import Login from '@/components/public/Login' 3 | import ResetPass from '@/components/public/ResetPass' 4 | import About from '@/components/public/About' 5 | 6 | export default { 7 | path: '/public', 8 | component: Public, 9 | children: [{ 10 | path: 'admin_login', 11 | component: Login 12 | },{ 13 | path:'client_login', 14 | component:Login 15 | }, { 16 | path: "admin_login/reset_pass", 17 | component: ResetPass 18 | }, { 19 | path: "client_login/reset_pass", 20 | component: ResetPass 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /client/static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/static/.gitkeep -------------------------------------------------------------------------------- /client/static/js/config.js: -------------------------------------------------------------------------------- 1 | window.actionUrl = "/api/v1"; 2 | -------------------------------------------------------------------------------- /client/static/js/main.js: -------------------------------------------------------------------------------- 1 | function clearInfo() { 2 | window.token = undefined; 3 | localStorage.removeItem('token'); 4 | localStorage.removeItem('userInfo'); 5 | } 6 | 7 | function clearClientInfo(){ 8 | window.clientToken = undefined; 9 | localStorage.removeItem('clientToken'); 10 | localStorage.removeItem('clientUserInfo'); 11 | } 12 | 13 | function getToken() { 14 | if (window.token == undefined || window.token==null) 15 | window.token = localStorage.getItem('token'); 16 | return window.token; 17 | } 18 | 19 | function getClientToken() { 20 | if (window.clientToken == undefined || window.clientToken==null) 21 | window.clientToken = localStorage.getItem('clientToken'); 22 | return window.clientToken; 23 | } 24 | 25 | function getUserInfo() { 26 | return JSON.parse(localStorage.getItem('userInfo')); 27 | } 28 | 29 | function getClientUserInfo() { 30 | return JSON.parse(localStorage.getItem('clientUserInfo')); 31 | } 32 | 33 | function alertConfirm(text) { 34 | return vm.$confirm(text, '提示', { 35 | confirmButtonText: "确定", 36 | cancelButtonText: "取消" 37 | }); 38 | } 39 | 40 | function alertMessage(message, type) { 41 | vm.$message({ 42 | message, 43 | type, 44 | center: true, 45 | duration:2000 46 | }); 47 | } 48 | 49 | function alertNotify(title, message, type) { 50 | vm.$notify({ 51 | title, 52 | message, 53 | type, 54 | duration:2000 55 | }) 56 | } 57 | -------------------------------------------------------------------------------- /client/static/student.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/static/student.xlsx -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | 3 | services: 4 | teach-system-mysql: 5 | image: mysql:5.7.25 6 | container_name: teach-system-mysql 7 | volumes: 8 | - /etc/localtime:/etc/localtime 9 | - ./timezone:/ect/timezone 10 | - ./files/mysql/data:/var/lib/mysql 11 | - ./files/mysql/my.cnf:/etc/mysql/my.cnf 12 | environment: 13 | - MYSQL_ROOT_PASSWORD=$MYSQL_PASS 14 | ports: 15 | - 3306:3306 16 | networks: 17 | - teachSystem 18 | 19 | teach-system-redis: 20 | image: redis:3.2.10 21 | container_name: teach-system-redis 22 | networks: 23 | - teachSystem 24 | volumes: 25 | - /etc/localtime:/etc/localtime 26 | - ./timezone:/ect/timezone 27 | - ./files/redis/data:/data 28 | - ./files/redis/redis.conf:/etc/redis/redis.conf 29 | ports: 30 | - 6379:6379 31 | command: redis-server /etc/redis/redis.conf 32 | 33 | teach-system-front: 34 | image: nginx 35 | container_name: teach-system-front 36 | depends_on: 37 | - teach-system-backend 38 | networks: 39 | - teachSystem 40 | volumes: 41 | - /etc/localtime:/etc/localtime 42 | - ./timezone:/ect/timezone 43 | - ./client/dist:/opt/dist 44 | - ./files/front/log:/var/log/nginx 45 | - ./files/front/nginx.conf:/etc/nginx/nginx.conf 46 | ports: 47 | - 8085:8080 48 | 49 | teach-system-backend: 50 | image: registry.cn-hangzhou.aliyuncs.com/fleyx/node:v1 51 | container_name: teach-system-backend 52 | depends_on: 53 | - teach-system-mysql 54 | - teach-system-redis 55 | networks: 56 | - teachSystem 57 | environment: 58 | - mysqlHost=teach-system-mysql 59 | - redisHost=teach-system-redis 60 | - mysqlPassword=$MYSQL_PASS 61 | - judgeToken=$JUDGE_TOKEN 62 | - judgeUrl=http://judge-server:8080 63 | - testSavePath=/opt/tests/test_case 64 | volumes: 65 | - /etc/localtime:/etc/localtime 66 | - ./timezone:/ect/timezone 67 | - ./teachSystem:/opt/workspace 68 | - ./JudgeServer/tests/:/opt/tests 69 | ports: 70 | - 8089:8088 71 | command: "/bin/bash /opt/start.sh" 72 | 73 | # 启动判题核心 74 | judge-server: 75 | image: registry.cn-hangzhou.aliyuncs.com/onlinejudge/judge_server 76 | container_name: judge-server 77 | read_only: true 78 | networks: 79 | - teachSystem 80 | cap_drop: 81 | - SETPCAP 82 | - MKNOD 83 | - NET_BIND_SERVICE 84 | - SYS_CHROOT 85 | - SETFCAP 86 | - FSETID 87 | tmpfs: 88 | - /tmp 89 | volumes: 90 | - ../JudgeServer/tests/test_case:/test_case:ro 91 | - ../JudgeServer/log:/log 92 | - ../JudgeServer/run:/judger 93 | environment: 94 | - BACKEND_URL=http://backend:80/api/judge_server_heartbeat 95 | - SERVICE_URL=http://judge-server:12358 96 | - TOKEN=$JUDGE_TOKEN 97 | ports: 98 | - "0.0.0.0:12358:8080" 99 | 100 | networks: 101 | teachSystem: 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /files/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | COPY sim.tar /opt/sim.tar 3 | RUN cd /opt && \ 4 | apt update && \ 5 | apt install -y wget && \ 6 | apt install -y xz-utils && \ 7 | tar -xvf sim.tar && \ 8 | wget https://npm.taobao.org/mirrors/node/v10.15.1/node-v10.15.1-linux-x64.tar.xz && \ 9 | xz -d node-v10.15.1-linux-x64.tar.xz && \ 10 | tar -xvf node-v10.15.1-linux-x64.tar && \ 11 | rm node-v10.15.1-linux-x64.tar && \ 12 | mv node-v10.15.1-linux-x64 node10 && \ 13 | printf '#!/bin/bash\ncd /opt/workspace\nnpm --registry https://registry.npm.taobao.org install >/dev/null 2>&1\nnode index.js' > start.sh && \ 14 | chmod +x start.sh 15 | ENV PATH=$PATH:/opt/node10/bin:/opt/sim NODE_ENV=production 16 | CMD [ "cd /opt/workspace && ./start.sh" ] 17 | -------------------------------------------------------------------------------- /files/front/nginx.conf: -------------------------------------------------------------------------------- 1 | user root; 2 | worker_processes auto; 3 | pid /run/nginx.pid; 4 | events { 5 | worker_connections 768; 6 | } 7 | http { 8 | # Basic Settings 9 | sendfile on; 10 | # SSL Settings 11 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 12 | # Logging Settings 13 | access_log /var/log/nginx/access.log; 14 | error_log /var/log/nginx/error.log; 15 | # Gzip Settings 16 | gzip on; 17 | tcp_nopush on; 18 | tcp_nodelay on; 19 | keepalive_timeout 65; 20 | types_hash_max_size 2048; 21 | gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/vnd.ms-fontobject font/ttf font/opentype font/x-woff image/svg+xml; 22 | gzip_min_length 1K; 23 | include /etc/nginx/mime.types; 24 | default_type application/octet-stream; 25 | ssl_prefer_server_ciphers on; 26 | upstream teach-system { 27 | server teach-system-backend:8088; 28 | keepalive 2000; 29 | } 30 | server { 31 | listen 8080; 32 | listen [::]:8080; 33 | index index.html; 34 | root /opt/dist/; 35 | server_name _; 36 | location /api/v1/ { 37 | proxy_pass http://teach-system; 38 | proxy_set_header Host $host; 39 | proxy_set_header X-Real-IP $remote_addr; 40 | client_max_body_size 100m; 41 | } 42 | location /files/ { 43 | proxy_pass http://teach-system/; 44 | proxy_set_header Host $host; 45 | proxy_set_header X-Real-IP $remote_addr; 46 | client_max_body_size 100m; 47 | } 48 | # location /dist/ { 49 | # alias /opt/dist/; 50 | # } 51 | location / { 52 | root /opt/dist; 53 | index index.html; 54 | try_files $uri $uri/ /index.html; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /files/mysql/data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/files/mysql/data/.gitkeep -------------------------------------------------------------------------------- /files/mysql/my.cnf: -------------------------------------------------------------------------------- 1 | [mysqld] 2 | user=mysql 3 | port=3306 4 | character-set-server=utf8 5 | default_authentication_plugin=mysql_native_password 6 | skip_ssl 7 | #不区分大小写 8 | lower_case_table_names=1 9 | [client] 10 | default-character-set=utf8 11 | [mysql] 12 | default-character-set=utf8 13 | -------------------------------------------------------------------------------- /files/redis/data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/files/redis/data/.gitkeep -------------------------------------------------------------------------------- /files/redis/redis.conf: -------------------------------------------------------------------------------- 1 | tcp-backlog 511 2 | bind 0.0.0.0 3 | # 空闲多久后关闭连接 4 | timeout 0 5 | # 心跳包时间间隔 6 | tcp-keepalive 0 7 | -------------------------------------------------------------------------------- /files/sim.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/files/sim.tar -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # mysql root密码通过环境变量配置 4 | #export MYSQL_PASS=123456 5 | # 判题服务密钥通过环境变量配置 6 | #export JUDGE_TOKEN=12345678 7 | 8 | #获取脚本所在目录 9 | path=$(cd `dirname $0`;pwd) 10 | pwd 11 | echo "Asia/Shanghai" >$path/timezone 12 | echo "mysql容器建立" 13 | 14 | #判断data中有没有teach_system数据库,没有就进行初始化数据库的操作 15 | omsCount=$(ls -l $path/files/mysql/data | grep -wc teach_system) 16 | if [ $omsCount == 0 ]; then 17 | echo "首次安装系统,进行sql初始化,请稍候,需等候一分钟左右" 18 | docker stop temp_mysql 19 | docker rm temp_mysql 20 | docker run --name=temp_mysql -itd -p 3306:3306 --privileged=true -v $path/files/mysql/data:/var/lib/mysql -v $path/files/mysql/my.cnf:/etc/mysql/my.cnf -v $path/files/mysql/init.sql:/opt/init.sql -e MYSQL_ROOT_PASSWORD=$MYSQL_PASS mysql:5.7.25 21 | sleep 50s 22 | docker exec temp_mysql bash -c "mysql -u root -p$MYSQL_PASS { 17 | let params = announcementHelper.checkGetParam(ctx.allParams.data); 18 | let res = await announcementDao.getByParam(params); 19 | ctx.onSuccess(res); 20 | } 21 | 22 | //获取某门课下所有可显示公告 23 | exports['GET /course/:c_id/announcement/open']=async(ctx,next)=>{ 24 | let res = await announcementDao.getOpenAnnouncement(ctx.params.c_id); 25 | ctx.onSuccess(res); 26 | } 27 | 28 | /** 29 | * 获取公告内容 30 | */ 31 | exports['GET /announcement/:a_id/content']= async (ctx, next) => { 32 | let res = await announcementDao.getContent(ctx.params.a_id); 33 | ctx.onSuccess(res); 34 | } 35 | 36 | /** 37 | * 切换公告显示状态 38 | * url param: 39 | * action:切换到true/false 40 | */ 41 | exports['PUT /announcement/:a_id/switch']= async (ctx, next) => { 42 | let res = await announcementDao.switchOne(ctx.params.a_id,ctx.allParams.action); 43 | ctx.onSuccess(res); 44 | } 45 | /** 46 | * 插入一条公告记录 /announcement 47 | */ 48 | exports['POST /announcement']= async (ctx, next) => { 49 | await announcementDao.addOne(ctx.allParams); 50 | ctx.onSuccess(); 51 | } 52 | /** 53 | * 删除一个公告 54 | */ 55 | exports['DELETE /announcement/:a_id']= async (ctx, next) => { 56 | await announcementDao.deleteOne(ctx.params.a_id); 57 | ctx.onSuccess(); 58 | } -------------------------------------------------------------------------------- /teachSystem/api/announcementHelper.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const ErrorHelper = require('../util/ErrorHelper.js'); 3 | 4 | class announcementHelper { 5 | static checkGetParam(data) { 6 | try { 7 | data = JSON.parse(new Buffer(data, 'base64').toString()); 8 | } catch (err) { 9 | throw ErrorHelper.Error400("参数不合法"); 10 | } 11 | let params = {}; 12 | try { 13 | params.c_id = data.c_id; 14 | params.start = data.start; 15 | params.count = data.count; 16 | let column = data.column; 17 | if (column == 'topic' || column == 'start_time' || column == "end_time") { 18 | params.column = column; 19 | } else { 20 | throw new Error(); 21 | } 22 | params.sort = data.sort == 'desc' ? 'desc' : 'asc'; 23 | } catch (err) { 24 | throw ErrorHelper.Error400("参数有误:"+JSON.stringify(params)); 25 | } 26 | return params; 27 | } 28 | } 29 | 30 | module.exports = announcementHelper; -------------------------------------------------------------------------------- /teachSystem/api/badge.js: -------------------------------------------------------------------------------- 1 | const badgeDao = require('../dao/badgeDao.js'); 2 | const config = require('../config/config.js'); 3 | 4 | //获取某用户所拥有的徽章 5 | exports['GET /user/:u_id/badge'] = async ctx => { 6 | let res = await badgeDao.getUserBadge(ctx.params.u_id); 7 | res.forEach(item => { 8 | item.b_img = config.host + '/badgeIcon/' + item.b_img; 9 | }) 10 | ctx.onSuccess(res); 11 | } 12 | 13 | //获取课程门数徽章详细数据 14 | exports['GET /user/:u_id/badge/:b_id'] = async ctx => { 15 | let {u_id,b_id,type} = ctx.params; 16 | let res = await badgeDao.getBadgeDetail(u_id,b_id,ctx.allParams.type); 17 | res.next.b_img = config.host + '/badgeIcon/' + res.next.b_img; 18 | ctx.onSuccess(res); 19 | } -------------------------------------------------------------------------------- /teachSystem/api/class.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const classDao = require('../dao/classDao.js'); 3 | const classHelper = require('./classHelper.js'); 4 | /** 5 | * 获取一个班的学生 /class/:class_id/user 6 | */ 7 | let get_classId_user = async (ctx, next) => { 8 | let res = await classDao.getStudentOfClass(ctx.params.class_id); 9 | ctx.onSuccess(res); 10 | } 11 | 12 | /** 13 | * 新增一个班级 14 | */ 15 | let post = async (ctx, next) => { 16 | let res = await classDao.addOne(ctx.allParams); 17 | ctx.onSuccess(res); 18 | } 19 | 20 | /** 21 | * 新增一名学生 /class/:class_id/add_one_student 22 | */ 23 | let post_addOneStudent = async (ctx, next) => { 24 | await classDao.addOneStudent(ctx.params.class_id, ctx.allParams); 25 | ctx.onSuccess(); 26 | } 27 | 28 | /** 29 | *execl提交,批量新增 /class/:class_id/add_many_student 30 | *execl格式:(无标题)第一列:姓名,第二列:学号,第三列:密码(如账号已存在 ,密码设置无效) 31 | */ 32 | let post_addManyStudent = async (ctx, next) => { 33 | let tableData = classHelper.getTableInfo(ctx.allParams.xlsx); 34 | let res = await classDao.addManyStudent(ctx.allParams.class_id, tableData); 35 | ctx.onSuccess(res); 36 | } 37 | 38 | /** 39 | * 从班级中删除一名学生 /class/:class_id/user/:u_id 40 | */ 41 | let delete_classId_user_uId = async (ctx, next) => { 42 | await classDao.deleteOneStudent(ctx.params.class_id, ctx.params.u_id); 43 | ctx.onSuccess(); 44 | } 45 | 46 | /** 47 | * 删除一个班级 /class/:class_id 48 | */ 49 | let delete_classId=async(ctx,next)=>{ 50 | await classDao.deleteOneClass(ctx.params.class_id); 51 | ctx.onSuccess(); 52 | } 53 | 54 | module.exports = { 55 | 'GET /class/:class_id/user': get_classId_user, 56 | 'POST /class': post, 57 | 'POST /class/:class_id/add_one_student': post_addOneStudent, 58 | 'POST /class/:class_id/add_many_student': post_addManyStudent, 59 | 'DELETE /class/:class_id/user/:u_id': delete_classId_user_uId, 60 | "DELETE /class/:class_id":delete_classId 61 | } -------------------------------------------------------------------------------- /teachSystem/api/classHelper.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const js_xlsx = require('js-xlsx'); 3 | const xlsx = require('xlsx'); 4 | const fs = require('fs-extra'); 5 | 6 | class classHelper { 7 | static getTableInfo(file) { 8 | let content = xlsx.readFile(file.path); 9 | let sheetName = content.SheetNames[0]; 10 | let res = xlsx.utils.sheet_to_json(content.Sheets[sheetName],{header:['u_name','code','password']}); 11 | return res; 12 | } 13 | } 14 | 15 | module.exports = classHelper; -------------------------------------------------------------------------------- /teachSystem/api/courseHelper.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const ErrorHelper = require('../util/ErrorHelper.js'); 3 | class courseHelper{ 4 | static checkGetBaseInfoParam(param){ 5 | let keys = Object.keys(param); 6 | if(keys.length != 2){ 7 | return ErrorHelper.newError('只能携带c_id,column',406); 8 | } 9 | let validParam = ['c_name','code','c_picture','c_intro','teacher_intro','first_course','teach_plan','exam_type','reference_book']; 10 | if(!validParam.includes(param.column)){ 11 | return ErrorHelper.newError('参数不可用',406); 12 | } 13 | } 14 | } 15 | module.exports=courseHelper; -------------------------------------------------------------------------------- /teachSystem/api/courseware.js: -------------------------------------------------------------------------------- 1 | const coursewareDao = require('../dao/coursewareDao.js'); 2 | const uuid = require('uuid/v1'); 3 | const fs = require('fs-extra'); 4 | 5 | /** 6 | * 获取某一门课课件 /course/:c_id/courseware 7 | */ 8 | exports['GET /course/:c_id/courseware'] = async (ctx, next) => { 9 | let res = await coursewareDao.getAllofClass(ctx.params.c_id); 10 | ctx.onSuccess(res); 11 | } 12 | 13 | 14 | /** 15 | * 提交课件 /course/:c_id/courseware 16 | */ 17 | exports['POST /course/:c_id/courseware'] = async (ctx, next) => { 18 | let res = await coursewareDao.add(ctx.params.c_id, ctx.request.body.files); 19 | ctx.onSuccess(res); 20 | } 21 | 22 | /** 23 | * 删除课件/course/:c_id/courseware/:cw_id 24 | */ 25 | exports['DELETE /course/:c_id/courseware/:cw_id'] = async (ctx, next) => { 26 | await coursewareDao.deleteOne(ctx.params.c_id, ctx.params.cw_id); 27 | ctx.onSuccess(); 28 | } -------------------------------------------------------------------------------- /teachSystem/api/grade.js: -------------------------------------------------------------------------------- 1 | const uuid = require('uuid/v1'); 2 | const gradeDao = require('../dao/gradeDao.js'); 3 | 4 | /** 5 | * 获取某门课程下所有的年级 6 | * /course/:c_id/grade 7 | */ 8 | let get_course_cId_grade = async (ctx, next) => { 9 | let res = await gradeDao.getByCourseId(ctx.params.c_id); 10 | ctx.onSuccess(res); 11 | } 12 | 13 | let post = async (ctx, next) => { 14 | let res = await gradeDao.addOne(ctx.allParams); 15 | ctx.onSuccess(res); 16 | } 17 | 18 | /** 19 | * 删除一个年级,仅当该年级下无班级时才可删除 20 | */ 21 | let delete_gId= async (ctx, next) => { 22 | await gradeDao.deleteOneGrade(ctx.params.g_id); 23 | ctx.onSuccess(); 24 | } 25 | 26 | module.exports = { 27 | 'GET /course/:c_id/grade': get_course_cId_grade, 28 | 'POST /grade': post, 29 | 'DELETE /grade/:g_id': delete_gId 30 | } -------------------------------------------------------------------------------- /teachSystem/api/knowledgePoint.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const knowledgePointDao = require('../dao/knowledgePointDao.js'); 3 | 4 | /** 5 | * 返回某门课的全部知识点,按章节分类 6 | */ 7 | exports["GET /course/:c_id/knowledge_point"] = async (ctx, next) => { 8 | let res = await knowledgePointDao.getPontsOrderBySection(ctx.params.c_id); 9 | ctx.onSuccess(res); 10 | } 11 | 12 | //返回某位学生知识点答题情况 13 | exports["GET /user/:u_id/course/:c_id/knowledge_point/condition"]=async(ctx,next)=>{ 14 | let {u_id,c_id}=ctx.params; 15 | let res = await knowledgePointDao.getStudentCondition(u_id,c_id); 16 | ctx.onSuccess(res); 17 | } 18 | 19 | /** 20 | * 新增一个知识点 21 | */ 22 | exports["POST /knowledge_point"] = async (ctx, next) => { 23 | let res = await knowledgePointDao.addOne(ctx.allParams); 24 | ctx.onSuccess(res); 25 | } 26 | 27 | /** 28 | * 删除一个知识点 29 | */ 30 | exports[`DELETE /knowledge_point/:kp_id`] = async (ctx, next) => { 31 | await knowledgePointDao.deleteOne(ctx.params.kp_id); 32 | ctx.onSuccess(); 33 | } -------------------------------------------------------------------------------- /teachSystem/api/public.js: -------------------------------------------------------------------------------- 1 | const publicDao = require("../dao/publicDao.js"); 2 | /** 3 | * 图片上传 4 | */ 5 | exports["POST /public/img_upload"] = async ctx => { 6 | let res = await publicDao.uploadImg(ctx.allParams); 7 | ctx.onSuccess(res); 8 | }; 9 | 10 | /** 11 | * 获取系统时间 12 | */ 13 | exports["GET /public/system_time"] = async ctx => { 14 | ctx.onSuccess(Date.now()); 15 | }; 16 | 17 | /** 18 | * 登录 19 | */ 20 | exports["POST /public/login"] = async ctx => { 21 | await publicDao.login(ctx); 22 | }; 23 | 24 | /** 25 | * 获取验证码 26 | */ 27 | exports["GET /public/authcode"] = async ctx => { 28 | await publicDao.authCode(ctx); 29 | }; 30 | 31 | /** 32 | * 重置密码 33 | */ 34 | exports["POST /public/reset-password"] = async ctx => { 35 | await publicDao.resetPass(ctx); 36 | }; 37 | 38 | exports["POST /public/mail-auth-code"] = async ctx => { 39 | await publicDao.sendMail(ctx); 40 | }; 41 | -------------------------------------------------------------------------------- /teachSystem/api/questionAnswer.js: -------------------------------------------------------------------------------- 1 | const questionAnswerDao = require("../dao/questionAnswerDao.js"); 2 | 3 | //获取一个用户的对话type=start/reply/all(我发起的/我解答的/全部) 4 | //?type=start&c_id=&is_closed=1 5 | exports['GET /question_answer'] = async ctx => { 6 | let { 7 | c_id, 8 | type, 9 | is_closed 10 | } = ctx.allParams; 11 | let res = await questionAnswerDao.getQuestionAnswer(ctx.userInfo.u_id, c_id, type, is_closed); 12 | ctx.onSuccess(res); 13 | } 14 | 15 | //获取某对话消息中时间大于time的 time=? 16 | exports['GET /question_answer/:qa_id/detail'] = async ctx => { 17 | let { 18 | time, 19 | type 20 | } = ctx.allParams; 21 | let res = await questionAnswerDao.getMessages(ctx.userInfo.u_id, ctx.params.qa_id, time, type); 22 | ctx.onSuccess(res); 23 | } 24 | 25 | //新建一个答疑对话 26 | exports['POST /question_answer'] = async ctx => { 27 | let res = await questionAnswerDao.addOne(ctx.userInfo.u_id, ctx.allParams); 28 | ctx.onSuccess(res); 29 | } 30 | 31 | //对话中新增一条消息 32 | exports['POST /question_answer/:qa_id/question_answer_detail'] = async ctx => { 33 | let res = await questionAnswerDao.addOneMessage(ctx.userInfo.u_id, ctx.params.qa_id, ctx.allParams.content); 34 | ctx.onSuccess(res); 35 | } 36 | 37 | //关闭一个答疑对话 38 | exports['PUT /question_answer/:qa_id/close'] = async ctx => { 39 | await questionAnswerDao.closeOne(ctx.userInfo.u_id, ctx.allParams.start_u_id, ctx.params.qa_id); 40 | ctx.onSuccess(); 41 | } -------------------------------------------------------------------------------- /teachSystem/api/questionGroup.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const questionGroupDao = require('../dao/questionGroupDao.js'); 3 | const questionGroupHelper = require('./questionGroupHelper.js'); 4 | /** 5 | * 条件检索 6 | */ 7 | exports["GET /question_group"] = async (ctx, next) => { 8 | let param = questionGroupHelper.getParam(ctx.allParams); 9 | let res = await questionGroupDao.getByParam(param); 10 | ctx.onSuccess(res); 11 | } 12 | 13 | /** 14 | * 获取本题组所有题目 15 | */ 16 | exports["GET /question_group/:qg_id/all_question"] = async (ctx,next)=>{ 17 | let res = await questionGroupDao.getQuestion(ctx.params.qg_id); 18 | ctx.onSuccess(res); 19 | } 20 | 21 | /** 22 | * 新增一组题目 23 | */ 24 | exports["POST /course/:c_id/question_group"] = async (ctx, next) => { 25 | let res = await questionGroupDao.addOne(ctx.params.c_id, ctx.allParams); 26 | ctx.onSuccess(res); 27 | } 28 | 29 | /** 30 | * 删除一个题组 31 | */ 32 | exports["DELETE /course/:c_id/question_group/:qg_id"] = async(ctx,next)=>{ 33 | await questionGroupDao.deleteOne(ctx.params.c_id,ctx.params.qg_id); 34 | ctx.onSuccess(); 35 | } -------------------------------------------------------------------------------- /teachSystem/api/questionGroupHelper.js: -------------------------------------------------------------------------------- 1 | const ErrorHelper = require('../util/ErrorHelper.js'); 2 | class questionGroupHelper{ 3 | static getParam(params){ 4 | try{ 5 | params= JSON.parse(new Buffer(params.data,'base64').toString()); 6 | }catch(err){ 7 | ErrorHelper.Error400("参数不合法:"); 8 | } 9 | return params; 10 | } 11 | } 12 | 13 | module.exports=questionGroupHelper; -------------------------------------------------------------------------------- /teachSystem/api/questionLibrary.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const questionLibraryHelper = require('./questionLibraryHelper.js'); 3 | const questionLibraryDao = require('../dao/questionLibraryDao.js'); 4 | 5 | /** 6 | * 条件检索用户 7 | * { 8 | * searchContent:'', 搜索内容 9 | * searchColumn:'', 搜索字段 10 | * c_id:'',课程id 11 | * q_type:[],显示题型 12 | * isExam:0/1/2 0:练习用 1:考试用 2:所有 13 | * count:false 是否返回总记录数 14 | * start:0 15 | * pageSize:10 16 | * } 17 | */ 18 | exports["GET /question_library"] = async (ctx, next) => { 19 | let params = questionLibraryHelper.checkGetParam(ctx.allParams); 20 | let res = await questionLibraryDao.getByParam(params); 21 | ctx.onSuccess(res); 22 | } 23 | 24 | //获取展示题目需要信息 25 | exports["GET /question_library/:ql_id/show_question"] = async (ctx, next) => { 26 | let res = await questionLibraryDao.getShowQuestion(ctx.params.ql_id); 27 | ctx.onSuccess(res); 28 | } 29 | 30 | /** 31 | * 插入题目 32 | */ 33 | exports["POST /question_library"] = async (ctx, next) => { 34 | questionLibraryHelper.checkPostParam(ctx.allParams); 35 | let res = await questionLibraryDao.addOne(ctx.allParams); 36 | ctx.onSuccess(res); 37 | } 38 | 39 | /** 40 | * 删除题目 41 | */ 42 | exports["DELETE /question_library/:ql_id"] = async (ctx, next) => { 43 | await questionLibraryDao.deleteOne(ctx.params.ql_id); 44 | ctx.onSuccess(); 45 | } 46 | 47 | /** 48 | * 批量删除题目 49 | */ 50 | exports["DELETE /course/:c_id/question_library"] = async (ctx, next) => { 51 | let id = ctx.allParams.id.split(','); 52 | let failList = []; 53 | for (let i = 0; i < id.length; i++) { 54 | try { 55 | await questionLibraryDao.deleteOne(id[i]); 56 | } catch (err) { 57 | failList.push(id[i]); 58 | } 59 | } 60 | ctx.onSuccess(failList); 61 | } -------------------------------------------------------------------------------- /teachSystem/api/questionLibraryHelper.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const ErrorHelper = require('../util/ErrorHelper.js'); 3 | class questionLibraryHelper { 4 | //检查检索参数是否合法 5 | static checkGetParam(params) { 6 | try { 7 | params = JSON.parse(new Buffer(params.data, 'base64').toString()); 8 | if (params.searchColumn !== '' && (params.searchColumn !== 'ql_id' && params.searchColumn !== 'q_simple_description')) 9 | throw new Error(); 10 | let index = params.q_type.findIndex(item => typeof(item) !== 'number'); 11 | if (index != -1) 12 | throw new Error(); 13 | } catch (err) { 14 | throw ErrorHelper.newError("参数不合法", 406); 15 | } 16 | return params; 17 | } 18 | 19 | static checkPostParam(params) { 20 | if (params.c_id == undefined) 21 | throw ErrorHelper.newError("c_id不存在", 406); 22 | if (params.q_simple_description == undefined) 23 | throw ErrorHelper.newError("题目简述不存在", 406); 24 | if (params.q_description == undefined || params.q_description.length == 0) 25 | throw ErrorHelper.newError("题目内容为空", 406); 26 | if (params.q_description.length >= 65535) 27 | throw ErrorHelper.newError("题目内容超过65535", 406); 28 | if (params.q_type < 3) { 29 | if (params.alternative_answer == undefined || params.alternative_answer.length == 0) 30 | throw ErrorHelper.newError("备选项为空", 406); 31 | let length = 0; 32 | params.alternative_answer.forEach(item => length += item.length); 33 | if (length >= 4096) 34 | throw ErrorHelper.newError("备选项长度超过4096", 406); 35 | } else { 36 | params.alternative_answer = ''; 37 | } 38 | if (params.answer == undefined || params.answer.length == 0) 39 | throw ErrorHelper.newError("未指定正确答案", 406); 40 | } 41 | } 42 | 43 | module.exports = questionLibraryHelper; -------------------------------------------------------------------------------- /teachSystem/api/section.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const sectionDao = require('../dao/sectionDao.js'); 3 | 4 | /** 5 | * 获取某门课程所有章节 6 | */ 7 | exports['GET /course/:c_id/section'] = async (ctx, next) => { 8 | let res = await sectionDao.getByCourseId(ctx.params.c_id); 9 | ctx.onSuccess(res); 10 | } 11 | 12 | /** 13 | * 新增一个章节 14 | */ 15 | exports['POST /section'] = async (ctx, next) => { 16 | let res = await sectionDao.addOne(ctx.allParams); 17 | ctx.onSuccess(res); 18 | } 19 | /** 20 | * 删除一个章节 21 | */ 22 | exports['DELETE /section/:s_id'] = async (ctx, next) => { 23 | await sectionDao.deleteOne(ctx.params.s_id); 24 | ctx.onSuccess(); 25 | } -------------------------------------------------------------------------------- /teachSystem/api/testHelper.js: -------------------------------------------------------------------------------- 1 | const ErrorHelper = require('../util/ErrorHelper.js'); 2 | const MysqlHelper = require("../util/MysqlHelper.js"); 3 | 4 | class testHelper { 5 | static checkGetParam(param) { 6 | try { 7 | param = JSON.parse(new Buffer(param.data, 'base64').toString()); 8 | } catch (err) { 9 | throw ErrorHelper.Error400("参数不符合要求"); 10 | } 11 | return param; 12 | } 13 | //检查能否获取测试题 14 | static async checkTest(u_id, c_id, test_id) { 15 | let testData = await MysqlHelper.first(` 16 | select sc_id,submit_time,start_time,end_time,t_type,start_do_time from student_test_view where u_id=? and c_id=? and test_id=? 17 | `, u_id, c_id, test_id); 18 | if (testData == null) 19 | throw ErrorHelper.Error400("本次测试不存在"); 20 | let now = Date.now(); 21 | if (testData.start_time > now) 22 | throw ErrorHelper.Error400("测试还未开始"); 23 | if (testData.t_type == 2 && testData.submit_time > 0 && testData.end_time > now) 24 | throw ErrorHelper.Error403("考试正在进行,且您已提交,无法获取题目"); 25 | if (testData.end_time > now && testData.start_do_time == null) 26 | await MysqlHelper.update(`update student_test set start_time = ? where sc_id=? and test_id=?`, Date.now(), testData.sc_id, test_id); 27 | } 28 | //检查是否可以提交答案 29 | static async checkSubmit(u_id, c_id, test_id) { 30 | let testData = await MysqlHelper.first(` 31 | select sc_id,submit_time,start_time,end_time,t_type,start_do_time from student_test_view where u_id=? and c_id=? and test_id=? 32 | `, u_id, c_id, test_id); 33 | if (testData == null) 34 | throw ErrorHelper.Error400("本次测试不存在"); 35 | let now = Date.now(); 36 | if (testData.start_time > now) 37 | throw ErrorHelper.Error400("测试还未开始"); 38 | if ((testData.end_time - now) < -60 * 1000) //一分钟缓冲时间,避免提交失败 39 | throw ErrorHelper.Error400("测试已经结束"); 40 | // if (testData.submit_time > 0) 41 | // throw ErrorHelper.Error400("已经提交答案,请勿重复提交"); 42 | } 43 | } 44 | 45 | module.exports = testHelper; -------------------------------------------------------------------------------- /teachSystem/api/userHelper.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | let stringHelper = require('../util/StringHelper.js'); 3 | let ErrorHelper = require('../util/ErrorHelper.js'); 4 | 5 | class userHelper { 6 | //检查增加用户参数 7 | static checkAddUserParams(params) { 8 | if (stringHelper.isEmpty(params.u_name) || stringHelper.isEmpty(params.u_type) || 9 | stringHelper.isEmpty(params.code) || stringHelper.isEmpty(params.password)) { 10 | throw ErrorHelper.Error400("某项输入为空"); 11 | } 12 | } 13 | 14 | static checkChangePasswordParams(params,userInfo) { 15 | if (stringHelper.isEmpty(params.password) || stringHelper.isEmpty(params.newPassword) || stringHelper.isEmpty(params.repeatNewPassword)) { 16 | throw ErrorHelper.Error400("某项输入为空"); 17 | } 18 | if (params.newPassword != params.repeatNewPassword) { 19 | throw ErrorHelper.Error400("两次密码输入不一致"); 20 | } 21 | } 22 | 23 | //检查检索参数 24 | static getParams(data) { 25 | let params = {}; 26 | try { 27 | let param = new Buffer(data, 'base64').toString(); 28 | param = JSON.parse(param); 29 | params.orderBy = param.orderBy; 30 | params.start = param.start; 31 | params.count = param.count; 32 | params.sort = param.sort == 'desc' ? 'desc' : 'asc'; 33 | if (param.search != undefined && param.search.content.length > 0 && (param.search.filed == 'code' || param.search.filed == 'u_id' || param.search.filed == 'u_name') && typeof (param.search.content) == 'string') { 34 | params.search = param.search; 35 | } 36 | if (param.u_type.length > 0) { 37 | params.u_type = '('; 38 | for (let i = 0; i < param.u_type.length; i++) { 39 | if (typeof (param.u_type[i]) != 'number') { 40 | throw new Error(); 41 | } 42 | if (i == 0) { 43 | params.u_type += param.u_type[i]; 44 | } else { 45 | params.u_type += ',' + param.u_type[i]; 46 | } 47 | } 48 | params.u_type += ')'; 49 | } else { 50 | params.u_type = null; 51 | } 52 | return params; 53 | } catch (error) { 54 | throw ErrorHelper.Error400('参数不合法'); 55 | } 56 | } 57 | } 58 | 59 | module.exports = userHelper; -------------------------------------------------------------------------------- /teachSystem/authority/dbAuth.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 扫描所有路由加入到资源表中 3 | */ 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const config = require('../config/config.js'); 7 | const mysqlHelper = require('../util/MysqlHelper.js'); 8 | 9 | (async() => { 10 | let folderPath = path.join(config.rootPath, '/api'); 11 | let files = fs.readdirSync(folderPath); 12 | for (let i = 0; i < files.length; i++) { 13 | let file = path.join(folderPath, files[i]); 14 | let temp = require(file); 15 | let urls = Object.keys(temp); 16 | for (let j = 0; j < urls.length; j++) { 17 | let params = urls[j].split(' '); 18 | try { 19 | await mysqlHelper.execute(` 20 | insert into resource(url,method,description) values(?,?,?) 21 | `, params[1], params[0], ''); 22 | console.log(`新增资源:${params[1]},方法:${params[0]}`); 23 | } catch (error) { 24 | } 25 | } 26 | } 27 | console.log('更新权限资源完毕'); 28 | process.exit(); 29 | })(); -------------------------------------------------------------------------------- /teachSystem/authority/index.js: -------------------------------------------------------------------------------- 1 | const Koa = require('koa'); 2 | const koaBody = require('koa-body'); 3 | const Router = require('koa-router'); 4 | const static = require('koa-static'); 5 | const MysqlHelper = require('../util/MysqlHelper.js'); 6 | 7 | let app = new Koa(); 8 | let router = new Router(); 9 | 10 | let path = require('path'); 11 | 12 | //获取角色列表 13 | router.get('/role', async (ctx, next) => { 14 | let res = await MysqlHelper.row('select * from Jurisdiction'); 15 | ctx.body = res; 16 | console.log(ctx.body); 17 | await next(); 18 | }) 19 | 20 | //获取资源列表 21 | router.get('/url', async (ctx, next) => { 22 | let res = await MysqlHelper.row('select * from resource'); 23 | ctx.body = res; 24 | await next(); 25 | }) 26 | 27 | //获取某个角色权限 28 | router.get('/role_url', async (ctx, next) => { 29 | let roleId = ctx.query.roleId; 30 | ctx.body = await MysqlHelper.row(`select b.r_id from Jurisdiction_resource a inner join 31 | resource b on a.r_id = b.r_id where a.j_id = ?`, roleId); 32 | }) 33 | 34 | //新增一个角色 35 | router.get('/add_role', async ctx => { 36 | let name = ctx.query.name; 37 | let res = await MysqlHelper.execute(`insert into jurisdiction(content) values(?)`, 38 | name); 39 | ctx.body = { 40 | j_id:res.insertId, 41 | content: name 42 | } 43 | }) 44 | 45 | //修改权限 46 | router.get('/change_role_url', async (ctx, next) => { 47 | let j_id = ctx.query.j_id; 48 | let urls = JSON.parse(ctx.query.arr); 49 | await MysqlHelper.execute(`delete from Jurisdiction_resource where 50 | j_id=?`, j_id); 51 | for (let i = 0; i < urls.length; i++) { 52 | await MysqlHelper.execute(`insert into Jurisdiction_resource values(?,?)`, 53 | j_id, urls[i]); 54 | } 55 | ctx.body = "操作成功"; 56 | }) 57 | 58 | app.use(static(path.join(__dirname, 'static'), { 59 | maxAge: 0 60 | })).use(koaBody()).use(router.routes()).listen(8084); 61 | console.log("授权管理启动于:8084"); -------------------------------------------------------------------------------- /teachSystem/authority/readme.md: -------------------------------------------------------------------------------- 1 | 本文件夹用于给角色进行权限管理,不适用于用户权限管理。请使用后及时关闭。 -------------------------------------------------------------------------------- /teachSystem/authority/static/css/main.css: -------------------------------------------------------------------------------- 1 | #app{ 2 | min-width:900px; 3 | max-height:900px; 4 | margin-left:10%; 5 | margin-right:10%; 6 | } 7 | .top{ 8 | border-bottom:5px solid black; 9 | margin-bottom:20px; 10 | color: red; 11 | font-size: 1.5em; 12 | text-align: center; 13 | } 14 | .left{ 15 | width:30%; 16 | float: left; 17 | } 18 | .center{ 19 | width:2px; 20 | height:70%; 21 | background:black; 22 | float:left; 23 | } 24 | .right{ 25 | width:69%; 26 | float: right; 27 | } 28 | .submit{ 29 | margin-top:50px; 30 | text-align: center; 31 | } 32 | .role-item{ 33 | padding:5px; 34 | margin:5px; 35 | font-size:1.3em; 36 | line-height: 1.5em; 37 | } 38 | .url-item{ 39 | border-bottom:1px solid #dbd4d4; 40 | margin-bottom:5px; 41 | } 42 | .unchecked{ 43 | color:blue; 44 | background:#dceced; 45 | } 46 | .checked{ 47 | background:#959b9b; 48 | } 49 | .hover-pointer{ 50 | cursor:pointer; 51 | } 52 | -------------------------------------------------------------------------------- /teachSystem/authority/static/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 管理员授权 6 | 7 | 8 | 9 |
10 |
11 |
12 |
13 | 14 |
15 |
16 | 17 |
18 | 19 | 20 | 21 |
22 |
23 |
24 | 25 |
26 |
27 |
28 |
29 |
30 | 34 |
35 |
36 |
37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /teachSystem/config/judgeConfig.js: -------------------------------------------------------------------------------- 1 | const default_env = ["LANG=en_US.UTF-8", "LANGUAGE=en_US:en", "LC_ALL=en_US.UTF-8"]; 2 | module.exports = { 3 | c: { 4 | compile: { 5 | src_name: "main.c", 6 | exe_name: "main", 7 | max_cpu_time: 3000, 8 | max_real_time: 5000, 9 | max_memory: 128 * 1024 * 1024, 10 | compile_command: "/usr/bin/gcc -DONLINE_JUDGE -O2 -w -fmax-errors=3 -std=c99 {src_path} -lm -o {exe_path}" 11 | }, 12 | run: { 13 | command: "{exe_path}", 14 | seccomp_rule: "c_cpp", 15 | env: default_env 16 | } 17 | }, 18 | cpp: { 19 | compile: { 20 | src_name: "main.cpp", 21 | exe_name: "main", 22 | max_cpu_time: 3000, 23 | max_real_time: 5000, 24 | max_memory: 128 * 1024 * 1024, 25 | compile_command: "/usr/bin/g++ -DONLINE_JUDGE -O2 -w -fmax-errors=3 -std=c++11 {src_path} -lm -o {exe_path}" 26 | }, 27 | run: { 28 | command: "{exe_path}", 29 | seccomp_rule: "c_cpp", 30 | env: default_env 31 | } 32 | }, 33 | java: { 34 | name: "java", 35 | compile: { 36 | src_name: "Main.java", 37 | exe_name: "Main", 38 | max_cpu_time: 3000, 39 | max_real_time: 5000, 40 | max_memory: -1, 41 | compile_command: "/usr/bin/javac {src_path} -d {exe_dir} -encoding UTF8" 42 | }, 43 | run: { 44 | command: "/usr/bin/java -cp {exe_dir} -XX:MaxRAM={max_memory}k -Djava.security.manager -Dfile.encoding=UTF-8 -Djava.security.policy==/etc/java_policy -Djava.awt.headless=true Main", 45 | seccomp_rule: null, 46 | env: default_env, 47 | memory_limit_check_only: 1 48 | } 49 | }, 50 | py2: { 51 | compile: { 52 | src_name: "solution.py", 53 | exe_name: "solution.pyc", 54 | max_cpu_time: 3000, 55 | max_real_time: 5000, 56 | max_memory: 128 * 1024 * 1024, 57 | compile_command: "/usr/bin/python -m py_compile {src_path}" 58 | }, 59 | run: { 60 | command: "/usr/bin/python {exe_path}", 61 | seccomp_rule: "general", 62 | env: default_env 63 | } 64 | }, 65 | py3: { 66 | compile: { 67 | src_name: "solution.py", 68 | exe_name: "__pycache__/solution.cpython-35.pyc", 69 | max_cpu_time: 3000, 70 | max_real_time: 5000, 71 | max_memory: 128 * 1024 * 1024, 72 | compile_command: "/usr/bin/python3 -m py_compile {src_path}" 73 | }, 74 | run: { 75 | command: "/usr/bin/python3 {exe_path}", 76 | seccomp_rule: "general", 77 | env: ["PYTHONIOENCODING=UTF-8", ...default_env] 78 | } 79 | } 80 | }; 81 | -------------------------------------------------------------------------------- /teachSystem/dao/announcementDao.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const MysqlHelper = require('../util/MysqlHelper.js'); 3 | 4 | class announcementDao { 5 | static async getByParam(params) { 6 | let res; 7 | let data = await MysqlHelper.row(` 8 | select a_id,topic,start_time,end_time from announcement where c_id=? order by ${params.column} ${params.sort} limit ?,10 9 | `, params.c_id, params.start); 10 | data.forEach(item => { 11 | if (item.end_time > Date.now() && item.start_time < Date.now()) { 12 | item.isClosed = false; 13 | } else { 14 | item.isClosed = true; 15 | } 16 | }) 17 | if (params.count) { 18 | res = {}; 19 | res.count = await MysqlHelper.single(`select count(a_id) from announcement where c_id=?`, params.c_id); 20 | res.data = data; 21 | } else { 22 | res = data; 23 | } 24 | return res; 25 | } 26 | 27 | static async getOpenAnnouncement(c_id) { 28 | let now = Date.now(); 29 | let res = await MysqlHelper.row(` 30 | SELECT a_id,topic,start_time from announcement where c_id=? and start_time? order by start_time desc 31 | `, c_id, now, now); 32 | return res; 33 | } 34 | 35 | static async getContent(a_id) { 36 | return await MysqlHelper.single(`select content from announcement where a_id=?`, a_id); 37 | } 38 | 39 | static async switchOne(a_id, action) { 40 | let end_time = Date.now(); 41 | let start_time = Date.now(); 42 | if (action == 'true') { 43 | end_time += 30 * 365 * 24 * 60 * 60 * 1000; 44 | } 45 | await MysqlHelper.execute(`update announcement set end_time=?,start_time=? where a_id=?`, end_time, start_time, a_id); 46 | return {end_time,start_time}; 47 | } 48 | 49 | static async addOne(data) { 50 | if (data.start_time == '') { 51 | data.start_time = Date.now(); 52 | data.end_time = 4100731932000; 53 | } 54 | await MysqlHelper.execute(` 55 | insert into announcement(c_id,topic,content,start_time,end_time) values(?,?,?,?,?) 56 | `, data.c_id, data.topic, data.content, data.start_time, data.end_time); 57 | } 58 | 59 | static async deleteOne(a_id) { 60 | await MysqlHelper.execute(`delete from announcement where a_id=?`, a_id); 61 | } 62 | 63 | } 64 | 65 | module.exports = announcementDao; -------------------------------------------------------------------------------- /teachSystem/dao/badgeDao.js: -------------------------------------------------------------------------------- 1 | const MysqlHelper = require('../util/MysqlHelper.js'); 2 | const RedisHelper = require('../util/RedisHelper.js'); 3 | const badgeHelper = require('./badgeHelper.js'); 4 | 5 | class badgeDao { 6 | static async getUserBadge(u_id) { 7 | let redisKey = u_id + '_badge_update'; 8 | let res = await RedisHelper.getString(redisKey); 9 | if (res == null) { 10 | await badgeHelper.checkCourseNum(u_id); 11 | await badgeHelper.checkEvaluate(u_id); 12 | res = await MysqlHelper.row(` 13 | SELECT a.get_time,b.* FROM student_badge a INNER JOIN badge b ON a.b_id = b.b_id WHERE u_id = ? 14 | `, u_id); 15 | res = JSON.stringify(res); 16 | await RedisHelper.setString(redisKey,res,60*60); 17 | } 18 | return JSON.parse(res); 19 | } 20 | 21 | static async getBadgeDetail(u_id, b_id, type) { 22 | let res = {}; 23 | res.next = await MysqlHelper.first(`select b_name,b_img,get_condition,next_level from badge where b_id=?`, b_id); 24 | if (type == '1') { 25 | res.current = await MysqlHelper.single(`select count(*) from student_evaluate_view where u_id=?`, u_id) 26 | } else if (type == '2') { 27 | let data = await MysqlHelper.row(`select * from student_class where u_id=?`, u_id); 28 | let evaluate = 0, 29 | count = 0; 30 | data.forEach(item => { 31 | let questionNum = item.difficulty_1 + item.difficulty_2 + item.difficulty_3 + item.difficulty_4 + item.difficulty_5; 32 | if (questionNum >= 20) { 33 | evaluate += item.evaluate; 34 | count++; 35 | } 36 | }) 37 | res.current = count > 0 ? evaluate / count : 0; 38 | } 39 | return res; 40 | } 41 | } 42 | module.exports = badgeDao; -------------------------------------------------------------------------------- /teachSystem/dao/badgeHelper.js: -------------------------------------------------------------------------------- 1 | const MysqlHelper = require('../util/MysqlHelper.js'); 2 | class badgeHelper { 3 | //检查课程注册门数徽章获取情况 4 | static async checkCourseNum(u_id) { 5 | let count = await MysqlHelper.single(`select count(*) from student_evaluate_view where u_id=?`, u_id); 6 | let b_id = await MysqlHelper.single(`select b_id from student_badge where b_id>0 and b_id<5 and u_id=?`, u_id); 7 | if (count == 0 && b_id != null) { 8 | await MysqlHelper.delete(`delete from student_badge where b_id=? and u_id=?`,b_id, u_id); 9 | return; 10 | } 11 | let current; 12 | if (count < 4) 13 | current= 2; 14 | else if (count < 7) 15 | current= 3; 16 | else if (count >= 7) 17 | current= 4; 18 | if (b_id != null) { 19 | if (b_id != current) 20 | await MysqlHelper.update(`update student_badge set b_id=?,get_time=? where b_id=? and u_id=?`, current, Date.now(), b_id, u_id); 21 | } else { 22 | await MysqlHelper.insert(`insert into student_badge value(?,?,?)`, u_id, current, Date.now()); 23 | } 24 | } 25 | 26 | //平均能力值徽章<2:青铜 <4白银 <6黄金 <8铂金 <9砖石 <=10王者并且做题数大于20 27 | static async checkEvaluate(u_id) { 28 | let data = await MysqlHelper.row(`select * from student_class where u_id=?`, u_id); 29 | let id = await MysqlHelper.single(`select b_id from student_badge where b_id>4 and b_id<11 and u_id=?`, u_id); 30 | let evaluate = 0; 31 | let count = 0; 32 | data.forEach(item => { 33 | let questionNum = item.difficulty_1 + item.difficulty_2 + item.difficulty_3 + item.difficulty_4 + item.difficulty_5; 34 | if (questionNum >= 20) { 35 | evaluate += item.evaluate; 36 | count++; 37 | } 38 | }) 39 | evaluate = evaluate / count; 40 | let currentId = 5; 41 | if (evaluate < 2) 42 | currentId = 5; 43 | else if (evaluate < 4) 44 | currentId = 6; 45 | else if (evaluate < 6) 46 | currentId = 7; 47 | else if (evaluate < 8) 48 | currentId = 8; 49 | else if (evaluate < 9) 50 | currentId = 9; 51 | else if (evaluate <= 10) 52 | currentId = 10; 53 | if (id != null) { 54 | if (id != currentId) 55 | await MysqlHelper.execute(`update student_badge set b_id=?,get_time=? where b_id=? and u_id=?`, currentId, Date.now(), id, u_id); 56 | } else { 57 | await MysqlHelper.insert(`insert into student_badge value(?,?,?)`, u_id, currentId, Date.now()); 58 | } 59 | } 60 | 61 | 62 | } 63 | 64 | module.exports = badgeHelper; -------------------------------------------------------------------------------- /teachSystem/dao/coursewareDao.js: -------------------------------------------------------------------------------- 1 | const mysqlHelper = require('../util/MysqlHelper.js'); 2 | const stringHelper = require('../util/StringHelper.js'); 3 | const config = require('../config/config.js'); 4 | const fs = require('fs-extra'); 5 | const path = require('path'); 6 | 7 | class coursewareDao { 8 | static async getAllofClass(c_id) { 9 | let res = await mysqlHelper.row(`select * from courseware where c_id=?`, c_id); 10 | res.forEach(item=>{ 11 | item.downloadUrl = `${config.host}/course/${c_id}/courseware/${item.cw_name}`; 12 | }) 13 | return res; 14 | } 15 | 16 | static async add(c_id, files) { 17 | let dir = path.join(config.rootPath, 'files/course', c_id, 'courseware'); 18 | await fs.ensureDir(dir); 19 | let res = []; 20 | for (let key in files) { 21 | let file = files[key]; 22 | let filePath = path.join(dir, file.name); 23 | await fs.remove(filePath); 24 | await fs.rename(file.path, filePath); 25 | let reply = await mysqlHelper.execute(`insert into courseware(c_id,cw_name,create_time) value(?,?,?)`, c_id, file.name, Date.now()); 26 | res.push({ 27 | id: reply.insertId, 28 | create_time: Date.now(), 29 | cw_name:file.name 30 | }); 31 | } 32 | return res; 33 | } 34 | 35 | static async deleteOne(c_id, cw_id) { 36 | let name = await mysqlHelper.single(`select cw_name from courseware where cw_id=?`,cw_id); 37 | await fs.remove(path.join(config.rootPath,'files/course',c_id,'courseware',name)); 38 | await mysqlHelper.execute(`delete from courseware where c_id=? and cw_id=?`, c_id, cw_id); 39 | } 40 | 41 | } 42 | 43 | module.exports = coursewareDao; -------------------------------------------------------------------------------- /teachSystem/dao/gradeDao.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const mysqlHelper = require('../util/MysqlHelper.js'); 3 | const stringHelper = require('../util/StringHelper.js'); 4 | const ErrorHelper = require('../util/ErrorHelper.js'); 5 | 6 | 7 | class gradeDao { 8 | 9 | static async getByCourseId(courseId) { 10 | let res = await mysqlHelper.row(`select g_id,content from grade where c_id=? order by content`, courseId); 11 | res.forEach(item => { 12 | item.name = item.content; 13 | delete item.content; 14 | }); 15 | return res; 16 | } 17 | 18 | 19 | static async addOne(data) { 20 | let res = await mysqlHelper.execute(`insert into grade(c_id,content) value(?,?)`, data.c_id, data.content); 21 | return res.insertId; 22 | } 23 | 24 | static async deleteOneGrade(g_id) { 25 | let count = await mysqlHelper.single(`select count(*) from class where g_id=?`, g_id); 26 | if (count == 0) { 27 | await mysqlHelper.execute(`delete from grade where g_id=?`, g_id); 28 | } else { 29 | ErrorHelper.newError(`仅当该年级下无班级时可删除`, 403); 30 | } 31 | } 32 | } 33 | 34 | module.exports = gradeDao; -------------------------------------------------------------------------------- /teachSystem/dao/knowledgePointDao.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const MysqlHelper = require('../util/MysqlHelper.js'); 3 | const ErrorHelper = require('../util/ErrorHelper.js'); 4 | 5 | class knowledgePointDao { 6 | static async getPontsOrderBySection(c_id) { 7 | let sections = await MysqlHelper.row(`select s_id,s_name from section where c_id=?`, c_id); 8 | for (let i = 0; i < sections.length; i++) { 9 | sections[i].children = await MysqlHelper.row(`select kp_id,content from knowledge_point where s_id=?`, sections[i].s_id); 10 | } 11 | return sections; 12 | } 13 | //某学生知识点掌握情况 14 | static async getStudentCondition(u_id, c_id) { 15 | let res = await MysqlHelper.row(`select kp_id,count,right_count from student_knowledge_view where u_id=? and c_id=?`, u_id, c_id); 16 | return res; 17 | } 18 | 19 | 20 | 21 | static async addOne(data) { 22 | let res = await MysqlHelper.execute(`insert into knowledge_point(s_id,content) value(?,?)`, data.s_id, data.content); 23 | return res.insertId; 24 | } 25 | 26 | static async deleteOne(kp_id) { 27 | await MysqlHelper.delete(`delete from knowledge_point where kp_id=?`, kp_id); 28 | } 29 | } 30 | 31 | module.exports = knowledgePointDao; -------------------------------------------------------------------------------- /teachSystem/dao/sectionDao.js: -------------------------------------------------------------------------------- 1 | 'use strcit' 2 | const MysqlHelper = require('../util/MysqlHelper.js'); 3 | const ErrorHelper = require('../util/ErrorHelper.js'); 4 | 5 | class sectionDao{ 6 | static async getByCourseId(c_id){ 7 | let res = await MysqlHelper.row(`select s_id,s_name from section where c_id=?`,c_id); 8 | return res; 9 | } 10 | 11 | static async addOne(data){ 12 | let res = await MysqlHelper.insert(`insert into section(c_id,s_name) value(?,?)`,data.c_id,data.s_name); 13 | return res; 14 | } 15 | 16 | static async deleteOne(s_id){ 17 | await MysqlHelper.delete(`delete from section where s_id=?`,s_id); 18 | } 19 | } 20 | 21 | module.exports = sectionDao; -------------------------------------------------------------------------------- /teachSystem/dao/studentClassDao.js: -------------------------------------------------------------------------------- 1 | let mysqlHelper = require('../util/MysqlHelper.js'); 2 | 3 | class StudentClass { 4 | static async add(u_id,class_id) { 5 | await mysqlHelper.execute(` 6 | insert into student_class values(?,?) 7 | `, class_id, u_id); 8 | } 9 | } 10 | 11 | module.exports=StudentClass; -------------------------------------------------------------------------------- /teachSystem/files/badgeIcon/badge_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/badgeIcon/badge_10.png -------------------------------------------------------------------------------- /teachSystem/files/badgeIcon/badge_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/badgeIcon/badge_2.png -------------------------------------------------------------------------------- /teachSystem/files/badgeIcon/badge_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/badgeIcon/badge_5.png -------------------------------------------------------------------------------- /teachSystem/files/badgeIcon/badge_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/badgeIcon/badge_6.png -------------------------------------------------------------------------------- /teachSystem/files/badgeIcon/badge_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/badgeIcon/badge_7.png -------------------------------------------------------------------------------- /teachSystem/files/badgeIcon/badge_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/badgeIcon/badge_8.png -------------------------------------------------------------------------------- /teachSystem/files/badgeIcon/badge_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/badgeIcon/badge_9.png -------------------------------------------------------------------------------- /teachSystem/files/code/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/code/.gitkeep -------------------------------------------------------------------------------- /teachSystem/files/course/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/course/.gitkeep -------------------------------------------------------------------------------- /teachSystem/files/imgs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/imgs/.gitkeep -------------------------------------------------------------------------------- /teachSystem/files/temp/uploads/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/temp/uploads/.gitkeep -------------------------------------------------------------------------------- /teachSystem/files/tests/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/tests/.gitkeep -------------------------------------------------------------------------------- /teachSystem/files/userIcon/default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/userIcon/default.jpg -------------------------------------------------------------------------------- /teachSystem/index.js: -------------------------------------------------------------------------------- 1 | const Koa = require('koa'); 2 | const Router = require('koa-router'); 3 | const RouterMW = require('./middleware/controllerEngine.js'); 4 | const koaBody = require('koa-body'); 5 | const cors = require('koa2-cors'); 6 | const path = require('path'); 7 | const fs = require('fs'); 8 | const static = require('koa-static'); 9 | 10 | const config = require('./config/config.js'); 11 | const handleError = require('./middleware/handleError.js'); 12 | const auth = require('./middleware/auth.js'); 13 | const init = require('./middleware/init.js'); 14 | console.log(config); 15 | 16 | const app = new Koa(); 17 | // 开发阶段使用koa-static返回静态文件,部署后使用nginx代理 18 | app.use(static(config.staticPath)); 19 | 20 | 21 | let router = new Router({ 22 | prefix: config.url_prefix 23 | }); 24 | 25 | app.use(cors()); 26 | 27 | app.use(koaBody(config.bodyLimit)); 28 | 29 | app.use(init); 30 | 31 | app.use(handleError); 32 | 33 | router.use(auth); 34 | app.use(RouterMW(router, path.join(config.rootPath, 'api'))); 35 | 36 | app.listen(config.port); 37 | console.log('server listened ', config.port); -------------------------------------------------------------------------------- /teachSystem/log/log.js: -------------------------------------------------------------------------------- 1 | /* 2 | 访问日志 某ip访问某请求 3 | 操作日志 某用户执行某操作 4 | 响应时间日志 某接口的执行时间(认证时间,操作时间) 5 | */ 6 | const log4js = require('log4js'); 7 | const config = require('../config/config.js'); 8 | 9 | log4js.configure(config.log); 10 | const visitLog = log4js.getLogger('visit'); 11 | const operateLog = log4js.getLogger('operate'); 12 | const responseTimeLog = log4js.getLogger('responseTime'); 13 | 14 | let visit = (ip, uri, method) => { 15 | visitLog.info(ip + ' ' + uri + ' ' + method); 16 | } 17 | 18 | let operate = (uid, uri, method, data) => { 19 | operateLog.info(uid + ' ' + uri + ' ' + method + ' ' + data); 20 | } 21 | 22 | //单位ms 23 | let responseTime = (uri, method, identifyTime, operateTime) => { 24 | responseTimeLog.info(uri + ' ' + method + ' ' + identifyTime + ' ' + operateTime); 25 | } 26 | 27 | module.exports = { 28 | visit, 29 | operate, 30 | responseTime 31 | } -------------------------------------------------------------------------------- /teachSystem/log/operate/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/log/operate/.gitkeep -------------------------------------------------------------------------------- /teachSystem/log/responseTime/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/log/responseTime/.gitkeep -------------------------------------------------------------------------------- /teachSystem/log/visit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/log/visit/.gitkeep -------------------------------------------------------------------------------- /teachSystem/middleware/controllerEngine.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const koaBody = require('koa-body'); 4 | const config = require('../config/config.js'); 5 | 6 | function addMapping(router, filePath) { 7 | let mapping = require(filePath); 8 | for (let url in mapping) { 9 | if (url.startsWith('GET ')) { 10 | let temp = url.substring(4); 11 | router.get(temp, mapping[url]); 12 | console.log(`----GET:${temp}`); 13 | } else if (url.startsWith('POST ')) { 14 | let temp = url.substring(5); 15 | router.post(temp, mapping[url]); 16 | console.log(`----POST:${temp}`); 17 | } else if (url.startsWith('PUT ')) { 18 | let temp = url.substring(4); 19 | router.put(temp, mapping[url]); 20 | console.log(`----PUT:${temp}`) 21 | } else if (url.startsWith('DELETE ')) { 22 | let temp = url.substring(7); 23 | router.delete(temp, mapping[url]); 24 | console.log(`----DELETE: ${temp}`); 25 | } else { 26 | console.log(`xxxxx无效路径:${url}`); 27 | } 28 | } 29 | } 30 | 31 | function addControllers(router, filePath) { 32 | let files = fs.readdirSync(filePath); 33 | files.forEach(element => { 34 | let temp = path.join(filePath, element); 35 | let state = fs.statSync(temp); 36 | if (state.isDirectory()) { 37 | addControllers(router, temp); 38 | } else { 39 | if (!temp.endsWith('Helper.js')) { 40 | console.log('\n--开始处理: ' + element + "路由"); 41 | addMapping(router, temp); 42 | } 43 | } 44 | }); 45 | } 46 | 47 | function engine(router, folder) { 48 | addControllers(router, folder); 49 | return router.routes(); 50 | } 51 | 52 | module.exports = engine; -------------------------------------------------------------------------------- /teachSystem/middleware/handleError.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | let f = async (ctx, next) => { 3 | try { 4 | await next(); 5 | } catch (error) { 6 | if (error.status != undefined) { 7 | ctx.status = error.status; 8 | } else { 9 | ctx.status = 500; 10 | } 11 | ctx.body = error.message; 12 | console.error(error); 13 | } 14 | } 15 | 16 | module.exports = f; -------------------------------------------------------------------------------- /teachSystem/middleware/init.js: -------------------------------------------------------------------------------- 1 | const config = require("../config/config.js"); 2 | const ObjectHelper = require("../util/ObjectOperate"); 3 | const Log = require("../log/log.js"); 4 | 5 | let doSuccess = (ctx, body) => { 6 | switch (ctx.method) { 7 | case "GET": 8 | ctx.status = body !== null ? 200 : 204; 9 | ctx.body = body; 10 | break; 11 | case "POST": 12 | ctx.status = body !== null ? 201 : 204; 13 | ctx.body = body; 14 | break; 15 | case "PUT": 16 | ctx.status = body !== null ? 200 : 204; 17 | ctx.body = body; 18 | break; 19 | case "DELETE": 20 | ctx.status = body !== null ? 200 : 204; 21 | ctx.body = body; 22 | break; 23 | } 24 | Object.assign(ctx.allParams, ctx.params); 25 | //记录用户操作 26 | if (ctx.userInfo != undefined) { 27 | Log.operate(ctx.userInfo.u_id, ctx.path, ctx.method, JSON.stringify(ctx.allParams)); 28 | } 29 | }; 30 | 31 | let doFail = (ctx, body) => { 32 | if (body != null && body.message == "403") { 33 | ctx.status = 403; 34 | ctx.body = "无操作权限"; 35 | } else { 36 | ctx.status = 400; 37 | ctx.body = body.message; 38 | } 39 | }; 40 | 41 | module.exports = async (ctx, next) => { 42 | //合并请求参数到allParams 43 | let objs = []; 44 | if (ctx.method == "POST" || ctx.method == "PUT") { 45 | if (ctx.request.body) { 46 | if (ctx.request.body.fields != undefined && ctx.request.body.files != undefined) { 47 | objs.push(ctx.request.body.fields, ctx.request.body.files); 48 | } else { 49 | objs.push(ctx.request.body); 50 | } 51 | } 52 | } 53 | objs.push(ctx.query); 54 | ctx.allParams = ObjectHelper.combineObject(objs); 55 | 56 | ctx.onSuccess = function(body = null) { 57 | doSuccess(ctx, body); 58 | }; 59 | ctx.onFail = function(body = null) { 60 | doFail(ctx, body); 61 | }; 62 | let ip = config.env == "development" ? ctx.ip : ctx.request.header["x-real-ip"]; 63 | Log.visit(ip, ctx.URL.pathname, ctx.method); 64 | await next(); 65 | }; 66 | -------------------------------------------------------------------------------- /teachSystem/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "restdemo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha", 8 | "start": "node index.js", 9 | "updateSource": "node dbAuth.js" 10 | }, 11 | "author": "fxb", 12 | "license": "ISC", 13 | "dependencies": { 14 | "async": "^2.6.0", 15 | "fs-extra": "^5.0.0", 16 | "iconv-lite": "^0.4.23", 17 | "js-xlsx": "^0.8.22", 18 | "jsonwebtoken": "^8.2.0", 19 | "nodemailer": "^4.6.2", 20 | "trek-captcha": "^0.4.0", 21 | "koa": "^2.4.1", 22 | "koa-body": "^2.5.0", 23 | "koa-router": "^7.3.0", 24 | "koa-static": "^4.0.2", 25 | "koa2-cors": "^2.0.5", 26 | "log4js": "^2.3.12", 27 | "mysql": "^2.15.0", 28 | "redis": "^2.8.0", 29 | "request": "^2.85.0", 30 | "uuid": "^3.1.0", 31 | "xlsx": "^0.12.6" 32 | }, 33 | "devDependencies": { 34 | "mocha": "^4.0.1" 35 | } 36 | } -------------------------------------------------------------------------------- /teachSystem/test/courseDao.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const courseDao = require('../dao/courseDao.js'); 3 | 4 | describe('#addOne(data)', () => { 5 | it('should be done', async () => { 6 | let res = await courseDao.addOne({ 7 | code: '1234', 8 | c_name: "c++程序设计", 9 | teacher: [{ 10 | u_id: 14, 11 | u_name: 'asdf' 12 | }, { 13 | u_id: 15, 14 | u_name: 'asdf' 15 | }] 16 | }); 17 | await courseDao.deleteOne(res.c_id); 18 | }) 19 | }) 20 | 21 | describe('#updateOne(data,c_id)', () => { 22 | it('should be done', async () => { 23 | await courseDao.updateOne({ 24 | code: '12345', 25 | c_name: "c++", 26 | teacher: [{ 27 | u_id: 14, 28 | u_name: 'asdf' 29 | }] 30 | }, res.c_id); 31 | }) 32 | }) 33 | 34 | describe('#deleteOne(c_id)',()=>{ 35 | it('should be done',async()=>{ 36 | await courseDao.deleteOne(); 37 | }) 38 | }) -------------------------------------------------------------------------------- /teachSystem/util/ErrorHelper.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | class ErrorHelper { 4 | /** 5 | * 返回一个自定义错误 6 | * @param {String} message 7 | * @param {Number} status 8 | */ 9 | static newError(message, status) { 10 | return getError(message, status); 11 | } 12 | 13 | static Error403(message){ 14 | return getError(message,403); 15 | } 16 | static Error406(message){ 17 | return getError(message,406); 18 | } 19 | static Error400(message){ 20 | return getError(message,400); 21 | } 22 | 23 | } 24 | 25 | let getError = (message, status) => { 26 | let error = new Error(message); 27 | error.status = status; 28 | return error; 29 | } 30 | 31 | module.exports = ErrorHelper; -------------------------------------------------------------------------------- /teachSystem/util/MailHelper.js: -------------------------------------------------------------------------------- 1 | const nodeMailer = require("nodemailer"); 2 | const config = require("../config/config.js"); 3 | 4 | let transporter = nodeMailer.createTransport(config.mail); 5 | var mailOptions = { 6 | from: config.mail.auth.user // 发送者 7 | }; 8 | 9 | let resetPass = (code, mail) => { 10 | mailOptions.text = `您正在请求重置密码,验证码为(五分钟内有效):${code}`; 11 | mailOptions.html = ` 12 |

您正在请求重置密码,验证码为(五分钟内有效):

${code}

13 | `; 14 | mailOptions.subject = "验证码请求"; 15 | mailOptions.to = mail; 16 | return transporter.sendMail(mailOptions); 17 | }; 18 | 19 | module.exports = { 20 | resetPass 21 | }; 22 | -------------------------------------------------------------------------------- /teachSystem/util/ObjectOperate.js: -------------------------------------------------------------------------------- 1 | /* 2 | 合并node对象,对于相同的属性后面覆盖前面 3 | */ 4 | class ObjectOperation { 5 | static combineObject(...objs) { 6 | if (objs.length == 1 && objs[0] instanceof Array) { 7 | objs = objs[0]; 8 | } 9 | let sum = {}; 10 | let length = objs.length; 11 | for (let i = 0; i < length; i++) { 12 | sum = Object.assign(sum,objs[i]); 13 | } 14 | return sum; 15 | } 16 | } 17 | 18 | 19 | module.exports = ObjectOperation; -------------------------------------------------------------------------------- /teachSystem/util/RequestHelper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const request = require('request'); 3 | const fs = require('fs'); 4 | const iconv = require('iconv-lite'); 5 | 6 | class RequestHelper { 7 | static GET(url, headers, charset='utf-8') { 8 | var options = { 9 | method: 'GET', 10 | url: url, 11 | headers: headers || null 12 | }; 13 | return new Promise((resolve, reject) => { 14 | request(options) 15 | .on('error', (err) => { 16 | reject(err); 17 | }) 18 | .pipe(iconv.decodeStream(charset)).collect((err, body) => { 19 | try { 20 | resolve(JSON.parse(body)); 21 | } catch (error) { 22 | resolve(body); 23 | } 24 | }); 25 | }) 26 | } 27 | 28 | static POST(url, form, headers, charset = 'utf-8') { 29 | var options = { 30 | method: 'POST', 31 | url: url, 32 | // form: form || null, 33 | body:form, 34 | json:true, 35 | headers: headers || null 36 | }; 37 | return new Promise((resolve, reject) => { 38 | request(options) 39 | .on('error', (err) => { 40 | reject(err); 41 | }) 42 | .pipe(iconv.decodeStream(charset)).collect((err, body) => { 43 | try { 44 | resolve(JSON.parse(body)); 45 | } catch (error) { 46 | resolve(body); 47 | } 48 | }); 49 | }) 50 | } 51 | 52 | static async download(url, path) { 53 | if (fs.existsSync(path)) 54 | return; 55 | let options = { 56 | method: 'GET', 57 | url: url, 58 | headers: { 59 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36' 60 | } 61 | }; 62 | let stream = fs.createWriteStream(path); 63 | return new Promise((resolve, reject) => { 64 | request(options) 65 | .on('error', (err) => { 66 | console.log(err); 67 | }) 68 | .pipe(stream); 69 | stream.on('finish', () => { 70 | resolve(fs.statSync(path).size); 71 | }) 72 | }) 73 | } 74 | } 75 | 76 | module.exports = RequestHelper; -------------------------------------------------------------------------------- /teachSystem/util/TextVerify.js: -------------------------------------------------------------------------------- 1 | class TextVerify { 2 | //6-20位字母数字组合 3 | static isPassword(str = null) { 4 | if (str == null) { 5 | return false; 6 | } 7 | var reg = /^[\w]{6,20}$/; 8 | return reg.test(str); 9 | } 10 | 11 | //电子邮件地址 12 | static isEmail(str = null) { 13 | if (str == null) { 14 | return false; 15 | } 16 | var reg = /^\w+([-\.]\w+)*@\w+([\.-]\w+)*\.\w{2,4}$/; 17 | return reg.test(str); 18 | } 19 | 20 | //手机号 21 | static isMobile(str = null) { 22 | if (str == null) { 23 | return false; 24 | } 25 | var reg = /^1\d{10}$/; 26 | return reg.test(str); 27 | } 28 | } 29 | 30 | module.exports = TextVerify; 31 | -------------------------------------------------------------------------------- /teachSystem/util/TimeHelper.js: -------------------------------------------------------------------------------- 1 | class TimeHelper { 2 | static async sleep(duration) { 3 | return new Promise((resolve, reject) => { 4 | setTimeout(() => resolve(), duration); 5 | }); 6 | } 7 | } 8 | 9 | module.exports = TimeHelper; 10 | --------------------------------------------------------------------------------