课程名称:web前端架构师
课程章节:第15周 第八章 用户系统的设计与实现
主讲老师:张轩
课程内容: 创建用户模型
将我们之前写的 mongoose插件 替换为 egg-mongoose
npm i egg-mongoose
在 plugin.ts 文件中启用 egg-mongoose 插件
const plugin: EggPlugin = { mongoose: { enable: true, package: 'egg-mongoose', }, };
在 config.default.ts 文件中配置mongoose,配置与我们之前写的一样,不过 egg-mongoose 支持传入 options
config.mongoose = { url: 'mongodb://localhost:27017', // 可以将 url 中的用户名、密码、数据库名等放到 options 上 options: { user: 'root', pass: '*******', dbName: 'lego', }, };
import { Application } from 'egg'; export interface User{ username: string password: string email?: string nickname?: string pickture?: string phoneNumber?: string createdAt: Date updatedAt: Date } export default (app: Application) => { const mongoose = app.mongoose; const Schema = mongoose.Schema; const UserSchema = new Schema<User>({ username: String, password: String, email: String, nickname: String, phoneNumber: String, }, { timestamps: true }); return mongoose.model<User>('User', UserSchema); };
这里需要注意的是 Schema 第二个参数传 timestamps 可以帮我们自动创建 craeteAt 和 updateAt的值
首先完成第一个接口,创建用户
在 serve 中编写操作数据库的代码, 在 service 目录下创建 user.ts
import { Service } from 'egg'; import { User } from '../model/User'; export default class UserService extends Service { public async createByEmail(payload: User) { const { ctx } = this; const { username, password } = payload; const user: Partial<User> = { username, password, email: username, }; return ctx.app.model.User.create(user); } public async findById(id: string) { return this.ctx.app.model.User.findById(id); } }
编写 controller, 调用service ,并把数据返回给用户
import { Controller } from 'egg'; export default class UserController extends Controller { public async createByEmail() { const { ctx, service } = this; const res = await service.user.createByEmail(ctx.request.body); ctx.body = { res, code: 0 }; } }
最后配置路由
import { Application } from 'egg'; export default (app: Application) => { const { controller, router } = app; router.post('/signup', controller.user.createByEmail); };
这样代码创建用户代码就完成了
由于 egg-mongoose 使用的 mongoose 版本是5.x的,很长时间没有更新了。它对 ts 的支持并不友好,我上面我们写代码时,很多时候时没有提示的信息,这样使用很不舒服。例如
export default (app: Application) => { const mongoose = app.mongoose; const Schema = mongoose.Schema; const UserSchema = new Schema<User>({ // 这里编写没有提示 username: String, password: String, email: String, nickname: String, phoneNumber: String, }, { timestamps: true }); return mongoose.model<User>('User', UserSchema); };
我们在写 servie 下调用调用 model 操作数据库时,也是没有提示的。例如下面代码
export default class UserService extends Service { public async createByEmail(payload: User) { const { ctx } = this; const { username, password } = payload; const user: Partial<User> = { username, password, email: username, }; // 这里不会提示, ctx.app.model.User 是 Model<any, {}, {}> return ctx.app.model.User.create(user); } public async findById(id: string) { // 这里不会提示, ctx.app.model.User 是 Model<any, {}, {}> // this.ctx.app.model.User.findById(id) 这个结果也没有字段提示 return this.ctx.app.model.User.findById(id); }
我们可以 在 typings/index.d.ts 文件中声明 User 类型
import 'egg'; import { User } from '../app/model/user' declare module 'egg' { interface MongooseModels{ User: Model<User> } }
然后到代码里验证
public async findById(id: string) { // this.ctx.app.model. 时就会出现提示 User const res = await this.ctx.app.model.User.findById(id); if (res) { // res. 时,就会出现 res 上的一些属性,比如username password 等 console.log(res.username); } return res; }
上面代码虽然实现了有提示信息,但是需要我们每次都要编写代码,不是很好
我们每次保存代码都会自动生成 typings 下的代码,除了index.d.ts ,其他代码都会自动声明 对应文件夹下的 .d.ts 文件,我们可以查看 typings/app/model/index.d.ts 文件
import 'egg'; // 导入我们 model 目录下的所有文件 import ExportUser from '../../../app/model/User'; declare module 'egg' { interface IModel { // key 时 User,即我们的文件名首字母大写,值就是我们 export default导出的返回值 // 每次保存都会自动生成 所有。[文件名]:[export default导出的返回值] User: ReturnType<typeof ExportUser>; } }
每次保存文件,它都会自动生成,导入我们的代码,生成类型声明文件 ,IModel 这个接口会导入我们 model 下的所有文件,所以我们可以在 index.d.ts 中利用 IModel 来实现我们想要的,每次修改完代码,不需要手动来修改 index.d.ts
import 'egg'; import { User } from '../app/model/user' declare module 'egg' { interface MongooseModels extends IModel{ } }