From f1d7b4895cfebc45b05cb0e6702ad631a9f8aafa Mon Sep 17 00:00:00 2001 From: qubiaobiao <3294694717@qq.com> Date: Fri, 27 Mar 2026 17:50:54 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8C=97=E6=96=97=E5=AF=BC=E8=88=AA=E5=AE=9A?= =?UTF-8?q?=E4=BD=8D=E9=80=9A=E4=BF=A1=E7=BB=BC=E5=90=88=E5=AE=9E=E8=B7=B5?= =?UTF-8?q?=E8=B5=9B=E6=9C=8D=E5=8A=A1=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintignore | 1 + .eslintrc | 4 + .gitignore | 15 ++ .sequelizerc | 10 ++ README.md | 31 ++++ app/controller/Bind.js | 63 ++++++++ app/controller/Check.js | 64 ++++++++ app/controller/Contest.js | 67 +++++++++ app/controller/Exam.js | 133 +++++++++++++++++ app/controller/Topic.js | 74 ++++++++++ app/core/base_controller.js | 133 +++++++++++++++++ app/core/base_service.js | 112 ++++++++++++++ app/middleware/log.js | 72 +++++++++ app/model3/BDCard.js | 22 +++ app/model4/Account.js | 24 +++ app/model4/ChoiceTopic.js | 22 +++ app/model4/Contest.js | 29 ++++ app/model4/Examination.js | 34 +++++ app/model4/TShortMsg.js | 24 +++ app/model4/TuserBind.js | 18 +++ app/router.js | 31 ++++ app/service/Bind.js | 41 ++++++ app/service/Check.js | 37 +++++ app/service/Contest.js | 75 ++++++++++ app/service/Exam.js | 243 +++++++++++++++++++++++++++++++ app/service/Topic.js | 113 ++++++++++++++ config/config.default.js | 143 ++++++++++++++++++ config/plugin.js | 22 +++ controllerLog/2025/2/28.txt | 1 + controllerLog/2025/5/19.txt | 2 + controllerLog/2025/5/21.txt | 3 + database/config.json | 23 +++ jsconfig.json | 5 + package.json | 48 ++++++ serviceLog/2025/5/19.txt | 11 ++ serviceLog/2025/5/20.txt | 1 + serviceLog/2025/5/22.txt | 5 + serviceLog/2025/5/23.txt | 13 ++ serviceLog/2025/5/27.txt | 9 ++ serviceLog/2025/6/30.txt | 1 + serviceLog/2025/7/1.txt | 2 + serviceLog/2025/7/23.txt | 2 + serviceLog/2025/7/8.txt | 4 + test/app/controller/home.test.js | 23 +++ 44 files changed, 1810 insertions(+) create mode 100644 .eslintignore create mode 100644 .eslintrc create mode 100644 .gitignore create mode 100644 .sequelizerc create mode 100644 README.md create mode 100644 app/controller/Bind.js create mode 100644 app/controller/Check.js create mode 100644 app/controller/Contest.js create mode 100644 app/controller/Exam.js create mode 100644 app/controller/Topic.js create mode 100644 app/core/base_controller.js create mode 100644 app/core/base_service.js create mode 100644 app/middleware/log.js create mode 100644 app/model3/BDCard.js create mode 100644 app/model4/Account.js create mode 100644 app/model4/ChoiceTopic.js create mode 100644 app/model4/Contest.js create mode 100644 app/model4/Examination.js create mode 100644 app/model4/TShortMsg.js create mode 100644 app/model4/TuserBind.js create mode 100644 app/router.js create mode 100644 app/service/Bind.js create mode 100644 app/service/Check.js create mode 100644 app/service/Contest.js create mode 100644 app/service/Exam.js create mode 100644 app/service/Topic.js create mode 100644 config/config.default.js create mode 100644 config/plugin.js create mode 100644 controllerLog/2025/2/28.txt create mode 100644 controllerLog/2025/5/19.txt create mode 100644 controllerLog/2025/5/21.txt create mode 100644 database/config.json create mode 100644 jsconfig.json create mode 100644 package.json create mode 100644 serviceLog/2025/5/19.txt create mode 100644 serviceLog/2025/5/20.txt create mode 100644 serviceLog/2025/5/22.txt create mode 100644 serviceLog/2025/5/23.txt create mode 100644 serviceLog/2025/5/27.txt create mode 100644 serviceLog/2025/6/30.txt create mode 100644 serviceLog/2025/7/1.txt create mode 100644 serviceLog/2025/7/23.txt create mode 100644 serviceLog/2025/7/8.txt create mode 100644 test/app/controller/home.test.js diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..4ebc8ae --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +coverage diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..f304bdd --- /dev/null +++ b/.eslintrc @@ -0,0 +1,4 @@ +{ + "extends": "eslint-config-egg", + "root": true +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..652759b --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +logs/ +npm-debug.log +yarn-error.log +node_modules/ +*-lock.json +*-lock.yaml +yarn.lock +coverage/ +.idea/ +run/ +.DS_Store +*.sw* +*.un~ +typings/ +.nyc_output/ diff --git a/.sequelizerc b/.sequelizerc new file mode 100644 index 0000000..01d9847 --- /dev/null +++ b/.sequelizerc @@ -0,0 +1,10 @@ +'use strict'; + +const path = require('path'); + +module.exports = { + "config": path.join(__dirname, 'database/config.json'), + 'migrations-path': path.join(__dirname, 'database/migrations'), + 'seeders-path': path.join(__dirname, 'database/seeders'), + 'models-path': path.join(__dirname, 'app/model'), +}; diff --git a/README.md b/README.md new file mode 100644 index 0000000..6a02535 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# open-map-assistant + + + +## QuickStart + + + +see [egg docs][egg] for more detail. + +### Development + +```bash +npm i +npm run dev +open http://localhost:7001/ +``` + +### Deploy + +```bash +npm start +npm stop +``` + +### npm scripts + +- Use `npm run lint` to check code style. +- Use `npm test` to run unit test. + +[egg]: https://eggjs.org diff --git a/app/controller/Bind.js b/app/controller/Bind.js new file mode 100644 index 0000000..85ee4ee --- /dev/null +++ b/app/controller/Bind.js @@ -0,0 +1,63 @@ +const Controller = require('../core/base_controller'); + +const fileName = "Bind"; + +class BindController extends Controller { + // 绑定北斗卡 + async bindCard() { + const { ctx } = this; + try { + let rdmcode = await this.checkUid(ctx); + if (!rdmcode) { + this.setResponse(ctx, 401, 'token过期或错误', "none"); + return; + } + const uid = JSON.parse(rdmcode).uid; + const { card_id, card_imsi } = ctx.request.body; + const result = await ctx.service.bind.bindCard(uid, card_id, card_imsi); + if (result == "error") { + this.setResponse(ctx, 500, '服务器错误', "none"); + return; + } + if (result) { + this.setResponse(ctx, 200, '操作成功', "deconstruction", result); + } else { + this.setResponse(ctx, 404, '操作失败', "none"); + } + } catch (error) { + const blockName = fileName + "--" + "bindCard"; + ctx.app.middleware.log('../../controllerLog', blockName + ">>>>" + error); + this.setResponse(ctx, 500, '服务器错误', "none"); + } + } + + // 解绑北斗卡 + async unBindCard() { + const { ctx } = this; + try { + let rdmcode = await this.checkUid(ctx); + if (!rdmcode) { + this.setResponse(ctx, 401, 'token过期或错误', "none"); + return; + } + const uid = JSON.parse(rdmcode).uid; + const { card_id, card_imsi } = ctx.request.body; + const result = await ctx.service.bind.unBindCard(uid, card_id, card_imsi); + if (result == "error") { + this.setResponse(ctx, 500, '服务器错误', "none"); + return; + } + if (result) { + this.setResponse(ctx, 200, '操作成功', "deconstruction", result); + } else { + this.setResponse(ctx, 404, '操作失败', "none"); + } + } catch (error) { + const blockName = fileName + "--" + "unBindCard"; + ctx.app.middleware.log('../../controllerLog', blockName + ">>>>" + error); + this.setResponse(ctx, 500, '服务器错误', "none"); + } + } +} + +module.exports = BindController; diff --git a/app/controller/Check.js b/app/controller/Check.js new file mode 100644 index 0000000..4ed7d52 --- /dev/null +++ b/app/controller/Check.js @@ -0,0 +1,64 @@ +const Controller = require('../core/base_controller'); + +const fileName = "Check"; + +class CheckController extends Controller { + // 检查竞赛账号是否已经绑定北斗卡 + async checkUser() { + const { ctx } = this; + try { + let rdmcode = await this.checkUid(ctx); + if (!rdmcode) { + this.setResponse(ctx, 401, 'token过期或错误', "none"); + return; + } + const uid = JSON.parse(rdmcode).uid; + const result = await ctx.service.check.checkUser(uid); + if (result == "error") { + this.setResponse(ctx, 500, '服务器错误', "none"); + return; + } + if (result) { + const data = { + card_id: result.dataValues.card_id, + }; + this.setResponse(ctx, 200, '操作成功', "deconstruction", data); + } else { + this.setResponse(ctx, 404, '操作失败', "none"); + } + } catch (error) { + const blockName = fileName + "--" + "checkUser"; + ctx.app.middleware.log('../../controllerLog', blockName + ">>>>" + error); + this.setResponse(ctx, 500, '服务器错误', "none"); + } + } + + // 检查北斗卡是否已经被绑定 + async checkBind() { + const { ctx } = this; + try { + let rdmcode = await this.checkUid(ctx); + if (!rdmcode) { + this.setResponse(ctx, 401, 'token过期或错误', "none"); + return; + } + const { card_id } = ctx.request.body; + const result = await ctx.service.check.checkBind(card_id); + if (result == "error") { + this.setResponse(ctx, 500, '服务器错误', "none"); + return; + } + if (result) { + this.setResponse(ctx, 200, '操作成功', "none"); + } else { + this.setResponse(ctx, 404, '操作失败', "none"); + } + } catch (error) { + const blockName = fileName + "--" + "checkBind"; + ctx.app.middleware.log('../../controllerLog', blockName + ">>>>" + error); + this.setResponse(ctx, 500, '服务器错误', "none"); + } + } +} + +module.exports = CheckController; diff --git a/app/controller/Contest.js b/app/controller/Contest.js new file mode 100644 index 0000000..de7e938 --- /dev/null +++ b/app/controller/Contest.js @@ -0,0 +1,67 @@ +const Controller = require('../core/base_controller'); + +const fileName = "Contest"; + +class ContestController extends Controller { + // 系统获取竞赛基本信息 + async contestMsg() { + const { ctx } = this; + try { + let rdmcode = await this.checkUid(ctx); + if (!rdmcode) { + this.setResponse(ctx, 401, 'token过期或错误', "none"); + return; + } + const uid = JSON.parse(rdmcode).uid; + const result = await ctx.service.contest.contestMsg(uid); + if (result == "error") { + this.setResponse(ctx, 500, '服务器错误', "none"); + return; + } + if (result) { + const data = { + contestMsg: result.dataValues + } + this.setResponse(ctx, 200, '操作成功', "deconstruction", data); + } else { + this.setResponse(ctx, 404, '操作失败', "none"); + } + } catch (error) { + const blockName = fileName + "--" + "contestMsg"; + ctx.app.middleware.log('../../controllerLog', blockName + ">>>>" + error); + this.setResponse(ctx, 500, '服务器错误', "none"); + } + } + + // 系统获取排名信息 + async ranking() { + const { ctx } = this; + try { + let rdmcode = await this.checkUid(ctx); + if (!rdmcode) { + this.setResponse(ctx, 401, 'token过期或错误', "none"); + return; + } + const uid = JSON.parse(rdmcode).uid; + const result = await ctx.service.contest.ranking(uid); + if (result == "error") { + this.setResponse(ctx, 500, '服务器错误', "none"); + return; + } + if (result) { + const data = { + rankList: result + } + this.setResponse(ctx, 200, '操作成功', "deconstruction", data); + } else { + this.setResponse(ctx, 404, '操作失败', "none"); + } + } catch (error) { + const blockName = fileName + "--" + "ranking"; + ctx.app.middleware.log('../../controllerLog', blockName + ">>>>" + error); + this.setResponse(ctx, 500, '服务器错误', "none"); + } + } +} + +module.exports = ContestController; diff --git a/app/controller/Exam.js b/app/controller/Exam.js new file mode 100644 index 0000000..718d880 --- /dev/null +++ b/app/controller/Exam.js @@ -0,0 +1,133 @@ +const Controller = require('../core/base_controller'); + +const fileName = "Exam"; + +class ExamController extends Controller { + // 学生获取历史消息 + async getHistoryMsg() { + const { ctx } = this; + try { + let rdmcode = await this.checkUid(ctx); + if (!rdmcode) { + this.setResponse(ctx, 401, 'token过期或错误', "none"); + return; + } + const uid = JSON.parse(rdmcode).uid; + const body = ctx.request.body; + const result = await ctx.service.exam.getHistoryMsg(uid, body); + if (result == "error") { + this.setResponse(ctx, 500, '服务器错误', "none"); + return; + } + if (result) { + let historyMsg = []; + result.rows.forEach(element => { + historyMsg.push(element.dataValues); + }); + const data = { + historyMsg: historyMsg, + count: result.count + }; + this.setResponse(ctx, 200, '操作成功', "deconstruction", data); + } else { + this.setResponse(ctx, 404, '操作失败', "none"); + } + } catch (error) { + const blockName = fileName + "--" + "getHistoryMsg"; + ctx.app.middleware.log('../../controllerLog', blockName + ">>>>" + error); + this.setResponse(ctx, 500, '服务器错误', "none"); + } + } + + // 学生删除历史消息 + async delHistoryMsg() { + const ctx = this.ctx; + try { + let rdmcode = await checkUid(ctx); + if (!rdmcode) { + setResponse(ctx, 401, 'token过期或错误', "none"); + return; + } + const uid = JSON.parse(rdmcode).uid; + const body = ctx.request.body; + const model = decryptRequest(body.model); + const password = decryptRequest(body.password); + const requestMsg = body.userMsg; + const resetList = await ctx.service.exam.delHistoryMsg(uid, model, password, requestMsg); + if (resetList == "error") { + setResponse(ctx, 500, '服务器错误', "none"); + return; + } + if (resetList) { + setResponse(ctx, 200, '删除历史消息成功', "none"); + } else { + setResponse(ctx, 404, '删除历史消息失败', "none"); + } + } catch (error) { + const blockName = fileName + "--" + "delHistoryMsg"; + ctx.app.middleware.utils.log('../../../controllerLog', blockName + ">>>>" + error); + setResponse(ctx, 500, '服务器错误', "none"); + } + } + + // 学生完成答题 + async answer() { + const { ctx } = this; + try { + let rdmcode = await this.checkUid(ctx); + if (!rdmcode) { + this.setResponse(ctx, 401, 'token过期或错误', "none"); + return; + } + const uid = JSON.parse(rdmcode).uid; + const body = ctx.request.body; + const result = await ctx.service.exam.answer(uid, body); + // await ctx.service.bind.unBindCard(uid); + if (result == "error") { + this.setResponse(ctx, 500, '服务器错误', "none"); + return; + } + if (result) { + this.setResponse(ctx, 200, '操作成功', "none"); + } else { + this.setResponse(ctx, 404, '操作失败', "none"); + } + } catch (error) { + const blockName = fileName + "--" + "answer"; + ctx.app.middleware.log('../../controllerLog', blockName + ">>>>" + error); + this.setResponse(ctx, 500, '服务器错误', "none"); + } + } + + // 学生查看成绩信息 + async grades() { + const { ctx } = this; + try { + let rdmcode = await this.checkUid(ctx); + if (!rdmcode) { + this.setResponse(ctx, 401, 'token过期或错误', "none"); + return; + } + const uid = JSON.parse(rdmcode).uid; + const result = await ctx.service.exam.grades(uid); + if (result == "error") { + this.setResponse(ctx, 500, '服务器错误', "none"); + return; + } + if (result) { + const data = { + grades: result.dataValues + }; + this.setResponse(ctx, 200, '操作成功', "deconstruction", data); + } else { + this.setResponse(ctx, 404, '操作失败', "none"); + } + } catch (error) { + const blockName = fileName + "--" + "grades"; + ctx.app.middleware.log('../../controllerLog', blockName + ">>>>" + error); + this.setResponse(ctx, 500, '服务器错误', "none"); + } + } +} + +module.exports = ExamController; diff --git a/app/controller/Topic.js b/app/controller/Topic.js new file mode 100644 index 0000000..0f4f7b2 --- /dev/null +++ b/app/controller/Topic.js @@ -0,0 +1,74 @@ +const Controller = require('../core/base_controller'); + +const fileName = "Topic"; + +class TopicController extends Controller { + // 学生抽取/读取题目 + async topic() { + const { ctx } = this; + try { + let rdmcode = await this.checkUid(ctx); + if (!rdmcode) { + this.setResponse(ctx, 401, 'token过期或错误', "none"); + return; + } + const uid = JSON.parse(rdmcode).uid; + const bind = await ctx.service.check.checkUser(uid); + if (!bind) { + this.setResponse(ctx, 333, '未绑定北斗卡', "none"); + return; + } + const { answerId } = ctx.request.body; + const result = await ctx.service.topic.topic(uid, answerId); + if (bind == "error" || result == "error") { + this.setResponse(ctx, 500, '服务器错误', "none"); + return; + } + if (result.code == 200) { + const data = { + examination: result.examination, + shortMsg: result.shortMsg, + }; + this.setResponse(ctx, 200, '操作成功', "deconstruction", data); + } else { + this.setResponse(ctx, 404, '操作失败', "none"); + } + } catch (error) { + const blockName = fileName + "--" + "topic"; + ctx.app.middleware.log('../../controllerLog', blockName + ">>>>" + error); + this.setResponse(ctx, 500, '服务器错误', "none"); + } + } + + // 系统读取单选题 + async radio() { + const { ctx } = this; + try { + let rdmcode = await this.checkUid(ctx); + if (!rdmcode) { + this.setResponse(ctx, 401, 'token过期或错误', "none"); + return; + } + const { radioArr } = ctx.request.body; + const result = await ctx.service.topic.radio(radioArr); + if (result == "error") { + this.setResponse(ctx, 500, '服务器错误', "none"); + return; + } + if (result) { + const data = { + radioTopic: result, + } + this.setResponse(ctx, 200, '操作成功', "deconstruction", data); + } else { + this.setResponse(ctx, 404, '操作失败', "none"); + } + } catch (error) { + const blockName = fileName + "--" + "radio"; + ctx.app.middleware.log('../../controllerLog', blockName + ">>>>" + error); + this.setResponse(ctx, 500, '服务器错误', "none"); + } + } +} + +module.exports = TopicController; diff --git a/app/core/base_controller.js b/app/core/base_controller.js new file mode 100644 index 0000000..4b5e3cb --- /dev/null +++ b/app/core/base_controller.js @@ -0,0 +1,133 @@ +const { Controller } = require('egg'); + +const sha256 = require("crypto-js/sha256"); + +const CryptoJS = require("crypto-js"); + +const KEY = CryptoJS.enc.Utf8.parse('BDQH'); + +class BaseController extends Controller { + shaForm = (body, salt) => { + return sha256(sha256(body).toString() + salt).toString(); + } + + encryptRequest = (body) => { + return CryptoJS.AES.encrypt(body, KEY, { + mode: CryptoJS.mode.ECB, + padding: CryptoJS.pad.Pkcs7 + }).toString(); + } + + decryptRequest = (body) => { + return CryptoJS.AES.decrypt(body, KEY, { + mode: CryptoJS.mode.ECB, + padding: CryptoJS.pad.Pkcs7 + }).toString(CryptoJS.enc.Utf8); + } + + // 用户uid生成函数 + generateNumberString = (side) => { + // 获取当前日期(年月日) + const now = new Date(); + const year = String(now.getFullYear()).padStart(4, '0'); + const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份从0开始,所以加1 + const date = String(now.getDate()).padStart(2, '0'); + const dateString = year + month + date; + + // 获取当前时间戳的后三位 + const timestamp = String(Date.now()).slice(-3); + + // 生成三位随机数字 + const randomDigits = Math.floor(Math.random() * 100).toString().padStart(3, '0'); + + // 组合成最终的15位字符串 + const fullNumberString = dateString + timestamp + randomDigits + side; + + return fullNumberString; + } + + // 随机盐生成函数 + generateRandomString = () => { + const lowerCaseLetters = 'abcdefghijklmnopqrstuvwxyz'; + const upperCaseLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + const numbers = '0123456789'; + + let result = ''; + + // 生成5位随机小写字母 + for (let i = 0; i < 5; i++) { + const randomIndex = Math.floor(Math.random() * lowerCaseLetters.length); + result += lowerCaseLetters[randomIndex]; + } + + // 生成5位随机大写字母 + for (let i = 0; i < 5; i++) { + const randomIndex = Math.floor(Math.random() * upperCaseLetters.length); + result += upperCaseLetters[randomIndex]; + } + + // 生成2位随机数字 + for (let i = 0; i < 2; i++) { + const randomIndex = Math.floor(Math.random() * numbers.length); + result += numbers[randomIndex]; + } + + // 打乱整个字符串 + const characters = result.split(''); + for (let i = characters.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [characters[i], characters[j]] = [characters[j], characters[i]]; + } + + return characters.join(''); + } + + // 随机生成12位客户端id + generateClientId = () => { + const numbers = '0123456789'; + + let result = ''; + + // 生成4位随机数字 + for (let i = 0; i < 4; i++) { + const randomIndex = Math.floor(Math.random() * numbers.length); + result += numbers[randomIndex]; + } + + // 打乱整个字符串 + const characters = result.split(''); + for (let i = characters.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [characters[i], characters[j]] = [characters[j], characters[i]]; + } + + // 获取当前日期(年月日) + const now = new Date(); + const year = String(now.getFullYear()).padStart(4, '0'); + const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份从0开始,所以加1 + const date = String(now.getDate()).padStart(2, '0'); + const dateString = year + month + date; + + const fullNumberString = dateString + characters.join(''); + + return fullNumberString; + } + + checkUid = async (ctx) => { + const token = ctx.request.header.authorization; + const redis = ctx.app.redis; + const contest = await redis.get('db5').get(token); + return contest ? contest : false; + }; + + setResponse = (ctx, code, msg, model, data = {}) => { + const modelType = { + "none": { code, msg }, + "deconstruction": { code, msg, ...data }, + "polymerization": { code, msg, data } + } + ctx.body = modelType[model]; + } +} + +module.exports = BaseController; \ No newline at end of file diff --git a/app/core/base_service.js b/app/core/base_service.js new file mode 100644 index 0000000..d7ea2e1 --- /dev/null +++ b/app/core/base_service.js @@ -0,0 +1,112 @@ +const { Service } = require('egg'); + +const { Op } = require('sequelize'); + +const sha256 = require("crypto-js/sha256"); + +const CryptoJS = require("crypto-js"); + +const KEY = CryptoJS.enc.Utf8.parse('BDQH'); + +class BaseService extends Service { + shaForm = (body, salt) => { + return sha256(sha256(body).toString() + salt).toString(); + } + + encryptRequest = (body) => { + return CryptoJS.AES.encrypt(body, KEY, { + mode: CryptoJS.mode.ECB, + padding: CryptoJS.pad.Pkcs7 + }).toString(); + } + + decryptRequest = (body) => { + return CryptoJS.AES.decrypt(body, KEY, { + mode: CryptoJS.mode.ECB, + padding: CryptoJS.pad.Pkcs7 + }).toString(CryptoJS.enc.Utf8); + } + + // uid生成函数 + generateNumberString = () => { + // 获取当前日期(年月日) + const now = new Date(); + const year = String(now.getFullYear()).padStart(4, '0'); + const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份从0开始,所以加1 + const date = String(now.getDate()).padStart(2, '0'); + const dateString = year + month + date; + + // 获取当前时间戳的后四位 + const timestamp = String(Date.now()).slice(-4); + + // 生成四位随机数字 + const randomDigits = Math.floor(Math.random() * 10000).toString().padStart(4, '0'); + + // 组合成最终的16位数字字符串 + const fullNumberString = dateString + timestamp + randomDigits; + + return fullNumberString; + } + + // 随机盐生成函数 + generateRandomString = () => { + const lowerCaseLetters = 'abcdefghijklmnopqrstuvwxyz'; + const upperCaseLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + const numbers = '0123456789'; + + let result = ''; + + // 生成5位随机小写字母 + for (let i = 0; i < 5; i++) { + const randomIndex = Math.floor(Math.random() * lowerCaseLetters.length); + result += lowerCaseLetters[randomIndex]; + } + + // 生成5位随机大写字母 + for (let i = 0; i < 5; i++) { + const randomIndex = Math.floor(Math.random() * upperCaseLetters.length); + result += upperCaseLetters[randomIndex]; + } + + // 生成2位随机数字 + for (let i = 0; i < 2; i++) { + const randomIndex = Math.floor(Math.random() * numbers.length); + result += numbers[randomIndex]; + } + + // 打乱整个字符串 + const characters = result.split(''); + for (let i = characters.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [characters[i], characters[j]] = [characters[j], characters[i]]; + } + + return characters.join(''); + } + + // 全角字符转半角字符 + toHalfWidth = (str) => { + // 全角字符到半角字符的映射表 + const fullWidthToHalfWidthMap = { + '!': '!', '"': '"', '#': '#', '$': '$', '%': '%', '&': '&', ''': "'", + '(': '(', ')': ')', '*': '*', '+': '+', ',': ',', '-': '-', '.': '.', + '/': '/', ':': ':', ';': ';', '<': '<', '=': '=', '>': '>', '?': '?', + '@': '@', 'A': 'A', 'B': 'B', 'C': 'C', 'D': 'D', 'E': 'E', 'F': 'F', + 'G': 'G', 'H': 'H', 'I': 'I', 'J': 'J', 'K': 'K', 'L': 'L', 'M': 'M', + 'N': 'N', 'O': 'O', 'P': 'P', 'Q': 'Q', 'R': 'R', 'S': 'S', 'T': 'T', + 'U': 'U', 'V': 'V', 'W': 'W', 'X': 'X', 'Y': 'Y', 'Z': 'Z', '[': '[', + '\': '\\', ']': ']', '^': '^', '_': '_', '`': '`', 'a': 'a', 'b': 'b', + 'c': 'c', 'd': 'd', 'e': 'e', 'f': 'f', 'g': 'g', 'h': 'h', 'i': 'i', + 'j': 'j', 'k': 'k', 'l': 'l', 'm': 'm', 'n': 'n', 'o': 'o', 'p': 'p', + 'q': 'q', 'r': 'r', 's': 's', 't': 't', 'u': 'u', 'v': 'v', 'w': 'w', + 'x': 'x', 'y': 'y', 'z': 'z', '{': '{', '|': '|', '}': '}', '~': '~', + '0': '0', '1': '1', '2': '2', '3': '3', '4': '4', '5': '5', '6': '6', + '7': '7', '8': '8', '9': '9', ' ': ' ' // 全角空格到半角空格的映射 + }; + + // 遍历输入字符串,将全角字符替换为半角字符 + return str.split('').map(char => fullWidthToHalfWidthMap[char] || char).join(''); + } +} + +module.exports = BaseService; \ No newline at end of file diff --git a/app/middleware/log.js b/app/middleware/log.js new file mode 100644 index 0000000..6d7323a --- /dev/null +++ b/app/middleware/log.js @@ -0,0 +1,72 @@ +// 错误日志 +const fs = require('fs'); +const os = require('os'); +const path = require('path'); + +const dirCheck = (dirPath) => { + // 检查文件夹是否存在 + fs.access(dirPath, fs.constants.F_OK, (err) => { + if (err) { + // 文件夹不存在 + console.log('文件夹不存在,正在创建...'); + fs.mkdir(dirPath, { recursive: true }, (err) => { + if (err) throw err; + console.log('文件夹已创建'); + }); + } else { + // 文件夹存在 + console.log('文件夹已存在'); + } + }); +} + +module.exports = (filePath, msg) => { + // 获取时间 + let now = new Date(), + times = { + year: now.getFullYear(), + month: now.getMonth() + 1, + day: now.getDate(), + hour: now.getHours(), + minute: now.getMinutes(), + second: now.getSeconds() + }; + // 获取操作系统类型 + const type = os.type(); + console.log(`操作系统类型: ${type}`); + filePath = path.join(__dirname, filePath); + dirCheck(filePath); + const yearPath = filePath + '/' + times.year; + dirCheck(yearPath); + const monthPath = yearPath + '/' + times.month; + dirCheck(monthPath); + const dayPath = monthPath + '/' + times.day + '.txt'; + const logTime = '[' + times.year + '-' + times.month + '-' + times.day + '>>>' + times.hour + ':' + times.minute + ':' + times.second + ']' + '>>>'; + let crlf; + // 判断操作系统类型,设置换行符 + if (type === 'Windows_NT') { + crlf = '\n'; + } else { + crlf = '\r\n'; + } + // 读取文件内容 + fs.readFile(dayPath, 'utf8', (err, data) => { + if (err) { + if (err.code === 'ENOENT') { + // 文件不存在,直接写入新内容(这里实际上还是使用了覆盖的方式) + fs.writeFile(dayPath, logTime + msg + crlf, (err) => { + if (err) throw err; + console.log('文件已创建并写入新内容'); + }); + } else { + throw err; + } + } else { + // 文件存在,追加新内容 + fs.appendFile(dayPath, logTime + msg + crlf, (err) => { + if (err) throw err; + console.log('新内容已追加到文件'); + }); + } + }); +}; \ No newline at end of file diff --git a/app/model3/BDCard.js b/app/model3/BDCard.js new file mode 100644 index 0000000..414352d --- /dev/null +++ b/app/model3/BDCard.js @@ -0,0 +1,22 @@ +'use strict'; + +module.exports = (app) => { + const { INTEGER, CHAR, DATE } = app.Sequelize; + + const BDCard = app.model3.define('bd-card', { + id: { type: INTEGER(8), primaryKey: true, autoIncrement: true }, + card_id: CHAR(8), + card_imsi: CHAR(18), + type: INTEGER(3), + status: INTEGER(3), + created_at: DATE, + updated_at: DATE, + },{ + freezeTableName: true, + timestamp: true, + createdAt: true, + updatedAt: true, + }); + + return BDCard; +}; \ No newline at end of file diff --git a/app/model4/Account.js b/app/model4/Account.js new file mode 100644 index 0000000..e2931ee --- /dev/null +++ b/app/model4/Account.js @@ -0,0 +1,24 @@ +'use strict'; + +module.exports = (app) => { + const { INTEGER, CHAR, STRING, DATE } = app.Sequelize; + + const Account = app.model4.define('account', { + id: { type: INTEGER(8), primaryKey: true, autoIncrement: true }, + uid: CHAR(15), + salt: CHAR(24), + contest_uid: CHAR(15), + account_name: STRING(20), + password: CHAR(64), + status: INTEGER(3), + created_at: DATE, + login_status: INTEGER(3), + },{ + freezeTableName: true, + timestamp: true, + createdAt: true, + updatedAt: false, + }); + + return Account; +}; \ No newline at end of file diff --git a/app/model4/ChoiceTopic.js b/app/model4/ChoiceTopic.js new file mode 100644 index 0000000..609cc99 --- /dev/null +++ b/app/model4/ChoiceTopic.js @@ -0,0 +1,22 @@ +'use strict'; + +module.exports = (app) => { + const { INTEGER, CHAR, STRING } = app.Sequelize; + + const ChoiceTopic = app.model4.define('choice-topic', { + id: { type: INTEGER(5), primaryKey: true, autoIncrement: true }, + topic: STRING(300), + options_a: STRING(300), + options_b: STRING(300), + options_c: STRING(300), + options_d: STRING(300), + answer: CHAR(1), + },{ + freezeTableName: true, + timestamp: false, + createdAt: false, + updatedAt: false, + }); + + return ChoiceTopic; +}; \ No newline at end of file diff --git a/app/model4/Contest.js b/app/model4/Contest.js new file mode 100644 index 0000000..7ddf723 --- /dev/null +++ b/app/model4/Contest.js @@ -0,0 +1,29 @@ +'use strict'; + +module.exports = (app) => { + const { INTEGER, CHAR, STRING, DATE } = app.Sequelize; + + const Contest = app.model4.define('contest', { + id: { type: INTEGER(8), primaryKey: true, autoIncrement: true }, + uid: CHAR(15), + contest_name: STRING(50), + contest_organizers: STRING(50), + contest_term: STRING(5), + type: INTEGER(3), + status: INTEGER(3), + online_start: DATE, + online_end: DATE, + offline_start: DATE, + offline_end: DATE, + contest_start: DATE, + contest_end: DATE, + created_at: DATE, + },{ + freezeTableName: true, + timestamp: true, + createdAt: true, + updatedAt: false, + }); + + return Contest; +}; \ No newline at end of file diff --git a/app/model4/Examination.js b/app/model4/Examination.js new file mode 100644 index 0000000..08113eb --- /dev/null +++ b/app/model4/Examination.js @@ -0,0 +1,34 @@ +'use strict'; + +module.exports = (app) => { + const { INTEGER, CHAR, STRING, DATE } = app.Sequelize; + + const Examination = app.model4.define('examination', { + id: { type: INTEGER(8), primaryKey: true, autoIncrement: true }, + user_uid: CHAR(15), + contest_uid: CHAR(15), + exam_type: INTEGER(3), + topic_type: INTEGER(3), + topic1: STRING(2), + topic2: STRING(2), + topic3: STRING(50), + topic5: STRING(10), + answer: STRING(20), + answer1: STRING(2), + answer2: STRING(2), + answer3: STRING(10), + answer4: STRING(20), + answer5: STRING(10), + score: INTEGER(3), + score_collection: CHAR(9), + created_at: DATE, + updated_at: DATE, + },{ + freezeTableName: true, + timestamp: true, + createdAt: true, + updatedAt: true, + }); + + return Examination; +}; \ No newline at end of file diff --git a/app/model4/TShortMsg.js b/app/model4/TShortMsg.js new file mode 100644 index 0000000..ae1d7a7 --- /dev/null +++ b/app/model4/TShortMsg.js @@ -0,0 +1,24 @@ +'use strict'; + +module.exports = (app) => { + const { INTEGER, CHAR, TEXT, DATE } = app.Sequelize; + + const TShortMsg = app.model4.define('tshort-msg', { + id: { type: INTEGER(8), primaryKey: true, autoIncrement: true }, + user_uid: CHAR(15), + card_id: CHAR(8), + receiver_address: CHAR(8), + original_msg: TEXT, + send_type: INTEGER(1), + encode_type: INTEGER(1), + msg_data: TEXT, + created_at: DATE, + },{ + freezeTableName: true, + timestamp: true, + createdAt: true, + updatedAt: false, + }); + + return TShortMsg; +}; \ No newline at end of file diff --git a/app/model4/TuserBind.js b/app/model4/TuserBind.js new file mode 100644 index 0000000..97a9f1c --- /dev/null +++ b/app/model4/TuserBind.js @@ -0,0 +1,18 @@ +'use strict'; + +module.exports = (app) => { + const { INTEGER, CHAR } = app.Sequelize; + + const TuserBind = app.model4.define('tuser-bind', { + id: { type: INTEGER(8), primaryKey: true, autoIncrement: true }, + card_id: CHAR(8), + user_uid: CHAR(15), + },{ + freezeTableName: true, + timestamp: false, + createdAt: false, + updatedAt: false, + }); + + return TuserBind; +}; \ No newline at end of file diff --git a/app/router.js b/app/router.js new file mode 100644 index 0000000..2a601a0 --- /dev/null +++ b/app/router.js @@ -0,0 +1,31 @@ +/** + * @param {Egg.Application} app - egg application + */ +module.exports = app => { + const { router, controller } = app; + + // 系统获取竞赛基本信息 + router.post("/contestMsg", controller.contest.contestMsg); + // 系统获取排名信息 + router.post("/ranking", controller.contest.ranking); + // 检查竞赛账号是否已经绑定北斗卡 + router.post("/checkUser", controller.check.checkUser); + // 检查北斗卡是否已经被绑定 + router.post("/checkBind", controller.check.checkBind); + // 绑定北斗卡 + router.post("/bindCard", controller.bind.bindCard); + // 解绑北斗卡 + router.post("/unBindCard", controller.bind.unBindCard); + // 学生抽取/读取题目 + router.post("/topic", controller.topic.topic); + // 系统读取单选题 + router.post("/radio", controller.topic.radio); + // 学生获取历史消息 + router.post("/getHistoryMsg", controller.exam.getHistoryMsg); + // 学生删除历史消息 + router.post('/delHistoryMsg', controller.exam.delHistoryMsg); + // 学生完成答题 + router.post("/answer", controller.exam.answer); + // 学生查看成绩信息 + router.post("/grades", controller.exam.grades); +}; diff --git a/app/service/Bind.js b/app/service/Bind.js new file mode 100644 index 0000000..db37298 --- /dev/null +++ b/app/service/Bind.js @@ -0,0 +1,41 @@ +'use strict'; + +const Service = require('../core/base_service'); + +const { Op } = require('sequelize'); + +const fileName = "Bind"; + +class BindService extends Service { + // 竞赛账号绑定北斗卡 + async bindCard(uid, card_id, card_imsi) { + const { ctx } = this; + try { + const card = await ctx.model3.BDCard.update({ status: 3 }, { where: { card_id, card_imsi, status: 0 } }); + if (card[0] == 0) return false; + const result = await ctx.model4.TuserBind.create({ user_uid: uid, card_id }); + return result ? true : false; + } catch (error) { + const blockName = fileName + "--" + "bindCard"; + ctx.app.middleware.log('../../serviceLog', blockName + ">>>>" + error); + return 'error'; + } + } + + // 竞赛账号解绑北斗卡 + async unBindCard(uid, card_id, card_imsi) { + const { ctx } = this; + try { + const card = await ctx.model3.BDCard.update({ status: 0 }, { where: { card_id, card_imsi, status: 3 } }); + if (card[0] == 0) return false; + const result = await ctx.model4.TuserBind.destroy({ where: { user_uid: uid } }); + return result.length == 0 ? false : true; + } catch (error) { + const blockName = fileName + "--" + "unBindCard"; + ctx.app.middleware.log('../../serviceLog', blockName + ">>>>" + error); + return 'error'; + } + } +} + +module.exports = BindService; \ No newline at end of file diff --git a/app/service/Check.js b/app/service/Check.js new file mode 100644 index 0000000..74ef55c --- /dev/null +++ b/app/service/Check.js @@ -0,0 +1,37 @@ +'use strict'; + +const Service = require('../core/base_service'); + +const { Op } = require('sequelize'); + +const fileName = "Check"; + +class CheckService extends Service { + // 检查竞赛账号是否已经绑定北斗卡 + async checkUser(uid) { + const { ctx } = this; + try { + const result = await ctx.model4.TuserBind.findOne({ where: { user_uid: uid } }); + return result || false; + } catch (error) { + const blockName = fileName + "--" + "checkUser"; + ctx.app.middleware.log('../../serviceLog', blockName + ">>>>" + error); + return 'error'; + } + } + + // 检查北斗卡是否已经被绑定 + async checkBind(card_id) { + const { ctx } = this; + try { + const result = await ctx.model3.BDCard.findOne({ where: { card_id, status: 0 } }); + return result || false; + } catch (error) { + const blockName = fileName + "--" + "checkBind"; + ctx.app.middleware.log('../../serviceLog', blockName + ">>>>" + error); + return 'error'; + } + } +} + +module.exports = CheckService; \ No newline at end of file diff --git a/app/service/Contest.js b/app/service/Contest.js new file mode 100644 index 0000000..1f1de99 --- /dev/null +++ b/app/service/Contest.js @@ -0,0 +1,75 @@ +'use strict'; + +const Service = require('../core/base_service'); + +const { Op } = require('sequelize'); + +const fileName = "Contest"; + +class ContestService extends Service { + // 系统获取竞赛基本信息 + async contestMsg(uid) { + const { ctx } = this; + try { + const user = await ctx.model4.Account.findOne({ attributes: ['contest_uid'], where: { uid } }); + if (!user) return false; + const result = await ctx.model4.Contest.findOne({ where: { uid: user.dataValues.contest_uid }, order: [['created_at', 'DESC']] }); + console.log(result) + return result || false; + } catch (error) { + const blockName = fileName + "--" + "contestMsg"; + ctx.app.middleware.log('../../serviceLog', blockName + ">>>>" + error); + return 'error'; + } + } + + // 系统获取排名信息 + async ranking(uid) { + const { ctx } = this; + try { + // 按得分降序排序,得分相同时按创建时间降序排序 + let include = [ + { + model: ctx.model4.Account, + attributes: ['account_name'], + } + ]; + ctx.model4.Examination.belongsTo(ctx.model4.Account, { foreignKey: 'user_uid', targetKey: 'uid' }); + const result = await ctx.model4.Examination.findAll({ + attributes: ['score', 'created_at', 'updated_at'], + where: { updated_at: { [Op.ne]: null } }, + // 先按得分降序排序 + order: [['score', 'DESC'],], + limit: 100, + include, + }); + if (result.length == 0) return false; + // 使用 Set 获取不同得分值 + const uniqueScore = new Set(result.map(item => item.dataValues.score)); + // 将 Set 转换为数组 + const uniqueScoreArr = Array.from(uniqueScore); + // 对数组进行降序排序 + uniqueScoreArr.sort((a, b) => b - a); + let allScoreArr = []; + uniqueScoreArr.forEach(score => { + let sameScoreArr = []; + result.forEach(item => { + if (item.dataValues.score == score) { + sameScoreArr.push(item); + item.timeDifference = item.dataValues.updated_at - item.dataValues.created_at; + } + }); + // 按创建时间降序排序 + sameScoreArr.sort((a, b) => b.timeDifference - a.timeDifference); + allScoreArr.push(...sameScoreArr); + }); + return allScoreArr; + } catch (error) { + const blockName = fileName + "--" + "ranking"; + ctx.app.middleware.log('../../serviceLog', blockName + ">>>>" + error); + return 'error'; + } + } +} + +module.exports = ContestService; \ No newline at end of file diff --git a/app/service/Exam.js b/app/service/Exam.js new file mode 100644 index 0000000..7e43366 --- /dev/null +++ b/app/service/Exam.js @@ -0,0 +1,243 @@ +'use strict'; + +const Service = require('../core/base_service'); + +const { Op } = require('sequelize'); + +const fileName = "Exam"; + +class ExamService extends Service { + // 学生获取历史消息 + async getHistoryMsg(uid, body) { + const { ctx } = this; + try { + let { offset, limit, searchOptions, sortOptions } = body; + offset = (offset - 1) * limit; + let attributes = ['id', 'card_id', 'encode_type', 'msg_data', 'created_at'], + where = { user_uid: uid }, order = [], dbOptions, joinSubstring = [], + substring = ['card_id', 'msg_data'], key = ['encode_type'], between = ['created_at']; + attributes.forEach(element => { + if (searchOptions[element]) { + if (searchOptions[element] == '*空') { + if (joinSubstring.includes(element)) { + where[element] = null; + } else { + where[element] = [null, '']; + } + } else if (substring.includes(element)) { + where[element] = { [Op.substring]: searchOptions[element] }; + } else if (key.includes(element)) { + where[element] = searchOptions[element] == "1" ? true : false; + } else if (between.includes(element)) { + where[element] = { [Op.between]: [searchOptions[element][0], searchOptions[element][1]] }; + } + } + if (sortOptions[element] == true) { + order.push([element, 'ASC']); + } else if (sortOptions[element] == false) { + order.push([element, 'DESC']); + } + }); + dbOptions = { attributes, offset, limit, where, order }; + const result = await ctx.model4.TShortMsg.findAndCountAll(dbOptions); + return result.rows.length == 0 ? false : result; + } catch (error) { + const blockName = fileName + "--" + "getHistoryMsg"; + ctx.app.middleware.log('../../serviceLog', blockName + ">>>>" + error); + return 'error'; + } + } + + // 学生删除历史消息 + async delHistoryMsg(uid, modelType, password, requestMsg) { + const { ctx } = this; + try { + const result = await ctx.model4.Account.findOne({ where: { uid } }); + if (!result || result.dataValues.password !== this.shaForm(password, this.decryptRequest(result.dataValues.salt))) return false; + let where = {}; + if (modelType == "single") { + const { card_id, encode_type, msg_data, origin_time } = requestMsg; + where = { user_uid: uid, card_id, encode_type, msg_data, created_at: origin_time }; + } else if (modelType == "multiple") { + let card_id = [], encode_type = [], msg_data = [], created_at = []; + for (const key in requestMsg) { + requestMsg[key].forEach(element => { + card_id.push(element.card_id), + encode_type.push(element.encode_type), + msg_data.push(element.msg_data), + created_at.push(element.origin_time) + }); + } + where = { user_uid: uid, card_id, encode_type, msg_data, created_at }; + } else if (modelType == "all") { + where = { user_uid: uid }; + } + const resetList = await ctx.model4.TShortMsg.destroy({ where }); + return resetList.length == 0 ? false : resetList; + } catch (error) { + const blockName = fileName + "--" + "delHistoryMsg"; + ctx.app.middleware.utils.log('../../../serviceLog', blockName + ">>>>" + error); + return 'error'; + } + } + + // 学生完成答题 + async answer(uid, body) { + const { ctx } = this; + try { + // 根据 updated_at 来判断是否已经完成过答题,确保只能提交一次答题,如果已经完成答题则不允许再次提交答案 + const userExam = await ctx.model4.Examination.findOne({ attributes: ['topic1', 'topic2', 'topic3', 'topic5'], where: { user_uid: uid, updated_at: null } }); + if (!userExam) return false; + // 根据答案进行评分 + const shortMsg = await ctx.model4.TShortMsg.findAll({ attributes: ['id', 'msg_data'], where: { user_uid: uid } }); + if (shortMsg.length == 0) return false; + const { answer1, answer2, answer3, answer4, answer5 } = body; + const answerObj = {}; + const answerArr = [answer1, answer2, answer3, answer4, answer5]; + shortMsg.forEach(element => { + if (element.dataValues.id == answer1) { + if (element.dataValues.msg_data.length > 2) { + answerObj.answer1 = element.dataValues.msg_data.slice(0, 2); + } else { + answerObj.answer1 = element.dataValues.msg_data; + } + } + if (element.dataValues.id == answer2) { + if (element.dataValues.msg_data.length > 2) { + answerObj.answer2 = element.dataValues.msg_data.slice(0, 2); + } else { + answerObj.answer2 = element.dataValues.msg_data; + } + } + if (element.dataValues.id == answer3) { + if (element.dataValues.msg_data.length > 10) { + answerObj.answer3 = element.dataValues.msg_data.slice(0, 10); + } else { + answerObj.answer3 = element.dataValues.msg_data; + } + } + if (element.dataValues.id == answer4) { + if (element.dataValues.msg_data.length > 20) { + answerObj.answer4 = element.dataValues.msg_data.slice(0, 20); + } else { + answerObj.answer4 = element.dataValues.msg_data; + } + } + if (element.dataValues.id == answer5) { + if (element.dataValues.msg_data.length > 10) { + answerObj.answer5 = element.dataValues.msg_data.slice(0, 10); + } else { + answerObj.answer5 = element.dataValues.msg_data; + } + } + }); + let score = 0; + let scoreCollection = []; + if (this.toHalfWidth(answerObj.answer1) == userExam.dataValues.topic1) { + score = score + 1; + scoreCollection.push(1); + } else { + scoreCollection.push(0); + } + if (this.toHalfWidth(answerObj.answer2) == userExam.dataValues.topic2) { + score = score + 1; + scoreCollection.push(1); + } else{ + scoreCollection.push(0); + } + let topic3Arr = userExam.dataValues.topic3.split(","); + const radioAnswer = await ctx.model4.ChoiceTopic.findAll({ attributes: ['id', 'answer'], where: { id: topic3Arr } }); + if (radioAnswer.length == 0) return false; + let answer3Str = ""; + const radioAnswerObj = {}; + radioAnswer.forEach(item => { + radioAnswerObj[item.dataValues.id] = item.dataValues.answer; + }); + topic3Arr.forEach(element => { + answer3Str = answer3Str + radioAnswerObj[element]; + }); + if (this.toHalfWidth(answerObj.answer3) == answer3Str) { + score = score + 3; + scoreCollection.push(1); + } else { + scoreCollection.push(0); + } + if (this.toHalfWidth(answerObj.answer4).length == 17) { + const longitude = parseFloat(this.toHalfWidth(answerObj.answer4).slice(0, 3) + "." + this.toHalfWidth(answerObj.answer4).slice(3, 9)); + const latitude = parseFloat(this.toHalfWidth(answerObj.answer4).slice(9, 11) + "." + this.toHalfWidth(answerObj.answer4).slice(11)); + if (-180 <= longitude && longitude <= 180 && -90 <= latitude && latitude <= 90) { + score = score + 3; + scoreCollection.push(1); + } else { + scoreCollection.push(0); + } + } + if (this.toHalfWidth(answerObj.answer5) == userExam.dataValues.topic5) { + score = score + 2; + scoreCollection.push(1); + } else { + scoreCollection.push(0); + } + const updateMsg = { + answer: answerArr.join(","), + answer1: answerObj.answer1, + answer2: answerObj.answer2, + answer3: answerObj.answer3, + answer4: answerObj.answer4, + answer5: answerObj.answer5, + score, + score_collection: scoreCollection.join(","), + updated_at: new Date(), + }; + const result = await ctx.model4.Examination.update(updateMsg, { where: { user_uid: uid, updated_at: null } }); + return !result[0] == 0; + } catch (error) { + const blockName = fileName + "--" + "answer"; + ctx.app.middleware.log('../../serviceLog', blockName + ">>>>" + error); + return 'error'; + } + } + + // 学生查看成绩信息 + async grades(uid) { + const { ctx } = this; + try { + let include = [{ + model: ctx.model4.Account, + attributes: ['account_name'], + }]; + ctx.model4.Examination.belongsTo(ctx.model4.Account, { foreignKey: 'user_uid', targetKey: 'uid' }); + const userExam = await ctx.model4.Examination.findOne({ + attributes: ['score', 'score_collection', 'created_at', 'updated_at'], + where: { user_uid: uid, updated_at: { [Op.ne]: null } }, + include + }); + if (!userExam) return false; + // 查找得分比当前得分高的数据数目 + const higherScoresCount = await ctx.model4.Examination.findAndCountAll({ + where: { + score: { [Op.gt]: userExam.dataValues.score }, + updated_at: { [Op.ne]: null } + } + }); + // 查找相同得分的所有数据 + const sameScores = await ctx.model4.Examination.findAndCountAll({ + where: { + score: userExam.dataValues.score, + created_at: { [Op.lt]: userExam.dataValues.created_at }, + updated_at: { [Op.ne]: null } + } + }); + // 计算总排名(比当前得分高的数目 + 相同得分的数目 + 1(当前用户自己)) + const totalRank = higherScoresCount.count + sameScores.count + 1; + userExam.dataValues.totalRank = totalRank; + return userExam; + } catch (error) { + const blockName = fileName + "--" + "grades"; + ctx.app.middleware.log('../../serviceLog', blockName + ">>>>" + error); + return 'error'; + } + } +} + +module.exports = ExamService; \ No newline at end of file diff --git a/app/service/Topic.js b/app/service/Topic.js new file mode 100644 index 0000000..675ef35 --- /dev/null +++ b/app/service/Topic.js @@ -0,0 +1,113 @@ +'use strict'; + +const Service = require('../core/base_service'); + +const { Op } = require('sequelize'); + +const fileName = "Topic"; + +class TopicService extends Service { + // 学生抽取/读取题目 + async topic(uid, answerId) { + const { ctx } = this; + try { + let contestTopic = await ctx.model4.Examination.findOne({ where: { user_uid: uid } }); + if (!contestTopic) { + const result = await ctx.model4.ChoiceTopic.findAndCountAll(); + // 选择题一共10道,首先获取数据数目 + let bdCardMsg = []; + // 若不满10道题,则直接将题目 id 放入数组中 + if (result.rows.length < 10 || result.rows.length == 10) { + bdCardMsg = result.rows.map(item => item.id); + } else { + // 创建一个空数组来存储随机数 + let randomNumbers = []; + // 循环,直到数组中的元素数量达到十个 + while (randomNumbers.length < 10) { + let arrNum; + do { + // 获取一个随机数 + arrNum = Math.floor(Math.random() * result.count); + } while (randomNumbers.includes(arrNum)); // 检查随机数是否已存在,如果存在则重新抽取 + // 将随机数添加到数组中 + randomNumbers.push(arrNum); + // 将随机数作为下标,向 bdCardMsg 数组中添加对应题目数据 + bdCardMsg.push(result.rows[arrNum].id); + } + } + const hanzi = "北斗航天科学教育科普导航定位通信"; + const randomCharIndex = Math.floor(Math.random() * hanzi.length); + const randomChar = hanzi.charAt(randomCharIndex); + const number = "0123456789"; + const randomNumIndex = Math.floor(Math.random() * number.length); + const randomNum = number.charAt(randomNumIndex); + const sensor = ["温度", "湿度", "超声波传感器", "光线"]; + const randomArrCharIndex = Math.floor(Math.random() * sensor.length); + const randomArrChar = sensor[randomArrCharIndex]; + let sensorData, num; + if (randomArrChar == "温度") { + num = Math.floor(Math.random() * 899) + 100; + num = num.toString().slice(0, -1) + "." + num.toString().slice(-1); + sensorData = "温度" + num + "度"; + } else if (randomArrChar == "湿度") { + num = Math.floor(Math.random() * 899) + 100; + num = num.toString().slice(0, -1) + "." + num.toString().slice(-1); + sensorData = "湿度" + num + "%"; + } else if (randomArrChar == "超声波传感器") { + num = Math.floor(Math.random() * 89) + 10; + sensorData = "距离" + num + "厘米"; + } else if (randomArrChar == "光线") { + num = Math.floor(Math.random() * 3095) + 1000; + sensorData = "光线强度" + num; + } + const user = await ctx.model4.Account.findOne({ attributes: ['contest_uid'], where: { uid } }); + if (!user) return { code: 404 }; + const contest = await ctx.model4.Contest.findOne({ attributes: ['type'], where: { uid: user.dataValues.contest_uid, status: 0 } }); + if (!contest) return { code: 404 }; + const form = { + user_uid: uid, + contest_uid: user.dataValues.contest_uid, + exam_type: contest.dataValues.type, + topic1: randomChar, + topic2: randomNum, + topic3: bdCardMsg.join(","), + topic5: sensorData, + created_at: new Date(), + }; + contestTopic = await ctx.model4.Examination.create(form, { silent: true }); + if (!contestTopic) return { code: 404 }; + } + contestTopic.dataValues.topic3.match(/[^a-zA-Z]/g).join(''); + let idArr; + if (contestTopic.dataValues.updated_at != null) { + idArr = contestTopic.dataValues.answer.split(","); + } else { + idArr = answerId; + } + let attributes = ['id', 'card_id', 'encode_type', 'msg_data', 'created_at'], + where = { id: idArr, user_uid: uid }, + dbOptions = { attributes, where }; + const result = await ctx.model4.TShortMsg.findAndCountAll(dbOptions); + return { code: 200, examination: contestTopic.dataValues, shortMsg: result.rows }; + } catch (error) { + const blockName = fileName + "--" + "topic"; + ctx.app.middleware.log('../../serviceLog', blockName + ">>>>" + error); + return 'error'; + } + } + + // 系统读取单选题 + async radio(radioArr) { + const { ctx } = this; + try { + const Radio = await ctx.model4.ChoiceTopic.findAll({ attributes: ['id', 'topic', 'options_a', 'options_b', 'options_c', 'options_d'], where: { id: radioArr } }); + return Radio.length == 0 ? false : Radio; + } catch (error) { + const blockName = fileName + "--" + "radio"; + ctx.app.middleware.log('../../serviceLog', blockName + ">>>>" + error); + return 'error'; + } + } +} + +module.exports = TopicService; \ No newline at end of file diff --git a/config/config.default.js b/config/config.default.js new file mode 100644 index 0000000..1b3ed2a --- /dev/null +++ b/config/config.default.js @@ -0,0 +1,143 @@ +/* eslint valid-jsdoc: "off" */ + +/** + * @param {Egg.EggAppInfo} appInfo app info + */ +module.exports = appInfo => { + /** + * built-in config + * @type {Egg.EggAppConfig} + **/ + const config = exports = {}; + + // use for cookie sign key, should change to your own and keep security + config.keys = appInfo.name + '_1715070315660_7890'; + + // add your middleware config here + // config.middleware = []; + + config.security = { + csrf:{ + enable:false + }, + }; + + config.redis = { + // 开发/测试环境 + clients: { + db5: { + port: 6379, + host: '127.0.0.1', + password: '74191221', + db: 5 + } + } + + // 生产环境 + // clients: { + // db5: { + // port: 6379, + // host: '127.0.0.1', + // password: 'bd3@htcc2023', + // db: 7 + // } + // } + }; + + config.sequelize = { + // 开发/测试环境 + datasources: [ + { + dialect: 'mariadb', + host: 'localhost', + port: 3306, + username: 'root', + password: '74191221', + database: 'bside', + }, + { + dialect: 'mariadb', + host: 'localhost', + port: 3306, + username: 'root', + password: '74191221', + database: 'cside', + delegate: 'model2', + baseDir: 'model2', + }, + { + dialect: 'mariadb', + host: 'localhost', + port: 3306, + username: 'root', + password: '74191221', + database: 'insiders', + delegate: 'model3', + baseDir: 'model3', + }, + { + dialect: 'mariadb', + host: 'localhost', + port: 3306, + username: 'root', + password: '74191221', + database: 'bdContest', + delegate: 'model4', + baseDir: 'model4', + } + ] + + // 生产环境 + // datasources: [ + // { + // dialect: 'mariadb', + // host: 'localhost', + // port: 3306, + // username: 'bdqh_platform', + // password: '124689@XXxx', + // database: 'bside', + // }, + // { + // dialect: 'mariadb', + // host: 'localhost', + // port: 3306, + // username: 'bdqh_platform', + // password: '124689@XXxx', + // database: 'cside', + // delegate: 'model2', + // baseDir: 'model2', + // }, + // { + // dialect: 'mariadb', + // host: 'localhost', + // port: 3306, + // username: 'bdqh_platform', + // password: '124689@XXxx', + // database: 'insiders', + // delegate: 'model3', + // baseDir: 'model3', + // }, + // { + // dialect: 'mariadb', + // host: 'localhost', + // port: 3306, + // username: 'bdqh_platform', + // password: '124689@XXxx', + // database: 'bdContest', + // delegate: 'model4', + // baseDir: 'model4', + // } + // ] + }; + + // add your user config here + const userConfig = { + // myAppName: 'egg', + salt:'bdContest' + }; + + return { + ...config, + ...userConfig, + }; +}; diff --git a/config/plugin.js b/config/plugin.js new file mode 100644 index 0000000..5a65fbe --- /dev/null +++ b/config/plugin.js @@ -0,0 +1,22 @@ +/** @type Egg.EggPlugin */ +module.exports = { + // had enabled by egg + // static: { + // enable: true, + // } + + redis : { + enable: true, + package: 'egg-redis', + }, + + sequelize : { + enable: true, + package: 'egg-sequelize', + }, + + validate : { + enable: true, + package: 'egg-validate', + }, +}; diff --git a/controllerLog/2025/2/28.txt b/controllerLog/2025/2/28.txt new file mode 100644 index 0000000..c38c9f7 --- /dev/null +++ b/controllerLog/2025/2/28.txt @@ -0,0 +1 @@ +[2025-2-28>>>13:56:4]>>>Contest--contestMsg>>>>TypeError: Cannot read properties of undefined (reading 'get') diff --git a/controllerLog/2025/5/19.txt b/controllerLog/2025/5/19.txt new file mode 100644 index 0000000..75a78d5 --- /dev/null +++ b/controllerLog/2025/5/19.txt @@ -0,0 +1,2 @@ +[2025-5-19>>>15:43:25]>>>Exam--topic>>>>ReferenceError: uid is not defined +[2025-5-19>>>17:16:58]>>>Exam--topic>>>>ReferenceError: uid is not defined diff --git a/controllerLog/2025/5/21.txt b/controllerLog/2025/5/21.txt new file mode 100644 index 0000000..b2a3f83 --- /dev/null +++ b/controllerLog/2025/5/21.txt @@ -0,0 +1,3 @@ +[2025-5-21>>>12:7:39]>>>Exam--topic>>>>TypeError: Cannot read properties of undefined (reading 'length') +[2025-5-21>>>12:9:7]>>>Exam--topic>>>>TypeError: Cannot read properties of undefined (reading 'length') +[2025-5-21>>>12:9:17]>>>Exam--topic>>>>TypeError: Cannot read properties of undefined (reading 'length') diff --git a/database/config.json b/database/config.json new file mode 100644 index 0000000..1b65e53 --- /dev/null +++ b/database/config.json @@ -0,0 +1,23 @@ +{ + "development": { + "username": "root", + "password": 74191221, + "database": "vm_centos", + "host": "127.0.0.1", + "dialect": "mariadb" + }, + "test": { + "username": "root", + "password": 74191221, + "database": "vm_centos", + "host": "127.0.0.1", + "dialect": "mariadb" + }, + "production": { + "username": "root", + "password": 74191221, + "database": "vm_centos", + "host": "127.0.0.1", + "dialect": "mariadb" + } +} diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..1bbed3f --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,5 @@ +{ + "include": [ + "**/*" + ] +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..13332d1 --- /dev/null +++ b/package.json @@ -0,0 +1,48 @@ +{ + "name": "bd-contest", + "version": "1.0.0", + "description": "", + "private": true, + "egg": { + "declarations": true + }, + "dependencies": { + "bd-contest": "file:", + "crypto-js": "^4.2.0", + "egg": "^3.17.5", + "egg-redis": "^2.6.0", + "egg-scripts": "2", + "egg-sequelize": "^6.0.0", + "egg-validate": "^2.0.2", + "ioredis": "^5.4.1", + "mariadb": "^3.3.0", + "mysql2": "^3.9.7", + "sequelize": "^6.37.3" + }, + "devDependencies": { + "egg-bin": "6", + "egg-mock": "5", + "eslint": "8", + "eslint-config-egg": "13", + "sequelize-cli": "^6.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "scripts": { + "start": "egg-scripts start --daemon --port=8018 --title=egg-server-bd-contest", + "stop": "egg-scripts stop --title=egg-server-bd-contest", + "dev": "egg-bin dev --port=8018", + "test": "npm run lint -- --fix && npm run test:local", + "test:local": "egg-bin test", + "cov": "egg-bin cov", + "lint": "eslint .", + "ci": "npm run lint && npm run cov" + }, + "repository": { + "type": "git", + "url": "" + }, + "author": "", + "license": "MIT" +} diff --git a/serviceLog/2025/5/19.txt b/serviceLog/2025/5/19.txt new file mode 100644 index 0000000..22158c8 --- /dev/null +++ b/serviceLog/2025/5/19.txt @@ -0,0 +1,11 @@ +[2025-5-19>>>15:43:27]>>>Exam--historyMsg>>>>TypeError: Cannot read properties of undefined (reading 'card_id') +[2025-5-19>>>15:43:28]>>>Exam--unBindCard>>>>TypeError: Cannot read properties of undefined (reading 'update') +[2025-5-19>>>15:43:29]>>>Exam--grades>>>>SequelizeDatabaseError: (conn:70, no: 1054, SQLState: 42S22) Unknown column 'examination.uid' in 'where clause' +sql: SELECT `score`, `created_at` FROM `examination` AS `examination` WHERE `examination`.`uid` = '20240613698218T' LIMIT 1; - parameters:[] +[2025-5-19>>>17:19:17]>>>Exam--checkBind>>>>TypeError: Cannot read properties of undefined (reading 'findOne') +[2025-5-19>>>17:26:2]>>>Exam--checkBind>>>>TypeError: Cannot read properties of undefined (reading 'findOne') +[2025-5-19>>>17:28:31]>>>Exam--checkBind>>>>SequelizeDatabaseError: (conn:177, no: 1054, SQLState: 42S22) Unknown column 'tuser-bind.uid' in 'where clause' +sql: SELECT `id`, `card_id`, `user_uid` FROM `tuser-bind` AS `tuser-bind` WHERE `tuser-bind`.`uid` = '20240613698218T' LIMIT 1; - parameters:[] +[2025-5-19>>>17:44:56]>>>Exam--historyMsg>>>>TypeError: Cannot read properties of undefined (reading 'card_id') +[2025-5-19>>>17:49:29]>>>Exam--grades>>>>SequelizeDatabaseError: (conn:283, no: 1054, SQLState: 42S22) Unknown column 'examination.uid' in 'where clause' +sql: SELECT `score`, `created_at` FROM `examination` AS `examination` WHERE `examination`.`uid` = '20240613698218T' LIMIT 1; - parameters:[] diff --git a/serviceLog/2025/5/20.txt b/serviceLog/2025/5/20.txt new file mode 100644 index 0000000..0e6c185 --- /dev/null +++ b/serviceLog/2025/5/20.txt @@ -0,0 +1 @@ +[2025-5-20>>>16:26:6]>>>Contest--contestMsg>>>>TypeError: Cannot read properties of undefined (reading 'findOne') diff --git a/serviceLog/2025/5/22.txt b/serviceLog/2025/5/22.txt new file mode 100644 index 0000000..5a2d258 --- /dev/null +++ b/serviceLog/2025/5/22.txt @@ -0,0 +1,5 @@ +[2025-5-22>>>16:21:11]>>>Exam--radio>>>>Error: WHERE parameter "id" has invalid "undefined" value +[2025-5-22>>>16:23:0]>>>Exam--radio>>>>Error: WHERE parameter "id" has invalid "undefined" value +[2025-5-22>>>16:23:33]>>>Exam--radio>>>>Error: WHERE parameter "id" has invalid "undefined" value +[2025-5-22>>>17:19:18]>>>Exam--radio>>>>TypeError: item.trim is not a function +[2025-5-22>>>17:19:49]>>>Exam--radio>>>>TypeError: item.trim is not a function diff --git a/serviceLog/2025/5/23.txt b/serviceLog/2025/5/23.txt new file mode 100644 index 0000000..17fba7e --- /dev/null +++ b/serviceLog/2025/5/23.txt @@ -0,0 +1,13 @@ +[2025-5-23>>>16:41:25]>>>Exam--unBindCard>>>>TypeError: Cannot read properties of undefined (reading 'update') +[2025-5-23>>>16:43:56]>>>Exam--unBindCard>>>>Error: WHERE parameter "card_id" has invalid "undefined" value +[2025-5-23>>>16:44:3]>>>Exam--unBindCard>>>>Error: WHERE parameter "card_id" has invalid "undefined" value +[2025-5-23>>>16:44:3]>>>Exam--unBindCard>>>>Error: WHERE parameter "card_id" has invalid "undefined" value +[2025-5-23>>>16:45:11]>>>Exam--unBindCard>>>>Error: WHERE parameter "card_id" has invalid "undefined" value +[2025-5-23>>>16:46:41]>>>Exam--unBindCard>>>>Error: WHERE parameter "card_id" has invalid "undefined" value +[2025-5-23>>>16:46:43]>>>Exam--unBindCard>>>>Error: WHERE parameter "card_id" has invalid "undefined" value +[2025-5-23>>>16:47:18]>>>Exam--unBindCard>>>>Error: WHERE parameter "card_id" has invalid "undefined" value +[2025-5-23>>>16:48:44]>>>Exam--unBindCard>>>>Error: WHERE parameter "card_id" has invalid "undefined" value +[2025-5-23>>>16:56:41]>>>Exam--answer>>>>SequelizeDatabaseError: (conn:486, no: 1054, SQLState: 42S22) Unknown column 'uid' in 'where clause' +sql: UPDATE `examination` SET `answer1`=?,`answer2`=?,`answer3`=?,`answer4`=?,`answer5`=?,`score`=?,`updated_at`=? WHERE `uid` = ? AND `updated_at` IS NULL - parameters:['教','7','DCCBDAACBB','12312312387123123','湿度92.1%',0,'2025-05-23 08:56:41.329','20240613698... +[2025-5-23>>>17:57:52]>>>Exam--answer>>>>TypeError: radioAnswer.foreach is not a function +[2025-5-23>>>17:58:5]>>>Exam--answer>>>>TypeError: radioAnswer.foreach is not a function diff --git a/serviceLog/2025/5/27.txt b/serviceLog/2025/5/27.txt new file mode 100644 index 0000000..e042705 --- /dev/null +++ b/serviceLog/2025/5/27.txt @@ -0,0 +1,9 @@ +[2025-5-27>>>9:51:50]>>>Exam--unBindCard>>>>Error: WHERE parameter "card_id" has invalid "undefined" value +[2025-5-27>>>11:7:52]>>>Exam--checkBind>>>>Error: WHERE parameter "card_id" has invalid "undefined" value +[2025-5-27>>>11:8:7]>>>Exam--checkBind>>>>Error: WHERE parameter "card_id" has invalid "undefined" value +[2025-5-27>>>17:44:39]>>>Exam--answer>>>>SequelizeDatabaseError: (conn:856, no: 1406, SQLState: 22001) Data too long for column 'answer3' at row 1 +sql: UPDATE `examination` SET `answer1`=?,`answer2`=?,`answer3`=?,`answer4`=?,`answer5`=?,`score`=?,`updated_at`=? WHERE `user_uid` = ? AND `updated_at` IS NULL - parameters:['7','教','12312312387123123','DCCBDAACBB','1',0,'2025-05-27 09:44:39.861','202406136982... +[2025-5-27>>>17:53:21]>>>Exam--answer>>>>SequelizeDatabaseError: (conn:908, no: 1406, SQLState: 22001) Data too long for column 'answer2' at row 1 +sql: UPDATE `examination` SET `answer1`=?,`answer2`=?,`answer3`=?,`answer4`=?,`answer5`=?,`score`=?,`updated_at`=? WHERE `user_uid` = ? AND `updated_at` IS NULL - parameters:['7','湿度92.1%','12312312387123123','DCCBDAACBB','教',0,'2025-05-27 09:53:21.027','202406... +[2025-5-27>>>17:56:19]>>>Exam--answer>>>>SequelizeDatabaseError: (conn:929, no: 1406, SQLState: 22001) Data too long for column 'answer1' at row 1 +sql: UPDATE `examination` SET `answer1`=?,`answer2`=?,`answer3`=?,`answer4`=?,`answer5`=?,`score`=?,`updated_at`=? WHERE `user_uid` = ? AND `updated_at` IS NULL - parameters:['123456789123456789','123456789123456789','123456789123456789','123456789123456789','1... diff --git a/serviceLog/2025/6/30.txt b/serviceLog/2025/6/30.txt new file mode 100644 index 0000000..64fd9aa --- /dev/null +++ b/serviceLog/2025/6/30.txt @@ -0,0 +1 @@ +[2025-6-30>>>16:9:47]>>>Topic--topic>>>>ReferenceError: dbOptions is not defined diff --git a/serviceLog/2025/7/1.txt b/serviceLog/2025/7/1.txt new file mode 100644 index 0000000..a7f6a94 --- /dev/null +++ b/serviceLog/2025/7/1.txt @@ -0,0 +1,2 @@ +[2025-7-1>>>16:33:42]>>>Contest--contestMsg>>>>SequelizeConnectionError: (conn:-1, no: 45012, SQLState: 08S01) Connection timeout: failed to create socket after 1680ms +[2025-7-1>>>16:33:43]>>>Contest--ranking>>>>SequelizeConnectionError: (conn:324, no: 45012, SQLState: 08S01) Connection timeout: failed to create socket after 1434ms diff --git a/serviceLog/2025/7/23.txt b/serviceLog/2025/7/23.txt new file mode 100644 index 0000000..4bedcf1 --- /dev/null +++ b/serviceLog/2025/7/23.txt @@ -0,0 +1,2 @@ +[2025-7-23>>>17:10:39]>>>Exam--answer>>>>TypeError: str.split is not a function +[2025-7-23>>>17:10:41]>>>Exam--answer>>>>TypeError: str.split is not a function diff --git a/serviceLog/2025/7/8.txt b/serviceLog/2025/7/8.txt new file mode 100644 index 0000000..2996b11 --- /dev/null +++ b/serviceLog/2025/7/8.txt @@ -0,0 +1,4 @@ +[2025-7-8>>>16:32:31]>>>Topic--topic>>>>TypeError: Cannot read properties of null (reading 'split') +[2025-7-8>>>16:32:38]>>>Topic--topic>>>>TypeError: Cannot read properties of null (reading 'split') +[2025-7-8>>>16:32:58]>>>Topic--topic>>>>TypeError: Cannot read properties of null (reading 'split') +[2025-7-8>>>16:34:15]>>>Topic--topic>>>>TypeError: Cannot read properties of null (reading 'split') diff --git a/test/app/controller/home.test.js b/test/app/controller/home.test.js new file mode 100644 index 0000000..ea43dbb --- /dev/null +++ b/test/app/controller/home.test.js @@ -0,0 +1,23 @@ +const { strict: assert } = require('node:assert'); +const path = require('node:path'); +const { statSync } = require('node:fs'); +const { app } = require('egg-mock/bootstrap'); + +describe('test/app/controller/home.test.js', () => { + it('should assert', async () => { + const pkg = require('../../../package.json'); + assert(app.config.keys.startsWith(pkg.name)); + }); + + it('should typings exists', async () => { + const typings = path.join(__dirname, '../../../typings'); + assert(statSync(typings)); + }); + + it('should GET /', async () => { + return app.httpRequest() + .get('/') + .expect('hi, egg') + .expect(200); + }); +});