feat: add Swagger API docs (/docs) with DTO schemas
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
bbc06ae775
commit
24790e8985
@ -30,6 +30,7 @@
|
||||
"@nestjs/core": "^12.0.1",
|
||||
"@nestjs/jwt": "^12.0.2",
|
||||
"@nestjs/platform-express": "^12.0.1",
|
||||
"@nestjs/swagger": "^12.0.2",
|
||||
"@prisma/client": "^6.19.3",
|
||||
"bcryptjs": "^3.0.3",
|
||||
"class-transformer": "^0.5.1",
|
||||
|
||||
98
pnpm-lock.yaml
generated
98
pnpm-lock.yaml
generated
@ -178,6 +178,9 @@ importers:
|
||||
'@nestjs/platform-express':
|
||||
specifier: ^12.0.1
|
||||
version: 12.1.2(@nestjs/common@12.1.2(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@7.2.0))(@nestjs/core@12.1.2)(supports-color@7.2.0)
|
||||
'@nestjs/swagger':
|
||||
specifier: ^12.0.2
|
||||
version: 12.0.2(@nestjs/common@12.1.2(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@7.2.0))(@nestjs/core@12.1.2)(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(typescript@6.0.3)
|
||||
'@prisma/client':
|
||||
specifier: ^6.19.3
|
||||
version: 6.19.3(prisma@6.19.3(typescript@6.0.3))(typescript@6.0.3)
|
||||
@ -616,6 +619,9 @@ packages:
|
||||
resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
'@microsoft/tsdoc@0.17.0':
|
||||
resolution: {integrity: sha512-p68VexhnH7ojf3U27RdryUDqnKJgKaWUbCL96vJR7N0mqDuhKG5OIECk3sCPPdynxjk+yoVhRkScqtv5KQjzsw==}
|
||||
|
||||
'@nestjs/cli@12.0.8':
|
||||
resolution: {integrity: sha512-iqm+KXb8cnpnlD4hMBf1w2/4i1GSs0DCLI7oyP4MTmwf9KmjTG1SpAjkl/D+kKuUToycBrMF4+eSwyPVdTMGKQ==}
|
||||
engines: {node: '>= 20.11'}
|
||||
@ -683,6 +689,20 @@ packages:
|
||||
peerDependencies:
|
||||
'@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0
|
||||
|
||||
'@nestjs/mapped-types@12.0.0':
|
||||
resolution: {integrity: sha512-aX7lxZcqXldtnSr0bgT2ZPQvpTpIwBMW1GMX29o4s3q02vmq6JoTYuPW0GwYXa8BiFMRX7WbGZVDA703pNqOMQ==}
|
||||
engines: {node: '>=20.19.0'}
|
||||
peerDependencies:
|
||||
'@nestjs/common': ^10.0.0 || ^11.0.0 || ^12.0.0
|
||||
class-transformer: ^0.4.0 || ^0.5.0
|
||||
class-validator: ^0.13.0 || ^0.14.0 || ^0.15.0
|
||||
reflect-metadata: ^0.1.12 || ^0.2.0
|
||||
peerDependenciesMeta:
|
||||
class-transformer:
|
||||
optional: true
|
||||
class-validator:
|
||||
optional: true
|
||||
|
||||
'@nestjs/mau@0.2.8':
|
||||
resolution: {integrity: sha512-nIb0i3c/KhKEbxZfYMEKv9Mcvk5miBdZcexnvkgnAUhoMO2BUjOGT+Khf2cZzu7B51Y7v01NG2JVl/HdQEUQ6w==}
|
||||
engines: {node: '>= 20.0.0'}
|
||||
@ -704,6 +724,27 @@ packages:
|
||||
prettier:
|
||||
optional: true
|
||||
|
||||
'@nestjs/swagger@12.0.2':
|
||||
resolution: {integrity: sha512-/IDj6o2k1wqZybhIkMaJp58YNrAos/4TwTf3zBn/j1flKpMb5Tyfbj9sP6rpLucQ7PzHHu7n4ucmeHWEisfn6A==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
peerDependencies:
|
||||
'@fastify/static': ^8.0.0 || ^9.0.0 || ^10.0.0
|
||||
'@nestjs/common': ^12.0.0
|
||||
'@nestjs/core': ^12.0.0
|
||||
class-transformer: '*'
|
||||
class-validator: '*'
|
||||
reflect-metadata: ^0.1.12 || ^0.2.0
|
||||
typescript: ^5.5.0 || ^6.0.0
|
||||
peerDependenciesMeta:
|
||||
'@fastify/static':
|
||||
optional: true
|
||||
class-transformer:
|
||||
optional: true
|
||||
class-validator:
|
||||
optional: true
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
'@nestjs/testing@12.1.2':
|
||||
resolution: {integrity: sha512-JgetYeGHGK6iUEztXyiQLOfYLWqUgAyexLMzhuAMasgLiDgYShh/PoRESE9tzpRZo4eWKme3uSZ9DFR6Myuojw==}
|
||||
peerDependencies:
|
||||
@ -1011,6 +1052,9 @@ packages:
|
||||
'@rolldown/pluginutils@1.0.1':
|
||||
resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
|
||||
|
||||
'@scarf/scarf@1.4.0':
|
||||
resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==}
|
||||
|
||||
'@sindresorhus/is@4.6.0':
|
||||
resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
|
||||
engines: {node: '>=10'}
|
||||
@ -1160,6 +1204,9 @@ packages:
|
||||
append-field@1.0.0:
|
||||
resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==}
|
||||
|
||||
argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
|
||||
array-timsort@1.0.3:
|
||||
resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==}
|
||||
|
||||
@ -1474,6 +1521,9 @@ packages:
|
||||
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-toolkit@1.52.0:
|
||||
resolution: {integrity: sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA==}
|
||||
|
||||
esbuild@0.28.2:
|
||||
resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==}
|
||||
engines: {node: '>=18'}
|
||||
@ -1698,6 +1748,10 @@ packages:
|
||||
js-tokens@10.0.0:
|
||||
resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==}
|
||||
|
||||
js-yaml@5.4.2:
|
||||
resolution: {integrity: sha512-m+aqu+LwO1O6sIopafj8HUVl5aawITwZQe/yHpMCKjaWBaA/d07B/QdMb3529REftiU+RMMHL3Vlsw3hON7vWg==}
|
||||
hasBin: true
|
||||
|
||||
json-schema-traverse@1.0.0:
|
||||
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
|
||||
|
||||
@ -2284,6 +2338,9 @@ packages:
|
||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
swagger-ui-dist@5.33.0:
|
||||
resolution: {integrity: sha512-wpdK+m6BU5yj6pmUdMskZVTSWYG4DLglAx3sIhylloY37i8O37IrH+YEpqdXNfpaTGxILRBFzUqLF2jKqbfI7A==}
|
||||
|
||||
thread-stream@4.2.0:
|
||||
resolution: {integrity: sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==}
|
||||
engines: {node: '>=20'}
|
||||
@ -2762,6 +2819,8 @@ snapshots:
|
||||
|
||||
'@lukeed/csprng@1.1.0': {}
|
||||
|
||||
'@microsoft/tsdoc@0.17.0': {}
|
||||
|
||||
'@nestjs/cli@12.0.8(@types/node@24.19.1)(prettier@3.9.9)':
|
||||
dependencies:
|
||||
'@angular-devkit/core': 22.2.0(chokidar@5.0.0)
|
||||
@ -2818,6 +2877,14 @@ snapshots:
|
||||
'@types/jsonwebtoken': 9.0.10
|
||||
jsonwebtoken: 9.0.3
|
||||
|
||||
'@nestjs/mapped-types@12.0.0(@nestjs/common@12.1.2(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@7.2.0))(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)':
|
||||
dependencies:
|
||||
'@nestjs/common': 12.1.2(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@7.2.0)
|
||||
reflect-metadata: 0.2.2
|
||||
optionalDependencies:
|
||||
class-transformer: 0.5.1
|
||||
class-validator: 0.15.1
|
||||
|
||||
'@nestjs/mau@0.2.8':
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
@ -2853,6 +2920,23 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- chokidar
|
||||
|
||||
'@nestjs/swagger@12.0.2(@nestjs/common@12.1.2(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@7.2.0))(@nestjs/core@12.1.2)(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(typescript@6.0.3)':
|
||||
dependencies:
|
||||
'@microsoft/tsdoc': 0.17.0
|
||||
'@nestjs/common': 12.1.2(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@7.2.0)
|
||||
'@nestjs/core': 12.1.2(@nestjs/common@12.1.2(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@7.2.0))(@nestjs/platform-express@12.1.2)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
'@nestjs/mapped-types': 12.0.0(@nestjs/common@12.1.2(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@7.2.0))(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)
|
||||
'@standard-schema/spec': 1.1.0
|
||||
es-toolkit: 1.52.0
|
||||
js-yaml: 5.4.2
|
||||
path-to-regexp: 8.4.2
|
||||
reflect-metadata: 0.2.2
|
||||
swagger-ui-dist: 5.33.0
|
||||
optionalDependencies:
|
||||
class-transformer: 0.5.1
|
||||
class-validator: 0.15.1
|
||||
typescript: 6.0.3
|
||||
|
||||
'@nestjs/testing@12.1.2(@nestjs/common@12.1.2(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@7.2.0))(@nestjs/core@12.1.2)(@nestjs/platform-express@12.1.2)':
|
||||
dependencies:
|
||||
'@nestjs/common': 12.1.2(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@7.2.0)
|
||||
@ -3028,6 +3112,8 @@ snapshots:
|
||||
|
||||
'@rolldown/pluginutils@1.0.1': {}
|
||||
|
||||
'@scarf/scarf@1.4.0': {}
|
||||
|
||||
'@sindresorhus/is@4.6.0': {}
|
||||
|
||||
'@standard-schema/spec@1.1.0': {}
|
||||
@ -3207,6 +3293,8 @@ snapshots:
|
||||
|
||||
append-field@1.0.0: {}
|
||||
|
||||
argparse@2.0.1: {}
|
||||
|
||||
array-timsort@1.0.3: {}
|
||||
|
||||
asap@2.0.6: {}
|
||||
@ -3479,6 +3567,8 @@ snapshots:
|
||||
has-tostringtag: 1.0.2
|
||||
hasown: 2.0.4
|
||||
|
||||
es-toolkit@1.52.0: {}
|
||||
|
||||
esbuild@0.28.2:
|
||||
optionalDependencies:
|
||||
'@esbuild/aix-ppc64': 0.28.2
|
||||
@ -3755,6 +3845,10 @@ snapshots:
|
||||
|
||||
js-tokens@10.0.0: {}
|
||||
|
||||
js-yaml@5.4.2:
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
|
||||
json-schema-traverse@1.0.0: {}
|
||||
|
||||
json5@2.2.3: {}
|
||||
@ -4364,6 +4458,10 @@ snapshots:
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
|
||||
swagger-ui-dist@5.33.0:
|
||||
dependencies:
|
||||
'@scarf/scarf': 1.4.0
|
||||
|
||||
thread-stream@4.2.0:
|
||||
dependencies:
|
||||
real-require: 1.0.0
|
||||
|
||||
@ -1,20 +1,25 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class LoginDto {
|
||||
@ApiProperty({ description: '租户编码' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
tenantCode!: string;
|
||||
|
||||
@ApiProperty({ description: '用户名' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
username!: string;
|
||||
|
||||
@ApiProperty({ description: '密码' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
password!: string;
|
||||
}
|
||||
|
||||
export class RefreshDto {
|
||||
@ApiProperty({ description: '刷新令牌' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
refreshToken!: string;
|
||||
|
||||
@ -24,6 +24,9 @@ export class JwtAuthGuard implements CanActivate {
|
||||
if (isPublic) return true;
|
||||
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const url: string = request.originalUrl ?? request.url ?? '';
|
||||
if (url.startsWith('/docs')) return true;
|
||||
|
||||
const token = this.extractToken(request);
|
||||
if (!token) throw new UnauthorizedException('未登录');
|
||||
try {
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsInt, IsOptional, Min } from 'class-validator';
|
||||
|
||||
export class PageQuery {
|
||||
@ApiPropertyOptional({ description: '页码(从 1 开始)', default: 1 })
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
page = 1;
|
||||
|
||||
@ApiPropertyOptional({ description: '每页条数', default: 20 })
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@ -16,6 +19,7 @@ export class PageQuery {
|
||||
}
|
||||
|
||||
export class IdParam {
|
||||
@ApiPropertyOptional({ description: '记录 ID' })
|
||||
@IsOptional()
|
||||
id!: string;
|
||||
}
|
||||
|
||||
@ -1,12 +1,31 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class CreateConfigDto {
|
||||
@IsString() @IsNotEmpty() key!: string;
|
||||
@IsOptional() @IsString() value?: string;
|
||||
@IsOptional() @IsString() remark?: string;
|
||||
@ApiProperty({ description: '配置 key(租户内唯一)' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
key!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '配置值' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
value?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '备注' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export class UpdateConfigDto {
|
||||
@IsOptional() @IsString() value?: string;
|
||||
@IsOptional() @IsString() remark?: string;
|
||||
@ApiPropertyOptional({ description: '配置值' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
value?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '备注' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
@ -1,27 +1,80 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsInt, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class CreateDictTypeDto {
|
||||
@IsString() @IsNotEmpty() code!: string;
|
||||
@IsString() @IsNotEmpty() name!: string;
|
||||
@IsOptional() @IsString() remark?: string;
|
||||
@ApiProperty({ description: '字典类型编码' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
code!: string;
|
||||
|
||||
@ApiProperty({ description: '字典类型名称' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
name!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '备注' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export class UpdateDictTypeDto {
|
||||
@IsOptional() @IsString() name?: string;
|
||||
@IsOptional() @IsString() status?: string;
|
||||
@IsOptional() @IsString() remark?: string;
|
||||
@ApiPropertyOptional({ description: '字典类型名称' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
name?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '状态:enabled/disabled' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
status?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '备注' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export class CreateDictItemDto {
|
||||
@IsString() @IsNotEmpty() dictTypeCode!: string;
|
||||
@IsString() @IsNotEmpty() label!: string;
|
||||
@IsString() @IsNotEmpty() value!: string;
|
||||
@IsOptional() @IsInt() sort?: number;
|
||||
@ApiProperty({ description: '所属字典类型 code' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
dictTypeCode!: string;
|
||||
|
||||
@ApiProperty({ description: '标签(显示名)' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
label!: string;
|
||||
|
||||
@ApiProperty({ description: '值' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
value!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '排序', default: 0 })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
sort?: number;
|
||||
}
|
||||
|
||||
export class UpdateDictItemDto {
|
||||
@IsOptional() @IsString() label?: string;
|
||||
@IsOptional() @IsString() value?: string;
|
||||
@IsOptional() @IsInt() sort?: number;
|
||||
@IsOptional() @IsString() status?: string;
|
||||
@ApiPropertyOptional({ description: '标签' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
label?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '值' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
value?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '排序' })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
sort?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: '状态:enabled/disabled' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
status?: string;
|
||||
}
|
||||
|
||||
10
src/main.ts
10
src/main.ts
@ -2,6 +2,7 @@ import 'dotenv/config';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
import { Logger } from 'nestjs-pino';
|
||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||||
import { AppModule } from './app.module.js';
|
||||
|
||||
async function bootstrap(): Promise<void> {
|
||||
@ -10,6 +11,15 @@ async function bootstrap(): Promise<void> {
|
||||
app.enableCors();
|
||||
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
|
||||
app.useLogger(app.get(Logger));
|
||||
|
||||
const swaggerConfig = new DocumentBuilder()
|
||||
.setTitle('sysmgr API')
|
||||
.setDescription('通用系统管理脚手架 API 文档')
|
||||
.setVersion('1.0')
|
||||
.addBearerAuth()
|
||||
.build();
|
||||
SwaggerModule.setup('docs', app, SwaggerModule.createDocument(app, swaggerConfig));
|
||||
|
||||
await app.listen(process.env.PORT ?? 3000);
|
||||
}
|
||||
await bootstrap();
|
||||
|
||||
@ -1,17 +1,56 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsInt, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class CreateOrgDto {
|
||||
@IsString() @IsNotEmpty() code!: string;
|
||||
@IsString() @IsNotEmpty() name!: string;
|
||||
@IsOptional() @IsString() parentCode?: string;
|
||||
@IsOptional() @IsString() leader?: string;
|
||||
@IsOptional() @IsInt() sort?: number;
|
||||
@ApiProperty({ description: '组织 code(租户内唯一)' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
code!: string;
|
||||
|
||||
@ApiProperty({ description: '组织名称' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
name!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '父组织 code' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
parentCode?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '负责人' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
leader?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '排序', default: 0 })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
sort?: number;
|
||||
}
|
||||
|
||||
export class UpdateOrgDto {
|
||||
@IsOptional() @IsString() name?: string;
|
||||
@IsOptional() @IsString() parentCode?: string;
|
||||
@IsOptional() @IsString() leader?: string;
|
||||
@IsOptional() @IsInt() sort?: number;
|
||||
@IsOptional() @IsString() status?: string;
|
||||
@ApiPropertyOptional({ description: '组织名称' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
name?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '父组织 code' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
parentCode?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '负责人' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
leader?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '排序' })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
sort?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: '状态:enabled/disabled' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
status?: string;
|
||||
}
|
||||
|
||||
@ -1,26 +1,101 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsInt, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class CreateResourceDto {
|
||||
@IsString() @IsNotEmpty() type!: string;
|
||||
@IsString() @IsNotEmpty() code!: string;
|
||||
@IsString() @IsNotEmpty() name!: string;
|
||||
@IsOptional() @IsString() parentCode?: string;
|
||||
@IsOptional() @IsString() path?: string;
|
||||
@IsOptional() @IsString() component?: string;
|
||||
@IsOptional() @IsString() method?: string;
|
||||
@IsOptional() @IsString() apiPath?: string;
|
||||
@IsOptional() @IsString() icon?: string;
|
||||
@IsOptional() @IsInt() sort?: number;
|
||||
@ApiProperty({ description: '类型:menu/button/api' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
type!: string;
|
||||
|
||||
@ApiProperty({ description: '资源 code(租户内唯一,兼权限标识)' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
code!: string;
|
||||
|
||||
@ApiProperty({ description: '名称' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
name!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '父资源 code' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
parentCode?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '路由 path(menu)' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
path?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '前端组件(menu)' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
component?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'HTTP method(api)' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
method?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '接口 path(api)' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
apiPath?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '图标' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
icon?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '排序', default: 0 })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
sort?: number;
|
||||
}
|
||||
|
||||
export class UpdateResourceDto {
|
||||
@IsOptional() @IsString() name?: string;
|
||||
@IsOptional() @IsString() parentCode?: string;
|
||||
@IsOptional() @IsString() path?: string;
|
||||
@IsOptional() @IsString() component?: string;
|
||||
@IsOptional() @IsString() method?: string;
|
||||
@IsOptional() @IsString() apiPath?: string;
|
||||
@IsOptional() @IsString() icon?: string;
|
||||
@IsOptional() @IsInt() sort?: number;
|
||||
@IsOptional() @IsString() status?: string;
|
||||
@ApiPropertyOptional({ description: '名称' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
name?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '父资源 code' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
parentCode?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '路由 path' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
path?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '前端组件' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
component?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'HTTP method' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
method?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '接口 path' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
apiPath?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '图标' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
icon?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '排序' })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
sort?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: '状态:enabled/disabled' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
status?: string;
|
||||
}
|
||||
|
||||
@ -1,19 +1,53 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsArray, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class CreateRoleDto {
|
||||
@IsString() @IsNotEmpty() code!: string;
|
||||
@IsString() @IsNotEmpty() name!: string;
|
||||
@IsOptional() @IsString() dataScope?: string;
|
||||
@IsOptional() @IsString() remark?: string;
|
||||
@ApiProperty({ description: '角色编码' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
code!: string;
|
||||
|
||||
@ApiProperty({ description: '角色名称' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
name!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '数据权限:all/dept/dept_and_child/self/custom', default: 'all' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
dataScope?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '备注' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export class UpdateRoleDto {
|
||||
@IsOptional() @IsString() name?: string;
|
||||
@IsOptional() @IsString() dataScope?: string;
|
||||
@IsOptional() @IsString() status?: string;
|
||||
@IsOptional() @IsString() remark?: string;
|
||||
@ApiPropertyOptional({ description: '角色名称' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
name?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '数据权限:all/dept/dept_and_child/self/custom' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
dataScope?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '状态:enabled/disabled' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
status?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '备注' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export class AssignResourcesDto {
|
||||
@IsArray() @IsString({ each: true }) resourceCodes!: string[];
|
||||
@ApiProperty({ description: '资源 code 列表', type: [String] })
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
resourceCodes!: string[];
|
||||
}
|
||||
|
||||
@ -1,14 +1,41 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class CreateTenantDto {
|
||||
@IsString() @IsNotEmpty() code!: string;
|
||||
@IsString() @IsNotEmpty() name!: string;
|
||||
@IsOptional() @IsString() status?: string;
|
||||
@IsOptional() @IsString() remark?: string;
|
||||
@ApiProperty({ description: '租户编码' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
code!: string;
|
||||
|
||||
@ApiProperty({ description: '租户名称' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
name!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '状态:enabled/disabled', default: 'enabled' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
status?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '备注' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export class UpdateTenantDto {
|
||||
@IsOptional() @IsString() name?: string;
|
||||
@IsOptional() @IsString() status?: string;
|
||||
@IsOptional() @IsString() remark?: string;
|
||||
@ApiPropertyOptional({ description: '租户名称' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
name?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '状态:enabled/disabled' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
status?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '备注' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
@ -1,24 +1,78 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class CreateUserDto {
|
||||
@IsString() @IsNotEmpty() username!: string;
|
||||
@IsString() @IsNotEmpty() password!: string;
|
||||
@IsOptional() @IsString() nickname?: string;
|
||||
@IsOptional() @IsString() email?: string;
|
||||
@IsOptional() @IsString() phone?: string;
|
||||
@IsOptional() @IsString() avatar?: string;
|
||||
@IsOptional() @IsString() orgCode?: string;
|
||||
@ApiProperty({ description: '用户名' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
username!: string;
|
||||
|
||||
@ApiProperty({ description: '初始密码' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
password!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '昵称' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
nickname?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '邮箱' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
email?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '手机号' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
phone?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '头像' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
avatar?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '所属部门 code' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
orgCode?: string;
|
||||
}
|
||||
|
||||
export class UpdateUserDto {
|
||||
@IsOptional() @IsString() nickname?: string;
|
||||
@IsOptional() @IsString() email?: string;
|
||||
@IsOptional() @IsString() phone?: string;
|
||||
@IsOptional() @IsString() avatar?: string;
|
||||
@IsOptional() @IsString() orgCode?: string;
|
||||
@IsOptional() @IsString() status?: string;
|
||||
@ApiPropertyOptional({ description: '昵称' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
nickname?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '邮箱' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
email?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '手机号' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
phone?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '头像' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
avatar?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '所属部门 code' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
orgCode?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '状态:enabled/disabled/locked' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export class ResetPasswordDto {
|
||||
@IsString() @IsNotEmpty() password!: string;
|
||||
@ApiProperty({ description: '新密码' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
password!: string;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user