常年使用sails 開發(fā)node 后端 苦不堪言,文檔缺失,迭代較慢,并且周邊也不是那么完善,雖說基本能滿足需求 但是還是少了靈活性。 待到egg 推出之時(shí)就在觀察,最近新開項(xiàng)目遂采用egg - ts
- egg-mysql
這玩意沒有index.d.ts .所以 你想使用app.mysql是編譯不過的,所以要用 ts 的merge 來給Application上掛載一個(gè)mysql 我們可以這么做
./typings/index.d.ts 寫入
declare module 'egg' {
interface mysql {
get(tableName: String, find: {}): Promise<Any>
query(sql: String, values: Any[]): Promise<Any>
}
interface Application {
mysql: mysql;
}
}
可以在上面給mysql 加點(diǎn)方法這樣就有提示了。
egg-sequelize
為node中較為常用的orm 框架。
這個(gè)玩意我們可以細(xì)說。
首先你要做一個(gè)model 來定義這個(gè)表中的字段,但是為了方便開發(fā),我們得加點(diǎn)料。
- /app/model/model.ts 這里寫一個(gè)基類的basemodel 這樣以后所有的model都可以用到基類
import { Application } from 'egg';
import { snakeCase } from 'lodash';
import * as moment from 'moment';
import { DefineAttributes, SequelizeStatic } from 'sequelize';
export default function BaseModel(
app: Application,
table: string,
attributes: DefineAttributes,
options: object = {},
) {
const { Op, UUID, UUIDV4 } = app.Sequelize;
const modelSchema = app.model.define(table, {
id: {
type: UUID,
unique: true,
primaryKey: true,
allowNull: false,
defaultValue: UUIDV4,
},
...attributes,
...getDefaultAttributes(options, app.Sequelize),
}, {
timestamps: true, // 自動(dòng)維護(hù)時(shí)間戳 [ created_at、updated_at ]
underscored: true, // 不使用駝峰樣式自動(dòng)添加屬性,而是下劃線樣式 [ createdAt => created_at ]
// 禁止修改表名,默認(rèn)情況下,sequelize將自動(dòng)將所有傳遞的模型名稱(define的第一個(gè)參數(shù))轉(zhuǎn)換為復(fù)數(shù)
// 但是為了安全著想,復(fù)數(shù)的轉(zhuǎn)換可能會(huì)發(fā)生變化,所以禁止該行為
freezeTableName: false,
...options,
scopes: {
// 定義全局作用域,使用方法如: .scope('onlyTrashed') or .scope('onlyTrashed1', 'onlyTrashed12') [ 多個(gè)作用域 ]
onlyTrashed: {
// 只查詢軟刪除數(shù)據(jù)
where: {
deleted_at: {
[Op.not]: null,
},
},
},
},
});
modelSchema.getAttributes = (): string[] => {
return Object.keys(attributes);
};
modelSchema.findAttribute = (attribute: string): object | undefined => {
return (attributes as any)[attribute];
};
modelSchema.fillable = (): string[] => {
return [];
};
modelSchema.hidden = (): string[] => {
return [];
};
modelSchema.visible = (): string[] => {
return [];
};
return modelSchema;
}
function getDefaultAttributes(options: object, sequelize: SequelizeStatic): object {
const { DATE } = sequelize;
const defaultAttributes = {
created_at: {
type: DATE,
get() {
return moment((this as any).getDataValue('created_at')).format('YYYY-MM-DD HH:mm:ss');
},
},
updated_at: {
type: DATE,
get() {
return moment((this as any).getDataValue('updated_at')).format('YYYY-MM-DD HH:mm:ss');
},
},
};
// 需要從 options 讀取的配置信息,用于下方做過濾的條件
const attributes = ['createdAt', 'updatedAt', 'deletedAt'];
Object.keys(options).forEach((value: string) => {
if (attributes.includes(value) && (options as any)[value] === false) {
delete (defaultAttributes as any)[snakeCase(value)];
}
});
return defaultAttributes || {};
}
- /app/model/index.d.ts 將model 掛載到application上 同時(shí)給model 擴(kuò)展方法。
import { User } from './user';
declare module 'egg' {
interface Application {
Sequelize: SequelizeStatic
model: Sequelize
}
interface Context {
model: {
User: Model<User, {}>;
}
}
}
Mode
declare module 'sequelize' {
interface Model<TInstance, TAttributes> {
fillable(): string[];
hidden(): string[];
visible(): string[];
getAttributes(): string[];
findAttribute(attribute: string): object | undefined;
getDataValue(key: string): any;
setDataValue(key: string, value: any): void;
}
}
- app/model/user.ts
import { Application } from 'egg';
import BaseModel from './model';
export default function User(app: Application) {
const { INTEGER, DATE, STRING, BOOLEAN } = app.Sequelize;
const modelSchema = BaseModel(app, 'users', {
name: {
type: STRING(32),
unique: true,
allowNull: false,
comment: '用戶名',
},
email: {
type: STRING(64),
unique: true,
allowNull: true,
comment: '郵箱地址',
},
phone: {
type: STRING(20),
unique: true,
allowNull: true,
comment: '手機(jī)號(hào)碼',
},
avatar: {
type: STRING(150),
allowNull: true,
comment: '頭像',
},
real_name: {
type: STRING(30),
allowNull: true,
comment: '真實(shí)姓名',
},
signature: {
type: STRING(255),
allowNull: true,
comment: '簽名',
},
notify_count: {
type: INTEGER,
allowNull: false,
defaultValue: 0,
comment: '消息通知個(gè)數(shù)',
},
status: {
type: BOOLEAN,
allowNull: false,
defaultValue: 1,
comment: '用戶狀態(tài): 1 正常; 0 禁用',
},
password: {
type: STRING(255),
allowNull: false,
},
last_actived_at: DATE,
}, {
paranoid: true,
setterMethods: {
async password(value: any) {
(this as any).setDataValue('password', await app.createBcrypt(value))
},
},
});
return modelSchema;
}
這里我們就完成了一個(gè)model的開發(fā)
我們說這是采坑 那么就要說哪里坑了? 上面除了自己搞index.d.ts有點(diǎn)麻煩之外其他看著還好。
注意了 坑來了~
用sequelize 那么migration 也要用了吧?
我們?cè)賞ackage.json 內(nèi) 的script 加入
"migrate:new": "egg-sequelize migration:create",
"migrate:up": "egg-sequelize db:migrate",
"migrate:down": "egg-sequelize db:migrate:undo"
執(zhí)行命令npm run migration:create -- --name user生成一個(gè)migrations 文件夾,還會(huì)有一個(gè)空的文件 ....
1.你需要自己把user.ts 內(nèi)的model復(fù)制一份過去。
2.你需要執(zhí)行 ·npm run migration:up·
Sequelize CLI [Node: 8.11.3, CLI: 4.0.0, ORM: 4.38.0]
Loaded configuration file "node_modules/egg-sequelize/lib/database.js".
Using environment "development".
ERROR: Dialect needs to be explicitly supplied as of v4.0.0
采坑了吧!
我們看源文件 是需要從配置里 config.default.js讀取 而非 config.default.ts,所以我們需要再建一個(gè)文件config.default.js.bak 這樣做的目的是為了避免被egg 把js文件清除。 改bak 為 普通的js 再執(zhí)行 up 會(huì)發(fā)現(xiàn)終于成功了。。。。
2018-11-05
不知為何 egg-sequlize 安裝不上 (命令行命令無用),隧只能做些改變 使用 sequlize-cli 這一套。。。
有個(gè)注意項(xiàng): 需要自己寫個(gè) config.json給 seqlize去讀
這個(gè)問題還帶出來個(gè) mysql 字符集問題,原因就是config,json 沒有指定為utf8mb4 所以默認(rèn)為latin,導(dǎo)致中文或者emoji無法存入。