2018-09-29

用户管理


1.1 app/controller/base.js [#](#t11.1 app/controller/base.js)

app/controller/base.js

const {Controller} = require('egg');
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;

1.2 app/controller/restful.js [#](#t21.2 app/controller/restful.js)

app/controller/restful.js

const Service = require('egg').Service;
// ctx,app,service,config,logger
class ApiService extends Service {
    constructor(...args) {
        super(...args);
    }
    async select() {
        return await this.app.mysql.select(this.model);
    }
    async get(id) {
        return await this.app.mysql.get(this.model,{id});
    }
    async insert(entity) {
        return await this.app.mysql.insert(this.model,entity);
    }
    async update(entity) {
        return await this.app.mysql.update(this.model,entity);
    }
    async delete(id) {
        return await this.app.mysql.delete(this.model,{id});
    }
}

module.exports = ApiService;

1.3 app/controller/users.js [#](#t31.3 app/controller/users.js)

app/controller/users.js

'use strict';
const ApiController = require('./restful');
class UserController extends ApiController {
    constructor(...args) {
        super(...args);
        this.model='users';
    }
}
module.exports = UserController;

1.4 app/router.js [#](#t41.4 app/router.js)

app/router.js

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

1.5 app/service/restful.js [#](#t51.5 app/service/restful.js)

app/service/restful.js

const Service = require('egg').Service;
// ctx,app,service,config,logger
class ApiService extends Service {
    constructor(...args) {
        super(...args);
    }
    async select() {
        return await this.app.mysql.select(this.model);
    }
    async get(id) {
        return await this.app.mysql.get(this.model,{id});
    }
    async insert(entity) {
        return await this.app.mysql.insert(this.model,entity);
    }
    async update(entity) {
        return await this.app.mysql.update(this.model,entity);
    }
    async delete(id) {
        return await this.app.mysql.delete(this.model,{id});
    }
}

module.exports = ApiService;

1.6 app/service/users.js [#](#t61.6 app/service/users.js)

app/service/users.js

const ApiService=require('./restful');
// ctx,app,service,config,logger
class UsersService extends ApiService {
    constructor(...args) {
        super(...args);
        this.model='users';
    }
}

module.exports = UsersService;

1.7 config/config.default.js [#](#t71.7 config/config.default.js)

config/config.default.js

'use strict';

module.exports = appInfo => {
  const config = exports = {};

  // use for cookie sign key, should change to your own and keep security
  config.keys = appInfo.name + '_1532576284179_9329';

  // add your config here
  config.middleware=[];
  config.security = {
    csrf: false
  }
  exports.mysql = {
    // 单数据库信息配置
    client: {
      // host
      host: 'localhost',
      // 端口号
      port: '3306',
      // 用户名
      user: 'root',
      // 密码
      password: '',
      // 数据库名
      database: 'cms',
    },
    // 是否加载到 app 上,默认开启
    app: true,
    // 是否加载到 agent 上,默认关闭
    agent: false,
  };
  config.logger={
      appLogName: `${appInfo.name}-web.log`,
      coreLogName: 'egg-web.log',
      agentLogName: 'egg-agent.log',
      errorLogName: 'common-error.log',
      level: 'DEBUG',
  }
  return config;
};

1.8 config/plugin.js [#](#t81.8 config/plugin.js)

config/plugin.js

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

1.9 users.test.js [#](#t91.9 users.test.js)

test/app/controller/users.test.js

const { app, assert } = require('egg-mock/bootstrap');
const querystring=require('querystring');
const _=require('lodash');
const uuid=require('uuid');
describe('test/app/controller/users.test.js', () => {
  let initialSize;
  let insertId;
  let username=uuid.v4();
  let newUser={
    username: username,
    password: username,
    email: `${username}@qq.com`,
    phone: username,
    gender: 1,
    birthday:'2018-09-09',
    address:'回龙观'
  }
  it('should GET /users',async () => {
    let response=await app.httpRequest().get('/users').expect(200);
    const users=response.body;
    initialSize=users.length;
    assert(Array.isArray(users));
  });

  it('should POST /users',async () => {
    let response=await app.httpRequest()
      .post('/users')
      .set('Content-Type','application/x-www-form-urlencoded')
      .set('Accept','application/json')
      .send(querystring.stringify(newUser))
      .expect(200);
    const user=response.body.data;
    newUser.id=user.id;
    insertId=user.id;
    assert(!isNaN(newUser.id));
  })

  it('should GET /users/:id',async () => {
    let response=await app.httpRequest()
      .get(`/users/${insertId}`)
      .set('Accept','application/json')
      .expect(200);

      let user=response.body.data;
      assert(_.isEqual(user.id,newUser.id));
      assert(_.isEqual(user.username,newUser.username));
      assert(_.isEqual(user.password,newUser.password));
  })

  it('should PUT /users/:id',async () => {
    newUser.username='更新后的username';
    let response=await app.httpRequest()
      .put(`/users/${insertId}`)
      .set('Content-Type','application/x-www-form-urlencoded')
      .send(querystring.stringify(newUser))
      .set('Accept','application/json')
      .expect(200);

      let user=response.body.data;
      assert(_.isEqual(user.username,newUser.username));
  })

  it('should DELETE /users/:id',async () => {
    let response=await app.httpRequest()
      .delete(`/users/${insertId}`)
      .set('Accept','application/json')
      .expect(200);

      let data=response.body.data;
      assert(_.isEqual(data,'删除成功'));
  })
});

1.10 test/app/service/users.test.js [#](#t101.10 test/app/service/users.test.js)

test/app/service/users.test.js

const _=require('lodash');
const uuid=require('uuid');
const { app,assert } = require('egg-mock/bootstrap');
describe('test/app/service/users.test.js',() => {
    let username=uuid.v4();
    let insertId;
    let newUser={
        username: username,
        password: username,
        email: `${username}@qq.com`,
        phone: username,
        gender: 1,
        birthday:'2018-09-09',
        address:'回龙观'
      }
    it('select', async () => {
        const ctx = app.mockContext();
        const result = await ctx.service.users.select();
        assert(Array.isArray(result));
    });

    it('insert', async () => {
        const ctx = app.mockContext();
        const result=await ctx.service.users.insert(newUser);
        insertId=result.insertId;
        newUser.id=insertId;
        assert(!isNaN(newUser.id));
    });

    it('get', async () => {
        const ctx = app.mockContext();
        const result = await ctx.service.users.get(insertId);
        assert(result.username = newUser.username);
    });

    it('update',async () => {
        newUser.username='更新后的username';
        const ctx = app.mockContext();
        const result = await ctx.service.users.update(newUser);
        assert(result.username = newUser.username);
    });

    it('delete', async () => {
        const ctx = app.mockContext();
        const result=await ctx.service.users.delete(insertId);
        assert(result.affectedRows===1);
    });
});

2. 角色管理 [#](#t112. 角色管理)

2.1 app/controller/roles.js [#](#t122.1 app/controller/roles.js)

const RestfulController = require('./restful');
class RolesController extends RestfulController {
  constructor(...args) {
    super(...args);
    this.model = 'roles';
  }
}

module.exports = RolesController;

2.2 app/service/roles.js [#](#t132.2 app/service/roles.js)

app/service/roles.js

const ApiService = require('./restful');
// ctx,app,service,config,logger
class RolesService extends ApiService {
  constructor(...args) {
    super(...args);
    this.model = 'roles';
  }
}

module.exports = RolesService;

3 权限 [#](#t143 权限)

3.1 app/controller/restful.js [#](#t153.1 app/controller/restful.js)

const BaseController = require('./base');
// ctx,app,service,config,logger
class RestfulController extends BaseController {
    constructor(...args) {
        super(...args);
    }
    async index() {
        //_page _limit x-total-count
        let {
            ctx,
            app,
            model,
            logger,
            service
        } = this;
        let {
            page,
            limit,
            ...where
        } = ctx.query;
        const list = await service[this.model].select({
            where,
            order: [
                ['create_time', 'desc'],
                ['id', 'desc']
            ],
            limit,
            offset: (page - 1) * limit
        });
        const total = await service[this.model].count(where);
        ctx.set('x-total-count', total);
        this.success({
            list,
            total
        });
    }

    async create() {
        let {
            ctx,
            app,
            service
        } = this;
        let body = ctx.request.body;
        let result = await service[this.model].insert(body);
        const insertSuccess = result.affectedRows === 1;
        if (insertSuccess > 0) {
            this.success({
                id: result.insertId
            });
        } else {
            this.error('添加失败');
        }
    }
    async show() {
        let {
            ctx,
            app,
            service
        } = this;
        let id = ctx.params.id;
        let model = await service[this.model].get(id);
        this.success(model);
    }
    async update() {
        let {
            ctx,
            app,
            service
        } = this;
        let id = ctx.params.id;
        let body = ctx.request.body;
        let result = await service[this.model].update(body);
        const insertSuccess = result.affectedRows === 1;
        if (insertSuccess > 0) {
            let updated = await service[this.model].get(id);
            this.success(updated);
        } else {
            this.error('更新失败');
        }
    }
    async destroy() {
        let {
            ctx,
            app,
            service
        } = this;
        let id = ctx.params.id;
        let result = await service[this.model].delete(parseInt(id));
        const insertSuccess = result.affectedRows === 1;
        if (insertSuccess > 0) {
            this.success('删除成功');
        } else {
            this.error('删除失败');
        }
    }
    async destroyMulti() {
        let {
            ctx,
            app,
            service
        } = this;
        let ids=ctx.request.body;
        let result = await service[this.model].deleteMulti(ids);
        const insertSuccess = result.affectedRows === 1;
        if (insertSuccess > 0) {
            this.success('删除成功');
        } else {
            this.error('删除失败');
        }
    }
}

module.exports = RestfulController;

3.2 app/controller/roles.js [#](#t163.2 app/controller/roles.js)

const RestfulController = require('./restful');
class RolesController extends RestfulController {
  constructor(...args) {
    super(...args);
    this.model = 'roles';
  }
  async setPermissions() {
        let {
            ctx,
            app,
            service
        } = this;
    let body=ctx.request.body;
        await service[this.model].setPermissions(body);
        this.success('设置权限成功');
  }
  async assignUsers() {
        let {
            ctx,
            app,
            service
        } = this;
    let body=ctx.request.body;
        await service[this.model].assignUsers(body);
        this.success('设置用户成功');
  }

  async getResources() {
        let {service}=this;
        let resources = await service[this.model].getResources();
        this.success(resources);
  }
  async getUsers() {
        let {service}=this;
        let users = await service[this.model].getUsers();
        this.success(users);
    }
}

module.exports = RolesController;

3.3 app/router.js [#](#t173.3 app/router.js)

module.exports = app => {
  const { router, controller } = app;
  router.get('/roles/getResources',controller.roles.getResources);
  router.get('/roles/getUsers',controller.roles.getUsers);
  router.resources('users','/users',controller.users);
  router.resources('roles','/roles',controller.roles);
  router.resources('userRole','/userRole',controller.userRole);
  router.resources('resources','/resources',controller.resources);
  router.resources('roleResource','/roleResource',controller.roleResource);

  router.delete('/users',controller.users.destroyMulti);
  router.post('/roles/setPermissions',controller.roles.setPermissions);
  router.post('/roles/assignUsers',controller.roles.assignUsers);

  /*
  router.resources('articles','/articles',controller.articles);
  router.resources('carousels','/carousels',controller.carousels);
  router.resources('categories','/categories',controller.categories);
  router.resources('config','/config',controller.config);
  router.resources('links','/links',controller.links);
  router.resources('navigations','/navigations',controller.navigations);
  router.resources('resources','/resources',controller.resources);
  */
};

3.4 app/service/restful.js [#](#t183.4 app/service/restful.js)

const Service = require('egg').Service;
// ctx,app,service,config,logger
class ApiService extends Service {
  constructor(...args) {
    super(...args);
  }
  async select(options) {
    return await this.app.mysql.select(this.model,options);
  }
  async count(where) {
    let total=await this.app.mysql.count(this.model,where);
    return total;
  }
  async get(id) {
    return await this.app.mysql.get(this.model, { id });
  }
  async insert(entity) {
    return await this.app.mysql.insert(this.model, entity);
  }
  async update(entity) {
    return await this.app.mysql.update(this.model, entity);
  }
  async delete(id) {
    return await this.app.mysql.delete(this.model, { id });
  }
  async deleteMulti(ids) {
    return await this.app.mysql.query(`delete from users where ?? IN (?)`,['id',ids]);
  }
}

module.exports = ApiService;

3.5 app/service/roles.js [#](#t193.5 app/service/roles.js)

const ApiService = require('./restful');
// ctx,app,service,config,logger
class RolesService extends ApiService {
  constructor(...args) {
    super(...args);
    this.model = 'roles';
  }
  async select(options) {
    let roles=await this.app.mysql.select(this.model,options);
    for (let i=0;i<roles.length;i++){
      let rows=await this.app.mysql.query(`select resource_id from role_resource where role_id=?`,[roles[i].id]);
      roles[i].resourceIds=rows.map(item => item.resource_id);

      rows=await this.app.mysql.query(`select user_id from user_role where role_id=?`,[roles[i].id]);
      roles[i].users=rows.map(item=>item.user_id);
    }
    return roles;
  }

  async setPermissions({roleId,resourceIds}) {
    await this.app.mysql.query(`delete from role_resource where role_id =?`,[roleId]);
    for (let i=0;i<resourceIds.length;i++){
      await this.app.mysql.query(`insert into role_resource(role_id,resource_id) values(?,?)`,[roleId,parseInt(resourceIds[i])]);
    }
  }
  async assignUsers({roleId,userIds}) {
    await this.app.mysql.query(`delete from user_role where role_id =?`,[roleId]);
    for (let i=0;i<userIds.length;i++){
      await this.app.mysql.query(`insert into user_role(role_id,user_id) values(?,?)`,[roleId,parseInt(userIds[i])]);
    }
  }

  async getUsers() {
    return await this.app.mysql.select('users');
  }
  async getResources() {
    let resources = await this.app.mysql.select('resources');
    let map={};
    let top=[];
    resources=resources.filter(resource => {
      resource.children=[];
      map[resource.id]=resource;
      if (resource.parent_id == 0) {
        top.push(resource);
      } else {
        return true;
      }
    });
    resources.forEach(resource => {
      let parentId=resource.parent_id;
      map[parentId].children.push(resource);
    });
    return top;
  }
}

module.exports = RolesService;

3. 验证码和跨域 [#](#t203. 验证码和跨域)

3.1 app/router.js [#](#t213.1 app/router.js)

app/router.js

  router.get('/captcha',controller.common.captcha);

3.2 app/controller/common.js [#](#t223.2 app/controller/common.js)

app/controller/common.js

const RestfulController=require('./restful');
const svgCaptcha = require('svg-captcha');
class UsersController extends RestfulController {
  constructor(...args) {
    super(...args);
    this.model = 'users';
  }
  async captcha() {
    let {ctx}=this;
    var captcha=svgCaptcha.create({});
    ctx.session.captcha=captcha.text;
    ctx.set('Content-Type','image/svg+xml');
    ctx.body=captcha.data;
  }
}

module.exports = UsersController;

3.2 config/config.default.js [#](#t233.2 config/config.default.js)

config/config.default.js

  exports.security = {
    domainWhiteList: [ 'http://localhost:8000' ],
  };

3.3 config/plugin.js [#](#t243.3 config/plugin.js)

config/plugin.js

// config/plugin.js
module.exports.passport = {
  enable: true,
  package: 'egg-passport',
};

module.exports.passportGithub = {
  enable: true,
  package: 'egg-passport-github',
};

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

4.注册和登录 #

4.1 app/router.js [#](#t264.1 app/router.js)

app/router.js

  const {router,controller}=app;
  const auth=app.middleware.auth({},app);
  router.post('/login',controller.users.login);
  router.post('/signup',controller.users.signup);
  router.get('/roles/getResources',auth,controller.roles.getResources);
  router.get('/roles/getUsers',auth,controller.roles.getUsers);
  router.resources('users','/users',auth,controller.users);
  router.resources('roles','/roles',auth,controller.roles);
  router.resources('userRole','/userRole',auth,controller.userRole);
  router.resources('resources','/resources',auth,controller.resources);
  router.resources('roleResource','/roleResource',auth,controller.roleResource);

  router.delete('/users',auth,controller.users.destroyMulti);

  router.post('/roles/setPermissions',auth,controller.roles.setPermissions);
  router.post('/roles/assignUsers',auth,controller.roles.assignUsers);

4.2 app/controller/common.js [#](#t274.2 app/controller/common.js)

app/controller/common.js

async captcha() {
    let {ctx}=this;
    var captcha=svgCaptcha.create({});
    ctx.session.captcha=captcha.text;
    console.log(ctx.session)
    ctx.set('Content-Type','image/svg+xml');
    ctx.body=captcha.data;
}

4.3 app/controller/users.js [#](#t284.3 app/controller/users.js)

app/controller/users.js

 const RestfulController=require('./restful');

const svgCaptcha=require('svg-captcha');
const {sign}=require('jsonwebtoken');
 class UsersController extends RestfulController {
   constructor(...args) {
     super(...args);
     this.model = 'users';
   }
  async login() {
    let {ctx,app}=this;
    let body=ctx.request.body;
    const result = await app.mysql.select('users',{
      where: {username:body.username,password:body.password},
      limit: 1,
      offset:0
    });
    if (result&&result.length>0) {
      let user=JSON.parse(JSON.stringify(result[0]));
      this.success(sign(user,this.config.jwtSecret));
    } else {
      this.error('登录失败');
    }
  }
  async signup() {
    let {ctx,app}=this;
    let body=ctx.request.body;
    let captcha=body.captcha;
    body.address=body.address.join('-');
    delete body.confirm;
    delete body.captcha;
    delete body.prefix;
    if (ctx.session.captcha && captcha.toLowerCase() == ctx.session.captcha.toLowerCase()) {
      const result=await app.mysql.insert('users',body);
      const insertSuccess = result.affectedRows === 1;
      if (insertSuccess > 0) {
        this.success({
          id: result.insertId
        });
      } else {
        this.error('添加失败');
      }
    } else{
      this.error('验证码错误!');
    }
  }
 }

 module.exports = UsersController;

4.4 app/middleware/auth.js [#](#t294.4 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'
                }
        }
    }
}

4.5 config/config.default.js [#](#t304.5 config/config.default.js)

config/config.default.js

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

  config.jwtSecret="zxmf";

  config.cors = {
    credentials: true
  }
  config.session= {
    renew: false,
  }

4.6 config/plugin.js [#](#t314.6 config/plugin.js)

config/plugin.js

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

3. 参考 [#](#t323. 参考)

Gitalking ...

Markdown is supported

Be the first guy leaving a comment!