当前位置:网站首页>2、 Encapsulation and tool classes for adding, deleting, modifying and checking in midway
2、 Encapsulation and tool classes for adding, deleting, modifying and checking in midway
2022-07-24 08:31:00 【Code road hero】
Before reading this article , You need to read the pre content in advance :
One 、Midway Additions and deletions
Two 、Midway Add, delete, modify and check the packaging and tool classes
3、 ... and 、Midway Interface security authentication
Four 、Midway Integrate Swagger And support JWT bearer
5、 ... and 、Midway Use of environment variables in
problem
- In most cases , All entity classes have unified fields , You need to extract the base class of the entity model ;
- Need to put Service The basic operation of ;
- Need to put Controller The basic operation of
extract Entity Base class
- Create directory
common; - Create base class
src/common/BaseEntity.ts;
// src/common/BaseEntity.ts
import {
Column, CreateDateColumn, PrimaryColumn, UpdateDateColumn } from 'typeorm';
export class BaseEntity {
@PrimaryColumn({
type: 'bigint' })
id: number;
@Column({
type: 'bigint' })
updaterId: number;
@Column({
type: 'bigint' })
createrId: number;
@CreateDateColumn()
createTime: Date;
@UpdateDateColumn()
updateTime: Date;
}
- Adjust entity class
src/entity/user.ts;
Inherit BaseEntity, And delete user.ts Common fields in .
// src/entity/user.ts
import {
EntityModel } from '@midwayjs/orm';
import {
Column } from 'typeorm';
import {
BaseEntity } from '../common/BaseEntity';
@EntityModel('user')
export class User extends BaseEntity {
@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: 'int', default: 1 })
status: number;
}
extract Service Base class
Create base class src/common/BaseService.ts;
// src/common/BaseService.ts
import {
In, Repository } from 'typeorm';
import {
BaseEntity } from './BaseEntity';
import {
FindOptionsWhere } from 'typeorm/find-options/FindOptionsWhere';
export abstract class BaseService<T extends BaseEntity> {
abstract getModel(): Repository<T>;
async save(o: T) {
if (!o.id) o.id = new Date().getTime();
return this.getModel().save(o);
}
async delete(id: number) {
return this.getModel().delete(id);
}
async findById(id: number): Promise<T> {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return this.getModel().findOneBy({
id });
}
async findByIds(ids: number[]): Promise<T[]> {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return this.getModel().findBy({
id: In(ids) });
}
async findOne(where: FindOptionsWhere<T>): Promise<T> {
return this.getModel().findOne({
where });
}
}
- A base class is defined as an abstract class
abstract, And add abstract interfacesabstract getModel(); <T extends BaseEntity>Generic usage , DefinitionTbyBaseEntitySubclasses of ;
adjustment 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 {
BaseService } from '../common/BaseService';
@Provide()
export class UserService extends BaseService<User> {
@InjectEntityModel(User)
model: Repository<User>;
getModel(): Repository<User> {
return this.model;
}
}
- Add inheritance
UserService extends BaseService<User>; - Implementation interface
getModel(), And back toRepository;
extract Controller Base class
Create base class src/common/BaseController.ts;
// src/common/BaseController.ts
import {
BaseService } from './BaseService';
import {
BaseEntity } from './BaseEntity';
import {
Body, Post, Query } from '@midwayjs/decorator';
/** * Controller Foundation class , Because class inheritance does not support decorative classes @Post、@Query、@Body etc. , * So the decoration here doesn't work , Otherwise, the implementation class will no longer need to write redundant code , * Here, stay here , In case you may support inherited decoration classes in the future */
export abstract class BaseController<T extends BaseEntity> {
abstract getService(): BaseService<T>;
@Post('/create')
async create(@Body() body: T): Promise<T> {
return this.getService().save(body);
}
@Post('/delete')
async delete(@Query('id') id: number): Promise<boolean> {
await this.getService().delete(id);
return true;
}
@Post('/update')
async update(@Body() body: T): Promise<T> {
return this.getService().save(body);
}
@Post('/findById')
async findById(@Query('id') id: number): Promise<T> {
return this.getService().findById(id);
}
@Post('/findByIds')
async findByIds(@Query('ids') ids: number[]): Promise<T[]> {
return this.getService().findByIds(ids);
}
}
- A base class is defined as an abstract class
abstract, And add abstract interfacesabstract getService(); <T extends BaseEntity>Generic usage , DefinitionTbyBaseEntitySubclasses of ;
adjustment 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 {
BaseController } from '../common/BaseController';
import {
BaseService } from '../common/BaseService';
@Controller('/api/user')
export class UserController extends BaseController<User> {
@Inject()
userService: UserService;
getService(): BaseService<User> {
return this.userService;
}
@Post('/create', {
description: ' establish ' })
async create(@Body() user: User): Promise<User> {
Object.assign(user, {
id: new Date().getTime(),
regtime: new Date(),
updaterId: 1,
createrId: 1,
});
return super.create(user);
}
@Post('/findById', {
description: ' Find... By primary key ' })
async findById(@Query('id') id: number): Promise<User> {
return super.findById(id);
}
@Post('/delete', {
description: ' Delete ' })
async delete(@Query('id') id: number): Promise<boolean> {
return super.delete(id);
}
}
- Add inheritance
UserController extends BaseController; - Implement abstract interfaces
getService(); - Call the base class method , Use
super.xxx();
Run unit tests
>npm run test
Test Suites: 2 passed, 2 total
Tests: 4 passed, 4 total
Snapshots: 0 total
Time: 10.686 s
Unified return result processing
middleware
web Middleware is called by the controller Before and after Called function method , We can use middleware before or after the implementation of the interface , Add some logic .
such as : Uniform return format 、 Interface authentication .
Unified interface status 、 Abnormal code
- add to
src/common/ErrorCode.ts;
// src/common/ErrorCode.ts
export class ErrorCode {
/** * 100000 normal */
static OK = 100000;
/** * 400000-500000 Platform exception */
static SYS_ERROR = 400000;
/** * 50000 Unknown exception */
static UN_ERROR = 500000;
/** * 60000-69999 Basic business exceptions */
static BIZ_ERROR = 600000;
}
- Add a generic exception class
src/common/CommonException.ts;
// src/common/CommonException.ts
import {
MidwayError } from '@midwayjs/core';
export class CommonException extends MidwayError {
code: number;
msg: string;
data: any;
constructor(code: number, msg: string) {
super(msg, code.toString());
this.code = code;
this.msg = msg;
}
}
Use the middleware unified interface to return the data format
Add Middleware src/middleware/format.middleware.ts
// src/middleware/format.middleware.ts
import {
IMiddleware } from '@midwayjs/core';
import {
Middleware } from '@midwayjs/decorator';
import {
NextFunction, Context } from '@midwayjs/koa';
import {
ErrorCode } from '../common/ErrorCode';
/** * Uniformly package the data returned by the interface */
@Middleware()
export class FormatMiddleware implements IMiddleware<Context, NextFunction> {
resolve() {
return async (ctx: Context, next: NextFunction) => {
const result = await next();
return {
code: ErrorCode.OK, msg: 'OK', data: result };
};
}
match(ctx) {
return ctx.path.indexOf('/api') === 0;
}
static getName(): string {
return 'API_RESPONSE_FORMAT';
}
}
@Middleware()Identify that this class is a middleware ;match(ctx)Method to determine which paths will be blocked ;
See :http://www.midwayjs.org/docs/middleware
Register middleware
Register middleware , Need modification src/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';
import {
FormatMiddleware } from './middleware/format.middleware';
@Configuration({
imports: [
orm, // introduce orm Components
koa,
validate,
{
component: info,
enabledEnvironment: ['local'],
},
],
importConfigs: [join(__dirname, './config')],
})
export class ContainerLifeCycle {
@App()
app: koa.Application;
async onReady() {
// Register middleware FormatMiddleware
this.app.useMiddleware([FormatMiddleware, ReportMiddleware]);
}
}
Postman View return results
At this point, the returned result has been repackaged .
exception handling
Unified exception handling uses exception filters , You can encapsulate exceptions here .
- Create or modify exception filters
src/filter/default.filter.ts;
// src/filter/default.filter.ts
import {
Catch } from '@midwayjs/decorator';
import {
Context } from '@midwayjs/koa';
import {
ErrorCode } from '../common/ErrorCode';
@Catch()
export class DefaultErrorFilter {
async catch(err: Error, ctx: Context) {
return {
code: ErrorCode.UN_ERROR, msg: err.message };
}
}
- Create or modify exception filters
src/filter/notfound.filter.ts;
// src/filter/notfound.filter.ts
import {
Catch } from '@midwayjs/decorator';
import {
httpError, MidwayHttpError } from '@midwayjs/core';
import {
Context } from '@midwayjs/koa';
@Catch(httpError.NotFoundError)
export class NotFoundFilter {
async catch(err: MidwayHttpError, ctx: Context) {
// 404 Mistakes will come here
ctx.redirect('/404.html');
}
}
- Register exception filters ;
// src/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';
import {
FormatMiddleware } from './middleware/format.middleware';
import {
NotFoundFilter } from './filter/notfound.filter';
import {
DefaultErrorFilter } from './filter/default.filter';
@Configuration({
imports: [
orm, // introduce orm Components
koa,
validate,
{
component: info,
enabledEnvironment: ['local'],
},
],
importConfigs: [join(__dirname, './config')],
})
export class ContainerLifeCycle {
@App()
app: koa.Application;
async onReady() {
this.app.useMiddleware([FormatMiddleware, ReportMiddleware]);
// Register exception filters
this.app.useFilter([NotFoundFilter, DefaultErrorFilter]);
}
}
- Use Postman verification ( Create user , Enter an overly long user name );

unit testing
Because the return value is adjusted , At this time, the unit test will report an error , We need to adjust the next unit . modify test/controller/user.test.ts.
o = result.body;
# Change it to
o = result.body.data;
>npm run test
Test Suites: 2 passed, 2 total
Tests: 4 passed, 4 total
Snapshots: 0 total
Time: 6.525 s, estimated 9 s
Tool class
problem & demand
- The database primary key needs to be an ordered 、 Globally unique long shaping ;
- The user's password needs to be encrypted , Be able to verify the password ;
- Business exceptions need to be returned to the front end , Use here
Assertion tool;
key generator
We use Snowflake Primary key generation algorithm .
Its advantages are : High performance , Low latency ; Independent applications ; In order of time .
The disadvantage is that : Need independent development and deployment .
Here we migrate the algorithm to the local , There is no problem with test development , Data centers and servers need to be configured for production use .
- Create a tool catalog
utils; - Create a tool class
src/utils/Snowflake.ts;
// src/utils/Snowflake.ts
import {
Provide } from '@midwayjs/decorator';
/** * Snowflake Primary key generation algorithm * The complete algorithm is generated ID The length is 20 position * But because of js Maximum 9007199254740991, More will overflow , More special treatment is needed . * So set the length here as 16 position id. Turn down the data center to 1 position , Turn down the server to 1 position , Turn the sequence bit down to 10 position * This means that up to two data centers are supported , Each data center supports up to two servers */
@Provide('idGenerate')
export class SnowflakeIdGenerate {
private twepoch = 0;
private workerIdBits = 1;
private dataCenterIdBits = 1;
private maxWrokerId = -1 ^ (-1 << this.workerIdBits); // The value is :1
private maxDataCenterId = -1 ^ (-1 << this.dataCenterIdBits); // The value is :1
private sequenceBits = 10;
private workerIdShift = this.sequenceBits; // The value is :10
private dataCenterIdShift = this.sequenceBits + this.workerIdBits; // The value is :11
// private timestampLeftShift =
// this.sequenceBits + this.workerIdBits + this.dataCenterIdBits; // The value is :12
private sequenceMask = -1 ^ (-1 << this.sequenceBits); // The value is :4095
private lastTimestamp = -1;
private workerId = 1; // Set the default value , Take... From the environment variable
private dataCenterId = 1;
private sequence = 0;
constructor(_workerId = 0, _dataCenterId = 0, _sequence = 0) {
if (this.workerId > this.maxWrokerId || this.workerId < 0) {
throw new Error('config.worker_id must max than 0 and small than maxWrokerId-[' + this.maxWrokerId + ']');
}
if (this.dataCenterId > this.maxDataCenterId || this.dataCenterId < 0) {
throw new Error(
'config.data_center_id must max than 0 and small than maxDataCenterId-[' + this.maxDataCenterId + ']',
);
}
this.workerId = _workerId;
this.dataCenterId = _dataCenterId;
this.sequence = _sequence;
}
private timeGen = (): number => {
return Date.now();
};
private tilNextMillis = (lastTimestamp): number => {
let timestamp = this.timeGen();
while (timestamp <= lastTimestamp) {
timestamp = this.timeGen();
}
return timestamp;
};
private nextId = (): number => {
let timestamp: number = this.timeGen();
if (timestamp < this.lastTimestamp) {
throw new Error('Clock moved backwards. Refusing to generate id for ' + (this.lastTimestamp - timestamp));
}
if (this.lastTimestamp === timestamp) {
this.sequence = (this.sequence + 1) & this.sequenceMask;
if (this.sequence === 0) {
timestamp = this.tilNextMillis(this.lastTimestamp);
}
} else {
this.sequence = 0;
}
this.lastTimestamp = timestamp;
// js Maximum 9007199254740991, More will overflow
// exceed 32 Bit length , Doing bit operations will overflow , Become negative , So here we do multiplication directly , Multiplication expands storage
const timestampPos = (timestamp - this.twepoch) * 4096;
const dataCenterPos = this.dataCenterId << this.dataCenterIdShift;
const workerPos = this.workerId << this.workerIdShift;
return timestampPos + dataCenterPos + workerPos + this.sequence;
};
generate = (): number => {
return this.nextId();
};
}
Password tool
Installation of components
>npm i bcryptjs --save
Add tool class src/utils/PasswordEncoder.ts
// src/utils/PasswordEncoder.ts
const bcrypt = require('bcryptjs');
/** * encryption . Add the prefix {bcrypt}, In order to be compatible with various encryption algorithms , For the time being, only bcrypt Algorithm */
export function encrypt(password) {
const salt = bcrypt.genSaltSync(5);
const hash = bcrypt.hashSync(password, salt, 64);
return '{bcrypt}' + hash;
}
/** * Decrypt */
export function decrypt(password, hash) {
if (hash.indexOf('{bcrypt}') === 0) {
hash = hash.slice(8);
}
return bcrypt.compareSync(password, hash);
}
Assertion tool
// src/common/Assert.ts
import {
CommonException } from './CommonException';
export class Assert {
/** * Not empty assertion */
static notNull(obj: any, errorCode: number, errorMsg: string) {
if (!obj) {
throw new CommonException(errorCode, errorMsg);
}
}
/** * Empty string assertion */
static notEmpty(obj: any, errorCode: number, errorMsg: string) {
if (!obj || '' === obj.trim()) {
throw new CommonException(errorCode, errorMsg);
}
}
/** * Boolean asserts that */
static isTrue(expression: boolean, errorCode: number, errorMsg: string) {
if (!expression) {
throw new CommonException(errorCode, errorMsg);
}
}
}
copyright , Reprint please indicate the source [ Code track success ]
边栏推荐
- Hack the box - Introduction to networking module detailed Chinese tutorial
- The beta version of move protocol is stable, and it is temporarily decided to expand the scale of the prize pool
- Install SQL Server database
- Is gamefi in decline or in the future?
- Go: how to gracefully time out
- Musk responded that the brain has been uploaded to the cloud: already did it!
- M-dao creates a one-stop Dao platform, allowing hundreds of millions of players to join Dao space
- Detailed explanation of wechat applet page configuration and sitemap configuration parameters
- Golang implements sanggi diagram and analyzes user behavior trajectory
- P1739 expression bracket matching problem solution
猜你喜欢

Aquanee: the true meaning of "p2e"

JQ native write bullet frame mask layer
![[wechat applet development (II)] custom navigation bar](/img/87/d390b50d1bd23acee9b46fbe7fe180.png)
[wechat applet development (II)] custom navigation bar

Okaleido tiger NFT is about to log in to binance NFT platform, and the era of NFT rights and interests is about to start
![[Linux] Oracle VirtualBox installation CentOS 8](/img/fc/ea1070b93d3f1dbc52e60045834ea9.png)
[Linux] Oracle VirtualBox installation CentOS 8

Local warehouse associated with remote warehouse

Move protocol global health declaration, step into Web3 in sports

图新地球:如何导入修改了高程基准(椭球)的CAD文件

2022.7.11 overall solution

Larave uses sanctum for API authentication
随机推荐
Go: how to gracefully time out
Move protocol launched a beta version, and you can "0" participate in p2e
Alibaba cloud deploys SSL certificates
Digital collections "chaos", 100 billion market changes are coming?
[MySQL] 08: aggregate function
Is gamefi in decline or in the future?
Clear up some disk space and try again
MySQL index filesort
Recursive performance test
[redis] how much do you know about bloom filter and cuckoo filter?
Use the bark app to realize the process of pushing messages to mobile phones
Wargames bandit (21-33) problem solving essay
"Explanation" change exchange
Summary of points management system project
Mysql database advanced
"Solution" friend of Vulcan
SQL learning
Brief notes on the key points of distributed system principle introduction
A knight's journey
Wargames bandit (11-20) problem solving essay