美文网首页
一、Midway 增删改查

一、Midway 增删改查

作者: 码道功臣 | 来源:发表于2022-07-18 19:23 被阅读0次

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

    一、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<User>;
    
      async create(user: User): Promise<User> {
        return this.userModel.save(user);
      }
    
      async findById(id: number): Promise<User> {
        return this.userModel.findOneBy({ id });
      }
    
      async delete(id: number): Promise<DeleteResult> {
        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<User> {
        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<User> {
        return this.userService.findById(id);
      }
    
      @Post('/delete', { description: '删除' })
      async delete(@Query('id') id: number): Promise<DeleteResult> {
        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<Framework>();
        } 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<Framework>() 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: ['<rootDir>/test/fixtures'],
      coveragePathIgnorePatterns: ['<rootDir>/test/'],
      // 添加如下一行代码,引入jest初始化文件
      setupFilesAfterEnv: ['<rootDir>/jest.setup.js']
    };
    

    开发调试

    IntelliJ IDEA中Debug

    • 运行/调试配置


      运行/调试配置
    • 启动Debug


      启动Debug

    使用Postman测试

    • 新增


      新增
    • 查找


      查找
    • 删除


      删除

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

    相关文章

      网友评论

          本文标题:一、Midway 增删改查

          本文链接:https://www.haomeiwen.com/subject/mqydirtx.html