From a69396001743f5c39f49fbd084749e2b7c598b70 Mon Sep 17 00:00:00 2001 From: luoer <952222@163.com> Date: Thu, 17 Aug 2023 20:16:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=99=BB=E9=99=86?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- content/database/db.sqlite | Bin 147456 -> 163840 bytes src/database/database.module.ts | 45 ++++++++----- src/database/entities/base.ts | 24 ++++--- src/database/suscribers/entify.subscriber.ts | 40 ++++++++++++ src/database/suscribers/request.middleware.ts | 11 ++++ src/modules/auth/auth.controller.ts | 4 +- src/modules/auth/auth.service.ts | 3 +- src/modules/role/dto/update-role.dto.ts | 7 ++- src/modules/role/entities/role.entity.ts | 13 +++- src/modules/role/role.service.ts | 13 +++- src/modules/user/dto/create-user.dto.ts | 24 +++---- src/modules/user/entities/user.entity.ts | 2 +- src/modules/user/user.service.ts | 22 +++++-- src/monitor/log/dto/create-log.dto.ts | 10 +++ src/monitor/log/dto/find-log.dto.ts | 13 ++++ src/monitor/log/dto/update-log.dto.ts | 4 ++ src/monitor/log/entities/authLog.entity.ts | 47 ++++++++++++++ src/monitor/log/entities/log.entity.ts | 2 + src/monitor/log/index.ts | 5 ++ .../log/interceptors/authLog.interceptor.ts | 22 +++++++ src/monitor/log/log.controller.ts | 59 ++++++++++++++++++ src/monitor/log/log.module.ts | 13 ++++ src/monitor/log/log.service.ts | 55 ++++++++++++++++ src/types/env.d.ts | 1 + 24 files changed, 389 insertions(+), 50 deletions(-) create mode 100644 src/database/suscribers/entify.subscriber.ts create mode 100644 src/database/suscribers/request.middleware.ts create mode 100644 src/monitor/log/dto/create-log.dto.ts create mode 100644 src/monitor/log/dto/find-log.dto.ts create mode 100644 src/monitor/log/dto/update-log.dto.ts create mode 100644 src/monitor/log/entities/authLog.entity.ts create mode 100644 src/monitor/log/entities/log.entity.ts create mode 100644 src/monitor/log/index.ts create mode 100644 src/monitor/log/interceptors/authLog.interceptor.ts create mode 100644 src/monitor/log/log.controller.ts create mode 100644 src/monitor/log/log.module.ts create mode 100644 src/monitor/log/log.service.ts diff --git a/content/database/db.sqlite b/content/database/db.sqlite index b9145fb854337b753f95614b8689f29d8f8cbebf..596a4311ae0d1118f4da35dde4367c79723758a6 100644 GIT binary patch delta 3531 zcmcgudvKK16~E_uKX&(Kfh46^vD41vVr+%3~pqG7hCi z*My9v1Ndnd6@(e+Vf*pJAx4UtJI&J@; z-_E_?ch8>3J?EZt?ysv$?dsCDdz>2xA>}x991@NPcia<%4f~hwp-^%lhszmvmPoXZ z&=d4c+D9+YoAe5ODe27FNbjZlNda`wi)1p+XGtD(w60wrYm2r$xukRbnpm_&S2R8s z+G1;3MTcq}`o@GH8jzv0UD1)=+1}bVUbFLV$Z*?`x<1z4k!wdjj?N-mlC@aX<(udgZ+@<;qNi3>&G_4q|g_9o6mrnl)$d6Br4e-Hje zX6)Eeawwx9UR7L_d$)8tufmxz*wW8_+YN-zLRmz=FA@wzDr+tu+C6yq&GJBK%G%iS z=+G};xRiV`;f!$Z#LODbalbzhT)_sUr_mM|_oxT_rP!W3Fwpc3RE3YniofC6u5o_ONVbImT=|a8MjM zI2cXB-B$6*rq_O9Gliw_nU#MU&%21+z`OJV__xN}daD$BDy_*1Sj8dXb)^XJ|iTe}m3(c~oZIqp(>|eM=TaWOIRF z%{-g)EDv(o?iBg~(7)0_dX}EI6knkq(D#|j-_RzPXsHnu=Cvv_QBGptmPS#qBEqPA zDkOIGIH7zsp^sBle??uZoKT9Sb^3R;)7k^V5lwV#wh!aq+sFdLnNw~qJPLyxALmOF zb?<=}1mmh~NZ*gPEn68)+;<9QXTGF)+^^hif39kE@KW_Ez)g5V#a$P;+@ZH-OmT22th+ zk2s#P^^qG=CvGC^Q650|&(y>pog(KK6+zGDj%f3m)-|yetJW>SdE`U7e_?%HW4*Vr z?wfP!y%R_1iQo3arc7||43r}ssC&E<*R5K%+UU~U#(=}ity-UW`UK<((Uz7r>q~BK zi)}>IVJ)~=d*bPS7%NDNp~vW#i~jr5a8G$wnk?t%=E9bk0_{{qWM; zmTVLod+q<}kc|C~#|l`FQs_y*viTeR1Utrg`bT;a8_+Ss!q@l6%kjlfL$SDt|1}IF*wy0yBcesz zftJevFZ20={(ry?g~FI7qMw&{^G(=#uG*F>1KLh(NtQ+jB zFG-E@rLJKN<35|Kf#`3`JNO1|s&YncRGMv){;D3(e#eaQT5qkC!8=FI4BkP^=l|kv vwt$31N?tQR<<)6o8dVwB4i%>nl7AGENI?%qO1ckU=93$f_uU1Av`hInkThSl delta 2557 zcmd5;eNa@_6~E`*-M8<(eQ)nufrS-678Z({Q1^q6Wwl~vN>M_nD4ldLMj_Baf0&Tdz+nR7uoL_H{ANOYzZ4D>Cn@>t+S)6p=;;n?cL2?>5@n@psS;;+0Jef zN$za!ayj^$%NIQ&NeEafS(Y<-d#6IF7q)k{bu=_NB|xyVqq`?vLzL*&L=-m5?mM2b zpQ4jO7OCfMeYH`)rAy?mf9(}LYQC>^T40{Q~@T)KNlr8l-t7qa=q3a7bO z7oLO#=va2+R3%;Rd%W-&X{8ejYJFZk)98YK|)1%uJz(rAz?LnBLY zO^tVP{#@+wpAOy`J<`+M-4iXcISFHIPTthi#HU`)6T;#1osHD+>6&PC>^vyW$JN${ z;*fDDIBfrS>1ly1I|e4QNt}t>Iz~3b{=#mvNlZ-@H5$8dKl^{YLC{zJJI%Z7{hRiF zga3K8oq$*w)ZJee}ht!9C^~H#`G+ zNKY-ny2{2S%hKkSm`|m_^WZCCDf}UCe@>-06bMEN17(H55??3~Eei0NS&#W}2K0gc zm@B2~PZVQp+>6Xyqx5n7LNR8yn=fIaGScU%As@>ah`YVqpQrEfDojnH%+Lq$$lxhDAt)N zsudynWtzvv_`BPdgIFljeFU;o`%d?#psf40|aJ0CDe^BmIEFu6lOBj;Ry1%hB zixxnpbK=hk`;4R|05@(xmgmF|2zx+0DE^S?b{oiY;%Wd2*@J{K5c9!iP*!6^_s}xC zhtg@5X{P`l!LpxW8TtZPY=JZZQq6goh~jm^HZoZokgrLr#Ova9+xxZ`=_HK^L&7?^ z1`#qu)^WGK#9TiPQ-ZnZEqDdYW50sGg0TU$u_eLZjU4AkU<_#8Ld-)$%x{nBA*F ziPSC>WG-C_0~G%)wEpes=xJzdYfiMa-J;)-nURr_S>4MTIk9cGU_XCO8sJsxFJo)s z;4#OpK_j(-T_3x510vw#KeVUC^t-UqrnHlNv0cB0Lb28-W;U1eLtX-Cm2uEa?)wN?c*}3FRf}k~mGj zWe>xvcyb(+zKA;&Mx7h{QXpb6V5rDEN&*QkmA|g0fam8Yfdp+zbC9;8Z*+xB@{0?#0Mu=@h?G$$^ z%cS4Rby6b+zsF|ewf_l>p4MsLyIUS|TA?G41*72zU(s5|+qd@dPg=esE3c7_iT45i z?X7+#Xzec$ux|W{)_gv*wNG)C!qvH>;Lo)dID*(qqQL;N>L3`K+9m+6C%m0h_;Pz5 MAADCqqC5!y0(6~>p#T5? diff --git a/src/database/database.module.ts b/src/database/database.module.ts index 4a4f65b..01bc2f4 100644 --- a/src/database/database.module.ts +++ b/src/database/database.module.ts @@ -1,24 +1,37 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { ConfigService } from '@/config'; import { SnakeNamingStrategy } from 'typeorm-naming-strategies'; +import { EntitySubscripber } from './suscribers/entify.subscriber'; +import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'; +import { RequestMiddleware } from './suscribers/request.middleware'; /** * 数据库模块 * @description 基于 `typeorm` 封装 */ -export const DatabaseModule = TypeOrmModule.forRootAsync({ - useFactory: (config: ConfigService) => { - if (config.dbType === 'sqlite') { - return { - type: config.dbType, - database: config.dbSqlitePath, - synchronize: true, - autoLoadEntities: true, - namingStrategy: new SnakeNamingStrategy(), - }; - } - if (config.dbType === 'mysql') { - } - }, - inject: [ConfigService], -}); +@Module({ + imports: [ + TypeOrmModule.forRootAsync({ + useFactory: (config: ConfigService) => { + if (config.dbType === 'sqlite') { + return { + type: config.dbType, + database: config.dbSqlitePath, + synchronize: true, + autoLoadEntities: true, + namingStrategy: new SnakeNamingStrategy(), + }; + } + if (config.dbType === 'mysql') { + } + }, + inject: [ConfigService], + }), + ], + providers: [EntitySubscripber], +}) +export class DatabaseModule implements NestModule { + configure(consumer: MiddlewareConsumer) { + consumer.apply(RequestMiddleware).forRoutes('*'); + } +} diff --git a/src/database/entities/base.ts b/src/database/entities/base.ts index d7cc7c3..f632dd1 100644 --- a/src/database/entities/base.ts +++ b/src/database/entities/base.ts @@ -13,42 +13,48 @@ export class BaseEntity { */ @PrimaryGeneratedColumn({ comment: '自增ID' }) id: number; + /** * 创建时间 * @example "2022-01-01 10:10:10" */ @CreateDateColumn({ comment: '创建时间' }) createdAt: Date; + /** - * 创建人ID - * @example 1 + * 创建人 + * @example '绝弹(1)' */ @Column({ comment: '创建人', nullable: true }) - createdBy: number; + createdBy: string; + /** * 更新时间 * @example "2022-01-02 11:11:11" */ @UpdateDateColumn({ comment: '更新时间' }) updatedAt: Date; + /** - * 更新人ID - * @example 1 + * 更新人 + * @example '绝弹(1)' */ @Column({ comment: '更新人', nullable: true }) - updatedBy: number; + updatedBy: string; + /** * 删除时间 * @example "2022-01-03 12:12:12" */ @Exclude() - @DeleteDateColumn({ comment: '删除时间' }) + @DeleteDateColumn({ comment: '删除时间', select: false }) deleteddAt: Date; + /** * 删除人ID * @example 1 */ @Exclude() - @Column({ comment: '删除人', nullable: true }) - deletedBy: number; + @Column({ comment: '删除人', nullable: true, select: false }) + deletedBy: string; } diff --git a/src/database/suscribers/entify.subscriber.ts b/src/database/suscribers/entify.subscriber.ts new file mode 100644 index 0000000..63c9803 --- /dev/null +++ b/src/database/suscribers/entify.subscriber.ts @@ -0,0 +1,40 @@ +import { Injectable } from '@nestjs/common'; +import { Request } from 'express'; +import { EntitySubscriberInterface, InsertEvent, DataSource, UpdateEvent, SoftRemoveEvent } from 'typeorm'; + +/** + * 实体订阅器 + * @description 自动插入创建/更新用户的ID + */ +@Injectable() +export class EntitySubscripber implements EntitySubscriberInterface { + static request: Request; + + constructor(private datasource: DataSource) { + this.datasource.subscribers.push(this); + } + + static setRequest(req: Request) { + this.request = req; + } + + beforeInsert(event: InsertEvent): void | Promise { + event.entity.createdBy = this.getUser(); + } + + beforeUpdate(event: UpdateEvent): void | Promise { + event.entity.updatedBy = this.getUser(); + } + + beforeSoftRemove(event: SoftRemoveEvent): void | Promise { + event.entity.deletedBy = this.getUser(); + } + + getUser() { + const user = EntitySubscripber.request?.user; + if (!user) { + return; + } + return `${user.nickname}(${user.id})`; + } +} diff --git a/src/database/suscribers/request.middleware.ts b/src/database/suscribers/request.middleware.ts new file mode 100644 index 0000000..7b08b70 --- /dev/null +++ b/src/database/suscribers/request.middleware.ts @@ -0,0 +1,11 @@ +import { Injectable, NestMiddleware } from '@nestjs/common'; +import { Request } from 'express'; +import { EntitySubscripber } from './entify.subscriber'; + +@Injectable() +export class RequestMiddleware implements NestMiddleware { + use(req: R, res: T, next: (error?: any) => void) { + EntitySubscripber.setRequest(req); + next(); + } +} diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index a392ba8..cd8f3a4 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -1,9 +1,10 @@ -import { Body, Controller, HttpCode, HttpStatus, Post } from '@nestjs/common'; +import { Body, Controller, HttpCode, HttpStatus, Post, UseInterceptors } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { AuthService } from './auth.service'; import { AuthUserDto } from './dto/auth-user.dto'; import { Public } from './jwt'; import { LoginedUserVo } from './vo/logined-user.vo'; +import { AuthLogInterceptor } from '@/monitor/log'; @ApiTags('auth') @Controller('auth') @@ -16,6 +17,7 @@ export class AuthController { @Post('login') @Public() @HttpCode(HttpStatus.OK) + @UseInterceptors(AuthLogInterceptor) login(@Body() user: AuthUserDto): Promise { return this.authService.signIn(user); } diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index a83c445..5150ee3 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -17,7 +17,8 @@ export class AuthService { throw new UnauthorizedException('密码错误'); } const loginedUser = Object.assign(new LoginedUserVo(), user); - loginedUser.token = await this.jwtService.signAsync({ id: user.id, username: user.username }); + const { id, username, nickname } = loginedUser; + loginedUser.token = await this.jwtService.signAsync({ id, username, nickname }); return loginedUser; } } diff --git a/src/modules/role/dto/update-role.dto.ts b/src/modules/role/dto/update-role.dto.ts index 450134d..8db879c 100644 --- a/src/modules/role/dto/update-role.dto.ts +++ b/src/modules/role/dto/update-role.dto.ts @@ -1,4 +1,9 @@ import { PartialType } from '@nestjs/swagger'; import { CreateRoleDto } from './create-role.dto'; +import { IsInt, IsOptional } from 'class-validator'; -export class UpdateRoleDto extends PartialType(CreateRoleDto) {} +export class UpdateRoleDto extends PartialType(CreateRoleDto) { + @IsOptional() + @IsInt({ each: true }) + permissionIds?: number[]; +} diff --git a/src/modules/role/entities/role.entity.ts b/src/modules/role/entities/role.entity.ts index 44ed513..19b86ad 100644 --- a/src/modules/role/entities/role.entity.ts +++ b/src/modules/role/entities/role.entity.ts @@ -1,7 +1,7 @@ import { BaseEntity } from '@/database'; import { Permission } from '@/modules/permission/entities/permission.entity'; import { User } from 'src/modules/user'; -import { Column, Entity, JoinTable, ManyToMany } from 'typeorm'; +import { Column, Entity, JoinTable, ManyToMany, RelationId } from 'typeorm'; @Entity() export class Role extends BaseEntity { @@ -11,18 +11,21 @@ export class Role extends BaseEntity { */ @Column({ comment: '角色名称' }) name: string; + /** * 角色标识 * @example 'admin' */ @Column({ comment: '角色标识' }) slug: string; + /** * 角色描述 * @example '拥有所有权限' */ @Column({ comment: '角色描述', nullable: true }) description: string; + /** * 角色权限 * @example [1, 2, 3] @@ -30,6 +33,14 @@ export class Role extends BaseEntity { @JoinTable() @ManyToMany(() => Permission, (permission) => permission.roles) permissions: Permission[]; + + /** + * 角色权限ID + * @example [1, 2, 3] + */ + @RelationId('permissions') + permissionIds: number[]; + /** * 角色用户 * @example [1, 2, 3] diff --git a/src/modules/role/role.service.ts b/src/modules/role/role.service.ts index ffab6ae..a140a74 100644 --- a/src/modules/role/role.service.ts +++ b/src/modules/role/role.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { CreateRoleDto } from './dto/create-role.dto'; @@ -26,7 +26,16 @@ export class RoleService { return `This action returns a #${id} role`; } - update(id: number, updateRoleDto: UpdateRoleDto) { + async update(id: number, updateRoleDto: UpdateRoleDto) { + const role = this.roleRepository.findOne({ where: { id } }); + if (!role) { + throw new NotFoundException('角色不存在'); + } + if (updateRoleDto.permissionIds) { + const permissions = updateRoleDto.permissionIds.map((id) => ({ id })); + await this.roleRepository.save({ id, permissions }); + delete updateRoleDto.permissionIds; + } return this.roleRepository.update(id, updateRoleDto); } diff --git a/src/modules/user/dto/create-user.dto.ts b/src/modules/user/dto/create-user.dto.ts index 14fb0a3..fc6f7d3 100644 --- a/src/modules/user/dto/create-user.dto.ts +++ b/src/modules/user/dto/create-user.dto.ts @@ -1,4 +1,3 @@ -import { Role } from '@/modules/role/entities/role.entity'; import { IsInt, IsOptional, IsString } from 'class-validator'; export class CreateUserDto { @@ -8,12 +7,6 @@ export class CreateUserDto { */ @IsString() username: string; - /** - * 用户密码 - * @example 'password' - */ - @IsString() - password: string; /** * 用户昵称 * @example '绝弹' @@ -21,17 +14,24 @@ export class CreateUserDto { @IsString() nickname: string; /** - * 用户头像 - * @example './assets/222421415123.png ' + * 用户密码 + * @example 'password' */ @IsOptional() @IsString() - avatar: string; + password?: string; /** - * 用户角色 + * 头像ID + * @example 1 + */ + @IsOptional() + @IsString() + avatarId?: number; + /** + * 角色ID列表 * @example [1, 2, 3] */ @IsOptional() @IsInt({ each: true }) - roles: Role[]; + roleIds?: number[]; } diff --git a/src/modules/user/entities/user.entity.ts b/src/modules/user/entities/user.entity.ts index 6e02f7a..1268278 100644 --- a/src/modules/user/entities/user.entity.ts +++ b/src/modules/user/entities/user.entity.ts @@ -62,7 +62,7 @@ export class User extends BaseEntity { * 用户角色 */ @ApiHideProperty() - @ManyToMany(() => Role, (role) => role.user) + @ManyToMany(() => Role, (role) => role.user, { cascade: true }) @JoinTable() roles: Role[]; diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index aea8299..bd34241 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -1,7 +1,7 @@ import { BaseService } from '@/common/base'; -import { Injectable } from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Like, Repository } from 'typeorm'; +import { In, Like, Repository } from 'typeorm'; import { CreateUserDto } from './dto/create-user.dto'; import { FindUserDto } from './dto/find-user.dto'; import { UpdateUserDto } from './dto/update-user.dto'; @@ -18,8 +18,9 @@ export class UserService extends BaseService { */ async create(createUserDto: CreateUserDto) { const user = this.userRepository.create(createUserDto); - if (createUserDto.roles) { - user.roles = createUserDto.roles.map((id) => ({ id })) as any; + if (createUserDto.roleIds) { + user.roles = createUserDto.roleIds.map((id) => ({ id })) as any; + delete createUserDto.roleIds; } await this.userRepository.save(user); return user.id; @@ -29,7 +30,7 @@ export class UserService extends BaseService { * 查找所有用户 */ async findMany(findUserdto: FindUserDto) { - const { nickname: _nickname, } = findUserdto; + const { nickname: _nickname } = findUserdto; const nickname = _nickname && Like(`%${_nickname}%`); const { skip, take } = this.paginizate(findUserdto, { full: true }); return this.userRepository.findAndCount({ skip, take, where: { nickname } }); @@ -46,7 +47,16 @@ export class UserService extends BaseService { /** * 根据用户id */ - update(id: number, updateUserDto: UpdateUserDto) { + async update(id: number, updateUserDto: UpdateUserDto) { + const user = await this.userRepository.findOne({ where: { id } }); + if (!user) { + throw new NotFoundException('用户不存在'); + } + if (updateUserDto.roleIds) { + const roles = updateUserDto.roleIds.map((id) => ({ id })); + await this.userRepository.save({ id, roles }); + delete updateUserDto.roleIds; + } return this.userRepository.update(id, updateUserDto); } diff --git a/src/monitor/log/dto/create-log.dto.ts b/src/monitor/log/dto/create-log.dto.ts new file mode 100644 index 0000000..1935c5e --- /dev/null +++ b/src/monitor/log/dto/create-log.dto.ts @@ -0,0 +1,10 @@ +import { IsString } from 'class-validator'; + +export class CreateLogDto { + /** + * 字段描述(Swagger用途) + * @example 'demo' + */ + @IsString() + demo: string; +} diff --git a/src/monitor/log/dto/find-log.dto.ts b/src/monitor/log/dto/find-log.dto.ts new file mode 100644 index 0000000..d4690da --- /dev/null +++ b/src/monitor/log/dto/find-log.dto.ts @@ -0,0 +1,13 @@ +import { PaginationDto } from '@/common/response'; +import { IntersectionType } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class FindLogDto extends IntersectionType(PaginationDto) { + /** + * 字段描述(Swagger用途) + * @example '示例值' + */ + @IsOptional() + @IsString() + demo?: string; +} diff --git a/src/monitor/log/dto/update-log.dto.ts b/src/monitor/log/dto/update-log.dto.ts new file mode 100644 index 0000000..99da378 --- /dev/null +++ b/src/monitor/log/dto/update-log.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/swagger'; +import { CreateLogDto } from './create-log.dto'; + +export class UpdateLogDto extends PartialType(CreateLogDto) {} diff --git a/src/monitor/log/entities/authLog.entity.ts b/src/monitor/log/entities/authLog.entity.ts new file mode 100644 index 0000000..a53626a --- /dev/null +++ b/src/monitor/log/entities/authLog.entity.ts @@ -0,0 +1,47 @@ +import { BaseEntity } from '@/database'; +import { Column, Entity } from 'typeorm'; + +@Entity({ orderBy: { id: 'DESC' } }) +export class AuthLog extends BaseEntity { + /** + * 用户昵称 + * @example '绝弹' + */ + @Column() + nickname: string; + + /** + * 操作描述 + * @example 1 + */ + @Column() + description: string; + + /** + * 登陆IP + * @example '127.0.0.1' + */ + @Column() + ip: string; + + /** + * 登陆地址 + * @example '广东省深圳市' + */ + @Column() + addr: string; + + /** + * 浏览器 + * @example 'chrome' + */ + @Column() + browser: string; + + /** + * 操作系统 + * @example 'windows 10' + */ + @Column() + os: string; +} diff --git a/src/monitor/log/entities/log.entity.ts b/src/monitor/log/entities/log.entity.ts new file mode 100644 index 0000000..dde6161 --- /dev/null +++ b/src/monitor/log/entities/log.entity.ts @@ -0,0 +1,2 @@ + +export class Log {} \ No newline at end of file diff --git a/src/monitor/log/index.ts b/src/monitor/log/index.ts new file mode 100644 index 0000000..3988558 --- /dev/null +++ b/src/monitor/log/index.ts @@ -0,0 +1,5 @@ +export * from './entities/authLog.entity'; +export * from './log.controller'; +export * from './log.module'; +export * from './log.service'; +export * from './interceptors/authLog.interceptor'; diff --git a/src/monitor/log/interceptors/authLog.interceptor.ts b/src/monitor/log/interceptors/authLog.interceptor.ts new file mode 100644 index 0000000..b048cdd --- /dev/null +++ b/src/monitor/log/interceptors/authLog.interceptor.ts @@ -0,0 +1,22 @@ +import { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common'; +import { Observable, tap } from 'rxjs'; +import { LogService } from '../log.service'; + +export class AuthLogInterceptor implements NestInterceptor { + constructor(private logger: LogService) {} + + intercept(context: ExecutionContext, next: CallHandler): Observable | Promise> { + return next.handle().pipe( + tap({ + next(data) { + console.log('auth ok', data); + }, + error(err) { + console.log('auth err', err); + }, + }), + ); + } + + success() {} +} diff --git a/src/monitor/log/log.controller.ts b/src/monitor/log/log.controller.ts new file mode 100644 index 0000000..19be047 --- /dev/null +++ b/src/monitor/log/log.controller.ts @@ -0,0 +1,59 @@ +import { BaseController } from '@/common/base'; +import { Respond, RespondType } from '@/common/response'; +import { Body, Controller, Delete, Get, Param, Patch, Post, Query, ParseIntPipe } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; +import { CreateLogDto } from './dto/create-log.dto'; +import { FindLogDto } from './dto/find-log.dto'; +import { UpdateLogDto } from './dto/update-log.dto'; +import { AuthLog } from './entities/authLog.entity'; +import { LogService } from './log.service'; + +@ApiTags('log') +@Controller('logs') +export class LogController extends BaseController { + constructor(private logService: LogService) { + super(); + } + + /** + * 新增日志管理 + */ + @Post() + addLog(@Body() createLogDto: CreateLogDto) { + return this.logService.create(createLogDto); + } + + /** + * 根据分页/过滤参数查询日志管理 + */ + @Get() + @Respond(RespondType.PAGINATION) + @ApiOkResponse({ isArray: true, type: AuthLog }) + getLogs(@Query() query: FindLogDto) { + return this.logService.findMany(query); + } + + /** + * 根据ID查询日志管理 + */ + @Get(':id') + getLog(@Param('id', ParseIntPipe) id: number): Promise { + return this.logService.findOne(id); + } + + /** + * 根据ID更新日志管理 + */ + @Patch(':id') + updateLog(@Param('id', ParseIntPipe) id: number, @Body() updateLogDto: UpdateLogDto) { + return this.logService.update(+id, updateLogDto); + } + + /** + * 根据ID删除日志管理 + */ + @Delete(':id') + delLog(@Param('id', ParseIntPipe) id: number) { + return this.logService.remove(+id); + } +} diff --git a/src/monitor/log/log.module.ts b/src/monitor/log/log.module.ts new file mode 100644 index 0000000..253471f --- /dev/null +++ b/src/monitor/log/log.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { AuthLog } from './entities/authLog.entity'; +import { LogController } from './log.controller'; +import { LogService } from './log.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([AuthLog])], + controllers: [LogController], + providers: [LogService], + exports: [LogService], +}) +export class LogModule {} diff --git a/src/monitor/log/log.service.ts b/src/monitor/log/log.service.ts new file mode 100644 index 0000000..1df8081 --- /dev/null +++ b/src/monitor/log/log.service.ts @@ -0,0 +1,55 @@ +import { BaseService } from '@/common/base'; +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Like, Repository } from 'typeorm'; +import { CreateLogDto } from './dto/create-log.dto'; +import { FindLogDto } from './dto/find-log.dto'; +import { UpdateLogDto } from './dto/update-log.dto'; +import { AuthLog } from './entities/authLog.entity'; + +@Injectable() +export class LogService extends BaseService { + constructor(@InjectRepository(AuthLog) private logRepository: Repository) { + super(); + } + + /** + * 新增日志管理 + */ + async create(createLogDto: CreateLogDto) { + const log = this.logRepository.create(); + await this.logRepository.save(log); + return log.id; + } + + /** + * 条件/分页查询 + */ + async findMany(findLogdto: FindLogDto) { + const { page, size } = findLogdto; + const { skip, take } = this.formatPagination(page, size, true); + return this.logRepository.findAndCount({ skip, take }); + } + + /** + * 根据ID查询 + */ + findOne(idOrOptions: number | Partial) { + const where = typeof idOrOptions === 'number' ? { id: idOrOptions } : (idOrOptions as any); + return this.logRepository.findOne({ where }); + } + + /** + * 根据ID更新 + */ + update(id: number, updateLogDto: UpdateLogDto) { + // return this.logRepository.update(); + } + + /** + * 根据ID删除(软删除) + */ + remove(id: number) { + return this.logRepository.softDelete(id); + } +} diff --git a/src/types/env.d.ts b/src/types/env.d.ts index 68adb6f..42ffe1e 100644 --- a/src/types/env.d.ts +++ b/src/types/env.d.ts @@ -11,6 +11,7 @@ declare module 'express' { user?: { id: number; username: string; + nickname: string; }; } }