<

1.初始化项目 #

$ npm i egg-init -g
$ egg-init cms-api --type=simple
$ cd cms-api
$ npm i
$ npm run dev

2.使用MYSQL #

2.1 数据库脚本 #

CREATE TABLE user (
 id int(11) PRIMARY KEY AUTO_INCREMENT,
 username varchar(255) NULL,
 password varchar(255) NULL,
 email varchar(255) NULL,
 phone varchar(255) NULL,
 gender tinyint(255) NULL,
 birthday datetime NULL,
 address varchar(255) NULL
);

CREATE TABLE role (
 id int(11) PRIMARY KEY AUTO_INCREMENT,
 name varchar(255) NULL
);

CREATE TABLE role_user (
role_id int(11) NOT NULL,
user_id int(11) NOT NULL,
PRIMARY KEY (user_id, role_id) 
);

CREATE TABLE resource (
id int(11) PRIMARY KEY AUTO_INCREMENT,
name varchar(255) NOT NULL
);

CREATE TABLE role_resource (
role_id int(11) NOT NULL,
resource_id int(255) NOT NULL,
PRIMARY KEY (role_id, resource_id) 
);

2.2 在egg使用mysql #

cnpm i egg-mysql -S

config/config.default.js

config.security = {
    csrf:false
} 
config.mysql = {
    // 单数据库信息配置
    client: {
      // host
      host: 'localhost',
      // 端口号
      port: '3306',
      // 用户名
      user: 'root',
      // 密码
      password: 'root',
      // 数据库名
      database: 'cms',
}

config/plugin.js

exports.mysql = {
    enable: true,
    package: 'egg-mysql'
};

3. 用户管理 #

3.1 app/router.js #

module.exports = app => {
  const { router, controller } = app;
  router.get('/',controller.home.index);
  router.resources('user', '/api/user', controller.user);
};

3.2 controller/user.js #

app/controller/user.js

const {Controller} = require('egg');
class UserController extends Controller {
  async index() {
    let {ctx,service}=this;
    let users=await service.user.select();
    ctx.body = users;
  }

  async create() {
    let {ctx,service}=this;
    let user=ctx.request.body;
    await service.user.create(user);
    ctx.body={
      code: 0,
      data:'success!'
    }
  }

  async update() {
    let {ctx,service}=this;
    let user=ctx.request.body;
    user.id=ctx.params.id;
    let savedUser=await service.user.update(user);
    ctx.body={
      code: 0,
      data:'success!'
    }
  }
  async destroy() {
    let {ctx,service}=this;
    let id=ctx.params.id;
    await service.user.delete(id);
    ctx.body={
      code: 0,
      data:'success!'
    }
  }
}

module.exports = UserController;

3.3 service/user.js #

app/service/user.js

const {Service}=require('egg');
class UserService extends Service{
    async select() {
        return await this.app.mysql.select('user');
    }
    async create(entity) {
        return await this.app.mysql.insert('user',entity);
    }
    async update(entity) {
        return await this.app.mysql.update('user',entity);
    }
    async delete(id) {
        return await this.app.mysql.delete('user',{id});
    }
}
module.exports=UserService;

4. 提取基类 #

4.1 app/controller/api.js #

app/controller/api.js

const BaseController = require('./base');
class ApiController extends BaseController {
    async index() {
    let {ctx,service}=this;
    let list=await service[this.entity].select();
    ctx.body = list;
  }

  async create() {
    let {ctx,service}=this;
    let user=ctx.request.body;
    await service[this.entity].create(user);
    ctx.body={
      code: 0,
      data:'success!'
    }
  }

  async update() {
    let {ctx,service}=this;
    let user=ctx.request.body;
    user.id=ctx.params.id;
    await service[this.entity].update(user);
    ctx.body={
      code: 0,
      data:'success!'
    }
  }
  async destroy() {
    let {ctx,service}=this;
    let id=ctx.params.id;
    await service[this.entity].delete(id);
    ctx.body={
      code: 0,
      data:'success!'
    }
  }
}
module.exports=ApiController;

4.2 app/controller/base.js #

app/controller/base.js

const Controller = require('egg').Controller;
class BaseController extends Controller {
    success(data) {
        let {ctx}=this;
        ctx.body={
            code: 0,
            data
        }
    }
    error(error) {
        let {ctx}=this;
        ctx.status=404;
        ctx.body={
            code: 1,
            error
        }
    }
}
module.exports=BaseController;

4.3 app/service/base.js #

app/service/base.js

const {Service}=require('egg');
class BaseService extends Service{
    async select() {
        return await this.app.mysql.select(this.entity);
    }
    async create(entity) {
        return await this.app.mysql.insert(this.entity,entity);
    }
    async update(entity) {
        return await this.app.mysql.update(this.entity,entity);
    }
    async delete(id) {
        return await this.app.mysql.delete(this.entity,{id});
    }
}
module.exports=BaseService;

4.4 controller/user.js #

app/controller/user.js

const ApiController = require('./api');
class UserController extends ApiController {
  constructor(...args) {
    super(...args);
    this.entity='user';
  }
}

module.exports = UserController;

4.5 service/user.js #

app/service/user.js

const BaseService = require('./base');
class UserService extends BaseService{
    constructor(...args) {
        super(...args);
        this.entity='user';
    }
}
module.exports=UserService;

5. 角色 #

5.1 app/router.js #

app/router.js

+ router.resources('role', '/api/role', controller.role);

5.2 controller/role.js #

app/controller/role.js

const ApiController = require('./api');
class RoleController extends ApiController {
  constructor(...args) {
    super(...args);
    this.entity='role';
  }
}

module.exports = RoleController;

5.3 app/service/role.js #

app/service/role.js

const BaseService = require('./base');
class RoleService extends BaseService{
    constructor(...args) {
        super(...args);
        this.entity='role';
    }
}
module.exports=RoleService;

6. 其它功能 #

6.1 app/controller/api.js #

const BaseController = require('./base');
class ApiController extends BaseController {
    async index() {
    const {ctx,service}=this;
    const {pageNum,pageSize,...where}=ctx.query;
    let result=await service[this.entity].list(isNaN(pageNum)?1:parseInt(pageNum),isNaN(pageSize)?this.config.PAGE_SIZE:parseInt(pageSize),where);
    this.success(result);
  }

  async create() {
    let {ctx,service}=this;
    let user=ctx.request.body;
    let result = await service[this.entity].create(user);
    result>0? this.success('添加成功'):this.error('添加失败');
  }

  async update() {
    let {ctx,service}=this;
    let user=ctx.request.body;
    user.id=ctx.params.id;
    let result = await service[this.entity].update(user);
    result>0? this.success('更新成功'):this.error('更新失败');
  }
  async destroy() {
    let {ctx,service}=this;
    let id=ctx.params.id;
    let ids=ctx.request.body;
    if (!ids) {ids=[id]}
    let result = await service[this.entity].delete(ids);
    result>0? this.success('删除成功'):this.error('删除失败');
  }
}
module.exports=ApiController;

6.2 app/controller/base.js #

app/controller/base.js

const Controller = require('egg').Controller;
class BaseController extends Controller {
    success(data) {
        let {ctx}=this;
        ctx.body={
            code: 0,
            data
        }
    }
    error(error) {
        let {ctx}=this;
        ctx.status=500;
        ctx.body={
            code: 1,
            error
        }
    }
}
module.exports=BaseController;

6.3 app/router.js #

app/router.js

module.exports = app => {
  const { router, controller } = app;
  router.get('/',controller.home.index);
  router.resources('user','/api/user',controller.user);
  router.resources('role','/api/role',controller.role);
  router.resources('resource', '/api/resource', controller.resource);
  router.resources('roleUser', '/api/roleUser', controller.roleUser);
  router.resources('roleResource', '/api/roleResource', controller.roleResource);
};

6.4 service/base.js #

app/service/base.js

const {Service}=require('egg');
class BaseService extends Service{
    async list(pageNum,pageSize,where) {
        const {app} = this;
        const list=await app.mysql.select(this.entity,{
            where,
            order: [['id','desc']],
            offset: (pageNum-1)*pageSize,
            limit :pageSize
        });
        const total=await app.mysql.count(this.entity,where);
    //返回当页的数据和总记录数
        return {list,total};
    }
    async create(entity) {
        const {app}=this;
        let result=await app.mysql.insert(this.entity,entity);
        const affectedRows=result.affectedRows;
        return affectedRows;
    }
    async update(entity) {
        const {app}=this;
        let result = await app.mysql.update(this.entity,entity);
        const affectedRows=result.affectedRows;
        return affectedRows;
    }
    async delete(ids) {
        const {app}=this;
        let result = await app.mysql.delete(this.entity,{id:ids});
        const affectedRows=result.affectedRows;
        return affectedRows;
    }
}
module.exports=BaseService;

6.5 controller/resource.js #

app/controller/resource.js

const ApiController = require('./api');
class ResourceController extends ApiController {
  constructor(...args) {
    super(...args);
    this.entity='resource';
  }
}
module.exports = ResourceController;

6.6 controller/roleResource.js #

app/controller/roleResource.js

const ApiController = require('./api');
class RoleResourceController extends ApiController {
  constructor(...args) {
    super(...args);
    this.entity='roleResource';
  }
}
module.exports = RoleResourceController;

6.7 controller/roleUser.js #

app/controller/roleUser.js

const ApiController = require('./api');
class RoleUserController extends ApiController {
  constructor(...args) {
    super(...args);
    this.entity='roleUser';
  }
}

module.exports = RoleUserController;

6.8 app/service/resource.js #

service/resource.js

const BaseService = require('./base');
class ResourceService extends BaseService{
    constructor(...args) {
        super(...args);
        this.entity='resource';
    }
}
module.exports=ResourceService;

6.9 service/roleResource.js #

app/service/roleResource.js

const BaseService = require('./base');
class roleResourceService extends BaseService{
    constructor(...args) {
        super(...args);
        this.entity='role_resource';
    }
}
module.exports=roleResourceService;

6.10 app/service/roleUser.js #

app/service/roleUser.js

const BaseService = require('./base');
class roleUserService extends BaseService{
    constructor(...args) {
        super(...args);
        this.entity='role_user';
    }
}
module.exports=roleUserService;

7. 权限管理 #

7.1 controller/role.js #

app/controller/role.js

const ApiController = require('./api');
class RoleController extends ApiController {
  constructor(...args) {
    super(...args);
    this.entity='role';
  }

  async getResource() {
    const { app, ctx, service } = this;
    const result = await service[this.entity].getResource();
    ctx.body = result;
  }
  async setResource() {
    const { app, ctx, service } = this;
    let body = ctx.request.body;//{roleId,resourceIds
    const result = await service[this.entity].setResource(body);
    ctx.body = result;
  }
  async getUser() {
    const { app, ctx, service } = this;
    const result = await service[this.entity].getUser();
    ctx.body = result;
  }
  async setUser() {
    const { app, ctx, service } = this;
    let body = ctx.request.body;//{roleId,userIds}
    const result = await service[this.entity].setUser(body);
    ctx.body = result;
  }
}

module.exports = RoleController;

7.2 service/role.js #

app/service/role.js

const BaseService = require('./base');
class RoleService extends BaseService{
    constructor(...args) {
        super(...args);
        this.entity='role';
    }
    async list(pageNum, pageSize, where) {
        const { app } = this;
        const list = await app.mysql.select(this.entity, {
          where,
          orders: [['id', 'desc']],
          offset: (pageNum - 1) * pageSize,
          limit: pageSize,
        });
        //list  resourceIds=代表这个角色所拥有的资源ID数组
        for (let i = 0; i < list.length; i++) {
          let rows = await app.mysql.select('role_resource', {
            where: { role_id: list[i].id }
          });//[{role_id:1,resource_id:2},{role_id:1,resource_id:3}]
          list[i].resourceIds = rows.map(item => item.resource_id);//[2,3]

          rows = await app.mysql.select('role_user', {
            where: { role_id: list[i].id }
          });//[{role_id:1,resource_id:2},{role_id:1,resource_id:3}]
          list[i].userIds = rows.map(item => item.user_id);//[2,3]
        }
        //count用来统计总条数
        const total = await app.mysql.count(this.entity, where);
        return { list, total };
    }
    async getResource() {
        const { app } = this;
        const list = await app.mysql.select('resource');
        let rootMenus = [];
        let map = {};
        list.forEach(item => {
          item.children = [];
          map[item.id] = item;//创建了一个Key-Value,key就是对象的ID,值就是对象本身
          if (item.parent_id == 0) {
              rootMenus.push(item);
          } else {
               map[item.parent_id].children.push(item)
          }
        });
        return rootMenus;
      }
    async setResource(values) {
      const {app} = this;
      let {roleId,resourceIds} = values;
      const conn = await app.mysql.beginTransaction(); // 初始化事务
      try {
         //1.先删除 所有的关联记录 DELETE FROM role_resource WHERE role_id =1
          await conn.query(`DELETE FROM role_resource WHERE role_id = ?`,[roleId]);
          //2.插入新的关联记录
          for(let i=0;i<resourceIds.length;i++){
            let resourceId = resourceIds[i];
            await conn.insert('role_resource',{role_id:roleId,resource_id:resourceId});
          }
        await conn.commit(); // 提交事务
      } catch (err) {
        // error, rollback
        await conn.rollback(); // 一定记得捕获异常后回滚事务!!
        throw err;
      }
      return '修改权限成功!'
  }
    async getUser() {
        const { app } = this;
        const list = await app.mysql.select('user');
        return list;
      }
    async setUser(values){
      const {app} = this;
      let {roleId,userIds} = values;
      const conn = await app.mysql.beginTransaction(); // 初始化事务
      try {
         //1.先删除 所有的关联记录 DELETE FROM role_user WHERE role_id=1
          await conn.query(`DELETE FROM role_user WHERE role_id=?`,[roleId]);
          //2.插入新的关联记录
          for(let i=0;i<userIds.length;i++){
            let userId = userIds[i];
            await conn.insert('role_user',{role_id:roleId,user_id:userId});
          }
        await conn.commit(); // 提交事务
      } catch (err) {
        // error, rollback
        await conn.rollback(); // 一定记得捕获异常后回滚事务!!
        throw err;
      }

      return '给角色分配用户成功!'
    }

}
module.exports=RoleService;

7.3 app/router.js #

app/router.js

router.post('/role/setUser', controller.role.setUser);
router.get('/role/getUser', controller.role.getUser);
router.post('/role/setResource', controller.role.setResource);
router.get('/role/getResource', controller.role.getResource);

8. 验证码 #

9.1 config/config.default.js #

config/config.default.js

config.security = {
    csrf: false,
    domainWhiteList: [ 'http://127.0.0.1:8000' ]
} 

9.2 config/plugin.js #

config/plugin.js

exports.cors = {
    enable: true,
    package: 'egg-cors',
};

10. 注册登录 #

10.1 app/controller/user.js #

app/controller/user.js

const ApiController = require('./api');
const {sign} = require('jsonwebtoken');
class UserController extends ApiController {
  constructor(...args) {
    super(...args);
    this.entity = 'user';
  }

   async signin() {
    let {ctx,app} = this;
    let body = ctx.request.body;
    const result = await app.mysql.select('user', {where: {username: body.username,password: body.password},
      limit: 1,
      offset: 0
    });
    if (result && result.length > 0) {
      let user = JSON.parse(JSON.stringify(result[0]));
      let list = await app.mysql.query(`SELECT resource.* FROM role_user,role_resource,resource where role_user.role_id = role_resource.role_id AND role_resource.resource_id = resource.id AND role_user.user_id = ? ORDER BY resource.id ASC`,[user.id]);
      let resources = [];
      let map = {};
      list.forEach(item => {
          item.children = [];
          map[item.id] = item;
          if (item.parent_id == 0) {
            resources.push(item);
          } else {
            map[item.parent_id].children.push(item);
          }
      });
      user.resources=resources;
      this.success(sign(user, this.config.jwtSecret));
    } else {
      this.error('登录失败');
    }
  }
  async signup() {
    let {ctx,app} = this;
    const { agreement, prefix, phone, address, repassword, captcha, ...user } = ctx.request.body;
    if (!agreement) {
      return this.error('请同意协议再注册!');
    }
    if (user.password !== repassword) {
      return this.error('密码和确认密码不一致!');
    }
    if (!captcha || !ctx.session.captcha || captcha.toLowerCase() !== ctx.session.captcha.toLowerCase()) {
      //return this.error('验证码不正确!');
    }
    user.phone = prefix + '-' + phone;
    user.address = address.join('-');
    const result = await app.mysql.insert('user', user);
    if (result.affectedRows > 0) {
      this.success({
        id: result.insertId,
      });
    } else {
      this.error('注册失败');
    }
  }
}

module.exports = UserController;

10.2 config.default.js #

config/config.default.js

config.security = {
    csrf: false,
    domainWhiteList: [ 'http://localhost:8000' ]
} 
config.jwtSecret="zfpx";
config.cors = {
    credentials: true
}

10.3 app/middleware/auth.js #

app/middleware/auth.js

let {verify}=require('jsonwebtoken');
function verifyToken(token,jwtSecret) {
    return new Promise(function (resolve,reject) {
        verify(token,jwtSecret,async (err,data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}
module.exports=(options,app) => {
    return async function (ctx,next) {
        const token=ctx.get('authorization');
        if (token) {
            try {
                let user = await verifyToken(token,app.config.jwtSecret);
                ctx.session.user=user;
                await next();
            } catch (err) {
                ctx.status=401;
                ctx.body={
                    code: 1,
                    error:'token验证失败'
                }
            }
        } else {
            ctx.status=401;
                ctx.body={
                    code: 1,
                    error:'请提供token'
                }
        }
    }
}

10.4 app/router.js #

app/router.js

  const auth=app.middleware.auth({},app);
  router.post('/api/signin',controller.user.signin);
  router.post('/api/signup',controller.user.signup);
  router.get('/captcha',controller.index.captcha);
  router.get('/',controller.home.index);
  router.post('/role/setUser', controller.role.setUser);
  router.get('/role/getUser', controller.role.getUser);
  router.post('/role/setResource', controller.role.setResource);
  router.get('/role/getResource', controller.role.getResource);
  router.resources('user','/api/user',auth,controller.user);
  router.resources('role','/api/role',auth,controller.role);
  router.resources('resource', '/api/resource', auth,controller.resource);
  router.resources('roleUser', '/api/roleUser', auth,controller.roleUser);
  router.resources('roleResource', '/api/roleResource', auth,controller.roleResource);

11.使用 VSCode 进行调试 #

// .vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Launch Egg",
      "type": "node",
      "request": "launch",
      "cwd": "${workspaceRoot}",
      "runtimeExecutable": "npm",
      "windows": { "runtimeExecutable": "npm.cmd" },
      "runtimeArgs": [ "run", "debug" ],
      "console": "integratedTerminal",
      "protocol": "auto",
      "restart": true,
      "port": 9229,
      "autoAttachChildProcesses": true
    }
  ]
}

12. 参考 #