file module import { createParamDecorator } from '@nestjs/common' ; import * as lodash from 'lodash' ; import { Types } from 'mongoose' ; import { HttpBadRequestError } from '../common/errors/bad-request.error' ; import { HttpForbiddenError } from '../common/errors/forbidden.error' ; import { ECommentState , EOriginState , EPublicState , EPublishState , ESortType } from '../common/types/interfaces/state.interface' ; export enum EQueryParamsField { Page = 'page' , PerPage = 'per_page' , Sort = 'sort' , Date = 'date' , Keyword = 'keyword' , State = 'state' , Public = 'public' , Origin = 'origin' , ParamsId = 'paramsId' , CommentState = 'commentState' } export interface IQueryParamsConfig { [ key ] : string | number | boolean | Types . ObjectId | Date | RegExp | IQueryParamsConfig ; } export interface IQueryParamsResult { querys : IQueryParamsConfig ; options : IQueryParamsConfig ; params : IQueryParamsConfig ; origin : IQueryParamsConfig ; request : any ; visitors : { ip : string ; ua : string ; referer : string ; } ; isAuthenticated : boolean ; } interface ITransformConfigObject { [ key ] : string | number | boolean ; } export type TTransformConfig = EQueryParamsField | string | ITransformConfigObject ; interface IValidateError { name : string ; field : EQueryParamsField ; isAllowed : boolean ; isIllegal : boolean ; setValue ( ) : void ; } function description example QueryParams example QueryParams example QueryParams export const QueryParams = createParamDecorator ( ( customConfig , request ) : IQueryParamsResult => { const isAuthenticated = request . isAuthenticated ( ) ; const transformConfig = { [ EQueryParamsField . Page ] : 1 , [ EQueryParamsField . PerPage ] : true , [ EQueryParamsField . ParamsId ] : 'id' , [ EQueryParamsField . Sort ] : true , } ; if ( customConfig ) { customConfig . forEach ( field => { if ( lodash . isString ( field ) ) { transformConfig [ field ] = true ; } if ( lodash . isObject ( field ) ) { Object . assign ( transformConfig , field ) ; } } ) ; } const querys = { } ; const options = { } ; const params = lodash . merge ( { url : request . url } , request . params ) ; const date = request . query . date ; const paramsId = request . params [ transformConfig . paramsId as string ] ; const [ page , per_page , sort , state , ppublic , origin ] = [ request . query . page || transformConfig . page , request . query . per_page , request . query . sort , request . query . state , request . query . public , request . query . origin , ] . map ( item => item != null ? Number ( item ) : item ) ; const validates = [ { name : '路由/ID' , field : EQueryParamsField . ParamsId , isAllowed : true , isIllegal : paramsId != null && ! isAuthenticated && isNaN ( paramsId ) , setValue ( ) { if ( paramsId != null ) { params [ transformConfig . paramsId as string ] = isNaN ( paramsId ) ? Types . ObjectId ( paramsId ) : Number ( paramsId ) ; } } , } , { name : '排序/sort' , field : EQueryParamsField . Sort , isAllowed : lodash . isUndefined ( sort ) || [ ESortType . Asc , ESortType . Desc , ESortType . Hot ] . includes ( sort ) , isIllegal : false , setValue ( ) { options . sort = { _id : sort != null ? sort : ESortType . Desc , } ; } , } , { name : '目标页/page' , field : EQueryParamsField . Page , isAllowed : lodash . isUndefined ( page ) || ( lodash . isInteger ( page ) && Number ( page ) > 0 ) , isIllegal : false , setValue ( ) { if ( page != null ) { options . page = page ; } } , } , { name : '每页数量/per_page' , field : EQueryParamsField . PerPage , isAllowed : lodash . isUndefined ( per_page ) || ( lodash . isInteger ( per_page ) && Number ( per_page ) > 0 ) , isIllegal : false , setValue ( ) { if ( per_page != null ) { options . limit = per_page ; } } , } , { name : '日期查询/date' , field : EQueryParamsField . Date , isAllowed : lodash . isUndefined ( date ) || new Date ( date ) . toString ( ) !== 'Invalid Date' , isIllegal : false , setValue ( ) { if ( date != null ) { const queryDate = new Date ( date ) ; querys . create_at = { $gte : new Date ( ( ( queryDate as any ) / 1000 - 60 * 60 * 8 ) * 1000 ) , $lt : new Date ( ( ( queryDate as any ) / 1000 + 60 * 60 * 16 ) * 1000 ) , } ; } } , } , { name : '发布状态/state' , field : EQueryParamsField . State , isAllowed : lodash . isUndefined ( state ) || ( transformConfig [ EQueryParamsField . CommentState ] ? [ ECommentState . Auditing , ECommentState . Deleted , ECommentState . Published , ECommentState . Spam ] . includes ( state ) : [ EPublishState . Published , EPublishState . Draft , EPublishState . Recycle ] . includes ( state ) ) , isIllegal : ! isAuthenticated && state != null && state !== ( transformConfig [ EQueryParamsField . CommentState ] ? ECommentState . Published : EPublishState . Published ) , setValue ( ) { if ( state != null ) { querys . state = state ; return false ; } if ( ! isAuthenticated ) { querys . state = transformConfig [ EQueryParamsField . CommentState ] ? ECommentState . Published : EPublishState . Published ; } } , } , { name : '公开状态/public' , field : EQueryParamsField . Public , isAllowed : lodash . isUndefined ( ppublic ) || [ EPublicState . Public , EPublicState . Password , EPublicState . Secret ] . includes ( ppublic ) , isIllegal : ppublic != null && ! isAuthenticated && ppublic !== EPublicState . Public , setValue ( ) { if ( ppublic != null ) { querys . public = ppublic ; return false ; } if ( ! isAuthenticated ) { querys . public = EPublicState . Public ; } } , } , { name : '来源状态/origin' , field : EQueryParamsField . Origin , isAllowed : lodash . isUndefined ( origin ) || [ EOriginState . Original , EOriginState . Hybrid , EOriginState . Reprint ] . includes ( origin ) , isIllegal : false , setValue ( ) { if ( origin != null ) { querys . origin = origin ; } } , } , ] ; const isEnableField = ( field ) => field != null && field !== false ; validates . forEach ( validate => { if ( ! isEnableField ( transformConfig [ validate . field ] ) ) { return false ; } if ( ! validate . isAllowed ) { throw new HttpBadRequestError ( '参数不合法：' + validate . name ) ; } if ( validate . isIllegal ) { throw new HttpForbiddenError ( '权限与参数匹配不合法：' + validate . name ) ; } validate . setValue ( ) ; } ) ; /**
     * 处理剩余的规则外参数
     * 1. 用户传入配置与默认配置混合得到需要处理的参数字段
     * 2. 内置一堆关键参数的校验器
     * 3. 剩下的非内部校验的非关键参数，在此合并至 querys
     */ const isProcessedFields = validates . map ( validate => validate . field ) ; const allAllowFields = Object . keys ( transformConfig ) ; const todoFields = lodash . difference ( allAllowFields , isProcessedFields ) ; todoFields . forEach ( field => { const targetValue = request . query [ field ] ; if ( targetValue != null ) { querys [ field ] = targetValue ; } } ) ; request . queryParams = { querys , options , params , isAuthenticated } ; const ip = ( request . headers [ 'x-forwarded-for' ] || request . headers [ 'x-real-ip' ] || request . connection . remoteAddress || request . socket . remoteAddress || request . connection . socket . remoteAddress || request . ip || request . ips [ 0 ] ) . replace ( '::ffff:' , '' ) ; const ua = request . headers [ 'user-agent' ] ; const result = { querys , options , params , request , origin : request . query , visitors : { ip , ua , referer : request . referer } , isAuthenticated , } ; return result ; } ) ;