From 3a322a17a8c83cc7da409d35c2f37b14f90bcac2 Mon Sep 17 00:00:00 2001 From: myshijian <624071587@qq.com> Date: Fri, 7 May 2021 00:07:25 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/app.js | 73 +++++++++ app/bin/server.js | 14 ++ app/config/index.js | 4 + app/package.json | 16 +- app/src/controller/playerController.js | 75 +++++++++ app/src/middleware/auth.js | 22 +++ app/src/model/player.js | 17 ++ app/src/model/user.js | 15 ++ app/src/router/www.js | 22 +++ app/src/service/playerService.js | 46 ++++++ app/src/utils/db.js | 217 +++++++++++++++++++++++++ app/src/utils/index.js | 59 +++++++ app/src/utils/redisStore.js | 28 ++++ app/src/utils/writeJson.js | 26 +++ 14 files changed, 630 insertions(+), 4 deletions(-) create mode 100644 app/app.js create mode 100644 app/bin/server.js create mode 100644 app/config/index.js create mode 100644 app/src/controller/playerController.js create mode 100644 app/src/middleware/auth.js create mode 100644 app/src/model/player.js create mode 100644 app/src/model/user.js create mode 100644 app/src/router/www.js create mode 100644 app/src/service/playerService.js create mode 100644 app/src/utils/db.js create mode 100644 app/src/utils/index.js create mode 100644 app/src/utils/redisStore.js create mode 100644 app/src/utils/writeJson.js diff --git a/app/app.js b/app/app.js new file mode 100644 index 0000000..39c9529 --- /dev/null +++ b/app/app.js @@ -0,0 +1,73 @@ +const Koa = require('koa'); +const app = new Koa(); +const path = require('path'); +//压缩 +let compress = require('koa-compress'); + +app.use(compress({ + filter: function (content_type) { + return /text/i.test(content_type) + }, + threshold: 2048, + flush: require('zlib').Z_SYNC_FLUSH +})); + + +const session = require('koa-session2'); +const Store = require('./src/utils/redisStore'); +const config = { + redis: { + port: 6379, + host: 'localhost', + family: 4, + // password: '123456', + db: 0 + }, +}; +app.use(session({ + store: new Store(config.redis), + maxAge: 12 * 60 * 60 * 1000,//有效时间 +})); + +//静态资源 +const serve = require('koa-static'); +let Static = path.join(__dirname, 'static'); +app.use(serve(Static)); + + +//图片上传 +let koaBody = require('koa-body'); //解析上传文件的插件 +app.use(koaBody({ + multipart: true, + formidable: { + maxFileSize: 50 * 1024 * 1024,// 设置上传文件大小最大限制,默认50M + multipart: true, // 是否支持多文件上传 encoding:'utf-8', + uploadDir: path.join(__dirname, './static/images'),//文件上传的文件夹 + keepExtensions: true, // 保持文件的后缀 + }, + stict: false,//严格模式,启用后不会解析 GET, HEAD, DELETE 请求 默认值 true +})); + +//插件 +const utils = require('./src/utils'); +//配置文件,挂在挂载在ctx +app.context.$config = require('./config'); +app.context.$utils = utils; +app.context.$json = require('./src/utils/writeJson') +//挂载服务 +utils.setMiddlewareName(app, path.join(__dirname, './src/service')) + +//错误事件监听 +app.on('error', (err, ctx) => { + let path = ctx.path; + console.log(path, err); + ctx.response.type = 'html'; + // ctx.response.status=500; + ctx.response.body = { + err: 4, + msg: err.message + } +}); + + +module.exports = app; diff --git a/app/bin/server.js b/app/bin/server.js new file mode 100644 index 0000000..b728548 --- /dev/null +++ b/app/bin/server.js @@ -0,0 +1,14 @@ +const app = require('../app'); // 引入koa + +/* +* 登录中间件 +* */ +app.use(require('./../src/middleware/auth')) + + +const www = require('../src/router/www') +app.use(www.routes()).use(www.allowedMethods()); + +app.listen(3000, () => { + console.log("Server is listening on port 3000"); +}); diff --git a/app/config/index.js b/app/config/index.js new file mode 100644 index 0000000..d432601 --- /dev/null +++ b/app/config/index.js @@ -0,0 +1,4 @@ +/* +* 配置文件 +* */ +module.exports = {} diff --git a/app/package.json b/app/package.json index 6b480b3..4a314af 100755 --- a/app/package.json +++ b/app/package.json @@ -3,15 +3,23 @@ "name": "node-examination", "version": "0.0.1", "description": "Building a RESTful CRUD API with Node.js, Express/Koa and MongoDB.", - "main": "server.js", + "main": "./bin/server.js", "scripts": { - "start": "NODE_ENV=development node server.js", - "start:prod": "NODE_ENV=production node server.js", + "start": "node ./bin/server.js", "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": { "express": "^4.17.1", - "mongoose": "^5.9.2" + "ioredis": "^4.27.2", + "koa": "^2.13.1", + "koa-body": "^4.2.0", + "koa-compress": "^5.0.1", + "koa-router": "^10.0.0", + "koa-session2": "^2.2.10", + "koa-static": "^5.0.0", + "mongoose": "^5.12.7", + "validate": "^5.1.0", + "zlib": "^1.0.5" }, "devDependencies": { "chai": "^4.2.0" diff --git a/app/src/controller/playerController.js b/app/src/controller/playerController.js new file mode 100644 index 0000000..c5517ae --- /dev/null +++ b/app/src/controller/playerController.js @@ -0,0 +1,75 @@ +const validateSchema = { + createDate: { + type: Date, + required: String, + message: { + required: '请选择时间' + } + },//创建时间 + name: { + type: String, + required: true + },//姓名 + age: { + type: Number, + required: true + },//年龄 + sex: { // 性别 1男 2女 + type: Number, + enum: [1, 2] + }, + description: { + type: String + },//描述 +} + +module.exports = { + + async addPlayer(ctx) { + let body = ctx.request.body; + let obj = ctx.$utils.validate(validateSchema, body) + if (obj.err) { + ctx.$json(ctx, 'error', obj) + return false + } + await ctx.$playerService.create(body) + ctx.$json(ctx, 'success', '创建成功') + }, + + async deletePlayer(ctx) { + let id = ctx.params.id; + let arr = await ctx.$playerService.find({id}) + if (!arr.length) { + ctx.$json(ctx, 'error', 'id错误') + return false + } + await ctx.$playerService.deleteOne({id}); + ctx.$json(ctx, 'success', '删除成功') + }, + + async getPlayerById(ctx) { + let id = ctx.params.id; + let arr = await ctx.$playerService.find({id}) + ctx.$json(ctx, 'success', arr[0]) + }, + + async updatePlayer(ctx) { + let id = ctx.params.id; + let body = ctx.request.body; + let obj = ctx.$utils.validate(validateSchema, body) + if (obj.err) { + ctx.$json(ctx, 'error', obj) + return false + } + await ctx.$playerService.updateOne({id}, body) + ctx.$json(ctx, 'success') + }, + + async list(ctx) { + let query = ctx.request.query; + let data = await ctx.$playerService.pageQuery(query) + ctx.$json(ctx, 'success', data) + }, + + +} diff --git a/app/src/middleware/auth.js b/app/src/middleware/auth.js new file mode 100644 index 0000000..f84aa56 --- /dev/null +++ b/app/src/middleware/auth.js @@ -0,0 +1,22 @@ +module.exports = async (ctx, next) => { + let url = ctx.url; + let params = ctx.params; + let admin_token = ctx.session.admin_token || ''; + for (let p in params) { + let str = '/' + params[p]; + url = url.replace(str, ''); + } + const noAuth = ['/login', '/logout', '/', '/player']; + if (noAuth.includes(url)) { + return await next(); + } + + //未登录 + if (!admin_token) { + ctx.$json(ctx, 'noLogin') + return false; + } + + + await next(); +} diff --git a/app/src/model/player.js b/app/src/model/player.js new file mode 100644 index 0000000..96ec138 --- /dev/null +++ b/app/src/model/player.js @@ -0,0 +1,17 @@ +const mongoose = require('mongoose') + +module.exports = new mongoose.Schema({ + date: Date,//记录时间 + createDate: Date,//创建时间 + name: String,//姓名 + age: Number,//年龄 + sex: { // 性别 1男 2女 + type: Number, + enum: [1, 2] + }, + description: String,//描述 + status: { //审核状态 0未审核 1审核 + type: Number, + enum: [0, 1] + } +}) diff --git a/app/src/model/user.js b/app/src/model/user.js new file mode 100644 index 0000000..3a87bf1 --- /dev/null +++ b/app/src/model/user.js @@ -0,0 +1,15 @@ +const mongoose = require('mongoose') +/* +* 用户表 +* */ +module.exports = new mongoose.Schema({ + createDate: Date,//创建时间 + account: String,//账号 + password: String,//密码 + token: String,//登录token + description: String,//描述 + status: { //审核状态 0未审核 1审核 + type: Number, + enum: [0, 1] + } +}) diff --git a/app/src/router/www.js b/app/src/router/www.js new file mode 100644 index 0000000..19f2ec3 --- /dev/null +++ b/app/src/router/www.js @@ -0,0 +1,22 @@ +const Router = require('koa-router'); // 引入koa-router +let router = new Router() +/* +* 玩家操作 +* */ +const player = require('../controller/playerController') +//添加 +router.post('/player', player.addPlayer); +//删除 +router.delete('/player/:id', player.deletePlayer); +//根据id获取单个信息 +router.get('/player/:id', player.getPlayerById); +//更新 +router.put('/player/:id', player.updatePlayer); +//列表 +router.get('/player', player.list); + +router.get('/',async (ctx)=>{ + ctx.$json(ctx,'success','首页') +}) + +module.exports = router diff --git a/app/src/service/playerService.js b/app/src/service/playerService.js new file mode 100644 index 0000000..00724cf --- /dev/null +++ b/app/src/service/playerService.js @@ -0,0 +1,46 @@ +const db = require('./../utils/db') +const model = 'player' +module.exports = { + async create(data) { + data.date = Date.now(); + data.status = 0; + return await db.create(model, data) + }, + + async updateOne(where, data) { + data.status = 0; + return await db.updateOne(model, where, data) + }, + + async deleteOne(where) { + return await db.deleteOne(model, where) + }, + + async find(where) { + return await db.find(model, where) + }, + + async pageQuery({page = 1, pageSize = 10, regStr = '', sort = '', endTime = '', startTime = ''}) { + let where = {}; + if (startTime && endTime) { + where.createDate = {$lte: parseInt(endTime), $gte: parseInt(startTime)} + } + if (regStr) { + let $regex = new RegExp(regStr, 'gi'); + where.$or = [ + {name: {$regex}}, + {description: {$regex}}, + ]; + } + if (sort) { + try { + sort = JSON.parse(sort) + } catch (e) { + sort = {createDate: -1} + } + } + return await db.pageQuery({model, pageSize, page, where}) + + } + +} diff --git a/app/src/utils/db.js b/app/src/utils/db.js new file mode 100644 index 0000000..877899f --- /dev/null +++ b/app/src/utils/db.js @@ -0,0 +1,217 @@ +const mongoose = require('mongoose'); +const path = require('path'); + +const uri = 'mongodb://localhost/test'; +const connection = mongoose.createConnection(uri, { + useNewUrlParser: true, + useUnifiedTopology: true, + poolSize: 4 +}); + +//返回mongo temp +const temp = (model) => { + let filepath = path.join(__dirname, './../model/' + model); + let schema = require(filepath); + //集合名称与model同名 + return connection.model(model, schema, model); +}; + + +//查询数据格式处理 +const queryDeal = (where) => { + var obj = {} + for (let w in where) { + let value = '' + try { + value = JSON.parse(where[w]) + } catch (e) { + value = where[w] + } + //如果为数组 + if (Array.isArray(value) && value.length) { + var arr = []; + for (let i in value) { + arr.push(value[i]) + } + if (w === '$or' && obj.$or) { + obj.$or = obj.$or.concat(arr) + } else { + obj.$or = arr + } + } else { + //非数组 + obj[w] = w === '_id' ? mongoose.Types.ObjectId(value) : value + } + } + return obj +} + +module.exports = { + //新增文档 create + create(model, obj) { + return new Promise((resolve, reject) => { + temp(model).create(obj, function (err, dos) { + if (err) { + reject('数据插入失败') + } else { + resolve(dos) + } + }) + }) + }, + //新增文档 insertMany + insertMany(model, arr) { + return new Promise((resolve, reject) => { + temp(model).insertMany(arr, function (err, dos) { + if (err) { + reject('数据插入失败') + } else { + resolve(dos) + } + }) + }) + }, + + //查找文档 + find(model, where = {}, sort = {date: -1}, select = {_id: 0, __v: 0}) { + return new Promise((resolve, reject) => { + where = queryDeal(where) + let cursor = temp(model).find(where).sort(sort).select(select); + cursor.exec(function (err, arr) { + if (err) { + reject('查询失败') + } else { + resolve(arr) + } + }) + }) + }, + + //分页查询 + findPage(model, where = {}, page = 1, sort = {date: -1}, select = {_id: 0, __v: 0}, pageNum = 10) { + return new Promise((resolve, reject) => { + where = queryDeal(where) + let cursor = temp(model).find(where).sort(sort).select(select).skip((page - 1) * pageNum).limit(pageNum); + cursor.exec(function (err, arr) { + if (err) { + reject('查询失败') + } else { + resolve(arr) + } + }) + }) + }, + + //查询集合数量 + count(model, where = {}) { + return new Promise((resolve, reject) => { + where = queryDeal(where) + temp(model).find(where).countDocuments(function (err, count) { + if (err) { + reject('查询失败') + } else { + resolve(count) + } + }) + }) + }, + + //分页查询 + async pageQuery({model, where = {}, page = 1, sort = {date: -1}, select = {_id: 0, __v: 0}, pageSize = 10}) { + let arr = await this.findPage(model, where, page, sort, select, pageSize) + let total = await this.count(model, where); + let pages = Math.ceil(total / pageSize); + return { + curPage: page, + pages, + pageSize, + total, + arr + } + }, + + //更新多条数据 + update(model, where, setData) { + return new Promise((resolve, reject) => { + /* + * 返回 raw 对象 { ok: 1, nModified: 0, n: 1 } + * ok状态1:成功,其他不成功,nModified 修改的条数,n找到的条数 + * */ + temp(model).updateMany(where, setData, function (err, raw) { + if (err) { + reject('更新失败') + } else { + if (raw.ok === 1 && raw.nModified >= 1 && raw.n >= 1) { + resolve('更新成功') + } else if (raw.nModified === 0 && raw.n >= 1 && raw.ok === 1) { + reject('内容未做修改,更新失败') + } else { + reject('更新失败') + } + } + }) + }) + }, + + //更新一条数据 + updateOne(model, where, setData) { + return new Promise((resolve, reject) => { + /* + * 返回 raw 对象 { ok: 1, nModified: 0, n: 1 } + * ok状态1:成功,其他不成功,nModified 修改的条数,n找到的条数 + * */ + temp(model).updateOne(where, setData, function (err, raw) { + if (err) { + reject('更新失败') + } else { + if (raw.ok === 1 && raw.nModified >= 1 && raw.n >= 1) { + resolve('更新成功') + } else if (raw.nModified === 0 && raw.n >= 1 && raw.ok === 1) { + reject('内容未做修改,更新失败') + } else { + reject('更新失败') + } + } + }) + }) + }, + + //删除文档 + deleteMany(model, where) { + return new Promise((resolve, reject) => { + temp(model).deleteMany(where, function (err, doc) { + if (err) { + reject('删除失败') + } else { + resolve(doc) + } + }) + }) + }, + + deleteOne(model, where) { + return new Promise((resolve, reject) => { + temp(model).deleteOne(where, function (err, doc) { + if (err) { + reject('删除失败') + } else { + resolve(doc) + } + }) + }) + }, + + //聚合管道查询 + aggregate(model, arr) { + return new Promise((resolve, reject) => { + temp(model).aggregate(arr).exec(where, function (err, doc) { + if (err) { + reject(err) + } else { + resolve(doc) + } + }) + }) + } +} + diff --git a/app/src/utils/index.js b/app/src/utils/index.js new file mode 100644 index 0000000..9963540 --- /dev/null +++ b/app/src/utils/index.js @@ -0,0 +1,59 @@ +const validateSchema = require('validate'); +const fs = require('fs') +const path = require('path') + +module.exports = { + /* + * 参数验证 + * */ + validate(schema, data) { + let validate = new validateSchema(schema); + let errors = validate.validate(data); + if (!errors.length) { + return {err: 0, msg: 'success'}; + } + errors = errors.map(val => { + return { + name: val.path, + message: val.message, + }; + }); + + return {message: errors[0].message, errors}; + }, + + //遍历目录下的所有文件 + getAllFile(dir) { + let res = [] + + function traverse(dir) { + fs.readdirSync(dir).forEach((file) => { + const pathname = path.join(dir, file) + if (fs.statSync(pathname).isDirectory()) { + traverse(pathname) + } else { + res.push(pathname) + } + }) + } + + traverse(dir) + return res; + }, + + //中间件命名设置 + setMiddlewareName(app, dir) { + function traverse(dir) { + fs.readdirSync(dir).forEach((file) => { + let pathname = path.join(dir, file) + if (fs.statSync(pathname).isDirectory()) { + traverse(pathname) + } else { + let key = file.replace('.js',''); + app.context['$'+key] = require(pathname) + } + }) + } + traverse(dir) + } +} diff --git a/app/src/utils/redisStore.js b/app/src/utils/redisStore.js new file mode 100644 index 0000000..31eff30 --- /dev/null +++ b/app/src/utils/redisStore.js @@ -0,0 +1,28 @@ +const Redis = require("ioredis"); +const { Store } = require("koa-session2"); + +class RedisStore extends Store { + constructor(redisConfig) { + super(); + this.redis = new Redis(redisConfig); + } + + async get(sid, ctx) { + let data = await this.redis.get(`SESSION:${sid}`); + return JSON.parse(data); + } + + async set(session, { sid = this.getID(24), maxAge = 1000000 } = {}, ctx) { + try { + // Use redis set EX to automatically drop expired sessions + await this.redis.set(`SESSION:${sid}`, JSON.stringify(session), 'EX', maxAge / 1000); + } catch (e) {} + return sid; + } + + async destroy(sid, ctx) { + return await this.redis.del(`SESSION:${sid}`); + } +} + +module.exports = RedisStore; diff --git a/app/src/utils/writeJson.js b/app/src/utils/writeJson.js new file mode 100644 index 0000000..bd37d44 --- /dev/null +++ b/app/src/utils/writeJson.js @@ -0,0 +1,26 @@ +/* +* 返回数据格式错误 +* */ +module.exports = (ctx, type, data ) => { + ctx.type = 'json' + let obj = { + noLogin: { + msg: '未登录', + err: 2 + }, + invalid: { + msg: '登录失效', + err: 3 + }, + error: { + msg: '一般错误', + err: 1 + }, + success: { + msg: '成功', + err: 0 + } + } + + ctx.body = {...obj[type], data} +}