diff --git a/src/app.module.ts b/src/app.module.ts index 67f7bad..21306aa 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -4,6 +4,7 @@ import { Inject, MiddlewareConsumer, Module, NestModule } from '@nestjs/common'; import { APP_GUARD } from '@nestjs/core'; import { EventEmitterModule } from '@nestjs/event-emitter'; import { MongooseModule } from '@nestjs/mongoose/dist/mongoose.module'; +import { Cache } from 'cache-manager'; import { redisClusterInsStore, redisInsStore } from 'cache-manager-redis-yet'; import * as config from 'src/constants'; @@ -70,7 +71,7 @@ import { UserModule } from './user'; ], }) export class AppModule implements NestModule { - constructor(@Inject(CACHE_MANAGER) private readonly cacheManager: any) {} + constructor(@Inject(CACHE_MANAGER) private readonly cacheManager: Cache) {} configure(consumer: MiddlewareConsumer): void { consumer.apply(RouteLoggerMiddleware).exclude('/hello').forRoutes('*'); diff --git a/src/common/cache.interceptor.ts b/src/common/cache.interceptor.ts index 43d87f3..ed3a5ce 100644 --- a/src/common/cache.interceptor.ts +++ b/src/common/cache.interceptor.ts @@ -9,9 +9,12 @@ import { NestInterceptor, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; +import Debug from 'debug'; import { isArray } from 'lodash'; import { Observable, tap } from 'rxjs'; +const debug = Debug('commom:cache-interceptor'); + function compileKey(keyStr: string, data: any) { if (!keyStr) return keyStr; if (!data) return keyStr; @@ -56,9 +59,12 @@ export class UnsetCacheInterceptor implements NestInterceptor { tap((data) => { // 从返回结果构造 key cacheKey = compileKey(cacheKey, data?.toJSON ? data.toJSON() : data); + // 清除缓存 typeof cacheKey === 'string' && // cacheKey 可能用逗号分隔 多个 key cacheKey.split(',').forEach((key) => this.cacheService.del(key)); + + debug(`unset cache: ${cacheKey}`); }) ); } @@ -100,6 +106,8 @@ export class SetCacheInterceptor extends CacheInterceptor { cacheKey = compileKey(cacheKey, { payload, ...payload }); } + debug(`set cache: ${cacheKey}`); + return cacheKey || super.trackBy(context); } } diff --git a/src/namespace/namespace.controller.ts b/src/namespace/namespace.controller.ts index ca44de8..bf4105b 100644 --- a/src/namespace/namespace.controller.ts +++ b/src/namespace/namespace.controller.ts @@ -1,5 +1,7 @@ +import { CacheKey } from '@nestjs/cache-manager'; import { Body, + CacheTTL, ConflictException, Controller, Delete, @@ -12,6 +14,7 @@ import { Post, Query, Res, + UseInterceptors, } from '@nestjs/common'; import { ApiCreatedResponse, @@ -24,6 +27,7 @@ import { } from '@nestjs/swagger'; import { Response } from 'express'; +import { SetCacheInterceptor, UnsetCacheInterceptor } from 'src/common'; import { ErrorCodes } from 'src/constants'; import { CreateNamespaceDto } from './dto/create-namespace.dto'; @@ -117,6 +121,8 @@ export class NamespaceController { type: 'string', description: 'Namespace id or key, if key should encodeURIComponent', }) + @UseInterceptors(SetCacheInterceptor) + @CacheTTL(24 * 3600) @Get(':namespaceIdOrKey') async get(@Param('namespaceIdOrKey') namespaceIdOrKey: string): Promise { const namespace = await this.namespaceService.get(namespaceIdOrKey); @@ -137,6 +143,8 @@ export class NamespaceController { description: 'The namespace updated.', type: Namespace, }) + @UseInterceptors(UnsetCacheInterceptor) + @CacheKey('/namespaces/:id,/namespaces/:key') @Patch(':namespaceIdOrKey') async update( @Param('namespaceIdOrKey') namespaceIdOrKey: string, @@ -158,6 +166,7 @@ export class NamespaceController { @ApiOperation({ operationId: 'deleteNamespace' }) @ApiNoContentResponse({ description: 'No content.' }) @HttpCode(HttpStatus.NO_CONTENT) + @UseInterceptors(UnsetCacheInterceptor) @Delete(':namespaceId') async delete(@Param('namespaceId') namespaceId: string): Promise { await this.namespaceService.delete(namespaceId); diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index fcb8f56..c479cef 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -1,18 +1,22 @@ +import { CacheKey } from '@nestjs/cache-manager'; import { BadRequestException, Body, + CacheTTL, ConflictException, Controller, Delete, Get, HttpCode, HttpStatus, + Inject, NotFoundException, Param, Patch, Post, Query, Res, + UseInterceptors, } from '@nestjs/common'; import { ApiCreatedResponse, @@ -23,7 +27,9 @@ import { ApiTags, } from '@nestjs/swagger'; import { Response } from 'express'; +import { RedisClientType } from 'redis'; +import { SetCacheInterceptor, UnsetCacheInterceptor } from 'src/common'; import * as config from 'src/constants'; import { NamespaceService } from 'src/namespace'; @@ -41,7 +47,8 @@ import { verifyIdentity } from './verify-identity'; export class UserController { constructor( private readonly userService: UserService, - private readonly namespaceService: NamespaceService + private readonly namespaceService: NamespaceService, + @Inject('REDIS_CLIENT') private readonly redis: RedisClientType ) {} /** @@ -153,6 +160,7 @@ export class UserController { const count = await this.userService.count(query); const data = await this.userService.list(query); res.set({ 'X-Total-Count': count.toString() }).json(data); + return data; } @@ -169,6 +177,8 @@ export class UserController { type: 'string', description: 'User id', }) + @UseInterceptors(SetCacheInterceptor) + @CacheTTL(24 * 3600) @Get(':userId') async get(@Param('userId') userId: string): Promise { const user = await this.userService.get(userId); @@ -190,6 +200,8 @@ export class UserController { type: User, }) @Patch(':userId') + @UseInterceptors(UnsetCacheInterceptor) + @CacheKey('/users/:id') async update( @Param('userId') userId: string, @Body() updateDto: UpdateUserDto @@ -288,6 +300,8 @@ export class UserController { description: 'The user upserted.', type: User, }) + @UseInterceptors(UnsetCacheInterceptor) + @CacheKey('/users/:id') @Post(':employeeId/@upsertUserByEmployeeId') async upsertByEmployeeId( @Param('employeeId') employeeId: string, @@ -387,6 +401,8 @@ export class UserController { description: 'The user upserted.', type: User, }) + @UseInterceptors(UnsetCacheInterceptor) + @CacheKey('/users/:id') @Post(':username/@upsertUserByUsername') async upsertByUsername( @Param('username') username: string, @@ -404,6 +420,8 @@ export class UserController { description: 'The user upserted.', type: User, }) + @UseInterceptors(UnsetCacheInterceptor) + @CacheKey('/users/:id') @Post(':email/@upsertUserByEmail') async upsertByEmail( @Param('email') email: string, @@ -420,6 +438,8 @@ export class UserController { description: 'The user upserted.', type: User, }) + @UseInterceptors(UnsetCacheInterceptor) + @CacheKey('/users/:id') @Post(':phone/@upsertUserByPhone') async upsertByPhone( @Param('phone') phone: string, @@ -434,6 +454,7 @@ export class UserController { @ApiOperation({ operationId: 'deleteUser' }) @ApiNoContentResponse({ description: 'No content.' }) @HttpCode(HttpStatus.NO_CONTENT) + @UseInterceptors(UnsetCacheInterceptor) @Delete(':userId') async delete(@Param('userId') userId: string): Promise { await this.userService.delete(userId); @@ -448,6 +469,8 @@ export class UserController { type: User, }) @HttpCode(HttpStatus.OK) + @UseInterceptors(UnsetCacheInterceptor) + @CacheKey('/users/:id') @Post(':userId/@verifyIdentity') async verifyIdentity(@Param('userId') userId: string): Promise { const user = await this.userService.get(userId);