使用的基础目录结构:Koa入门(一)—— Koa项目基础框架搭建
基础表:
CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`phone` varchar(20) NOT NULL COMMENT '手机号',
`password` varchar(64) NOT NULL COMMENT '密码',
`nick_name` varchar(50) NOT NULL DEFAULT '' COMMENT '昵称',
`sex` tinyint(4) NOT NULL DEFAULT '1' COMMENT '性别,1:男;2:女',
`created_at` datetime NOT NULL COMMENT '创建时间',
`updated_at` datetime DEFAULT NULL COMMENT '最后更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
sequelize 可以根据模型自动生成数据库表字段信息,不过个人比较喜欢自己建表,不喜欢使用这个功能
// src/main/config/index.js
export default {
middlewares: [
'globalError'
],
db: {
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
host: process.env.DB_HOST,
database: process.env.DB_DATABASE,
dialect: process.env.DB_DIALECT,
define: {
// 数据库字段下划线命名,匹配模型驼峰式命名
underscored: true,
// 禁用修改表名
freezeTableName: true
},
timezone: '+08:00',
// 返回的时间格式配置
dialectOptions: {
dateStrings: true,
typeCast: true
}
}
};
process.env的所有配置都在 .env
文件里:
NODE_ENV=dev
PORT=4000
DB_USERNAME=koa_demo
DB_PASSWORD=koa_demo
DB_HOST=localhost
DB_DATABASE=koa-demo
DB_DIALECT=mysql
// src/main/db/index.js
import config from '@main/config';
import Sequelize from 'sequelize';
import requireDirectory from 'require-directory';
const sequelize = new Sequelize(config.db);
// 加载模型
requireDirectory(module, '../model', {
visit: (obj) => {
if (obj.modelName) {
sequelize.define(obj.modelName, obj.attributes, obj.options);
}
}
});
export default sequelize;
这个文件负责加载 model
目录下的所有模型,暴露出 sequelize
供其他代码使用。
import Sequelize from 'sequelize';
import { isPhone } from '@main/util/ValidateUtil';
import { ParamError } from '@main/common/CommonError';
export const modelName = 'user';
export const attributes = {
phone: {
type: Sequelize.STRING(20),
unique: true,
validate: {
check(val) {
if (val) {
if (!isPhone(val)) {
throw new ParamError('电话号码格式不正确');
}
} else {
throw new ParamError('电话号码不能为空');
}
}
}
},
password: {
type: Sequelize.STRING(64),
validate: {
check(val) {
if (val) {
if (val.length < 6) {
throw new ParamError('密码不能小于6位');
}
} else {
throw new ParamError('密码不能为空');
}
}
}
},
nickName: Sequelize.STRING(50),
sex: Sequelize.TINYINT
};
export const options = {
tableName: 't_user'
};
每个模型JS都会暴露三个信息:
modelName
:模型名称。服务层将会通过这个模型名称获取到模型,DB层加载模型也是根据是否有modelName
来判断是否加载模型的,所以这个时必须提供的。attributes
:字段信息。可以带有一些字段值校验options
:表配置信息。目前只有表名配置,如果你某个表不想使用 created_at
等字段也可以在这里配置。sequelize 会自动提供 id, created_at 和 updated_at 字段,所以这里就没配置了
这里使用了一些自定义的校验工具类,我放在了 src/main/util
目录下:
const PHONE_PATTERN = /(13\d|14[579]|15[^4\D]|17[^49\D]|18\d)\d{8}/g;
/**
* 电话号码验证
* @param {string} val
*/
export const isPhone = (val) => {
return PHONE_PATTERN.test(val);
};
/**
* 空值验证
* @param {*} val
*/
export const isNull = (val) => {
return val === undefined || val === null;
};
// src/main/service/UserService.js
import sequelize from '@main/db';
import { isNull } from '@main/util/ValidateUtil';
import { ParamError } from '@main/common/CommonError';
const userModel = sequelize.model('user');
class UserService {
static async save(user) {
return await userModel.create(user);
}
static async findById(id) {
return await userModel.findByPk(id);
}
static async updateById(user) {
if (isNull(user.id)) {
throw new ParamError('ID不能为空');
}
return await userModel.update(user, {
where: {
id: user.id
}
});
}
};
export default UserService;
// src/main/controller/UserController.js
import Router from 'koa-router';
import UserService from '@main/service/UserService';
import koaBodyparser from 'koa-bodyparser'
let userRouter = new Router({
prefix: '/api/user'
});
userRouter.get('/:id', async ctx => {
ctx.body = ctx.renderJson(await UserService.findById(ctx.params.id));
});
userRouter.post('/', koaBodyparser(), async ctx => {
let params = ctx.request.body;
ctx.body = ctx.renderJson(await UserService.save(params));
});
userRouter.put('/', koaBodyparser(), async ctx => {
let params = ctx.request.body;
ctx.body = ctx.renderJson(await UserService.updateById(params));
});
export default userRouter;
这里的 ctx.renderJson
是在 app.js
中:
// ...
const app = new Koa();
/**
* 统一JSON返回格式
*/
app.context.renderJson = (data, msg = '操作成功', code = 200) => {
return {
code,
data,
msg
}
};
// ...
这里 Sequelize
校验抛出的异常是 Sequelize
自己提供的异常类,所以需要在全局异常处理处配置一下:
// src/main/middleware/globalError.js
import { CommonError, ParamError } from '@main/common/CommonError';
import { ValidationError } from 'sequelize/lib/errors';
export default () => {
return async (ctx, next) => {
try {
await next();
} catch (err) {
if (err instanceof ValidationError) {
ctx.body = new ParamError(err.errors[0].message);
} else if (err instanceof CommonError) {
ctx.body = err;
} else {
ctx.body = new CommonError();
}
console.error(err);
}
};
};
如果你数据库使用了主从同步,主写,从读,那么就需要连接两个数据库,多数据库的配置该如何配置?
NODE_ENV=dev
PORT=4000
# 主库配置
DB_WRITE_USERNAME=koa_demo_write
DB_WRITE_PASSWORD=koa_demo_write
DB_WRITE_HOST=127.0.0.1
DB_WRITE_DATABASE=koa-demo-write
DB_WRITE_DIALECT=mysql
# 从库配置
DB_READ_USERNAME=koa_demo_read
DB_READ_PASSWORD=koa_demo_read
DB_READ_HOST=127.0.0.1
DB_READ_DATABASE=koa-demo-read
DB_READ_DIALECT=mysql
export default {
middlewares: [
'globalError'
],
db: {
write: {
username: process.env.DB_WRITE_USERNAME,
password: process.env.DB_WRITE_PASSWORD,
host: process.env.DB_WRITE_HOST,
database: process.env.DB_WRITE_DATABASE,
dialect: process.env.DB_WRITE_DIALECT,
define: {
// 数据库字段下划线命名,匹配模型驼峰式命名
underscored: true,
// 禁用修改表名
freezeTableName: true
},
timezone: '+08:00',
// 返回的时间格式配置
dialectOptions: {
dateStrings: true,
typeCast: true
}
},
read: {
username: process.env.DB_READ_USERNAME,
password: process.env.DB_READ_PASSWORD,
host: process.env.DB_READ_HOST,
database: process.env.DB_READ_DATABASE,
dialect: process.env.DB_READ_DIALECT,
define: {
// 数据库字段下划线命名,匹配模型驼峰式命名
underscored: true,
// 禁用修改表名
freezeTableName: true
},
timezone: '+08:00',
// 返回的时间格式配置
dialectOptions: {
dateStrings: true,
typeCast: true
}
}
}
};
// src/main/db/index.js
import config from '@main/config';
import Sequelize from 'sequelize';
import requireDirectory from 'require-directory';
const sequelize = new Sequelize(config.db.write);
// 加载模型
requireDirectory(module, '../model', {
visit: (obj) => {
if (obj.modelName) {
sequelize.define(obj.modelName, obj.attributes, obj.options);
}
}
});
export default sequelize;
index.js
配置写数据库
这里还要添加一个读数据库 read.js
:
import config from '@main/config';
import Sequelize from 'sequelize';
import requireDirectory from 'require-directory';
const sequelize = new Sequelize(config.db.read);
// 加载模型
requireDirectory(module, '../model', {
visit: (obj) => {
if (obj.modelName) {
sequelize.define(obj.modelName, obj.attributes, obj.options);
}
}
});
export default sequelize;
因为读写数据库的表都是一样的,所以只要复制一份 index.js
为 read.js
修改 config.db.write
为 config.db.read
即可。
使用的时候如果是写入操作就引入:
import writeSequelize from '@main/db';
const userModel = writeSequelize .model('user');
如果是读操作就引入:
import readSequelize from '@main/db/read.js';
const userModel = readSequelize .model('user');
Gitee源码查看
相关文章: