一、Midway 增删改查

阅读本文前,需要提前阅读前置内容:

一、Midway 增删改查
二、Midway 增删改查的封装及工具类
三、Midway 接口安全认证
四、Midway 集成 Swagger 以及支持JWT bearer
五、Midway 中环境变量的使用

midway是阿里巴巴开源的,基于TypeScript语言开发的Nodejs后端框架。
本教程指导大家从0开始搭建一个midway项目。

其遵循遵循两种编程范式

  • 面向对象(OOP + Class + IoC);
  • 函数式(FP + Function + Hooks);

谁较容易上手学习

  • 懂Nodejs技术的前端开发;
  • 会TypeScript的后端开发;

在这里你可以掌握度如下知识

  • 面向对象的开发体验;
  • 增删改查及基类封装;
  • 数据库操作;
  • 缓存操作;
  • 用户安全认证及访问安全控制;
  • JWT访问凭证;
  • 分布式访问状态管理;
  • 密码加解密;
  • 统一返回结果封装;
  • 统一异常管理;
  • Snowflake主键生成;
  • Swagger集成及支持访问认证;
  • 环境变量的使用;
  • Docker镜像构建;
  • Serverless发布;

本项目源码

https://github.com/bestaone/midway-boot

LIVE DEMO

http://midway-boot.hiauth.cn/swagger-ui/index.html

环境准备

  • Nodejs 12+
  • Npm 8+
  • MySql 5+
  • Redis

开发工具

我们这里使用 IntelliJ IDEA

下载地址:https://www.jetbrains.com/zh-cn/idea/download

安装数据库

略...

安装Redis

略...

第一个midway项目

初始化创建

>npm init midway
  • 执行命令后,需要选择模板,标准项目需要选择:koa-v3;
  • 项目名可以自定义(我这里设置为midway-boot);

启动

>cd midway-boot
>npm run dev

启动后浏览器访问:http://127.0.0.1:7001

调整ESLint配置

为了保证代码分隔统一,我们调整下ESLint配置

// .prettierrc.js
module.exports = {
  ...require('mwts/.prettierrc.json'),
  endOfLine: "lf",        // 换行符使用 lf
  printWidth: 120,        // 一行最多 120 字符
  proseWrap: "preserve",  // 使用默认的折行标准
  semi: true,             // 行尾需要有分号
}

在windows中代码的首行、尾行不能有空行,否则ESLint提示格式错误,可能是bug。

项目结构介绍

├─src                     # 源码目录
│  ├─config               # 配置
│  ├─controller           # 控制器
│  ├─entity               # 数据对象模型
│  ├─filter               # 过滤器
│  ├─middleware           # 中间件
│  ├─service              # 服务类
│  ├─configurations.ts    # 服务生命周期管理及配置
│  └─interface.ts         # 接口定义
├─test                    # 测试类目录
├─bootstrap.js            # 启动入口
├─package.json            # 包管理配置
├─tsconfig.json           # TypeScript 编译配置文件

增删改查

ORM组件:TypeORM

TypeORM是Object Relation Mapping工具,提供的数据库操作能力。

安装依赖

>npm i @midwayjs/orm@3 typeorm --save

安装完后package.json文件中会多出如下配置

{
  "dependencies": {
    "@midwayjs/orm": "^3.3.6",
    "typeorm": "^0.3.7"
  }
}

引入组件

src/configuration.ts中引入 orm 组件

// configuration.ts
import { Configuration, App } from '@midwayjs/decorator';
import * as koa from '@midwayjs/koa';
import * as validate from '@midwayjs/validate';
import * as info from '@midwayjs/info';
import { join } from 'path';
import { ReportMiddleware } from './middleware/report.middleware';
import * as orm from '@midwayjs/orm';

@Configuration({
  imports: [
    orm, // 引入orm组件
    koa,
    validate,
    {
      component: info,
      enabledEnvironment: ['local'],
    },
  ],
  importConfigs: [join(__dirname, './config')],
})
export class ContainerLifeCycle {
  @App()
  app: koa.Application;

  async onReady() {
    this.app.useMiddleware([ReportMiddleware]);
  }
}

添加数据库配置

修改配置src/config/config.default.ts

// src/config/config.default.ts
import { MidwayConfig } from '@midwayjs/core';

export default {
  keys: '1657707214114_9253',
  koa: {
    port: 7001,
  },
  // 添加orm配置
  orm: {
    type: 'mysql',
    host: '127.0.0.1',      // 改成你的mysql数据库IP
    port: 3306,             // 改成你的mysql数据库端口
    username: 'root',       // 改成你的mysql数据库用户名(需要有创建表结构权限)
    password: '123456',     // 改成你的mysql数据库密码
    database: 'midway_boot',// 改成你的mysql数据库IP
    synchronize: true,      // 如果第一次使用,不存在表,有同步的需求可以写 true
    logging: true,
  },
} as MidwayConfig;

注意:首次启动没有创建表结构的,需要设置自动创建表接口synchronize: true

安装MySql驱动

>npm install mysql2 --save

安装完后package.json文件中会多出如下配置

{
  "dependencies": {
    "mysql2": "^2.3.3"
  }
}

orm的详细文档见:http://www.midwayjs.org/docs/extensions/orm

Entity、Service、Controller

创建Entity实体类

  • 创建目录src/entity;
  • 在该目录下创建实体类user.ts;
// src/entity/user.ts
import { EntityModel } from '@midwayjs/orm';
import {
  Column,
  CreateDateColumn,
  PrimaryColumn,
  UpdateDateColumn,
} from 'typeorm';

@EntityModel('user')
export class User {

  @PrimaryColumn({ type: 'bigint' })
  id: number;

  @Column({ length: 100, nullable: true })
  avatarUrl: string;

  @Column({ length: 20, unique: true })
  username: string;

  @Column({ length: 200 })
  password: string;

  @Column({ length: 20 })
  phoneNum: string;

  @Column()
  regtime: Date;

  @Column({ type: 'bigint' })
  updaterId: number;

  @Column({ type: 'bigint' })
  createrId: number;

  @CreateDateColumn()
  createTime: Date;

  @UpdateDateColumn()
  updateTime: Date;

  @Column({ type: 'int', default: 1 })
  status: number;

}
  • @EntityModel 用来定义一个实体类;
  • @Column 用来描述类的一个熟悉,对应数据库就是一个数据列;
  • @PrimaryColumn 用来定义一个主键,每个实体类必须要要主键;
  • @PrimaryGeneratedColumn 用来定义一个自增主键;
  • @CreateDateColumn 定义创建时,自动设置日期;
  • @UpdateDateColumn 定义更新时,自动设置日期;

对应的数据库结构

CREATE TABLE `user` (
  `id` bigint NOT NULL,
  `avatarUrl` varchar(100) DEFAULT NULL,
  `username` varchar(20) NOT NULL,
  `password` varchar(200) NOT NULL,
  `phoneNum` varchar(20) NOT NULL,
  `regtime` datetime NOT NULL,
  `updaterId` bigint NOT NULL,
  `createrId` bigint NOT NULL,
  `createTime` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
  `updateTime` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
  `status` int NOT NULL DEFAULT '1',
  PRIMARY KEY (`id`),
  UNIQUE KEY `IDX_78a916df40e02a9deb1c4b75ed` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

创建UserService

创建或者修改src/service/user.service.ts文件。

// src/service/user.service.ts
import { Provide } from '@midwayjs/decorator';
import { User } from '../eneity/user';
import { InjectEntityModel } from '@midwayjs/orm';
import { Repository } from 'typeorm';
import { DeleteResult } from 'typeorm/query-builder/result/DeleteResult';

@Provide()
export class UserService {

  @InjectEntityModel(User)
  userModel: Repository;

  async create(user: User): Promise {
    return this.userModel.save(user);
  }

  async findById(id: number): Promise {
    return this.userModel.findOneBy({ id });
  }

  async delete(id: number): Promise {
    return this.userModel.delete(id);
  }

}
  • @Provide 表示这个类将会由系统自动实例化,在使用的时候,只需要使用@Inject注入就可以了;
  • @InjectEntityModel 注入实体模型数据库操作工具;

注意:由于调整了UserService,src/controller/api.controller.tstest/controller/api.test.ts会报错,直接删掉即可

创建UserController

创建或者修改src/controller/user.controller.ts文件。

// src/controller/user.controller.ts
import { Inject, Controller, Query, Post, Body } from '@midwayjs/decorator';
import { User } from '../eneity/user';
import { UserService } from '../service/user.service';
import { DeleteResult } from 'typeorm/query-builder/result/DeleteResult';

@Controller('/api/user')
export class UserController {
  @Inject()
  userService: UserService;

  @Post('/create', { description: '创建' })
  async create(@Body() user: User): Promise {
    Object.assign(user, {
      id: new Date().getTime(),
      regtime: new Date(),
      updaterId: 1,
      createrId: 1,
    });
    return this.userService.save(user);
  }

  @Post('/findById', { description: '通过主键查找' })
  async findById(@Query('id') id: number): Promise {
    return this.userService.findById(id);
  }

  @Post('/delete', { description: '删除' })
  async delete(@Query('id') id: number): Promise {
    return this.userService.delete(id);
  }
}
  • @Inject()装饰类指定该对象会被自动注入;

单元测试

添加单元测试类

添加文件test/controller/user.test.ts

// test/controller/user.test.ts
import {close, createApp, createHttpRequest} from '@midwayjs/mock';
import {Application, Framework} from '@midwayjs/koa';
import {User} from '../../src/eneity/user'

describe('test/controller/user.test.ts', () => {

  let app: Application;
  let o: User;

  beforeAll(async () => {
    try {
      app = await createApp();
    } catch(err) {
      console.error('test beforeAll error', err);
      throw err;
    }
  });

  afterAll(async () => {
    await close(app);
  });

  // create
  it('should POST /api/user/create', async () => {
    o = new User();
    Object.assign(o, {
      username: new Date().getTime().toString(),
      password: new Date().getTime().toString(),
      phoneNum: new Date().getTime().toString(),
    });
    const result = await createHttpRequest(app).post('/api/user/create')
      .send(o);
    expect(result.status).toBe(200);
    // 将创建好的数据存起来,以供后面测试使用(返回的数据会有id)
    o = result.body;
  });

  // findById
  it('should POST /api/user/findById', async () => {
    const result = await createHttpRequest(app).post('/api/user/findById?id=' + o.id);
    expect(result.status).toBe(200);
  });

  // delete
  it('should POST /api/user/delete', async () => {
    const result = await createHttpRequest(app).post('/api/user/delete?id=' + o.id);
    expect(result.status).toBe(200);
  });
});
  • beforeAllafterAll 分别会在测试开始前、后执行;
  • createApp() BeforeAll阶段的error会忽略,需要手动处理异常;

单元测试的详细文档,见:http://www.midwayjs.org/docs/testing

执行单元测试

>npm run test

如果测试时间过长,会导致测试失败,那么我们需要修改超时时间

修改测试类的超时时间

  • 在根目录中添加文件jest.setup.js;
// jest.setup.js
// 只需要一行代码
// 设置单元测试超时时间
jest.setTimeout(60000);
  • 修改jest配置文件jest.config.js;
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  testPathIgnorePatterns: ['/test/fixtures'],
  coveragePathIgnorePatterns: ['/test/'],
  // 添加如下一行代码,引入jest初始化文件
  setupFilesAfterEnv: ['/jest.setup.js']
};

开发调试

IntelliJ IDEA中Debug

  • 运行/调试配置


    运行/调试配置
  • 启动Debug


    启动Debug

使用Postman测试

  • 新增


    新增
  • 查找


    查找
  • 删除


    删除

版权所有,转载请注明出处 [码道功成]

你可能感兴趣的:(一、Midway 增删改查)