feat: note module done
This commit is contained in:
@@ -6,34 +6,20 @@ import {
|
||||
CallHandler,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
Logger,
|
||||
NestInterceptor,
|
||||
} from '@nestjs/common'
|
||||
import { Reflector } from '@nestjs/core'
|
||||
import { InjectModel } from 'nestjs-typegoose'
|
||||
import { map } from 'rxjs'
|
||||
import { ArticleType } from '~/constants/article.constant'
|
||||
import { RedisKeys } from '~/constants/cache.constant'
|
||||
import { HTTP_RES_UPDATE_DOC_COUNT_TYPE } from '~/constants/meta.constant'
|
||||
import { NoteModel } from '~/modules/note/note.model'
|
||||
import { PostModel } from '~/modules/post/post.model'
|
||||
import { CacheService } from '~/processors/cache/cache.service'
|
||||
import { CountingService } from '~/processors/helper/helper.counting.service'
|
||||
import { getIp } from '~/utils/ip.util'
|
||||
import { getRedisKey } from '~/utils/redis.util'
|
||||
// ResponseInterceptor -> JSONSerializeInterceptor -> CountingInterceptor -> HttpCacheInterceptor
|
||||
@Injectable()
|
||||
export class CountingInterceptor<T> implements NestInterceptor<T, any> {
|
||||
private logger: Logger
|
||||
constructor(
|
||||
private readonly countingService: CountingService,
|
||||
private readonly reflector: Reflector,
|
||||
@InjectModel(PostModel)
|
||||
private readonly postModel: MongooseModel<PostModel>,
|
||||
@InjectModel(NoteModel)
|
||||
private readonly noteModel: MongooseModel<NoteModel>,
|
||||
private readonly redis: CacheService,
|
||||
) {
|
||||
this.logger = new Logger(CountingInterceptor.name)
|
||||
}
|
||||
) {}
|
||||
|
||||
intercept(context: ExecutionContext, next: CallHandler) {
|
||||
const handler = context.getHandler()
|
||||
@@ -45,7 +31,7 @@ export class CountingInterceptor<T> implements NestInterceptor<T, any> {
|
||||
handler,
|
||||
)
|
||||
if (documentType) {
|
||||
this.updateReadCount(
|
||||
this.countingService.updateReadCount(
|
||||
documentType as any,
|
||||
data.id || data?.data?.id,
|
||||
getIp(context.switchToHttp().getRequest()),
|
||||
@@ -56,42 +42,4 @@ export class CountingInterceptor<T> implements NestInterceptor<T, any> {
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
private async updateReadCount(
|
||||
type: keyof typeof ArticleType,
|
||||
id: string,
|
||||
ip: string,
|
||||
) {
|
||||
if (!ip) {
|
||||
this.logger.debug('无法更新阅读计数, IP 无效')
|
||||
return
|
||||
}
|
||||
if (!id) {
|
||||
this.logger.debug('无法更新阅读计数, ID 不存在')
|
||||
return
|
||||
}
|
||||
const modelMap = {
|
||||
Post: this.postModel,
|
||||
Note: this.noteModel,
|
||||
} as const
|
||||
|
||||
const model = modelMap[type]
|
||||
|
||||
const redis = this.redis.getClient()
|
||||
|
||||
const isReadBefore = await redis.sismember(
|
||||
getRedisKey(RedisKeys.Read, id),
|
||||
ip,
|
||||
)
|
||||
if (isReadBefore) {
|
||||
this.logger.debug('已经增加过计数了, ' + id)
|
||||
return
|
||||
}
|
||||
const doc = await model.findOne({ _id: id })
|
||||
await Promise.all([
|
||||
redis.sadd(getRedisKey(RedisKeys.Read, doc._id), ip),
|
||||
doc.updateOne({ $inc: { 'count.read': 1 } }),
|
||||
])
|
||||
this.logger.debug('增加计数, ' + doc.title)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
ForbiddenException,
|
||||
Get,
|
||||
HttpCode,
|
||||
Param,
|
||||
Patch,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
@@ -17,19 +20,29 @@ import { ApiName } from '~/common/decorator/openapi.decorator'
|
||||
import { IsMaster } from '~/common/decorator/role.decorator'
|
||||
import { UpdateDocumentCount } from '~/common/decorator/update-count.decorator'
|
||||
import { CannotFindException } from '~/common/exceptions/cant-find.exception'
|
||||
import { MongoIdDto } from '~/shared/dto/id.dto'
|
||||
import { CountingService } from '~/processors/helper/helper.counting.service'
|
||||
import { IntIdOrMongoIdDto, MongoIdDto } from '~/shared/dto/id.dto'
|
||||
import { SearchDto } from '~/shared/dto/search.dto'
|
||||
import {
|
||||
addConditionToSeeHideContent,
|
||||
addYearCondition,
|
||||
} from '~/utils/query.util'
|
||||
import { ListQueryDto, NoteQueryDto, PasswordQueryDto } from './note.dto'
|
||||
import {
|
||||
ListQueryDto,
|
||||
NidType,
|
||||
NoteQueryDto,
|
||||
PasswordQueryDto,
|
||||
} from './note.dto'
|
||||
import { NoteModel, PartialNoteModel } from './note.model'
|
||||
import { NoteService } from './note.service'
|
||||
|
||||
@ApiName
|
||||
@Controller({ path: 'notes' })
|
||||
export class NoteController {
|
||||
constructor(private readonly noteService: NoteService) {}
|
||||
constructor(
|
||||
private readonly noteService: NoteService,
|
||||
private readonly countingService: CountingService,
|
||||
) {}
|
||||
|
||||
@Get('latest')
|
||||
@ApiOperation({ summary: '获取最新发布一篇记录' })
|
||||
@@ -45,7 +58,6 @@ export class NoteController {
|
||||
isMaster ? '+location +coordinates' : '-location -coordinates',
|
||||
)
|
||||
|
||||
// this.noteService.shouldAddReadCount(latest, location.ip)
|
||||
return { data: latest.toObject(), next: next.toObject() }
|
||||
}
|
||||
|
||||
@@ -201,11 +213,108 @@ export class NoteController {
|
||||
return await this.noteService.model.findById(params.id)
|
||||
}
|
||||
|
||||
@Put('/:id')
|
||||
@Patch('/:id')
|
||||
@HttpCode(204)
|
||||
@Auth()
|
||||
async patch(@Body() body: PartialNoteModel, @Param() params: MongoIdDto) {
|
||||
await this.noteService.updateById(params.id, body)
|
||||
return
|
||||
}
|
||||
|
||||
@Get('like/:id')
|
||||
@HttpCode(204)
|
||||
async likeNote(
|
||||
@Param() param: IntIdOrMongoIdDto,
|
||||
@IpLocation() location: IpRecord,
|
||||
) {
|
||||
const id =
|
||||
typeof param.id === 'number'
|
||||
? (await this.noteService.model.findOne({ nid: param.id }).lean())._id
|
||||
: param.id
|
||||
if (!id) {
|
||||
throw new CannotFindException()
|
||||
}
|
||||
try {
|
||||
const res = await this.countingService.updateLikeCount(
|
||||
'Note',
|
||||
id,
|
||||
location.ip,
|
||||
)
|
||||
if (!res) {
|
||||
throw new BadRequestException('你已经喜欢过啦!')
|
||||
}
|
||||
return
|
||||
} catch (e: any) {
|
||||
throw new BadRequestException(e)
|
||||
}
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@Auth()
|
||||
@HttpCode(204)
|
||||
async deleteNote(@Param() params: MongoIdDto) {
|
||||
await this.noteService.deleteById(params.id)
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: '根据 nid 查找' })
|
||||
@Get('/nid/:nid')
|
||||
@UpdateDocumentCount('Note')
|
||||
async getNoteByNid(
|
||||
@Param() params: NidType,
|
||||
@IsMaster() isMaster: boolean,
|
||||
@Query() query: PasswordQueryDto,
|
||||
@Query('single') isSingle?: boolean,
|
||||
) {
|
||||
const id = await this.noteService.getIdByNid(params.nid)
|
||||
if (!id) {
|
||||
throw new CannotFindException()
|
||||
}
|
||||
return await this.getOneNote({ id }, isMaster, query, isSingle)
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: '根据 nid 修改' })
|
||||
@Put('/nid/:nid')
|
||||
@Auth()
|
||||
async modifyNoteByNid(@Param() params: NidType, @Body() body: NoteModel) {
|
||||
const id = await this.noteService.getIdByNid(params.nid)
|
||||
if (!id) {
|
||||
throw new CannotFindException()
|
||||
}
|
||||
return await this.modify(body, {
|
||||
id,
|
||||
})
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: '搜索' })
|
||||
@Get('/search')
|
||||
@Paginator
|
||||
async searchNote(@Query() query: SearchDto, @IsMaster() isMaster: boolean) {
|
||||
const { keyword, page, size } = query
|
||||
const select = '_id title created modified nid'
|
||||
|
||||
const keywordArr = keyword
|
||||
.split(/\s+/)
|
||||
.map((item) => new RegExp(String(item), 'ig'))
|
||||
|
||||
return await this.noteService.model.paginate(
|
||||
{
|
||||
$or: [{ title: { $in: keywordArr } }, { text: { $in: keywordArr } }],
|
||||
$and: [
|
||||
{ password: { $in: [undefined, null] } },
|
||||
{ hide: { $in: isMaster ? [false, true] : [false] } },
|
||||
{
|
||||
$or: [
|
||||
{ secret: { $in: [undefined, null] } },
|
||||
{ secret: { $lte: new Date() } },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
limit: size,
|
||||
page,
|
||||
select,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Transform } from 'class-transformer'
|
||||
import {
|
||||
IsDefined,
|
||||
IsEnum,
|
||||
IsIn,
|
||||
IsInt,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
@@ -15,11 +15,11 @@ import { PagerDto } from '~/shared/dto/pager.dto'
|
||||
|
||||
export class NoteQueryDto extends PagerDto {
|
||||
@IsOptional()
|
||||
@IsEnum(['title', 'created', 'modified', 'weather', 'mood'])
|
||||
@IsIn(['title', 'created', 'modified', 'weather', 'mood'])
|
||||
sortBy?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum([1, -1])
|
||||
@IsIn([1, -1])
|
||||
@ValidateIf((o) => o.sortBy)
|
||||
@Transform(({ value: v }) => v | 0)
|
||||
sortOrder?: 1 | -1
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { DocumentType } from '@typegoose/typegoose'
|
||||
import { compareSync } from 'bcrypt'
|
||||
import { isDefined } from 'class-validator'
|
||||
import { pick } from 'lodash'
|
||||
import { FilterQuery } from 'mongoose'
|
||||
import { InjectModel } from 'nestjs-typegoose'
|
||||
import { CannotFindException } from '~/common/exceptions/cant-find.exception'
|
||||
@@ -91,14 +93,15 @@ export class NoteService {
|
||||
}
|
||||
|
||||
public async updateById(id: string, doc: Partial<NoteModel>) {
|
||||
console.log(NoteModel.protectedKeys)
|
||||
|
||||
deleteKeys(doc, NoteModel.protectedKeys as any)
|
||||
deleteKeys(doc, ...NoteModel.protectedKeys)
|
||||
if (['title', 'text'].some((key) => isDefined(doc[key]))) {
|
||||
doc.modified = new Date()
|
||||
}
|
||||
await this.noteModel.updateOne(
|
||||
{
|
||||
_id: id,
|
||||
},
|
||||
{ ...doc, modified: new Date() },
|
||||
{ ...doc },
|
||||
)
|
||||
process.nextTick(async () => {
|
||||
Promise.all([
|
||||
@@ -115,6 +118,42 @@ export class NoteService {
|
||||
})
|
||||
}
|
||||
|
||||
async deleteById(id: string) {
|
||||
const doc = await this.noteModel.findById(id)
|
||||
if (!doc) {
|
||||
throw new CannotFindException()
|
||||
}
|
||||
|
||||
await this.noteModel.deleteOne({
|
||||
_id: id,
|
||||
})
|
||||
|
||||
process.nextTick(async () => {
|
||||
await Promise.all([
|
||||
this.webGateway.broadcast(
|
||||
EventTypes.NOTE_DELETE,
|
||||
pick(doc, ['_id', 'id', 'nid', 'created', 'modified']),
|
||||
),
|
||||
])
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找 nid 时候正确,返回 _id
|
||||
*
|
||||
* @param {number} nid
|
||||
* @returns {Types.ObjectId}
|
||||
*/
|
||||
async getIdByNid(nid: number) {
|
||||
const document = await this.model.findOne({
|
||||
nid,
|
||||
})
|
||||
if (!document) {
|
||||
return null
|
||||
}
|
||||
return document._id
|
||||
}
|
||||
|
||||
async needCreateDefult() {
|
||||
await this.noteModel.countDocuments({}).then((count) => {
|
||||
if (!count) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
@@ -15,10 +16,12 @@ import { ApiOperation } from '@nestjs/swagger'
|
||||
import { Types } from 'mongoose'
|
||||
import { Auth } from '~/common/decorator/auth.decorator'
|
||||
import { Paginator } from '~/common/decorator/http.decorator'
|
||||
import { IpLocation, IpRecord } from '~/common/decorator/ip.decorator'
|
||||
import { ApiName } from '~/common/decorator/openapi.decorator'
|
||||
import { IsMaster } from '~/common/decorator/role.decorator'
|
||||
import { UpdateDocumentCount } from '~/common/decorator/update-count.decorator'
|
||||
import { CannotFindException } from '~/common/exceptions/cant-find.exception'
|
||||
import { CountingService } from '~/processors/helper/helper.counting.service'
|
||||
import { MongoIdDto } from '~/shared/dto/id.dto'
|
||||
import { SearchDto } from '~/shared/dto/search.dto'
|
||||
import {
|
||||
@@ -32,7 +35,10 @@ import { PostService } from './post.service'
|
||||
@Controller('posts')
|
||||
@ApiName
|
||||
export class PostController {
|
||||
constructor(private readonly postService: PostService) {}
|
||||
constructor(
|
||||
private readonly postService: PostService,
|
||||
private readonly countingService: CountingService,
|
||||
) {}
|
||||
|
||||
@Get('/')
|
||||
@Paginator
|
||||
@@ -140,6 +146,7 @@ export class PostController {
|
||||
}
|
||||
|
||||
@Get('search')
|
||||
@Paginator
|
||||
async searchPost(@Query() query: SearchDto, @IsMaster() isMaster: boolean) {
|
||||
const { keyword, page, size } = query
|
||||
const select = '_id title created modified categoryId slug'
|
||||
@@ -149,7 +156,7 @@ export class PostController {
|
||||
return await this.postService.findWithPaginator(
|
||||
{
|
||||
$or: [{ title: { $in: keywordArr } }, { text: { $in: keywordArr } }],
|
||||
...addConditionToSeeHideContent(isMaster),
|
||||
$and: [{ ...addConditionToSeeHideContent(isMaster) }],
|
||||
},
|
||||
{
|
||||
limit: size,
|
||||
@@ -159,4 +166,24 @@ export class PostController {
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Get('_thumbs-up')
|
||||
@HttpCode(204)
|
||||
async thumbsUpArticle(
|
||||
@Query() query: MongoIdDto,
|
||||
@IpLocation() location: IpRecord,
|
||||
) {
|
||||
const { ip } = location
|
||||
const { id } = query
|
||||
try {
|
||||
const res = await this.countingService.updateLikeCount('Post', id, ip)
|
||||
if (!res) {
|
||||
throw new BadRequestException('你已经支持过啦!')
|
||||
}
|
||||
} catch (e: any) {
|
||||
throw new BadRequestException(e)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
111
src/processors/helper/helper.counting.service.ts
Normal file
111
src/processors/helper/helper.counting.service.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { Injectable, Logger } from '@nestjs/common'
|
||||
import { Reflector } from '@nestjs/core'
|
||||
import { InjectModel } from 'nestjs-typegoose'
|
||||
import { ArticleType } from '~/constants/article.constant'
|
||||
import { RedisKeys } from '~/constants/cache.constant'
|
||||
import { NoteModel } from '~/modules/note/note.model'
|
||||
import { PostModel } from '~/modules/post/post.model'
|
||||
import { getRedisKey } from '~/utils/redis.util'
|
||||
import { CacheService } from '../cache/cache.service'
|
||||
|
||||
@Injectable()
|
||||
export class CountingService {
|
||||
private logger: Logger
|
||||
constructor(
|
||||
private readonly reflector: Reflector,
|
||||
@InjectModel(PostModel)
|
||||
private readonly postModel: MongooseModel<PostModel>,
|
||||
@InjectModel(NoteModel)
|
||||
private readonly noteModel: MongooseModel<NoteModel>,
|
||||
private readonly redis: CacheService,
|
||||
) {
|
||||
this.logger = new Logger(CountingService.name)
|
||||
}
|
||||
|
||||
get modelMap() {
|
||||
return {
|
||||
Post: this.postModel,
|
||||
Note: this.noteModel,
|
||||
} as const
|
||||
}
|
||||
|
||||
private checkIdAndIp(id: string, ip: string) {
|
||||
if (!ip) {
|
||||
this.logger.debug('无法更新阅读计数, IP 无效')
|
||||
return false
|
||||
}
|
||||
if (!id) {
|
||||
this.logger.debug('无法更新阅读计数, ID 不存在')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public async updateReadCount(
|
||||
type: keyof typeof ArticleType,
|
||||
id: string,
|
||||
ip: string,
|
||||
) {
|
||||
if (!this.checkIdAndIp(id, ip)) {
|
||||
return
|
||||
}
|
||||
|
||||
const model = this.modelMap[type]
|
||||
const doc = await model.findById(id)
|
||||
|
||||
if (!doc) {
|
||||
this.logger.debug('无法更新阅读计数, 文档不存在')
|
||||
return
|
||||
}
|
||||
|
||||
const redis = this.redis.getClient()
|
||||
|
||||
const isReadBefore = await redis.sismember(
|
||||
getRedisKey(RedisKeys.Read, id),
|
||||
ip,
|
||||
)
|
||||
if (isReadBefore) {
|
||||
this.logger.debug('已经增加过计数了, ' + id)
|
||||
return
|
||||
}
|
||||
await Promise.all([
|
||||
redis.sadd(getRedisKey(RedisKeys.Read, doc._id), ip),
|
||||
doc.updateOne({ $inc: { 'count.read': 1 } }),
|
||||
])
|
||||
this.logger.debug('增加阅读计数, (' + doc.title)
|
||||
}
|
||||
|
||||
public async updateLikeCount(
|
||||
type: keyof typeof ArticleType,
|
||||
id: string,
|
||||
ip: string,
|
||||
): Promise<boolean> {
|
||||
if (!this.checkIdAndIp(id, ip)) {
|
||||
throw '无法获取到 IP'
|
||||
}
|
||||
|
||||
const model = this.modelMap[type]
|
||||
const doc = await model.findById(id)
|
||||
|
||||
if (!doc) {
|
||||
throw '无法更新喜欢计数, 文档不存在'
|
||||
}
|
||||
|
||||
const redis = this.redis.getClient()
|
||||
|
||||
const isLikeBefore = await redis.sismember(
|
||||
getRedisKey(RedisKeys.Like, id),
|
||||
ip,
|
||||
)
|
||||
if (isLikeBefore) {
|
||||
this.logger.debug('已经增加过计数了, ' + id)
|
||||
return false
|
||||
}
|
||||
await Promise.all([
|
||||
redis.sadd(getRedisKey(RedisKeys.Like, doc._id), ip),
|
||||
doc.updateOne({ $inc: { 'count.like': 1 } }),
|
||||
])
|
||||
this.logger.debug('增加喜欢计数, (' + doc.title)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,15 @@
|
||||
import { Global, Module, Provider } from '@nestjs/common'
|
||||
import { CountingService } from './helper.counting.service'
|
||||
import { EmailService } from './helper.email.service'
|
||||
import { HttpService } from './helper.http.service'
|
||||
import { ImageService } from './helper.image.service'
|
||||
|
||||
const providers: Provider<any>[] = [EmailService, HttpService, ImageService]
|
||||
const providers: Provider<any>[] = [
|
||||
EmailService,
|
||||
HttpService,
|
||||
ImageService,
|
||||
CountingService,
|
||||
]
|
||||
|
||||
@Module({ imports: [], providers: providers, exports: providers })
|
||||
@Global()
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
import { UnprocessableEntityException } from '@nestjs/common'
|
||||
import { ApiProperty } from '@nestjs/swagger'
|
||||
import { IsMongoId } from 'class-validator'
|
||||
import { IsBooleanOrString } from '~/utils/validator/isBooleanOrString'
|
||||
import { Transform } from 'class-transformer'
|
||||
import { IsDefined, isMongoId, IsMongoId } from 'class-validator'
|
||||
|
||||
export class MongoIdDto {
|
||||
@IsMongoId()
|
||||
@ApiProperty({
|
||||
name: 'id',
|
||||
// enum: ['5e6f67e75b303781d2807279', '5e6f67e75b303781d280727f'],
|
||||
example: '5e6f67e75b303781d2807278',
|
||||
})
|
||||
@ApiProperty({ example: '5e6f67e75b303781d2807278' })
|
||||
id: string
|
||||
}
|
||||
|
||||
export class IntIdOrMongoIdDto {
|
||||
@IsBooleanOrString()
|
||||
@ApiProperty({ example: ['12', '5e6f67e75b303781d2807278'] })
|
||||
@IsDefined()
|
||||
@Transform(({ value }) => {
|
||||
if (isMongoId(value)) {
|
||||
return value
|
||||
}
|
||||
const nid = +value
|
||||
if (!isNaN(nid)) {
|
||||
return nid
|
||||
}
|
||||
throw new UnprocessableEntityException('Invalid id')
|
||||
})
|
||||
@ApiProperty({ example: [12, '5e6f67e75b303781d2807278'] })
|
||||
id: string | number
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export class BaseModel {
|
||||
created?: Date
|
||||
|
||||
static get protectedKeys() {
|
||||
return ['created']
|
||||
return ['created', 'id', '_id']
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,16 +47,35 @@ export function arrDifference(a1: string[], a2: string[]) {
|
||||
return diff
|
||||
}
|
||||
|
||||
export const deleteKeys = <T extends KV>(
|
||||
export function deleteKeys<T extends KV>(
|
||||
target: T,
|
||||
keys: (keyof T)[],
|
||||
): Partial<T>
|
||||
export function deleteKeys<T extends KV>(
|
||||
target: T,
|
||||
keys: readonly (keyof T)[],
|
||||
): Partial<T> => {
|
||||
): Partial<T>
|
||||
export function deleteKeys<T extends KV>(
|
||||
target: T,
|
||||
...keys: string[]
|
||||
): Partial<T>
|
||||
export function deleteKeys<T extends KV>(
|
||||
target: T,
|
||||
...keys: any[]
|
||||
): Partial<T> {
|
||||
if (!isObject(target)) {
|
||||
throw new TypeError('target must be Object, got ' + target)
|
||||
}
|
||||
|
||||
for (const key of keys) {
|
||||
Reflect.deleteProperty(target, key)
|
||||
if (Array.isArray(keys[0])) {
|
||||
for (const key of keys[0]) {
|
||||
Reflect.deleteProperty(target, key)
|
||||
}
|
||||
} else {
|
||||
for (const key of keys) {
|
||||
Reflect.deleteProperty(target, key)
|
||||
}
|
||||
}
|
||||
|
||||
return target
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user