关于 MCV Service 的 Response 封装(装饰器)

案例技术栈

  • Nestjs
  • Typescript
  • mongoose

调试工具

  • Apifox

在过去的很长一段时间里我封装了很多Service返回值的标准输出格式,但冠绝都不够优雅,最开始使用带泛型的函数包裹返回值。现在我发现使用Decorator 来重新实现让编码更优雅,也更语义化。

// format.ts

/*
 * @Descripttion: Format Service Response
 * @version: 0.0.0
 * @Author: Minyoung
 * @Date: 2022-03-27 21:09:58
 * @LastEditors: Minyoung
 * @LastEditTime: 2022-03-28 23:52:24
 */

/**
 * 格式化列表查询返回结果
 * @param ...rest 
 * @returns object { data, total, code, message }
 */
export function ListResponse() {
  return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
    const fn = descriptor.value;
    return {
      get() {
        return async (...rest) => {
          const { data: result, total } = await fn.call(this, ...rest);
          const data = result ? result : [];
          const code = result ? 200 : 201;
          const message = code === 200 ? 'success' : 'fail'
          return Promise.resolve({ data, total: total || 0, code, message });
        }
      }
    }
  }
}

/**
 * 格式化单查询返回结果
 * @param rest 
 * @returns object { data, code, message }
 */
export function BasicResponse() {
  return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
    const fn = descriptor.value;
    return {
      get() {
        return async (...rest) => {
          const { data } = await fn.call(this, ...rest);
          const code = data ? 200 : 201;
          const message = code === 200 ? 'success' : 'fail'
          return Promise.resolve({ data, code, message });
        }
      }
    }
  }
}

/**
 * 格式化创建返回结果
 * @param rest 
 * @returns object { result, code, message }
 */
 export function CreateResponse() {
  return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
    const fn = descriptor.value;
    return {
      get() {
        return async (...rest) => {
          const { result } = await fn.call(this, ...rest);
          const code = result ? 200 : 201;
          const message = code === 200 ? 'success' : 'fail'
          return Promise.resolve({ result, code, message });
        }
      }
    }
  }
}

/**
 * 格式化更新返回结果
 * @param rest 
 * @returns object { result, code, message }
 */
export function UpdateResponse() {
  // {
  //   "acknowledged": true,
  //   "modifiedCount": 1,
  //   "upsertedId": null,
  //   "upsertedCount": 0,
  //   "matchedCount": 1
  // }
  return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
    const fn = descriptor.value;
    return {
      get() {
        return async (...rest) => {
          const { result } = await fn.call(this, ...rest);
          const code = result.modifiedCount >= 1 ? 200 : 201;
          const message = code === 200 ? 'success' : 'fail'
          return Promise.resolve({ result, code, message });
        }
      }
    }
  }
}

/**
 * 格式化删除返回结果
 * @param rest 
 * @returns object { result, code, message }
 */
export function DeleteResponse() {
  // {
  //   acknowledged: true,
  //   deletedCount: 0
  // }
  return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
    const fn = descriptor.value;
    return {
      get() {
        return async (...rest) => {
          const { result } = await fn.call(this, ...rest);
          const code = result.deletedCount >= 1 ? 200 : 201;
          const message = code === 200 ? 'success' : 'fail'
          return Promise.resolve({ result, code, message });
        }
      }
    }
  }
}

应用于
// product.service.ts

/*
 * @Descripttion: 产品 Serive
 * @version: 0.0.0
 * @Author: Minyoung
 * @Date: 2022-03-26 20:48:07
 * @LastEditors: Minyoung
 * @LastEditTime: 2022-03-28 23:51:50
 */
import { Injectable } from '@nestjs/common';
import { CreateProductDto } from './dto/create-product.dto';
import { UpdateProductDto } from './dto/update-product.dto';
import { Product, ProductDocument } from './entities/product.entity';
import { Model } from 'mongoose';
import { InjectModel } from '@nestjs/mongoose';
import { ListResponse, BasicResponse, CreateResponse, UpdateResponse, DeleteResponse } from '../utils/format'

@Injectable()
export class ProductsService {

  @InjectModel(Product.name) private readonly product: Model<ProductDocument>

  constructor() {}

  @CreateResponse()
  async create(createProductDto: CreateProductDto) {
    const result = await this.product.create(createProductDto);
    return { result };
  }

  @ListResponse()
  async findAll() {
    const data = await this.product.find().exec();
    const total = await this.product.countDocuments().exec();
    return { data, total };
  }

  @BasicResponse()
  async findOne(id: number) {
    const data = await this.product.findById(id).exec();
    return { data };
  }

  @UpdateResponse()
  async update(_id: string, updateProductDto: UpdateProductDto) {
    const result = await this.product.updateOne({ _id }, {
      $set: updateProductDto
    });
    return { result };
  }

  @DeleteResponse()
  async remove(_id: string) {
    const result = await this.product.deleteOne({ _id }).exec();
    return { result };
  }
}

调试结果示例:
列表返回[success]

{
    "data": [
        {
            "comments": [],
            "_id": "624034a87da67ec5435bb04a",
            "title": "98年可乐",
            "price": 198,
            "sale": 0,
            "timeLimit": 100,
            "banners": [],
            "createdAt": 1648374952937,
            "updatedAt": 1648471880892
        }
    ],
    "total": 1,
    "code": 200,
    "message": "success"
}

删除返回[fail]

{
    "result": {
        "acknowledged": true,
        "deletedCount": 0
    },
    "code": 201,
    "message": "fail"
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值