Node.js

升级数据库

[egg官网](https://eggjs.org/zh-cn/intro/quickstart.html)

返回目录

1. 快速初始化项目

egg官网

$ mkdir egg-example && cd egg-example
$ npm init egg --type=simple
$ npm i

$ mkdir showcase && cd showcase
$ npm init egg --type=ts
$ npm i
$ npm run dev

2. 启动项目

$ npm run dev
$ open http://localhost:7001

3. 连接mysql数据库(使用sequelize可以跳过这一步)

  1. 安装对应的插件 egg-mysql :
$ npm i --save egg-mysql
  1. 开启插件:
// config/plugin.js
exports.mysql = {
  enable: true,
  package: 'egg-mysql',
};
  1. 配置
// config/config.${env}.js
exports.mysql = {
  // 单数据库信息配置
  client: {
    // host
    host: 'mysql.com',
    // 端口号
    port: '3306',
    // 用户名
    user: 'test_user',
    // 密码
    password: 'test_password',
    // 数据库名
    database: 'test',
  },
  // 是否加载到 app 上,默认开启
  app: true,
  // 是否加载到 agent 上,默认关闭
  agent: false,
};

4. 安装并配置 egg-sequelize

  1. 安装
$ npm install --save egg-sequelize mysql2
  1. 开启插件
exports.sequelize = {
  enable: true,
  package: 'egg-sequelize',
};
  1. 配置插件
config.sequelize = {
  dialect: 'mysql',
  host: '127.0.0.1',
  port: 3306,
  database: 'egg-sequelize-doc-default',
  // 用户名
  user: 'test_user',
  // 密码
  password: 'test_password',
  // 东八时区 (使用sequelize内置时间时用到)
  timezone: '+08:00',
};

5. eggjs路由群组

目前支持这三种属性

路由别名前缀: name
路由url前缀: prefix
中间件: middlewares

  1. 安装
$ npm i egg-router-group --save
  1. 开启插件
// {app_root}/config/plugin.js
exports.routerGroup = {
  enable: true,
  package: 'egg-router-group',
};
  1. 基本用法
// {app_root}/app/router.js
module.exports = app => {
  const { router, controller, middleware } = app;
  const m1 = middleware.m1();
  const m2 = middleware.m2({ key: 'value' });
  const m3 = middleware.m3();

  router.group({ name: 'home::', prefix: '/pre', middlewares: [ m1, m2 ] }, router => {
    // router-path: /pre/test, middlewares: m1, m2
    // router-path: /pre/test2, middlewares: m1, m2
    router.get('/test', controller.home.test).get('/test2', controller.home.test);
    // router-name: home::post, router-path: /pre/post, middlewares: m1, m2, m3
    router.post('post', '/post', m3, controller.home.post);

    // others
    router.all('/test', controller.home.all1);
    router.all('testname', '/test2', controller.home.all2);
    router.put('/put', controller.home.put);
    router.delete('/delete', controller.home.delete);
    router.del('/del', controller.home.del);
    router.options('/options', controller.home.options);
    router.patch('/patch', controller.home.patch);
    router.redirect('/redirect', '/');
    router.redirect('/redirect2', 'home::testname', 302);
  });

  // 设置单个属性
  router.group({ name: 'home::' }, router => {
    // router-path: /test
    router.get('/test', controller.home.test);
    // router-name: home::post, router-path: /post
    router.post('post', '/post', controller.home.post);
  });

  // group 同样支持链式操作
  router.group({ name: 'home::', prefix: '/pre', middlewares: [ m1, m2 ] }, router => {
    // router-path: /pre/test, middlewares: m1, m2
    // router-path: /pre/test2, middlewares: m1, m2 ⚠️这里🈶️group 设置的属性哦
    router.get('/test', controller.home.test).get('/test2', controller.home.test);
  })
    .get('/test3', controller.home.test); // router-path: /test3 ⚠️这里🈚group 设置的属性哦
};
  1. 中间件

支持传单个中间件或数组的形式

// {app_root}/app/router.js
module.exports = app => {
  const { router, controller, middleware } = app;
  const m1 = middleware.m1();
  const m2 = middleware.m2({ key: 'value' });

  router.group({ middlewares: [ m1, m2 ] }, router => {
    router.get('/test_m1', controller.home.test_m1);
    router.post('/test_m2/:id', controller.home.test_m2);
  });
  router.group({ middlewares: m1 }, router => {
    router.get('/test_m3', controller.home.test_m3);
    router.post('/test_m4/:id', controller.home.test_m4);
  });
};
  1. 路由别名前缀 & 路由url前缀

路由别名前缀 & 路由url前缀 只支持string类型

// {app_root}/app/router.js
module.exports = app => {
  // name
  router.group({ name: 'home::' }, router => {
    // router-name: home::post
    router.post('post', '/test/:id', controller.home.t1);
  });

  // prefix
  router.group({ prefix: '/pre' }, router => {
    // router-name: test, router-path: /pre/test2
    router.get('test', '/test2', controller.home.t2);
  });
};

6. sequelize+migration(可以不使用)

  1. 安装 sequelize-cli
npm install --save-dev sequelize-cli
  1. 新建 .sequelizerc 配置文件

在 egg 项目中,我们希望将所有数据库 Migrations 相关的内容都放在 database 目录下,所以我们在项目根目录下新建一个 .sequelizerc 配置文件:

'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'),
};
  1. 初始化 Migrations 配置文件和目录
npx sequelize init:config
npx sequelize init:migrations
  1. 数据库配置

执行完后会生成 database/config.json 文件和 database/migrations 目录,我们修改一下 database/config.json 中的内容,将其改成我们项目中使用的数据库配置:

{
"development": {
"username": "root",
"password": null,
"database": "egg-sequelize-doc-default",
"host": "127.0.0.1",
"dialect": "mysql"
},
"test": {
"username": "root",
"password": null,
"database": "egg-sequelize-doc-unittest",
"host": "127.0.0.1",
"dialect": "mysql"
}
}

  1. 创建表

此时 sequelize-cli 和相关的配置也都初始化好了,我们可以开始编写项目的第一个 Migration 文件来创建我们的一个 users 表了。

npx sequelize migration:generate --name=init-users

执行完后会在 database/migrations 目录下生成一个 migration 文件(${timestamp}-init-users.js),我们修改它来处理初始化 users 表:

'use strict';

module.exports = {
  // 在执行数据库升级时调用的函数,创建 users 表
  up: async (queryInterface, Sequelize) => {
    const { INTEGER, DATE, STRING } = Sequelize;
    await queryInterface.createTable('users', {
      id: { type: INTEGER, primaryKey: true, autoIncrement: true },
      name: STRING(30),
      age: INTEGER,
      created_at: DATE,
      updated_at: DATE,
    });
  },
  // 在执行数据库降级时调用的函数,删除 users 表
  down: async queryInterface => {
    await queryInterface.dropTable('users');
  },
};
  1. 执行 migrate 进行数据库变更
# 升级数据库
npx sequelize db:migrate
# 如果有问题需要回滚,可以通过 `db:migrate:undo` 回退一个变更
# npx sequelize db:migrate:undo
# 可以通过 `db:migrate:undo:all` 回退到初始状态
# npx sequelize db:migrate:undo:all

执行之后,我们的数据库初始化就完成了。

7. 使用sequelize-auto生成sequelize的Models(可以不使用)

  1. 全局安装sequelize-auto
npm install -g sequelize-auto
  1. 全局安装对应数据库的驱动,此处使用的是mysql
npm install -g mysql
  1. 在对应的文件夹下执行以下命令
sequelize-auto -h 数据库的IP地址 -d 数据库名 -u 用户名 -x 密码 -p 端口 -t 表名
  1. 使用第三步生成的Models
const Sequelize = require('sequelize');
const SequelizeInstance = require('../../config/db'); // sequelize的实例
const User = require('../../models/user')(SequelizeInstance, Sequelize); // 导入用户的模型

#$ 8. 编写model文件

根据自己创建的user表映射model文件
参考sequelize Datatypes 数据类型(https://demopark.github.io/sequelize-docs-Zh-CN/data-types.html)

// model/user.js

'use strict';

module.exports = app => {
  const { DATE, STRING, INTEGER, UUID, DataTypes } = app.Sequelize;

  const User = app.model.define(
    'user',
    {
      id: {
        type: INTEGER,
        autoIncrement: true,
        primaryKey: true,
        allowNull: false,
      },
      uuid: {
        type: UUID,
        allowNull: false,
        defaultValue: DataTypes.UUIDV4,
      },
      username: STRING(50),
      password: STRING(50),
      avator: STRING,
      sex: STRING(4),
      created_at: DATE(6),
    },
    {
      freezeTableName: true, // Model 对应的表名将与model名相同; (默认情况下,sequelize将自动将所有传递的模型名称(define的第一个参数)转换为复数.freezeTableName: true则不转换)
      underscored: true, // 自动设置所有属性的字段参数为下划线命名方式. 不会覆盖已经定义的字段选项
      // tableName: 'user',  // 定义表的名称
      // 不要忘记启用时间戳!
      // 自动写入时间戳 created_at updated_at
      timestamps: true,
      // 字段生成软删除时间戳 deleted_at
      paranoid: true,
      createdAt: 'created_at',
      // 我不想要 updatedAt
      updatedAt: false,

    }
  );

  return User;
};

9. 使用sequelize操作数据库

调用:ctx.model.User (User开头大写是因为定义的model中return的是User,注意和自己写的对应起来)

'use strict';

const Controller = require('egg').Controller;

class HomeController extends Controller {
  async register() {
    const { ctx } = this;
    console.log(ctx.request.body);
    const { username, password } = ctx.request.body;
    const result = await ctx.model.User.create({ username, password });
    console.log(result);
    if (result) {
      this.ctx.body = '注册成功';
    } else {
      this.ctx.body = '注册失败';
    }

  }
  async login() {
    const { ctx } = this;
    console.log(ctx.request.body);
    const { username, password } = ctx.request.body;
    const result = await ctx.model.User.findOne({
      where: { username, password },
    });
    console.log(result);
    if (result) {
      this.ctx.body = '登录成功';
    } else {
      this.ctx.body = '登录失败';
    }

  }
 
}

module.exports = HomeController;

10. jwt鉴权

这里使用插件egg-passport 、egg-passport-jwt、jsonwebtoken。
先安装jsonwebtoken、egg-passport,然后安装egg-passport-jwt。这有个坑,在egg-passport-jwt安装文档上没有说要求安装egg-passport。

开发规范