架构:eggjs + egg-jwt + egg-sequelize + egg-validate

数据库:mysql

实现功能:登录、验证码、权限菜单、鉴权、角色、用户、上传、下载、错误统一处理

api格式: restful + json

项目目录

app->controller->base_controller.js

const { Controller } = require('egg');
class BaseController extends Controller {// get user() {//     return this.ctx.session.user;// }success(msg = '', status = 200, data = {}) {const { ctx } = this;ctx.body = {msg,status,data};}error(msg = '', status = 500, data = {}) {const { ctx } = this;ctx.body = {msg,status,data};}// notFound(msg) {//     msg = msg || 'not found';//     this.ctx.throw(404, msg);// }
}
module.exports = BaseController;

app->controller->home.js

'use strict';const Controller = require('./base_controller');class HomeController extends Controller {async index() {this.success('hi, egg!', 200);}
}module.exports = HomeController;

app->controller->menu.js

'use strict';
const Controller = require('./base_controller');class menuController extends Controller {async index() {const { ctx } = this;ctx.validate({ isMenu: 'boolean' });const { isMenu } = ctx.request.body;this.success('查询成功', 200, {list: await ctx.service.menu.getMenu(isMenu)});}async update() {const { ctx } = this;ctx.validate({ name: 'string', pid: 'number' });ctx.validate({ id: 'string' }, ctx.params);const { name, pid } = ctx.request.body;await ctx.service.menu.editMenu(ctx.params.id, name, pid);if (ctx.status === 400) {this.error('编辑失败', 400);} else {this.success('编辑成功', 200);}}
}module.exports = menuController;

app->controller->role.js

'use strict';const Controller = require('./base_controller');class RoleController extends Controller {async index() {const { ctx } = this;this.success('查询成功', 200, {list: await ctx.service.role.getRole()});}async show() {const { ctx } = this;ctx.validate({ id: 'string' }, ctx.params);this.success('查询成功', 200, {list: await ctx.service.role.getRole(ctx.params.id)});}// 插入角色async create() {const { ctx } = this;ctx.validate({ name: 'string', pid: 'number'});const { name, pid } = ctx.request.body;await ctx.service.role.addRole(name, pid);this.success('新增成功', 200);}// 更新角色async update() {const { ctx } = this;ctx.validate({ name: 'string' });ctx.validate({ pid: 'string' }, ctx.params);const { name, pid } = ctx.request.body;await ctx.service.role.editRole(ctx.params.id, name, pid);if (ctx.status === 400) {this.error('修改失败', 400);} else {this.success('修改成功', 200);}}// 移除角色async remove() {const { ctx } = this;ctx.validate({ id: 'string' }, ctx.params);await ctx.service.role.removeRole(ctx.params.id);if (ctx.status === 400) {this.error('删除失败', 400);} else {this.success('删除成功', 200);}}
}
module.exports = RoleController;

app->controller->upload.js

const Controller = require('./base_controller');
const path = require('path');
const fs = require("fs");
const dayjs = require('dayjs');function mkdirsSync(dirname) {if (fs.existsSync(dirname)) {return true;} else {if (mkdirsSync(path.dirname(dirname))) {fs.mkdirSync(dirname);return true;}}
}module.exports = class extends Controller {// 上传单个文件async uploadOne() {const { ctx } = this;const file = ctx.request.files[0]console.log('-----------获取数据 start--------------');console.log(file);console.log('-----------获取数据 end--------------');// 基础的目录const uplaodBasePath = '../public/uploads';// 生成文件名const filename = `${Date.now()}${Number.parseInt(Math.random() * 1000,)}${path.extname(file.filename).toLocaleLowerCase()}`;// 生成文件夹const dirname = dayjs(Date.now()).format('YYYY/MM/DD');mkdirsSync(path.join(uplaodBasePath, dirname));// 生成写入路径const url = `/public/uploads/${dirname}/${filename}`try {//异步把文件流 写入fs.writeFileSync(`app/${url}`, fs.readFileSync(file.filepath));} catch (err) {console.error(err)}await ctx.service.upload.addUploadFile(filename, file.filename, url);this.success('上传成功', 200, {name: filename,org_name: file.filename,url})}// 上传多个文件async uploadMulti() {const { ctx } = this;const files = ctx.request.filesconsole.log('-----------获取数据 start--------------');console.log(files);console.log('-----------获取数据 end--------------');// 基础的目录const uplaodBasePath = '../public/uploads';let uplist = [];for (let i = 0; i < files.length; i++) {let element = files[i];// 生成文件名const filename = `${Date.now()}${Number.parseInt(Math.random() * 1000,)}${path.extname(element.filename).toLocaleLowerCase()}`;// 生成文件夹const dirname = dayjs(Date.now()).format('YYYY/MM/DD');mkdirsSync(path.join(uplaodBasePath, dirname));// 生成写入路径const url = `/public/uploads/${dirname}/${filename}`try {//异步把文件流 写入fs.writeFileSync(`app/${url}`, fs.readFileSync(element.filepath));} catch (err) {console.error(err)}uplist.push({name: filename,org_name: element.filename,url})await ctx.service.upload.addUploadFile(filename, element.filename, url);}this.success('上传成功', 200, {list: uplist})}// 通过id获取文件信息async show() {const { ctx } = this;ctx.validate({ id: 'string'}, ctx.params)const data = await ctx.service.upload.getUpload(ctx.params.id);this.success('查询成功', 200, data);}};

app->controller->user.js

'use strict';const Controller = require('./base_controller');
const svgCaptcha = require('svg-captcha');class UserController extends Controller {// 登录async login() {const { ctx, app } = this;const data = ctx.request.body;ctx.validate({ captcha: 'string', username: 'string', password: 'string'})// 判断验证码是否可用if(ctx.session.captcha === undefined){this.error('验证码失效,请重新获取', 400)return;}if(ctx.session.captcha!==data.captcha){this.error('请正确输入验证码', 400)return;}// 判断该用户是否存在并且密码是否正确const getUser = await ctx.service.user.validUser(data.username, data.password);if (getUser) {const token = app.jwt.sign({ username: data.username }, app.config.jwt.secret);this.success('登录成功', 200, {token,roleid: getUser.roleid});} else {this.error('用户名或密码错误', 400)}}// 获取验证码async captcha() {const { ctx } = this;const captcha = svgCaptcha.create({size: 4,              //图片验证码的字符数fontSize: 50,ignoreChars: 'Ooli',    //忽略的一些字符width: 100,height: 40,noise: 2,color: true,background: '#ffffff',});ctx.session.maxAge = 60 * 1000;ctx.session.captcha = captcha.text;       //text及data都是函数返回的string属性的对象  将图片验证码中的text传到session里边 ctx.response.type = 'image/svg+xml';     //返回的类型this.success('查询成功', 200, {svg: captcha.data,captcha: captcha.text});}// 获取所有用户async index() {const { ctx } = this;const data = await ctx.service.user.getUser();this.success('查询成功', 200, data);}// 通过id获取用户async show() {const { ctx } = this;ctx.validate({ id: 'string'}, ctx.params)const data = await ctx.service.user.getUser(ctx.params.id);this.success('查询成功', 200, data);}// 创建用户async create() {const { ctx } = this;ctx.validate({ username: 'string', roleid: 'number' });const { username, roleid } = ctx.request.body;await ctx.service.user.addUser(username, roleid);this.success('新增成功', 200);}// 修改密码async updatePwd() {const { ctx } = this;ctx.validate({ password: 'string' });ctx.validate({ password: 'string' }, ctx.params);const { password } = ctx.request.body;await ctx.service.user.editPwd(ctx.params.id, password);this.success('修改成功', 200);}// 明文密码得到加密密码async getMd5Data() {const { ctx } = this;ctx.validate({ data: 'string' }, ctx.params);this.success('查询成功', 200, {password: await ctx.service.user.getMd5Data(ctx.params.data)});}
}module.exports = UserController;

app->middleware->error_handler.js

'use strict';module.exports = () => {return async function errorHandler(ctx, next) {try {await next();} catch (err) {// 所有的异常都会在app上出发一个error事件,框架会记录一条错误日志ctx.app.emit('error', err, ctx);const status = err.status || 500;// 如果时生产环境的时候 500错误的详细错误内容不返回给客户端const error = status === 500 && ctx.app.config.env === 'prod' ? '网络错误' : err.message;ctx.body = {msg: error,status: status,data: {},};}};};

app->middleware->notfound_handler.js

module.exports = () => {return async function notFoundHandler(ctx, next) {await next();if (ctx.status === 404 && !ctx.body) {ctx.body = {msg: 'Not Found',status: ctx.status,data: {},};}};
};

app->mode->menu.js

'use strict';module.exports = app => {const { INTEGER, STRING } = app.Sequelize;const Menu = app.model.define('menu', {id: { type: INTEGER, primaryKey: true, autoIncrement: true },name: STRING(50),pid: INTEGER,}, {timestamps: false,});return Menu;
};

app->mode->role.js

'use strict';module.exports = app => {const { INTEGER, STRING } = app.Sequelize;const Role = app.model.define('role', {id: { type: INTEGER, primaryKey: true, autoIncrement: true },name: STRING(50),pid: INTEGER,}, {timestamps: false,});return Role;
};

app->mode->upload.js

'use strict';module.exports = app => {const { STRING, INTEGER } = app.Sequelize;const Upload = app.model.define('upload', {id: { type: INTEGER, primaryKey: true, autoIncrement: true },name: STRING(50),org_name: STRING(50),path: STRING(100)}, {timestamps: false,});return Upload;
};

app->mode->user.js

'use strict';module.exports = app => {const { STRING, INTEGER } = app.Sequelize;const User = app.model.define('user', {id: { type: INTEGER, primaryKey: true, autoIncrement: true },username: STRING(20),password: STRING(50),roleid: INTEGER,}, {timestamps: false,});User.associate = function() {app.model.User.belongsTo(app.model.Role, { foreignKey: 'roleid', targetKey: 'id', as: 'role' });};return User;
};

app->service->menu.js

'use strict';
const Service = require('egg').Service;
function toInt(str) {if (typeof str === 'number') return str;if (!str) return str;return parseInt(str, 10) || 0;
}
class MenuService extends Service {// 构建菜单权限树// 如果id为空,则构建所有的数据// id不为空,则构建以id为根结点的树buildTree(id, data, isMenu) {const res = [];if (id) {for (const item of data) {if (toInt(item.id) === toInt(id)) {item.children = getNode(id);res.push(item);}}} else {for (const item of data) {if (!item.pid) {item.children = getNode(item.id);res.push(item);}}}// 传入根结点id 递归查找所有子节点function getNode(id) {const node = [];for (const item of data) {if (toInt(item.pid) === toInt(id) && (isMenu === true ? item.children : true)) {item.children = getNode(item.id);node.push(item);}}if (node.length === 0) return;return node;}return res;}// 获取所有子节点集合getChildrenIds(treeData) {const res = [];function getIds(treeData, res) {for (const item of treeData) {res.push(item.id);if (item.children) { getIds(item.children, res); }}}getIds(treeData, res);return res;}// 查询角色并构建菜单树async getMenu(isMenu) {const { ctx } = this;const query = { limit: toInt(ctx.query.limit), offset: toInt(ctx.query.offset) };const data = await ctx.model.Menu.findAll({ query, raw: true });return this.buildTree(null, data, isMenu);}// 根据id查询角色async getMenuById(id) {const { ctx } = this;return await ctx.model.Menu.findByPk(toInt(id));}// 编辑菜单async editMenu(id, name, pid) {const { ctx } = this;const menu = await this.getMenuById(toInt(id));if (!menu) {ctx.status = 404;return;}await menu.update({ name: name || menu.name, pid: pid || menu.pid });ctx.status = 200;}
}module.exports = MenuService;

app->service->role.js

'use strict';
const Service = require('egg').Service;
function toInt(str) {if (typeof str === 'number') return str;if (!str) return str;return parseInt(str, 10) || 0;
}
class RoleService extends Service {// 构建树形结构数据// 如果id为空,则构建所有的数据// id不为空,则构建以id为根结点的树buildTree(id, data) {const res = [];if (id) {for (const item of data) {if (toInt(item.id) === toInt(id)) {item.children = getNode(id);res.push(item);}}} else {for (const item of data) {if (!item.pid) {item.children = getNode(item.id);res.push(item);}}}// 传入根结点id 递归查找所有子节点function getNode(id) {const node = [];for (const item of data) {if (toInt(item.pid) === toInt(id)) {item.children = getNode(item.id);node.push(item);}}if (node.length === 0) return;return node;}return res;}// 获取所有子节点集合getChildrenIds(treeData) {const res = [];function getIds(treeData, res) {for (const item of treeData) {res.push(item.id);if (item.children) { getIds(item.children, res); }}}getIds(treeData, res);return res;}// 查询角色并构建角色树async getRole(id) {const { ctx } = this;const query = { limit: toInt(ctx.query.limit), offset: toInt(ctx.query.offset) };const data = await ctx.model.Role.findAll({ query, raw: true });return this.buildTree(id, data);}// 根据id查询角色async getRoleById(id) {const { ctx } = this;return await ctx.model.Role.findByPk(toInt(id));}// 插入角色async addRole(name, pid) {const { ctx } = this;await ctx.model.Role.create({ name, pid });}// 修改角色async editRole(id, name, pid) {const { ctx } = this;const role = await this.getRoleById(toInt(id));if (!role) {ctx.status = 404;return;}await role.update({ name: name || role.name, pid: pid || role.pid });ctx.status = 200;}// 删除角色async removeRole(id) {const { ctx } = this;const roleTree = await this.getRole(toInt(id));const role = await this.getRoleById(toInt(id));if (!role) {ctx.status = 404;return;}const ids = this.getChildrenIds(roleTree);for (const i of ids) {const r = await this.getRoleById(toInt(i));r.destroy();}ctx.status = 200;}
}
module.exports = RoleService;

app->service->upload.js

'use strict';
const Service = require('egg').Service;function toInt(str) {if (typeof str === 'number') return str;if (!str) return str;return parseInt(str, 10) || 0;
}class UploadService extends Service {// 插入上传文件async addUploadFile(name, org_name, path) {const { ctx } = this;await ctx.model.Upload.create({ name, org_name, path });}// 获取上传文件async getUpload(id) {const { ctx } = this;return await ctx.model.Upload.findByPk(toInt(id));}
}module.exports = UploadService;

app->service->user.js

'use strict';const Service = require('egg').Service;
const crypto = require('crypto');
// 设置默认密码
const DEFAULT_PWD = '123456';
function toInt(str) {if (typeof str === 'number') return str;if (!str) return str;return parseInt(str, 10) || 0;
}class UserService extends Service {// 查询user表,验证密码和花名async validUser(username, password) {const data = await this.ctx.model.User.findAll();const pwd = this.getMd5Data(password);for (const item of data) {if (item.username === username && item.password === pwd) return item;}return false;}// 获取用户,不传id则查询所有async getUser(id) {const { ctx } = this;const query = { limit: toInt(ctx.query.limit), offset: toInt(ctx.query.offset) };if (id) {return await ctx.model.User.findByPk(toInt(id));}return await ctx.model.User.findAll({ query,attributes: [ 'username' ],include: [{model: ctx.model.Role,as: 'role',attributes: [ 'name', 'pid' ],}],});}// 新增人员async addUser(username, roleid) {const { ctx } = this;const password = this.getMd5Data(DEFAULT_PWD);await ctx.model.User.create({ username, password, roleid });}// 修改密码async editPwd(id, password) {const { ctx } = this;const user = await ctx.model.User.findByPk(id);const newPwd = this.getMd5Data(password);await user.update({ password: newPwd });}// 专门对数据进行md5加密的方法,输入明文返回密文getMd5Data(data) {return crypto.createHash('md5').update(data).digest('hex');}
}
module.exports = UserService;

app->router.js

'use strict';/*** @param {Egg.Application} app - egg application*/
module.exports = app => {const { router, controller, jwt } = app;const preUrl = '/api'router.get('/', controller.home.index);router.post(`${preUrl}/user/login`, controller.user.login);router.get(`${preUrl}/user/captcha`, controller.user.captcha);// 查询router.get(`${preUrl}/user`, controller.user.index);router.get(`${preUrl}/user/:id`, jwt, controller.user.show);// 生成经过md5加密后的密文router.get(`${preUrl}/user/getMd5/:data`, controller.user.getMd5Data);// 新增router.post(`${preUrl}/user`, jwt, controller.user.create);// 修改密码router.put(`${preUrl}/user/:id`, jwt, controller.user.updatePwd);// 获取角色router.get(`${preUrl}/role`, controller.role.index);router.get(`${preUrl}/role/:id`, controller.role.show);// 插入角色router.post(`${preUrl}/role`, jwt, controller.role.create);// 修改角色router.put(`${preUrl}/role/:id`, jwt, controller.role.update);// 删除角色router.delete(`${preUrl}/role/:id`, jwt, controller.role.remove);// 获取菜单router.get(`${preUrl}/menu`, controller.menu.index);// 编辑菜单router.put(`${preUrl}/menu/:id`, jwt, controller.menu.update);// 上传一个文件router.post(`${preUrl}/upload`, controller.upload.uploadOne);// 上传多个文件router.post(`${preUrl}/upload/multi`, controller.upload.uploadMulti);// 获取上传文件router.get(`${preUrl}/upload/:id`, controller.upload.show);
};

app->config->config.default.js

/* eslint valid-jsdoc: "off" */'use strict';/*** @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 securityconfig.keys = appInfo.name + '_1635393215721_8048';// add your middleware config hereconfig.middleware = ['errorHandler', 'notfoundHandler'];// add your user config hereconst userConfig = {// myAppName: 'egg',};config.jwt = {secret: '123456',};// 安全配置 (https://eggjs.org/zh-cn/core/security.html)config.security = {csrf: {enable: false,ignoreJSON: true,},// 允许访问接口的白名单domainWhiteList: [ 'http://localhost:8080' ],};// 跨域配置config.cors = {origin: '*',allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH',};config.sequelize = {dialect: 'mysql',host: '127.0.0.1',port: '3306',user: 'root',password: '123456',database: 'test',define: {underscored: true,freezeTableName: true,},};config.errorHandler = {enable: true, // 中间件开启配置match: '/api', // 设置请求中间件的请求路由,只对/api开头的有效// ignore: '', // 设置不经过这个中间件的请求路由};config.notfoundHandler = {enable: true, // 中间件开启配置match: '/api', // 设置请求中间件的请求路由,只对/api开头的有效// ignore: '', // 设置不经过这个中间件的请求路由};config.captcha = {enable: true,package: 'svg-captcha'}config.validate = {// convert: false,// validateRoot: false,};config.multipart = {fileSize: '50mb',mode: 'file',fileExtensions: ['.txt'],};return {...config,...userConfig,};
};

app->config->plugin.js

'use strict';/** @type Egg.EggPlugin */
module.exports = {// had enabled by egg// static: {//   enable: true,// }jwt: {enable: true,package: 'egg-jwt',},cors: {enable: true,package: 'egg-cors',},sequelize: {enable: true,package: 'egg-sequelize',},validate: {enable: true,package: 'egg-validate',}
};

eggjs 项目实践相关推荐

  1. SVM算法在项目实践中的应用!

    ↑↑↑关注后"星标"Datawhale 每日干货 & 每月组队学习,不错过 Datawhale干货 作者:苏丽敏,Datawhale优秀学习者,北理工计算机硕士 支持向量机 ...

  2. SAP WM Storage Location Reference在项目实践中的使用

    SAP WM Storage Location Reference在项目实践中的使用 笔者目前所在的一个项目是一个已经上了SAP系统,但是需要扩展到新工厂的项目.该项目在组织结构设计的时候,结合业务的 ...

  3. 学习Unix,可从事什么样的工作(1)《精通Unix下C语言与项目实践》读书笔记(3)...

    <精通Unix下C语言编程与项目实践>读书笔记(new) 文章试读 不拘一个遍程序系列:编程序不能一个脑袋钻到底,有时要学会变通,即所谓的曲线救国.一.二.三.四 职场规划:一些杂七杂八的 ...

  4. Node.js项目实践:构建可扩展的Web应用

    2019独角兽企业重金招聘Python工程师标准>>> Node.js项目实践:构建可扩展的Web应用 <Node.js项目实践:构建可扩展的Web应用>用专业的讲解方式 ...

  5. Unix下设计动态库的方法《精通Unix下C语言编程与项目实践》(一)

    精通Unix下C语言编程与项目实践 之动态库的生成 作者:朱云翔,胡平 3.3 动态库的生成 动态库的生成可分为三个步骤,设计库源码.编译位置无关码(PIC)型.o文件和链接动态库.链接动态库的命令包 ...

  6. Kotlin项目实践指南(上)

    关于作者 郭孝星,程序员,吉他手,主要从事Android平台基础架构方面的工作,欢迎交流技术方面的问题,可以去我的Github提issue或者发邮件至guoxiaoxingse@163.com与我交流 ...

  7. 《精通Unix下C语言与项目实践》读书笔记(16)

    <精通Unix下C语言编程与项目实践>读书笔记(new) 文章试读  不拘一个遍程序系列:编程序不能一个脑袋钻到底,有时要学会变通,即所谓的曲线救国.一.二.三.四 职场规划:一些杂七杂八 ...

  8. 在项目实践中用更优雅的方式处理数组问题

    在最近的项目中,遇到了比较多处理数组的场景,比如要对数组里面某个元素的某一个字段进行抽取归类,或者判断数组当中的某个元素是否符满足判断条件等. 网上关于使用ES5新的的API来代替for循环的文章已经 ...

  9. 项目实践精解:ASP.NET应用开发

    前 言 作者从事软件设计开发工作十多年,最近才萌发了写书的想法.因为作者希望推广一种最有效的学习捷径,这就是Project-Driven Training,也就是用项目实践来带动理论学习的方法.基于此 ...

  10. python编程入门指南 明日科技-python从入门到项目实践明日科技三剑客书籍视频...

    ┃ code(实例源码) ┃ ┃ 03.rar ┃ ┃ 04.rar ┃ ┃ 05.rar ┃ ┃ 06.rar ┃ ┃ 07.rar ┃ ┃ 08.rar ┃ ┃ 09.rar ┃ ┃ 10.rar ...

最新文章

  1. 2022-2028年中国氧化铟锡薄膜行业市场深度分析及前瞻研究报告
  2. 2018年计算机职称考试冲刺,2018年中级会计职称考试考前30天冲刺计划和学习方法...
  3. 国家微生物科学数据中心微生物组学数据汇交指南
  4. C语言试题三十三之比较两个字符串的长度,(不得调用c语言提供的求字符串长度的函数),函数返回较长的字符串。若两个字符串长度相同,则返回第一个字符串。
  5. 【华为云技术分享】气象模拟WRF容器化操作实践
  6. servlet文件实现弹出框
  7. 测试管理工具实践(小组作业)
  8. OKHTTP深入浅出(二)----基本用法
  9. c语言狗追兔子,[转载]狗追兔典型例题
  10. php数组匹配某一个元素的值
  11. 捕鱼达人(unity实现)
  12. Android中ButterKnife(黄油刀)的详细使用
  13. HTML5吃豆豆游戏开发实战(三)2d碰撞检测、重构
  14. 漏洞解决方案-Http host头攻击
  15. linux 游戏大作,20款值得一试 外媒盘点Mac平台游戏大作
  16. 【Pandas】数据结构Series 基本用法总结
  17. QT::QNetworkReply类readAll()读取不到数据
  18. MFC+OpenGL三维绘图(一)——简单绘图平台的搭建与实现图像的旋转、缩放
  19. 巧妙管理 不让IP地址冲突干扰局域网
  20. 基于matlab和proe的曲面建模,Proe曲面建模经典习题:创建电话

热门文章

  1. 08.C语言绘制系统界面
  2. 基于PC-DIMS脱机软件 的海克斯康三坐标机脱机编程软件手册。
  3. 一个码砖的码农,在CSDN写一年博客,出书了?
  4. 【读书笔记《Android游戏编程之从零开始》】19.游戏开发基础(游戏音乐与音效)
  5. 6 岁学编程,9 岁给电脑杂志社撰稿,19 岁收月薪 2 万 的 Offer | 程序人生 2020
  6. 在HFSS中用vbs脚本跑马灯?
  7. GJB-150湿热试验,国军标150试验机构
  8. 23种Python设计模式之建造者模式详解
  9. 王者荣耀2.0代码优化版C++
  10. [5-24]绿色精品软件每天更新[uc23整理]