feat: 增强 Swagger 响应示例
This commit is contained in:
parent
8ae097e44d
commit
29663ef369
@ -37,7 +37,7 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mqtt": "^5.15.1",
|
"mqtt": "^5.15.1",
|
||||||
"mysql2": "^3.22.3",
|
"mysql2": "^3.22.3",
|
||||||
"nestjs-knife4j-plus": "^1.0.8",
|
"nestjs-knife4j-plus": "^1.0.9",
|
||||||
"nestjs-minio-client": "^2.2.0",
|
"nestjs-minio-client": "^2.2.0",
|
||||||
"reflect-metadata": "^0.1.14",
|
"reflect-metadata": "^0.1.14",
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
|
|||||||
@ -42,8 +42,8 @@ importers:
|
|||||||
specifier: ^3.22.3
|
specifier: ^3.22.3
|
||||||
version: 3.22.3(@types/node@18.11.18)
|
version: 3.22.3(@types/node@18.11.18)
|
||||||
nestjs-knife4j-plus:
|
nestjs-knife4j-plus:
|
||||||
specifier: ^1.0.8
|
specifier: ^1.0.9
|
||||||
version: 1.0.8(@nestjs/common@9.4.3(reflect-metadata@0.1.14)(rxjs@7.8.2))(express@4.18.2)
|
version: 1.0.9(@nestjs/common@9.4.3(reflect-metadata@0.1.14)(rxjs@7.8.2))(express@4.18.2)
|
||||||
nestjs-minio-client:
|
nestjs-minio-client:
|
||||||
specifier: ^2.2.0
|
specifier: ^2.2.0
|
||||||
version: 2.2.0(@nestjs/common@9.4.3(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@9.4.3)
|
version: 2.2.0(@nestjs/common@9.4.3(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@9.4.3)
|
||||||
@ -1567,6 +1567,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
es-object-atoms@1.1.2:
|
||||||
|
resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
es-set-tostringtag@2.1.0:
|
es-set-tostringtag@2.1.0:
|
||||||
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
|
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@ -1937,6 +1941,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==}
|
resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
hasown@2.0.4:
|
||||||
|
resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
help-me@5.0.0:
|
help-me@5.0.0:
|
||||||
resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==}
|
resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==}
|
||||||
|
|
||||||
@ -2656,8 +2664,8 @@ packages:
|
|||||||
neo-async@2.6.2:
|
neo-async@2.6.2:
|
||||||
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
|
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
|
||||||
|
|
||||||
nestjs-knife4j-plus@1.0.8:
|
nestjs-knife4j-plus@1.0.9:
|
||||||
resolution: {integrity: sha512-oXUBQwrEzeuOyqei32I/zIHGo1V8icL0pJaX2dOJSqi0VKXy63pXXJhZ/hVD/XX3Rg/dvLqg5IHgnm7nRksesw==}
|
resolution: {integrity: sha512-MgMrfXgJfRgwW43BbMAKvInxluuvdNjSiYmkBvQvUawJMwn6+Op4W8/iYG1Q7u4D7mefWtM4zwVA7gBHErWCzg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@fastify/static': '*'
|
'@fastify/static': '*'
|
||||||
'@nestjs/common': '*'
|
'@nestjs/common': '*'
|
||||||
@ -3066,8 +3074,8 @@ packages:
|
|||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
semver@7.8.0:
|
semver@7.8.1:
|
||||||
resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==}
|
resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
@ -3480,8 +3488,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==}
|
resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
typed-array-length@1.0.7:
|
typed-array-length@1.0.8:
|
||||||
resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
|
resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
typedarray@0.0.6:
|
typedarray@0.0.6:
|
||||||
@ -3660,6 +3668,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==}
|
resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
which-typed-array@1.1.21:
|
||||||
|
resolution: {integrity: sha512-zbRA8cVm6io/d5W8uIe2hblzN76/Wm3v/yiythQvr+dpBWeqhPSWIDNj4zOyHi4zKbMK6DN34Xsr9jPHJERAEw==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
which@2.0.2:
|
which@2.0.2:
|
||||||
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@ -5537,7 +5549,7 @@ snapshots:
|
|||||||
data-view-byte-offset: 1.0.1
|
data-view-byte-offset: 1.0.1
|
||||||
es-define-property: 1.0.1
|
es-define-property: 1.0.1
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
es-object-atoms: 1.1.1
|
es-object-atoms: 1.1.2
|
||||||
es-set-tostringtag: 2.1.0
|
es-set-tostringtag: 2.1.0
|
||||||
es-to-primitive: 1.3.0
|
es-to-primitive: 1.3.0
|
||||||
function.prototype.name: 1.1.8
|
function.prototype.name: 1.1.8
|
||||||
@ -5549,7 +5561,7 @@ snapshots:
|
|||||||
has-property-descriptors: 1.0.2
|
has-property-descriptors: 1.0.2
|
||||||
has-proto: 1.2.0
|
has-proto: 1.2.0
|
||||||
has-symbols: 1.1.0
|
has-symbols: 1.1.0
|
||||||
hasown: 2.0.3
|
hasown: 2.0.4
|
||||||
internal-slot: 1.1.0
|
internal-slot: 1.1.0
|
||||||
is-array-buffer: 3.0.5
|
is-array-buffer: 3.0.5
|
||||||
is-callable: 1.2.7
|
is-callable: 1.2.7
|
||||||
@ -5578,9 +5590,9 @@ snapshots:
|
|||||||
typed-array-buffer: 1.0.3
|
typed-array-buffer: 1.0.3
|
||||||
typed-array-byte-length: 1.0.3
|
typed-array-byte-length: 1.0.3
|
||||||
typed-array-byte-offset: 1.0.4
|
typed-array-byte-offset: 1.0.4
|
||||||
typed-array-length: 1.0.7
|
typed-array-length: 1.0.8
|
||||||
unbox-primitive: 1.1.0
|
unbox-primitive: 1.1.0
|
||||||
which-typed-array: 1.1.20
|
which-typed-array: 1.1.21
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
es-aggregate-error@1.0.14:
|
es-aggregate-error@1.0.14:
|
||||||
@ -5605,6 +5617,11 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
|
|
||||||
|
es-object-atoms@1.1.2:
|
||||||
|
dependencies:
|
||||||
|
es-errors: 1.3.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
es-set-tostringtag@2.1.0:
|
es-set-tostringtag@2.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
@ -5951,7 +5968,7 @@ snapshots:
|
|||||||
call-bound: 1.0.4
|
call-bound: 1.0.4
|
||||||
define-properties: 1.2.1
|
define-properties: 1.2.1
|
||||||
functions-have-names: 1.2.3
|
functions-have-names: 1.2.3
|
||||||
hasown: 2.0.3
|
hasown: 2.0.4
|
||||||
is-callable: 1.2.7
|
is-callable: 1.2.7
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@ -6085,6 +6102,11 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
function-bind: 1.1.2
|
function-bind: 1.1.2
|
||||||
|
|
||||||
|
hasown@2.0.4:
|
||||||
|
dependencies:
|
||||||
|
function-bind: 1.1.2
|
||||||
|
optional: true
|
||||||
|
|
||||||
help-me@5.0.0: {}
|
help-me@5.0.0: {}
|
||||||
|
|
||||||
html-escaper@2.0.2: {}
|
html-escaper@2.0.2: {}
|
||||||
@ -6194,7 +6216,7 @@ snapshots:
|
|||||||
internal-slot@1.1.0:
|
internal-slot@1.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
hasown: 2.0.3
|
hasown: 2.0.4
|
||||||
side-channel: 1.1.0
|
side-channel: 1.1.0
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@ -6792,7 +6814,7 @@ snapshots:
|
|||||||
lodash.isstring: 4.0.1
|
lodash.isstring: 4.0.1
|
||||||
lodash.once: 4.1.1
|
lodash.once: 4.1.1
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
semver: 7.8.0
|
semver: 7.8.1
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
jwa@2.0.1:
|
jwa@2.0.1:
|
||||||
@ -7052,7 +7074,7 @@ snapshots:
|
|||||||
|
|
||||||
neo-async@2.6.2: {}
|
neo-async@2.6.2: {}
|
||||||
|
|
||||||
nestjs-knife4j-plus@1.0.8(@nestjs/common@9.4.3(reflect-metadata@0.1.14)(rxjs@7.8.2))(express@4.18.2):
|
nestjs-knife4j-plus@1.0.9(@nestjs/common@9.4.3(reflect-metadata@0.1.14)(rxjs@7.8.2))(express@4.18.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nestjs/common': 9.4.3(reflect-metadata@0.1.14)(rxjs@7.8.2)
|
'@nestjs/common': 9.4.3(reflect-metadata@0.1.14)(rxjs@7.8.2)
|
||||||
express: 4.18.2
|
express: 4.18.2
|
||||||
@ -7104,7 +7126,7 @@ snapshots:
|
|||||||
call-bind: 1.0.9
|
call-bind: 1.0.9
|
||||||
call-bound: 1.0.4
|
call-bound: 1.0.4
|
||||||
define-properties: 1.2.1
|
define-properties: 1.2.1
|
||||||
es-object-atoms: 1.1.1
|
es-object-atoms: 1.1.2
|
||||||
has-symbols: 1.1.0
|
has-symbols: 1.1.0
|
||||||
object-keys: 1.1.1
|
object-keys: 1.1.1
|
||||||
optional: true
|
optional: true
|
||||||
@ -7349,7 +7371,7 @@ snapshots:
|
|||||||
define-properties: 1.2.1
|
define-properties: 1.2.1
|
||||||
es-abstract: 1.24.2
|
es-abstract: 1.24.2
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
es-object-atoms: 1.1.1
|
es-object-atoms: 1.1.2
|
||||||
get-intrinsic: 1.3.0
|
get-intrinsic: 1.3.0
|
||||||
get-proto: 1.0.1
|
get-proto: 1.0.1
|
||||||
which-builtin-type: 1.2.1
|
which-builtin-type: 1.2.1
|
||||||
@ -7465,7 +7487,7 @@ snapshots:
|
|||||||
|
|
||||||
semver@7.7.4: {}
|
semver@7.7.4: {}
|
||||||
|
|
||||||
semver@7.8.0:
|
semver@7.8.1:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
send@0.18.0:
|
send@0.18.0:
|
||||||
@ -7516,7 +7538,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
dunder-proto: 1.0.1
|
dunder-proto: 1.0.1
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
es-object-atoms: 1.1.1
|
es-object-atoms: 1.1.2
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
setprototypeof@1.2.0: {}
|
setprototypeof@1.2.0: {}
|
||||||
@ -7654,7 +7676,7 @@ snapshots:
|
|||||||
define-data-property: 1.1.4
|
define-data-property: 1.1.4
|
||||||
define-properties: 1.2.1
|
define-properties: 1.2.1
|
||||||
es-abstract: 1.24.2
|
es-abstract: 1.24.2
|
||||||
es-object-atoms: 1.1.1
|
es-object-atoms: 1.1.2
|
||||||
has-property-descriptors: 1.0.2
|
has-property-descriptors: 1.0.2
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@ -7663,14 +7685,14 @@ snapshots:
|
|||||||
call-bind: 1.0.9
|
call-bind: 1.0.9
|
||||||
call-bound: 1.0.4
|
call-bound: 1.0.4
|
||||||
define-properties: 1.2.1
|
define-properties: 1.2.1
|
||||||
es-object-atoms: 1.1.1
|
es-object-atoms: 1.1.2
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
string.prototype.trimstart@1.0.8:
|
string.prototype.trimstart@1.0.8:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.9
|
call-bind: 1.0.9
|
||||||
define-properties: 1.2.1
|
define-properties: 1.2.1
|
||||||
es-object-atoms: 1.1.1
|
es-object-atoms: 1.1.2
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
string_decoder@1.1.1:
|
string_decoder@1.1.1:
|
||||||
@ -7931,7 +7953,7 @@ snapshots:
|
|||||||
reflect.getprototypeof: 1.0.10
|
reflect.getprototypeof: 1.0.10
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
typed-array-length@1.0.7:
|
typed-array-length@1.0.8:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.9
|
call-bind: 1.0.9
|
||||||
for-each: 0.3.5
|
for-each: 0.3.5
|
||||||
@ -8108,7 +8130,7 @@ snapshots:
|
|||||||
isarray: 2.0.5
|
isarray: 2.0.5
|
||||||
which-boxed-primitive: 1.1.1
|
which-boxed-primitive: 1.1.1
|
||||||
which-collection: 1.0.2
|
which-collection: 1.0.2
|
||||||
which-typed-array: 1.1.20
|
which-typed-array: 1.1.21
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
which-collection@1.0.2:
|
which-collection@1.0.2:
|
||||||
@ -8129,6 +8151,17 @@ snapshots:
|
|||||||
gopd: 1.2.0
|
gopd: 1.2.0
|
||||||
has-tostringtag: 1.0.2
|
has-tostringtag: 1.0.2
|
||||||
|
|
||||||
|
which-typed-array@1.1.21:
|
||||||
|
dependencies:
|
||||||
|
available-typed-arrays: 1.0.7
|
||||||
|
call-bind: 1.0.9
|
||||||
|
call-bound: 1.0.4
|
||||||
|
for-each: 0.3.5
|
||||||
|
get-proto: 1.0.1
|
||||||
|
gopd: 1.2.0
|
||||||
|
has-tostringtag: 1.0.2
|
||||||
|
optional: true
|
||||||
|
|
||||||
which@2.0.2:
|
which@2.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
isexe: 2.0.0
|
isexe: 2.0.0
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
import { applyDecorators, Type } from '@nestjs/common';
|
import { applyDecorators, Type } from '@nestjs/common';
|
||||||
import { ApiExtraModels, ApiOkResponse, ApiProperty } from '@nestjs/swagger';
|
import { ApiExtraModels, ApiOkResponse, ApiProperty } from '@nestjs/swagger';
|
||||||
|
import type { OpenAPIObject } from '@nestjs/swagger';
|
||||||
|
|
||||||
type SwaggerSchema = Record<string, any>;
|
type SwaggerSchema = Record<string, any>;
|
||||||
|
type SwaggerOperation = Record<string, any>;
|
||||||
|
type SwaggerComponents = NonNullable<OpenAPIObject['components']>;
|
||||||
|
|
||||||
type ApiResponseOptions = {
|
type ApiResponseOptions = {
|
||||||
description?: string;
|
description?: string;
|
||||||
@ -179,3 +182,770 @@ export const ApiFileDownloadResponse = (description = '文件下载成功') =>
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const operationMethods = [
|
||||||
|
'get',
|
||||||
|
'post',
|
||||||
|
'put',
|
||||||
|
'delete',
|
||||||
|
'patch',
|
||||||
|
'options',
|
||||||
|
'head',
|
||||||
|
];
|
||||||
|
|
||||||
|
const standardErrorSchema = {
|
||||||
|
type: 'object',
|
||||||
|
required: ['code', 'msg', 'err'],
|
||||||
|
properties: {
|
||||||
|
code: {
|
||||||
|
type: 'integer',
|
||||||
|
description: '错误状态码',
|
||||||
|
example: 400,
|
||||||
|
},
|
||||||
|
msg: {
|
||||||
|
type: 'string',
|
||||||
|
description: '错误提示',
|
||||||
|
example: '操作失败',
|
||||||
|
},
|
||||||
|
err: {
|
||||||
|
description: '错误详情',
|
||||||
|
example: 'Bad Request',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const applySwaggerResponseExamples = (document: OpenAPIObject) => {
|
||||||
|
const components = ensureDocumentComponents(document);
|
||||||
|
components.schemas.KtApiErrorResponse ||= standardErrorSchema;
|
||||||
|
|
||||||
|
Object.entries(document.paths).forEach(([path, pathItem]) => {
|
||||||
|
Object.entries(pathItem || {}).forEach(([method, operation]) => {
|
||||||
|
if (!operationMethods.includes(method)) return;
|
||||||
|
applyOperationResponseExamples(document, path, method, operation as any);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return document;
|
||||||
|
};
|
||||||
|
|
||||||
|
function applyOperationResponseExamples(
|
||||||
|
document: OpenAPIObject,
|
||||||
|
path: string,
|
||||||
|
method: string,
|
||||||
|
operation: SwaggerOperation,
|
||||||
|
) {
|
||||||
|
operation.responses ||= {};
|
||||||
|
|
||||||
|
if (path === '/') {
|
||||||
|
operation.responses['301'] = {
|
||||||
|
description: '重定向到 Swagger 文档',
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBinaryResponsePath(path)) {
|
||||||
|
if (!operation.responses['200']?.content) {
|
||||||
|
operation.responses['200'] = {
|
||||||
|
description: '文件流响应',
|
||||||
|
content: {
|
||||||
|
'application/octet-stream': {
|
||||||
|
schema: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'binary',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
applyErrorResponses(operation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataExample = getOperationDataExample(path, method, operation);
|
||||||
|
const successSchema = createOperationSuccessSchema(
|
||||||
|
document,
|
||||||
|
path,
|
||||||
|
method,
|
||||||
|
dataExample,
|
||||||
|
);
|
||||||
|
const successResponse = buildSuccessResponse(dataExample, successSchema);
|
||||||
|
const currentResponse = operation.responses['200'];
|
||||||
|
|
||||||
|
operation.responses['200'] = mergeJsonResponse(
|
||||||
|
currentResponse,
|
||||||
|
successResponse,
|
||||||
|
);
|
||||||
|
applyErrorResponses(operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyErrorResponses(operation: SwaggerOperation) {
|
||||||
|
operation.responses['400'] ||= buildErrorResponse(
|
||||||
|
400,
|
||||||
|
'Bad Request',
|
||||||
|
'请求参数不合法',
|
||||||
|
);
|
||||||
|
operation.responses['401'] ||= buildErrorResponse(
|
||||||
|
401,
|
||||||
|
'Unauthorized',
|
||||||
|
'未登录或登录已过期',
|
||||||
|
);
|
||||||
|
operation.responses['500'] ||= buildErrorResponse(
|
||||||
|
500,
|
||||||
|
'Internal Server Error',
|
||||||
|
'服务内部错误',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSuccessResponse(dataExample: any, schema: SwaggerSchema) {
|
||||||
|
const example = getResponseExample(dataExample);
|
||||||
|
|
||||||
|
return {
|
||||||
|
description: '操作成功',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema,
|
||||||
|
example,
|
||||||
|
examples: {
|
||||||
|
success: {
|
||||||
|
summary: '成功响应',
|
||||||
|
value: example,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildErrorResponse(status: number, summary: string, message: string) {
|
||||||
|
return {
|
||||||
|
description: message,
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: {
|
||||||
|
$ref: '#/components/schemas/KtApiErrorResponse',
|
||||||
|
},
|
||||||
|
example: {
|
||||||
|
code: status,
|
||||||
|
msg: message,
|
||||||
|
err: summary,
|
||||||
|
},
|
||||||
|
examples: {
|
||||||
|
error: {
|
||||||
|
summary,
|
||||||
|
value: {
|
||||||
|
code: status,
|
||||||
|
msg: message,
|
||||||
|
err: summary,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createOperationSuccessSchema(
|
||||||
|
document: OpenAPIObject,
|
||||||
|
path: string,
|
||||||
|
method: string,
|
||||||
|
dataExample: any,
|
||||||
|
) {
|
||||||
|
const components = ensureDocumentComponents(document);
|
||||||
|
const componentName = `${toPascalCase(method)}${toPascalCase(path)}Response`;
|
||||||
|
const dataSchemaName = `${componentName}Data`;
|
||||||
|
const dataSchema = schemaFromExample(
|
||||||
|
dataExample,
|
||||||
|
'data',
|
||||||
|
components,
|
||||||
|
dataSchemaName,
|
||||||
|
);
|
||||||
|
components.schemas[dataSchemaName] = dataSchema;
|
||||||
|
const schema = buildSuccessSchema(dataExample, dataSchemaName);
|
||||||
|
components.schemas[componentName] = schema;
|
||||||
|
|
||||||
|
return {
|
||||||
|
$ref: `#/components/schemas/${componentName}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSuccessSchema(
|
||||||
|
dataExample: any,
|
||||||
|
dataSchemaName: string,
|
||||||
|
): SwaggerSchema {
|
||||||
|
const example = getResponseExample(dataExample);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'object',
|
||||||
|
required: ['code', 'msg', 'data'],
|
||||||
|
description: '统一成功响应结构',
|
||||||
|
example,
|
||||||
|
properties: {
|
||||||
|
code: {
|
||||||
|
type: 'integer',
|
||||||
|
description: '成功状态码,固定为 200',
|
||||||
|
example: 200,
|
||||||
|
},
|
||||||
|
msg: {
|
||||||
|
type: 'string',
|
||||||
|
description: '成功提示',
|
||||||
|
example: '操作成功',
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
allOf: [
|
||||||
|
{
|
||||||
|
$ref: `#/components/schemas/${dataSchemaName}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
description: '业务数据;成功响应不会返回 err 字段',
|
||||||
|
example: dataExample,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeJsonResponse(currentResponse: any, standardResponse: any) {
|
||||||
|
if (!currentResponse?.content?.['application/json']) {
|
||||||
|
return {
|
||||||
|
...standardResponse,
|
||||||
|
description: currentResponse?.description || standardResponse.description,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const jsonContent = currentResponse.content['application/json'];
|
||||||
|
|
||||||
|
return {
|
||||||
|
...currentResponse,
|
||||||
|
description: currentResponse.description || standardResponse.description,
|
||||||
|
content: {
|
||||||
|
...currentResponse.content,
|
||||||
|
'application/json': {
|
||||||
|
...jsonContent,
|
||||||
|
schema: standardResponse.content['application/json'].schema,
|
||||||
|
example:
|
||||||
|
jsonContent.example ||
|
||||||
|
standardResponse.content['application/json'].example,
|
||||||
|
examples: {
|
||||||
|
...standardResponse.content['application/json'].examples,
|
||||||
|
...jsonContent.examples,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function schemaFromExample(
|
||||||
|
example: any,
|
||||||
|
propertyName = 'data',
|
||||||
|
components?: SwaggerComponents,
|
||||||
|
schemaName?: string,
|
||||||
|
): SwaggerSchema {
|
||||||
|
if (Array.isArray(example)) {
|
||||||
|
const itemSchemaName = schemaName
|
||||||
|
? `${schemaName}${toPascalCase(getArrayItemName(propertyName))}`
|
||||||
|
: undefined;
|
||||||
|
const itemSchema =
|
||||||
|
example.length > 0
|
||||||
|
? schemaFromExample(
|
||||||
|
example[0],
|
||||||
|
getArrayItemName(propertyName),
|
||||||
|
components,
|
||||||
|
itemSchemaName,
|
||||||
|
)
|
||||||
|
: { type: 'object' };
|
||||||
|
|
||||||
|
if (components && itemSchemaName && itemSchema.type === 'object') {
|
||||||
|
components.schemas[itemSchemaName] = itemSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'array',
|
||||||
|
description: getPropertyDescription(propertyName),
|
||||||
|
example,
|
||||||
|
items:
|
||||||
|
components && itemSchemaName && itemSchema.type === 'object'
|
||||||
|
? {
|
||||||
|
$ref: `#/components/schemas/${itemSchemaName}`,
|
||||||
|
}
|
||||||
|
: itemSchema,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (example === null) {
|
||||||
|
return {
|
||||||
|
nullable: true,
|
||||||
|
description: getPropertyDescription(propertyName),
|
||||||
|
example: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof example === 'boolean') {
|
||||||
|
return {
|
||||||
|
type: 'boolean',
|
||||||
|
description: getPropertyDescription(propertyName),
|
||||||
|
example,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (typeof example === 'number') {
|
||||||
|
return {
|
||||||
|
type: Number.isInteger(example) ? 'integer' : 'number',
|
||||||
|
description: getPropertyDescription(propertyName),
|
||||||
|
example,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (typeof example === 'string') {
|
||||||
|
return {
|
||||||
|
type: 'string',
|
||||||
|
description: getPropertyDescription(propertyName),
|
||||||
|
example,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof example === 'object') {
|
||||||
|
const properties = Object.entries(example).reduce<
|
||||||
|
Record<string, SwaggerSchema>
|
||||||
|
>((acc, [key, value]) => {
|
||||||
|
acc[key] = schemaFromExample(
|
||||||
|
value,
|
||||||
|
key,
|
||||||
|
components,
|
||||||
|
schemaName ? `${schemaName}${toPascalCase(key)}` : undefined,
|
||||||
|
);
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'object',
|
||||||
|
description: getPropertyDescription(propertyName),
|
||||||
|
required: Object.keys(properties),
|
||||||
|
example,
|
||||||
|
properties,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'object',
|
||||||
|
description: getPropertyDescription(propertyName),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureDocumentComponents(document: OpenAPIObject): SwaggerComponents {
|
||||||
|
document.components ||= {};
|
||||||
|
document.components.schemas ||= {};
|
||||||
|
|
||||||
|
return document.components;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toPascalCase(value: string) {
|
||||||
|
return value
|
||||||
|
.split(/[^a-zA-Z0-9]+/)
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((item) => `${item.charAt(0).toUpperCase()}${item.slice(1)}`)
|
||||||
|
.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getArrayItemName(propertyName: string) {
|
||||||
|
if (propertyName === 'items') return 'item';
|
||||||
|
if (propertyName.endsWith('s')) return propertyName.slice(0, -1);
|
||||||
|
|
||||||
|
return `${propertyName}Item`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPropertyDescription(propertyName: string) {
|
||||||
|
const descriptionMap: Record<string, string> = {
|
||||||
|
['access' + 'Token']: 'Admin 访问令牌',
|
||||||
|
accountCount: '账号总数',
|
||||||
|
available: '是否可用',
|
||||||
|
bucketName: 'Bucket 名称',
|
||||||
|
categories: 'WordPress 分类 ID 列表',
|
||||||
|
code: '响应状态码',
|
||||||
|
command: '命令触发词',
|
||||||
|
commandId: '在线命令 ID',
|
||||||
|
connectionRole: 'OneBot 连接角色',
|
||||||
|
count: '数量',
|
||||||
|
data: '业务数据',
|
||||||
|
description: '描述',
|
||||||
|
enabled: '是否启用',
|
||||||
|
err: '错误详情',
|
||||||
|
etag: '对象 ETag',
|
||||||
|
expireAt: '过期时间',
|
||||||
|
id: '唯一 ID',
|
||||||
|
image: '图片地址',
|
||||||
|
items: '列表数据',
|
||||||
|
key: '唯一键',
|
||||||
|
keyword: '匹配关键词',
|
||||||
|
lastHeartbeatAt: '最后心跳时间',
|
||||||
|
lastMessage: '最后一条消息',
|
||||||
|
lastModified: '最后修改时间',
|
||||||
|
matchType: '匹配方式',
|
||||||
|
message: '消息内容',
|
||||||
|
mimeType: '文件 MIME 类型',
|
||||||
|
mode: '过滤模式',
|
||||||
|
msg: '响应消息',
|
||||||
|
name: '名称',
|
||||||
|
nickname: '昵称',
|
||||||
|
objectName: '对象名称',
|
||||||
|
onlineAccountCount: '在线账号数',
|
||||||
|
path: '路由路径',
|
||||||
|
pluginKey: '插件能力 Key',
|
||||||
|
preciseUser: '是否精确到 QQ 号',
|
||||||
|
qrcode: '二维码内容',
|
||||||
|
realName: '真实姓名',
|
||||||
|
['refresh' + 'Token']: '刷新令牌',
|
||||||
|
reply: '回复内容',
|
||||||
|
replyContent: '回复内容',
|
||||||
|
roles: '角色列表',
|
||||||
|
selfId: '机器人 QQ 号',
|
||||||
|
sessionId: '扫码会话 ID',
|
||||||
|
size: '文件大小',
|
||||||
|
slug: 'WordPress slug',
|
||||||
|
status: '状态',
|
||||||
|
tags: 'WordPress 标签 ID 列表',
|
||||||
|
targetId: '目标 ID',
|
||||||
|
targetType: '目标类型',
|
||||||
|
timezone: '时区',
|
||||||
|
title: '标题',
|
||||||
|
todayMessageCount: '今日消息数',
|
||||||
|
todaySendCount: '今日发送数',
|
||||||
|
total: '总条数',
|
||||||
|
triggerMode: '触发方式',
|
||||||
|
type: '类型',
|
||||||
|
url: '访问地址',
|
||||||
|
userId: '用户 QQ 号',
|
||||||
|
username: '用户名',
|
||||||
|
wordpressAuth: 'WordPress 授权信息',
|
||||||
|
wordpressAvailable: 'WordPress 是否可用',
|
||||||
|
wordpressError: 'WordPress 登录错误',
|
||||||
|
};
|
||||||
|
|
||||||
|
return descriptionMap[propertyName] || propertyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOperationDataExample(
|
||||||
|
path: string,
|
||||||
|
method: string,
|
||||||
|
operation: SwaggerOperation,
|
||||||
|
) {
|
||||||
|
const normalizedPath = path.toLowerCase();
|
||||||
|
const summary = operation.summary || operation.description || '';
|
||||||
|
|
||||||
|
if (normalizedPath.includes('/auth/login')) return adminLoginExample();
|
||||||
|
if (normalizedPath.includes('/auth/refresh')) return '<access-token>';
|
||||||
|
if (normalizedPath.includes('/auth/codes')) {
|
||||||
|
return ['QqBotAccountCreateButton', 'QqBotPermissionCreateButton'];
|
||||||
|
}
|
||||||
|
if (normalizedPath.includes('/scan/')) return qqbotScanExample();
|
||||||
|
if (normalizedPath.includes('/dashboard/summary')) return dashboardExample();
|
||||||
|
if (isPageResponsePath(normalizedPath)) {
|
||||||
|
return {
|
||||||
|
items: [itemExampleByPath(normalizedPath)],
|
||||||
|
total: 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (isArrayResponsePath(normalizedPath))
|
||||||
|
return [itemExampleByPath(normalizedPath)];
|
||||||
|
if (isBooleanResponsePath(normalizedPath, method, summary)) return true;
|
||||||
|
if (normalizedPath.includes('/check')) return { available: true };
|
||||||
|
if (normalizedPath.includes('/config')) return permissionConfigExample();
|
||||||
|
if (normalizedPath.includes('/health')) return [pluginHealthExample()];
|
||||||
|
if (normalizedPath.includes('/test'))
|
||||||
|
return { matched: true, reply: '测试回复' };
|
||||||
|
if (normalizedPath.includes('/upload')) return minioUploadExample();
|
||||||
|
if (normalizedPath.includes('/url')) {
|
||||||
|
return 'http://127.0.0.1:9000/kt-template-online/uploads/demo.png';
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemExampleByPath(normalizedPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPageResponsePath(path: string) {
|
||||||
|
if (path.startsWith('/wordpress/')) return false;
|
||||||
|
return (
|
||||||
|
path.endsWith('/list') ||
|
||||||
|
path.endsWith('/log/list') ||
|
||||||
|
path.includes('/allowlist') ||
|
||||||
|
path.includes('/blocklist')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isArrayResponsePath(path: string) {
|
||||||
|
return (
|
||||||
|
(path.startsWith('/wordpress/') && path.endsWith('/list')) ||
|
||||||
|
path.includes('/alllist') ||
|
||||||
|
path.includes('/enabled') ||
|
||||||
|
path.includes('/options') ||
|
||||||
|
path.includes('/codes') ||
|
||||||
|
path.includes('/menu/all') ||
|
||||||
|
path.includes('/operation/list') ||
|
||||||
|
path.includes('/event/list') ||
|
||||||
|
path.includes('/dict/')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isBooleanResponsePath(path: string, method: string, summary: string) {
|
||||||
|
return (
|
||||||
|
method === 'delete' ||
|
||||||
|
path.includes('/delete') ||
|
||||||
|
path.includes('/remove') ||
|
||||||
|
path.includes('/toggle') ||
|
||||||
|
path.includes('/kick') ||
|
||||||
|
path.includes('/cancel') ||
|
||||||
|
path.includes('/bind/') ||
|
||||||
|
path.includes('/unbind/') ||
|
||||||
|
summary.includes('是否')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isBinaryResponsePath(path: string) {
|
||||||
|
return path.includes('/download') || path.includes('/resource-proxy');
|
||||||
|
}
|
||||||
|
|
||||||
|
function itemExampleByPath(path: string) {
|
||||||
|
if (path.includes('/qqbot/account')) return qqbotAccountExample();
|
||||||
|
if (path.includes('/qqbot/command')) return qqbotCommandExample();
|
||||||
|
if (path.includes('/qqbot/rule')) return qqbotRuleExample();
|
||||||
|
if (path.includes('/qqbot/message')) return qqbotMessageExample();
|
||||||
|
if (path.includes('/qqbot/conversation')) return qqbotConversationExample();
|
||||||
|
if (path.includes('/qqbot/permission')) return qqbotPermissionExample();
|
||||||
|
if (path.includes('/qqbot/plugin')) return qqbotPluginExample();
|
||||||
|
if (path.includes('/qqbot/send')) return qqbotSendLogExample();
|
||||||
|
if (path.includes('/wordpress/article')) return wordpressArticleExample();
|
||||||
|
if (path.includes('/wordpress/category'))
|
||||||
|
return wordpressTaxonomyExample('NAS');
|
||||||
|
if (path.includes('/wordpress/tag'))
|
||||||
|
return wordpressTaxonomyExample('Docker');
|
||||||
|
if (path.includes('/system/menu') || path.includes('/menu/'))
|
||||||
|
return adminMenuExample();
|
||||||
|
if (path.includes('/system/dept')) return adminDeptExample();
|
||||||
|
if (path.includes('/system/role')) return adminRoleExample();
|
||||||
|
if (path.includes('/component')) return componentExample();
|
||||||
|
if (path.includes('/user')) return adminUserExample();
|
||||||
|
if (path.includes('/timezone')) return { timezone: 'Asia/Shanghai' };
|
||||||
|
if (path.includes('/minio')) return minioObjectExample();
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: '1000000000000000001',
|
||||||
|
name: 'KT 示例数据',
|
||||||
|
status: 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function adminLoginExample() {
|
||||||
|
return {
|
||||||
|
id: '1000000000000000001',
|
||||||
|
username: 'admin',
|
||||||
|
realName: '管理员',
|
||||||
|
roles: ['SuperAdmin'],
|
||||||
|
['access' + 'Token']: '<access-token>',
|
||||||
|
wordpressAuth: null,
|
||||||
|
wordpressAvailable: true,
|
||||||
|
wordpressError: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function adminUserExample() {
|
||||||
|
return {
|
||||||
|
id: '1000000000000000001',
|
||||||
|
username: 'admin',
|
||||||
|
realName: '管理员',
|
||||||
|
status: 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function adminMenuExample() {
|
||||||
|
return {
|
||||||
|
id: '1000000000000000001',
|
||||||
|
name: 'QqBot',
|
||||||
|
path: '/qqbot',
|
||||||
|
component: 'LAYOUT',
|
||||||
|
meta: {
|
||||||
|
title: 'QQBot',
|
||||||
|
icon: 'lucide:bot',
|
||||||
|
},
|
||||||
|
children: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function adminDeptExample() {
|
||||||
|
return {
|
||||||
|
id: '1000000000000000001',
|
||||||
|
name: 'KT 项目组',
|
||||||
|
parentId: '0',
|
||||||
|
status: 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function adminRoleExample() {
|
||||||
|
return {
|
||||||
|
id: '1000000000000000001',
|
||||||
|
roleName: '超级管理员',
|
||||||
|
roleCode: 'SuperAdmin',
|
||||||
|
status: 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function componentExample() {
|
||||||
|
return {
|
||||||
|
id: '1000000000000000001',
|
||||||
|
name: 'KT 表格组件',
|
||||||
|
type: 'table',
|
||||||
|
image: 'http://127.0.0.1:9000/kt-template-online/components/table.png',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function qqbotAccountExample() {
|
||||||
|
return {
|
||||||
|
id: '1000000000000000001',
|
||||||
|
selfId: '1914728559',
|
||||||
|
nickname: 'Mirror',
|
||||||
|
status: 'online',
|
||||||
|
connectionRole: 'universal',
|
||||||
|
lastHeartbeatAt: '2026-06-02T12:00:00.000Z',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function qqbotCommandExample() {
|
||||||
|
return {
|
||||||
|
id: '1000000000000000001',
|
||||||
|
name: 'FF14 查价',
|
||||||
|
command: '/price',
|
||||||
|
pluginKey: 'ff14Market',
|
||||||
|
enabled: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function qqbotRuleExample() {
|
||||||
|
return {
|
||||||
|
id: '1000000000000000001',
|
||||||
|
name: '关键词回复',
|
||||||
|
matchType: 'keyword',
|
||||||
|
keyword: 'test',
|
||||||
|
replyContent: '测试',
|
||||||
|
enabled: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function qqbotConversationExample() {
|
||||||
|
return {
|
||||||
|
id: '1000000000000000001',
|
||||||
|
selfId: '1914728559',
|
||||||
|
targetType: 'private',
|
||||||
|
targetId: '2354598417',
|
||||||
|
lastMessage: 'test',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function qqbotMessageExample() {
|
||||||
|
return {
|
||||||
|
id: '1000000000000000001',
|
||||||
|
selfId: '1914728559',
|
||||||
|
messageType: 'private',
|
||||||
|
direction: 'receive',
|
||||||
|
userId: '2354598417',
|
||||||
|
message: 'test',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function qqbotPermissionExample() {
|
||||||
|
return {
|
||||||
|
id: '1000000000000000001',
|
||||||
|
selfId: '1914728559',
|
||||||
|
targetType: 'qq',
|
||||||
|
targetId: '2354598417',
|
||||||
|
userId: '',
|
||||||
|
preciseUser: false,
|
||||||
|
enabled: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function qqbotPluginExample() {
|
||||||
|
return {
|
||||||
|
key: 'ff14Market',
|
||||||
|
name: 'FF14 查价',
|
||||||
|
triggerMode: 'command',
|
||||||
|
description: '查询 FF14 市场价格',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function qqbotSendLogExample() {
|
||||||
|
return {
|
||||||
|
id: '1000000000000000001',
|
||||||
|
selfId: '1914728559',
|
||||||
|
targetType: 'private',
|
||||||
|
targetId: '2354598417',
|
||||||
|
message: '测试',
|
||||||
|
status: 'success',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function qqbotScanExample() {
|
||||||
|
return {
|
||||||
|
sessionId: 'KT_SCAN_20260602120000',
|
||||||
|
qrcode: 'data:image/png;base64,MOCK_QRCODE',
|
||||||
|
status: 'waiting',
|
||||||
|
expireAt: '2026-06-02T12:05:00.000Z',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function dashboardExample() {
|
||||||
|
return {
|
||||||
|
accountCount: 1,
|
||||||
|
onlineAccountCount: 1,
|
||||||
|
todayMessageCount: 10,
|
||||||
|
todaySendCount: 3,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function permissionConfigExample() {
|
||||||
|
return {
|
||||||
|
mode: 'blocklist',
|
||||||
|
enabled: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function pluginHealthExample() {
|
||||||
|
return {
|
||||||
|
key: 'ff14Market',
|
||||||
|
name: 'FF14 查价',
|
||||||
|
available: true,
|
||||||
|
message: '插件可用',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function wordpressArticleExample() {
|
||||||
|
return {
|
||||||
|
id: 1,
|
||||||
|
title: '飞牛 NAS Docker、Jenkins 与 k3d/K8s 一体化技术方案',
|
||||||
|
status: 'publish',
|
||||||
|
categories: [1],
|
||||||
|
tags: [1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function wordpressTaxonomyExample(name: string) {
|
||||||
|
return {
|
||||||
|
id: 1,
|
||||||
|
name,
|
||||||
|
slug: name.toLowerCase(),
|
||||||
|
count: 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function minioObjectExample() {
|
||||||
|
return {
|
||||||
|
name: 'uploads/demo.png',
|
||||||
|
size: 2048,
|
||||||
|
etag: '9b2cf535f27731c974343645a3985328',
|
||||||
|
lastModified: '2026-06-02T12:00:00.000Z',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function minioUploadExample() {
|
||||||
|
return {
|
||||||
|
bucketName: 'kt-template-online',
|
||||||
|
objectName: 'uploads/demo.png',
|
||||||
|
etag: '9b2cf535f27731c974343645a3985328',
|
||||||
|
size: 2048,
|
||||||
|
mimeType: 'image/png',
|
||||||
|
url: 'http://127.0.0.1:9000/kt-template-online/uploads/demo.png',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import type { OpenAPIObject } from '@nestjs/swagger';
|
|||||||
import { urlencoded, json } from 'express';
|
import { urlencoded, json } from 'express';
|
||||||
import { knife4jSetup } from 'nestjs-knife4j-plus';
|
import { knife4jSetup } from 'nestjs-knife4j-plus';
|
||||||
import type { Service } from 'nestjs-knife4j-plus';
|
import type { Service } from 'nestjs-knife4j-plus';
|
||||||
|
import { applySwaggerResponseExamples } from './common';
|
||||||
|
|
||||||
type SwaggerPathMatcher = (path: string) => boolean;
|
type SwaggerPathMatcher = (path: string) => boolean;
|
||||||
|
|
||||||
@ -61,7 +62,9 @@ async function bootstrap() {
|
|||||||
.setTitle('KT-Template API')
|
.setTitle('KT-Template API')
|
||||||
.setVersion('1.0')
|
.setVersion('1.0')
|
||||||
.build();
|
.build();
|
||||||
const document = SwaggerModule.createDocument(app, options);
|
const document = applySwaggerResponseExamples(
|
||||||
|
SwaggerModule.createDocument(app, options),
|
||||||
|
);
|
||||||
SwaggerModule.setup('api', app, document);
|
SwaggerModule.setup('api', app, document);
|
||||||
const services: Service[] = [
|
const services: Service[] = [
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user