diff --git a/docs/backend-api-specification.md b/docs/backend-api-specification.md deleted file mode 100644 index 96a9a1f0..00000000 --- a/docs/backend-api-specification.md +++ /dev/null @@ -1,3427 +0,0 @@ ---- -title: tiny-pro-backend v1.0.0 -language_tabs: - - shell: Shell - - http: HTTP - - javascript: JavaScript - - ruby: Ruby - - python: Python - - php: PHP - - java: Java - - go: Go -toc_footers: [] -includes: [] -search: true -code_clipboard: true -highlight_theme: darkula -headingLevel: 2 -generator: "@tarslib/widdershins v4.0.17" - ---- - -# TinyPro 后端接口规格说明书 - -> v1.0.0 - -Base URLs: - -# Auth - -## POST 登录接口 - -POST /auth/login - -> Body 请求参数 - -```json -{ - "email": "j.nschfnp@qq.com", - "password": "ut sed veniam" -} -``` - -### 请求参数 - -|名称|位置|类型|必选|说明| -|---|---|---|---|---| -|body|body|object| 否 |none| -|» email|body|string| 是 |none| -|» password|body|string| 否 |none| - -> 返回示例 - -> 201 Response - -```json -{ - "token": "string" -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|成功|Inline| - -### 返回数据结构 - -状态码 **201** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» token|string|true|none|经过加盐处理的AccessToken|none| - -## POST 登出 - -POST /auth/logout - -> Body 请求参数 - -```json -{ - "token": "string" -} -``` - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|body|body|object| 否 ||none| -|» token|body|string| 是 | 登录时返回的token|none| - -> 返回示例 - -> 200 Response - -```json -{} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -# i18 - -## POST 创建一个国际化词条 - -POST /i18 - -> Body 请求参数 - -```json -{ - "lang": 0, - "key": "string", - "content": "string" -} -``` - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|body|body|object| 否 ||none| -|» lang|body|number| 是 ||语言ID| -|» key|body|string| 是 ||词条键| -|» content|body|string| 是 ||词条内容| - -> 返回示例 - -> 200 Response - -```json -{ - "id": 0, - "lang": { - "id": 0, - "name": "string", - "i18": [ - { - "id": 0, - "lang": { - "id": 0, - "name": "string", - "i18": [ - null - ] - }, - "key": "string", - "content": "string" - } - ] - }, - "key": "string", - "content": "string" -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|[I18](#schemai18)| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## GET 获取国际化字段 - -GET /i18 - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|page|query|number| 否 ||页码.| -|limit|query|number| 否 ||获取数量| -|all|query|number| 否 ||是否全部获取, 如果不未0则忽略page与limit, 返回所有的国际化字段| -|key|query|string| 否 ||国际化字段的键, 如果设定则表示按照键来模糊查找| -|content|query|string| 否 ||国际化字段的值, 如果设定则表示按照值来模糊查找| - -> 返回示例 - -> 200 Response - -```json -{ - "items": [ - { - "id": 0, - "lang": { - "id": 0, - "name": "string", - "i18": [ - { - "id": null, - "lang": null, - "key": null, - "content": null - } - ] - }, - "key": "string", - "content": "string" - } - ], - "meta": { - "itemCount": 0, - "totalItems": 0, - "itemsPerPage": 0, - "currentPage": 0 - }, - "links": { - "first": "string", - "previous": "string", - "next": "string", - "last": "string" - } -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **200** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» items|[[I18](#schemai18)]|true|none||none| -|»» id|number|true|none|国际化字段的自增id|none| -|»» lang|[Lang](#schemalang)|true|none|国际化字段对应的语言信息|none| -|»»» id|number|true|none|语言ID|none| -|»»» name|string|true|none|语言名|none| -|»»» i18|[[I18](#schemai18)]|true|none|对应的国际化词条|none| -|»»»» id|number|true|none|国际化字段的自增id|none| -|»»»» lang|[Lang](#schemalang)|true|none|国际化字段对应的语言信息|none| -|»»»» key|string|true|none|国际化字段的键|none| -|»»»» content|string|true|none|国际化字段的实际内容|none| -|»» key|string|true|none|国际化字段的键|none| -|»» content|string|true|none|国际化字段的实际内容|none| -|» meta|[PaginationMeta](#schemapaginationmeta)|true|none||none| -|»» itemCount|number|false|none||none| -|»» totalItems|number|false|none||none| -|»» itemsPerPage|number|false|none||none| -|»» currentPage|number|false|none||none| -|» links|[PaginationLinks](#schemapaginationlinks)|true|none||none| -|»» first|string|false|none||none| -|»» previous|string|false|none||none| -|»» next|string|false|none||none| -|»» last|string|false|none||none| - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## GET 获取国际化表 - -GET /i18/format - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|lang|query|string| 否 ||语言名| - -> 返回示例 - -> 200 Response - -```json -{ - "[lang-name]": { - "[key]": "string" - } -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **200** - -*Record>* - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» [lang-name]|object|true|none||none| -|»» [key]|string|true|none||none| - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## GET 根据国际化字段ID获取某一个国际化字段 - -GET /i18/{id} - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|id|path|integer| 是 ||国际化字段ID| - -> 返回示例 - -> 200 Response - -```json -{ - "id": 0, - "lang": { - "id": 0, - "name": "string", - "i18": [ - { - "id": 0, - "lang": { - "id": 0, - "name": "string", - "i18": [ - null - ] - }, - "key": "string", - "content": "string" - } - ] - }, - "key": "string", - "content": "string" -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|[I18](#schemai18)| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## PATCH 修改一个国际化字段 - -PATCH /i18/{id} - -> Body 请求参数 - -```json -{ - "lang": 0, - "key": "string", - "content": "string" -} -``` - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|id|path|string| 是 ||国际化字段ID| -|body|body|object| 否 ||none| -|» lang|body|number| 否 ||语言名| -|» key|body|string| 否 ||国际化字段键| -|» content|body|string| 否 ||国际化字段值| - -> 返回示例 - -> 200 Response - -```json -{ - "id": 0, - "lang": { - "id": 0, - "name": "string", - "i18": [ - { - "id": 0, - "lang": { - "id": 0, - "name": "string", - "i18": [ - null - ] - }, - "key": "string", - "content": "string" - } - ] - }, - "key": "string", - "content": "string" -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|[I18](#schemai18)| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## DELETE 删除一个国际化字段 - -DELETE /i18/{id} - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|id|path|integer| 是 ||国际化字段ID| - -> 返回示例 - -> 200 Response - -```json -{ - "id": 0, - "lang": { - "id": 0, - "name": "string", - "i18": [ - { - "id": 0, - "lang": { - "id": 0, - "name": "string", - "i18": [ - null - ] - }, - "key": "string", - "content": "string" - } - ] - }, - "key": "string", - "content": "string" -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|[I18](#schemai18)| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -# user - -## POST 新建一个用户 - -POST /user/reg - -> Body 请求参数 - -```json -{ - "name": "string", - "email": "string", - "password": "string", - "roleIds": [ - 0 - ], - "department": "string", - "employeeType": "string", - "probationStart": "string", - "probationEnd": "string", - "probationDuration": "string", - "protocolStart": "string", - "protocolEnd": "string", - "address": "string", - "status": 0 -} -``` - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|body|body|object| 否 ||none| -|» name|body|string| 是 | 用户昵称|none| -|» email|body|string| 是 | 用户Email|登录使用| -|» password|body|string| 是 | 用户密码|none| -|» roleIds|body|[number]| 是 | 用户角色ID数组|none| -|» department|body|string| 否 | 遗留未知字段|1.1.0以前的遗留字段| -|» employeeType|body|string| 否 | 遗留未知字段|1.1.0以前的遗留字段| -|» probationStart|body|string| 否 | 遗留未知字段|1.1.0以前的遗留字段| -|» probationEnd|body|string| 否 | 遗留未知字段|1.1.0以前的遗留字段| -|» probationDuration|body|string| 否 | 遗留未知字段|1.1.0以前的遗留字段| -|» protocolStart|body|string| 否 | 遗留未知字段|1.1.0以前的遗留字段| -|» protocolEnd|body|string| 否 | 遗留未知字段|1.1.0以前的遗留字段| -|» address|body|string| 否 | 遗留未知字段|1.1.0以前的遗留字段| -|» status|body|number| 否 | 帐号状态|none| - -> 返回示例 - -> 200 Response - -```json -{ - "id": "string", - "name": "string", - "email": "string", - "password": "string", - "role": [ - { - "id": "string", - "name": "string", - "permission": [ - { - "id": 0, - "desc": "string", - "name": "string" - } - ], - "menus": [ - { - "id": 0, - "name": "string", - "order": 0, - "parentId": 0, - "menuType": "string", - "icon": "string", - "component": "string", - "path": "string", - "locale": "string" - } - ] - } - ], - "department": "string", - "employeeType": "string", - "probationENd": "string", - "robationDuration": "string", - "protocolStart": "string", - "protocolEnd": "string", - "address": "string", - "status": 0, - "createTime": "string", - "updateTime": "string", - "create_time": "string", - "salt": "string", - "update_time": "string" -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|[User](#schemauser)| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## GET 获取用户信息 - -GET /user/info/{email} - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|email|path|string| 是 ||用户邮箱| - -> 返回示例 - -> 200 Response - -```json -{ - "id": "string", - "name": "string", - "email": "string", - "password": "string", - "role": [ - { - "id": "string", - "name": "string", - "permission": [ - { - "id": 0, - "desc": "string", - "name": "string" - } - ], - "menus": [ - { - "id": 0, - "name": "string", - "order": 0, - "parentId": 0, - "menuType": "string", - "icon": "string", - "component": "string", - "path": "string", - "locale": "string" - } - ] - } - ], - "department": "string", - "employeeType": "string", - "probationENd": "string", - "robationDuration": "string", - "protocolStart": "string", - "protocolEnd": "string", - "address": "string", - "status": 0, - "createTime": "string", - "updateTime": "string", - "create_time": "string", - "salt": "string", - "update_time": "string" -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|[User](#schemauser)| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## DELETE 删除一个用户 - -DELETE /user/{email} - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|email|path|string| 是 ||用户邮箱| - -> 返回示例 - -> 200 Response - -```json -{ - "id": "string", - "name": "string", - "email": "string", - "password": "string", - "role": [ - { - "id": "string", - "name": "string", - "permission": [ - { - "id": 0, - "desc": "string", - "name": "string" - } - ], - "menus": [ - { - "id": 0, - "name": "string", - "order": 0, - "parentId": 0, - "menuType": "string", - "icon": "string", - "component": "string", - "path": "string", - "locale": "string" - } - ] - } - ], - "department": "string", - "employeeType": "string", - "probationENd": "string", - "robationDuration": "string", - "protocolStart": "string", - "protocolEnd": "string", - "address": "string", - "status": 0, - "createTime": "string", - "updateTime": "string", - "create_time": "string", - "salt": "string", - "update_time": "string" -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|[User](#schemauser)| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## PATCH 修改一个用户 - -PATCH /user/update - -如果修改了角色, 必须将该用户踢下线 (在redis中删除该用户的token) - -> Body 请求参数 - -```json -{ - "oldPassword": "string", - "newPassword": "string", - "email": "string", - "roleIds": [ - 0 - ], - "department": "string", - "employeeType": "string", - "probationStar": "string", - "probationEnd": "string", - "probationDuration": "string", - "protocolStart": "string", - "protocolEnd": "string", - "address": "string", - "status": 0, - "name": "string" -} -``` - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|body|body|object| 否 ||none| -|» oldPassword|body|string| 否 | 旧密码|none| -|» newPassword|body|string| 否 | 新密码|none| -|» email|body|string| 是 | 邮箱|none| -|» roleIds|body|[number]| 是 | 角色ID|none| -|» department|body|string| 是 ||none| -|» employeeType|body|string| 是 ||none| -|» probationStar|body|string| 是 ||none| -|» probationEnd|body|string| 是 ||none| -|» probationDuration|body|string| 是 ||none| -|» protocolStart|body|string| 是 ||none| -|» protocolEnd|body|string| 是 ||none| -|» address|body|string| 是 ||none| -|» status|body|number| 是 | 状态|none| -|» name|body|string| 是 | 用户名|none| - -> 返回示例 - -> 200 Response - -```json -{ - "id": "string", - "name": "string", - "email": "string", - "password": "string", - "role": [ - { - "id": "string", - "name": "string", - "permission": [ - { - "id": 0, - "desc": "string", - "name": "string" - } - ], - "menus": [ - { - "id": 0, - "name": "string", - "order": 0, - "parentId": 0, - "menuType": "string", - "icon": "string", - "component": "string", - "path": "string", - "locale": "string" - } - ] - } - ], - "department": "string", - "employeeType": "string", - "probationENd": "string", - "robationDuration": "string", - "protocolStart": "string", - "protocolEnd": "string", - "address": "string", - "status": 0, - "createTime": "string", - "updateTime": "string", - "create_time": "string", - "salt": "string", - "update_time": "string" -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|[User](#schemauser)| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## GET 获取用户列表 - -GET /user - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|name|query|string| 否 ||模糊查找的用户名| -|role|query|string| 否 ||用逗号分隔| -|email|query|string| 否 ||模糊查找的邮箱| - -> 返回示例 - -> 200 Response - -```json -{ - "items": [ - { - "id": "string", - "name": "string", - "email": "string", - "password": "string", - "role": [ - { - "id": "string", - "name": "string", - "permission": [ - {} - ], - "menus": [ - {} - ] - } - ], - "department": "string", - "employeeType": "string", - "probationENd": "string", - "robationDuration": "string", - "protocolStart": "string", - "protocolEnd": "string", - "address": "string", - "status": 0, - "createTime": "string", - "updateTime": "string", - "create_time": "string", - "salt": "string", - "update_time": "string" - } - ], - "meta": { - "itemCount": 0, - "totalItems": 0, - "itemsPerPage": 0, - "currentPage": 0 - }, - "links": { - "first": "string", - "previous": "string", - "next": "string", - "last": "string" - } -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **200** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» items|[[User](#schemauser)]|true|none||none| -|»» id|string|true|none|数据库自增id|none| -|»» name|string|true|none|用户名|none| -|»» email|string|true|none||登录邮箱| -|»» password|string|true|none|密码|none| -|»» role|[[Role](#schemarole)]|true|none|角色|none| -|»»» id|string|true|none|角色id|none| -|»»» name|string|true|none|角色名|none| -|»»» permission|[[Permission](#schemapermission)]|true|none|权限|none| -|»»»» id|number|true|none|权限ID|none| -|»»»» desc|string|true|none|权限介绍|none| -|»»»» name|string|true|none|权限键|none| -|»»» menus|[[Menu](#schemamenu)]|true|none|菜单|none| -|»»»» id|number|true|none|菜单id|none| -|»»»» name|string|true|none|菜单名|none| -|»»»» order|number|true|none|排序|none| -|»»»» parentId|number|false|none|父级id|none| -|»»»» menuType|string|true|none|保留字段|none| -|»»»» icon|string|false|none|图标名|none| -|»»»» component|string|true|none|组件名|none| -|»»»» path|string|true|none|路由路径|none| -|»»»» locale|string|true|none|国际化键|none| -|»» department|string|true|none||1.1.0以前的遗留字段| -|»» employeeType|string|true|none||1.1.0以前的遗留字段| -|»» probationENd|string|true|none||1.1.0以前的遗留字段| -|»» robationDuration|string|true|none||1.1.0以前的遗留字段| -|»» protocolStart|string|true|none||1.1.0以前的遗留字段| -|»» protocolEnd|string|true|none||1.1.0以前的遗留字段| -|»» address|string|true|none||1.1.0以前的遗留字段| -|»» status|number|true|none|帐号状态|none| -|»» createTime|string|true|none||none| -|»» updateTime|string|true|none||none| -|»» create_time|string|true|none||none| -|»» salt|string|true|none|bcrypt盐|none| -|»» update_time|string|true|none||none| -|» meta|[PaginationMeta](#schemapaginationmeta)|true|none||none| -|»» itemCount|number|false|none||none| -|»» totalItems|number|false|none||none| -|»» itemsPerPage|number|false|none||none| -|»» currentPage|number|false|none||none| -|» links|[PaginationLinks](#schemapaginationlinks)|true|none||none| -|»» first|string|false|none||none| -|»» previous|string|false|none||none| -|»» next|string|false|none||none| -|»» last|string|false|none||none| - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## PATCH 强制修改一个用户的密码 - -PATCH /user/admin/updatePwd - -> 返回示例 - -> 200 Response - -```json -{ - "email": "string", - "newPassword": "string", - "confirmPassword": "string" -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **200** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» email|string|true|none||强制修改密码的邮箱| -|» newPassword|string|true|none||新密码| -|» confirmPassword|string|true|none||确认新密码| - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## PATCH 修改自身密码 - -PATCH /user/updatePwd - -> Body 请求参数 - -```json -{ - "email": "string", - "token": "string", - "newPassword": "string", - "oldPassword": "string" -} -``` - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|body|body|object| 否 ||none| -|» email|body|string| 是 ||要修改用户的邮箱名| -|» token|body|string| 是 ||用户token| -|» newPassword|body|string| 是 ||新密码| -|» oldPassword|body|string| 是 ||旧密码| - -> 返回示例 - -> 200 Response - -```json -{} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -# role - -## POST 新增一个角色 - -POST /role - -> Body 请求参数 - -```json -{ - "name": "string", - "permissionIds": [ - 0 - ], - "menuIds": [ - 0 - ] -} -``` - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|body|body|object| 否 ||none| -|» name|body|string| 是 | 角色名|none| -|» permissionIds|body|[number]| 是 | 权限id数组|none| -|» menuIds|body|[number]| 是 | 菜单id数组|none| - -> 返回示例 - -> 200 Response - -```json -{ - "id": "string", - "name": "string", - "permission": [ - { - "id": 0, - "desc": "string", - "name": "string" - } - ], - "menus": [ - { - "id": 0, - "name": "string", - "order": 0, - "parentId": 0, - "menuType": "string", - "icon": "string", - "component": "string", - "path": "string", - "locale": "string" - } - ] -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|[Role](#schemarole)| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## GET 查找所有角色 - -GET /role - -> 返回示例 - -> 200 Response - -```json -[ - { - "id": "string", - "name": "string", - "permission": [ - { - "id": 0, - "desc": "string", - "name": "string" - } - ], - "menus": [ - { - "id": 0, - "name": "string", - "order": 0, - "parentId": 0, - "menuType": "string", - "icon": "string", - "component": "string", - "path": "string", - "locale": "string" - } - ] - } -] -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **200** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|*anonymous*|[[Role](#schemarole)]|false|none||none| -|» id|string|true|none|角色id|none| -|» name|string|true|none|角色名|none| -|» permission|[[Permission](#schemapermission)]|true|none|权限|none| -|»» id|number|true|none|权限ID|none| -|»» desc|string|true|none|权限介绍|none| -|»» name|string|true|none|权限键|none| -|» menus|[[Menu](#schemamenu)]|true|none|菜单|none| -|»» id|number|true|none|菜单id|none| -|»» name|string|true|none|菜单名|none| -|»» order|number|true|none|排序|none| -|»» parentId|number|false|none|父级id|none| -|»» menuType|string|true|none|保留字段|none| -|»» icon|string|false|none|图标名|none| -|»» component|string|true|none|组件名|none| -|»» path|string|true|none|路由路径|none| -|»» locale|string|true|none|国际化键|none| - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## PATCH 修改一个角色 - -PATCH /role - -> Body 请求参数 - -```json -{ - "id": 0, - "name": "string", - "permissionIds": [ - 0 - ], - "menuIds": [ - 0 - ] -} -``` - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|body|body|object| 否 ||none| -|» id|body|number| 是 | 角色id|none| -|» name|body|string| 否 | 角色名|none| -|» permissionIds|body|[number]| 否 | 角色权限id列表|none| -|» menuIds|body|[number]| 否 | 角色菜单列表|none| - -> 返回示例 - -> 200 Response - -```json -{ - "id": "string", - "name": "string", - "permission": [ - { - "id": 0, - "desc": "string", - "name": "string" - } - ], - "menus": [ - { - "id": 0, - "name": "string", - "order": 0, - "parentId": 0, - "menuType": "string", - "icon": "string", - "component": "string", - "path": "string", - "locale": "string" - } - ] -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|[Role](#schemarole)| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## GET 获取角色列表的详细信息 - -GET /role/detail - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|page|query|integer| 否 ||页数| -|limit|query|integer| 否 ||获取数量| -|name|query|string| 否 ||角色名| - -> 返回示例 - -> 200 Response - -```json -{ - "roleInfo": { - "items": [ - { - "id": "string", - "name": "string", - "permission": [ - { - "id": null, - "desc": null, - "name": null - } - ], - "menus": [ - { - "id": null, - "name": null, - "order": null, - "parentId": null, - "menuType": null, - "icon": null, - "component": null, - "path": null, - "locale": null - } - ] - } - ], - "meta": { - "itemCount": 0, - "totalItems": 0, - "itemsPerPage": 0, - "currentPage": 0 - }, - "links": { - "first": "string", - "previous": "string", - "next": "string", - "last": "string" - } - }, - "menuTree": [ - { - "id": 0, - "label": "string", - "children": [ - { - "id": null, - "label": "string", - "children": [ - {} - ], - "url": "string", - "component": "string", - "customIcon": "string", - "menuType": "string", - "parentId": "string", - "order": 0, - "locale": "string" - } - ], - "url": "string", - "component": "string", - "customIcon": "string", - "menuType": "string", - "parentId": "string", - "order": 0, - "locale": "string" - } - ] -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **200** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» roleInfo|object|true|none||none| -|»» items|[[Role](#schemarole)]|true|none||none| -|»»» id|string|true|none|角色id|none| -|»»» name|string|true|none|角色名|none| -|»»» permission|[[Permission](#schemapermission)]|true|none|权限|none| -|»»»» id|number|true|none|权限ID|none| -|»»»» desc|string|true|none|权限介绍|none| -|»»»» name|string|true|none|权限键|none| -|»»» menus|[[Menu](#schemamenu)]|true|none|菜单|none| -|»»»» id|number|true|none|菜单id|none| -|»»»» name|string|true|none|菜单名|none| -|»»»» order|number|true|none|排序|none| -|»»»» parentId|number|false|none|父级id|none| -|»»»» menuType|string|true|none|保留字段|none| -|»»»» icon|string|false|none|图标名|none| -|»»»» component|string|true|none|组件名|none| -|»»»» path|string|true|none|路由路径|none| -|»»»» locale|string|true|none|国际化键|none| -|»» meta|[PaginationMeta](#schemapaginationmeta)|true|none||none| -|»»» itemCount|number|false|none||none| -|»»» totalItems|number|false|none||none| -|»»» itemsPerPage|number|false|none||none| -|»»» currentPage|number|false|none||none| -|»» links|[PaginationLinks](#schemapaginationlinks)|true|none||none| -|»»» first|string|false|none||none| -|»»» previous|string|false|none||none| -|»»» next|string|false|none||none| -|»»» last|string|false|none||none| -|» menuTree|[[ITreeNodeData](#schemaitreenodedata)]|true|none||none| -|»» id|any|true|none|id|none| - -*anyOf* - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|»»» *anonymous*|number|false|none||none| - -*or* - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|»»» *anonymous*|string|false|none||none| - -*continued* - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|»» label|string|true|none|展示的标签|none| -|»» children|[[ITreeNodeData](#schemaitreenodedata)]|false|none|子集菜单|none| -|»»» id|any|true|none|id|none| -|»»» label|string|true|none|展示的标签|none| -|»»» children|[[ITreeNodeData](#schemaitreenodedata)]|false|none|子集菜单|none| -|»»» url|string|true|none|访问路由|none| -|»»» component|string|true|none|组件|none| -|»»» customIcon|string|true|none|图标名|none| -|»»» menuType|string|true|none|保留字段|none| -|»»» parentId|string|true|none|父级id|none| -|»»» order|integer|true|none|排序|none| -|»»» locale|string|true|none|国际化键|none| -|»» url|string|true|none|访问路由|none| -|»» component|string|true|none|组件|none| -|»» customIcon|string|true|none|图标名|none| -|»» menuType|string|true|none|保留字段|none| -|»» parentId|string|true|none|父级id|none| -|»» order|integer|true|none|排序|none| -|»» locale|string|true|none|国际化键|none| - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## DELETE 删除一个角色 - -DELETE /role/{id} - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|id|path|number| 是 ||角色id| - -> 返回示例 - -> 200 Response - -```json -{ - "id": "string", - "name": "string", - "permission": [ - { - "id": 0, - "desc": "string", - "name": "string" - } - ], - "menus": [ - { - "id": 0, - "name": "string", - "order": 0, - "parentId": 0, - "menuType": "string", - "icon": "string", - "component": "string", - "path": "string", - "locale": "string" - } - ] -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|[Role](#schemarole)| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## GET 获取一个角色的详细信息 - -GET /role/info/{id} - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|id|path|integer| 是 ||角色id| - -> 返回示例 - -> 200 Response - -```json -{ - "id": "string", - "name": "string", - "permission": [ - { - "id": 0, - "desc": "string", - "name": "string" - } - ], - "menus": [ - { - "id": 0, - "name": "string", - "order": 0, - "parentId": 0, - "menuType": "string", - "icon": "string", - "component": "string", - "path": "string", - "locale": "string" - } - ] -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|[Role](#schemarole)| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -# permission - -## POST 创建权限 - -POST /permission - -> Body 请求参数 - -```json -{ - "name": "string", - "desc": "string" -} -``` - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|body|body|object| 否 ||none| -|» name|body|string| 是 | 权限键|none| -|» desc|body|string| 是 | 权限简介|none| - -> 返回示例 - -> 200 Response - -```json -{ - "id": 0, - "desc": "string", - "name": "string" -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|[Permission](#schemapermission)| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## PATCH 修改权限 - -PATCH /permission - -> Body 请求参数 - -```json -{ - "name": "string", - "desc": "string", - "id": 0 -} -``` - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|body|body|[UpdatePermissionDTO](#schemaupdatepermissiondto)| 否 ||none| - -> 返回示例 - -> 200 Response - -```json -{} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## GET 获取权限 - -GET /permission - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|page|query|string| 否 ||页数| -|limit|query|string| 否 ||如果为0则查找所有| -|name|query|string| 否 ||权限名,where子句格式| - -> 返回示例 - -> 200 Response - -```json -{ - "items": [ - { - "id": 0, - "desc": "string", - "name": "string" - } - ], - "meta": { - "itemCount": 0, - "totalItems": 0, - "itemsPerPage": 0, - "currentPage": 0 - }, - "links": { - "first": "string", - "previous": "string", - "next": "string", - "last": "string" - } -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **200** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» items|[[Permission](#schemapermission)]|true|none||none| -|»» id|number|true|none|权限ID|none| -|»» desc|string|true|none|权限介绍|none| -|»» name|string|true|none|权限键|none| -|» meta|[PaginationMeta](#schemapaginationmeta)|true|none||none| -|»» itemCount|number|false|none||none| -|»» totalItems|number|false|none||none| -|»» itemsPerPage|number|false|none||none| -|»» currentPage|number|false|none||none| -|» links|[PaginationLinks](#schemapaginationlinks)|true|none||none| -|»» first|string|false|none||none| -|»» previous|string|false|none||none| -|»» next|string|false|none||none| -|»» last|string|false|none||none| - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## DELETE 删除权限 - -DELETE /permission/{id} - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|id|path|integer| 是 ||权限id| - -> 返回示例 - -> 200 Response - -```json -[ - { - "id": 0, - "desc": "string", - "name": "string" - } -] -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **200** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» id|number|true|none|权限ID|none| -|» desc|string|true|none|权限介绍|none| -|» name|string|true|none|权限键|none| - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -# menu - -## GET 获取用户菜单 - -GET /menu/role/{email} - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|email|path|string| 是 ||用户email| - -> 返回示例 - -> 200 Response - -```json -[ - { - "id": 0, - "label": "string", - "children": [ - { - "id": 0, - "label": "string", - "children": [ - { - "id": null, - "label": "string", - "children": [ - null - ], - "url": "string", - "component": "string", - "customIcon": "string", - "menuType": "string", - "parentId": "string", - "order": 0, - "locale": "string" - } - ], - "url": "string", - "component": "string", - "customIcon": "string", - "menuType": "string", - "parentId": "string", - "order": 0, - "locale": "string" - } - ], - "url": "string", - "component": "string", - "customIcon": "string", - "menuType": "string", - "parentId": "string", - "order": 0, - "locale": "string" - } -] -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **200** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|*anonymous*|[[ITreeNodeData](#schemaitreenodedata)]|false|none||none| -|» id|any|true|none|id|none| - -*anyOf* - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|»» *anonymous*|number|false|none||none| - -*or* - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|»» *anonymous*|string|false|none||none| - -*continued* - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» label|string|true|none|展示的标签|none| -|» children|[[ITreeNodeData](#schemaitreenodedata)]|false|none|子集菜单|none| -|»» id|any|true|none|id|none| -|»» label|string|true|none|展示的标签|none| -|»» children|[[ITreeNodeData](#schemaitreenodedata)]|false|none|子集菜单|none| -|»» url|string|true|none|访问路由|none| -|»» component|string|true|none|组件|none| -|»» customIcon|string|true|none|图标名|none| -|»» menuType|string|true|none|保留字段|none| -|»» parentId|string|true|none|父级id|none| -|»» order|integer|true|none|排序|none| -|»» locale|string|true|none|国际化键|none| -|» url|string|true|none|访问路由|none| -|» component|string|true|none|组件|none| -|» customIcon|string|true|none|图标名|none| -|» menuType|string|true|none|保留字段|none| -|» parentId|string|true|none|父级id|none| -|» order|integer|true|none|排序|none| -|» locale|string|true|none|国际化键|none| - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## GET 获取菜单树 - -GET /menu - -获取所有的菜单,并根绝parentId建立一个菜单树 - -> 返回示例 - -> 200 Response - -```json -[ - { - "id": 0, - "label": "string", - "children": [ - { - "id": 0, - "label": "string", - "children": [ - { - "id": null, - "label": "string", - "children": [ - null - ], - "url": "string", - "component": "string", - "customIcon": "string", - "menuType": "string", - "parentId": "string", - "order": 0, - "locale": "string" - } - ], - "url": "string", - "component": "string", - "customIcon": "string", - "menuType": "string", - "parentId": "string", - "order": 0, - "locale": "string" - } - ], - "url": "string", - "component": "string", - "customIcon": "string", - "menuType": "string", - "parentId": "string", - "order": 0, - "locale": "string" - } -] -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **200** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|*anonymous*|[[ITreeNodeData](#schemaitreenodedata)]|false|none||none| -|» id|any|true|none|id|none| - -*anyOf* - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|»» *anonymous*|number|false|none||none| - -*or* - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|»» *anonymous*|string|false|none||none| - -*continued* - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» label|string|true|none|展示的标签|none| -|» children|[[ITreeNodeData](#schemaitreenodedata)]|false|none|子集菜单|none| -|»» id|any|true|none|id|none| -|»» label|string|true|none|展示的标签|none| -|»» children|[[ITreeNodeData](#schemaitreenodedata)]|false|none|子集菜单|none| -|»» url|string|true|none|访问路由|none| -|»» component|string|true|none|组件|none| -|»» customIcon|string|true|none|图标名|none| -|»» menuType|string|true|none|保留字段|none| -|»» parentId|string|true|none|父级id|none| -|»» order|integer|true|none|排序|none| -|»» locale|string|true|none|国际化键|none| -|» url|string|true|none|访问路由|none| -|» component|string|true|none|组件|none| -|» customIcon|string|true|none|图标名|none| -|» menuType|string|true|none|保留字段|none| -|» parentId|string|true|none|父级id|none| -|» order|integer|true|none|排序|none| -|» locale|string|true|none|国际化键|none| - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## POST 新增一个菜单 - -POST /menu - -> Body 请求参数 - -```json -{ - "order": 0, - "menuType": "string", - "name": "string", - "path": "string", - "component": "string", - "icon": "string", - "locale": "string", - "parentId": 0 -} -``` - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|body|body|object| 否 ||none| -|» order|body|number| 是 ||none| -|» menuType|body|string| 是 ||none| -|» name|body|string| 是 ||none| -|» path|body|string| 是 ||none| -|» component|body|string| 是 ||none| -|» icon|body|string| 是 ||none| -|» locale|body|string| 是 ||none| -|» parentId|body|number¦null| 否 ||none| - -> 返回示例 - -> 200 Response - -```json -{ - "id": 0, - "name": "string", - "order": 0, - "parentId": 0, - "menuType": "string", - "icon": "string", - "component": "string", - "path": "string", - "locale": "string" -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|[Menu](#schemamenu)| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## PATCH 修改一个菜单 - -PATCH /menu - -> Body 请求参数 - -```json -{ - "order": 0, - "menuType": "string", - "name": "string", - "path": "string", - "component": "string", - "icon": "string", - "locale": "string", - "parentId": 0, - "id": 0 -} -``` - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|body|body|[UpdateMenuDTO](#schemaupdatemenudto)| 否 ||none| - -> 返回示例 - -> 200 Response - -```json -true -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|boolean| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## DELETE 删除一个菜单 - -DELETE /menu - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|id|query|integer| 否 ||菜单Id| -|parentId|query|integer| 否 ||父级菜单ID| - -> 返回示例 - -> 200 Response - -```json -{ - "id": 0, - "name": "string", - "order": 0, - "parentId": 0, - "menuType": "string", - "icon": "string", - "component": "string", - "path": "string", - "locale": "string" -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|[Menu](#schemamenu)| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -# mock - -## GET GET 的mock方法 - -GET /mock - -> 返回示例 - -> 200 Response - -```json -"string" -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|string| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## POST POST的mock方法 - -POST /mock - -> 返回示例 - -> 200 Response - -```json -"string" -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|string| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -# lang - -## GET 获取所有语言 - -GET /lang - -> 返回示例 - -> 200 Response - -```json -[ - { - "id": 0, - "name": "string", - "i18": [ - { - "id": 0, - "lang": { - "id": 0, - "name": "string", - "i18": [ - {} - ] - }, - "key": "string", - "content": "string" - } - ] - } -] -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **200** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|*anonymous*|[[Lang](#schemalang)]|false|none||none| -|» 国际化字段对应的语言信息|[Lang](#schemalang)|false|none|国际化字段对应的语言信息|none| -|»» id|number|true|none|语言ID|none| -|»» name|string|true|none|语言名|none| -|»» i18|[[I18](#schemai18)]|true|none|对应的国际化词条|none| -|»»» id|number|true|none|国际化字段的自增id|none| -|»»» lang|[Lang](#schemalang)|true|none|国际化字段对应的语言信息|none| -|»»»» id|number|true|none|语言ID|none| -|»»»» name|string|true|none|语言名|none| -|»»»» i18|[[I18](#schemai18)]|true|none|对应的国际化词条|none| -|»»» key|string|true|none|国际化字段的键|none| -|»»» content|string|true|none|国际化字段的实际内容|none| - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## POST 增加一个语言 - -POST /lang - -> Body 请求参数 - -```json -{ - "name": "string" -} -``` - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|body|body|[CreateLang](#schemacreatelang)| 否 ||none| - -> 返回示例 - -> 200 Response - -```json -{ - "id": 0, - "name": "string", - "i18": [ - { - "id": 0, - "lang": { - "id": 0, - "name": "string", - "i18": [ - { - "id": null, - "lang": null, - "key": null, - "content": null - } - ] - }, - "key": "string", - "content": "string" - } - ] -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|[Lang](#schemalang)| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## PACH 修改一个语言 - -PACH /lang/{id} - -> Body 请求参数 - -```json -{ - "name": "string" -} -``` - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|id|path|string| 是 ||语言ID| -|body|body|[UpdateLang](#schemaupdatelang)| 否 ||none| - -> 返回示例 - -> 200 Response - -```json -{ - "id": 0, - "name": "string", - "i18": [ - { - "id": 0, - "lang": { - "id": 0, - "name": "string", - "i18": [ - { - "id": null, - "lang": null, - "key": null, - "content": null - } - ] - }, - "key": "string", - "content": "string" - } - ] -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|[Lang](#schemalang)| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -## DELETE 删除一个语言 - -DELETE /lang/{id} - -### 请求参数 - -|名称|位置|类型|必选|中文名|说明| -|---|---|---|---|---|---| -|id|path|number| 是 ||语言ID| - -> 返回示例 - -> 200 Response - -```json -{ - "id": 0, - "name": "string", - "i18": [ - { - "id": 0, - "lang": { - "id": 0, - "name": "string", - "i18": [ - { - "id": null, - "lang": null, - "key": null, - "content": null - } - ] - }, - "key": "string", - "content": "string" - } - ] -} -``` - -### 返回结果 - -|状态码|状态码含义|说明|数据模型| -|---|---|---|---| -|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|[Lang](#schemalang)| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|401 未登录|Inline| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|403 权限不足|Inline| - -### 返回数据结构 - -状态码 **401** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|true|none||none| -|» message|string|true|none||none| - -状态码 **403** - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» statusCode|number|false|none||none| -|» message|string|false|none||none| - -# 数据模型 - -

UpdateLang

- - - - - - -```json -{ - "name": "string" -} - -``` - -### 属性 - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|name|string|false|none|语言名|none| - -

CreateLang

- - - - - - -```json -{ - "name": "string" -} - -``` - -### 属性 - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|name|string|true|none|语言名|none| - -

UpdateMenuDTO

- - - - - - -```json -{ - "order": 0, - "menuType": "string", - "name": "string", - "path": "string", - "component": "string", - "icon": "string", - "locale": "string", - "parentId": 0, - "id": 0 -} - -``` - -### 属性 - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|order|number|true|none|序号|none| -|menuType|string|true|none|菜单类型|none| -|name|string|true|none|菜单键 |唯一| -|path|string|true|none|菜单路径|none| -|component|string|true|none|组件|none| -|icon|string|true|none|图标名|none| -|locale|string|true|none|国际化字段|展示用| -|parentId|number¦null|false|none|父级id|none| -|id|number|true|none|菜单id|none| - -

CreateMenuDTO

- - - - - - -```json -{ - "order": 0, - "menuType": "string", - "name": "string", - "path": "string", - "component": "string", - "icon": "string", - "locale": "string", - "parentId": 0 -} - -``` - -### 属性 - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|order|number|true|none|序号|none| -|menuType|string|true|none|菜单类型|none| -|name|string|true|none|菜单键 |唯一| -|path|string|true|none|菜单路径|none| -|component|string|true|none|组件|none| -|icon|string|true|none|图标名|none| -|locale|string|true|none|国际化字段|展示用| -|parentId|number¦null|false|none|父级id|none| - -

UpdatePermissionDTO

- - - - - - -```json -{ - "name": "string", - "desc": "string", - "id": 0 -} - -``` - -### 属性 - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|name|string|false|none|权限键|none| -|desc|string|false|none|权限介绍|none| -|id|integer|true|none|权限id|none| - -

CreatePermissionDTO

- - - - - - -```json -{ - "name": "string", - "desc": "string" -} - -``` - -### 属性 - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|name|string|true|none|权限键|none| -|desc|string|true|none|权限介绍|none| - -

ITreeNodeData

- - - - - - -```json -{ - "id": 0, - "label": "string", - "children": [ - { - "id": 0, - "label": "string", - "children": [ - { - "id": null, - "label": "string", - "children": [ - {} - ], - "url": "string", - "component": "string", - "customIcon": "string", - "menuType": "string", - "parentId": "string", - "order": 0, - "locale": "string" - } - ], - "url": "string", - "component": "string", - "customIcon": "string", - "menuType": "string", - "parentId": "string", - "order": 0, - "locale": "string" - } - ], - "url": "string", - "component": "string", - "customIcon": "string", - "menuType": "string", - "parentId": "string", - "order": 0, - "locale": "string" -} - -``` - -### 属性 - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|id|any|true|none|id|none| - -anyOf - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» *anonymous*|number|false|none||none| - -or - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|» *anonymous*|string|false|none||none| - -continued - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|label|string|true|none|展示的标签|none| -|children|[[ITreeNodeData](#schemaitreenodedata)]|false|none|子集菜单|none| -|url|string|true|none|访问路由|none| -|component|string|true|none|组件|none| -|customIcon|string|true|none|图标名|none| -|menuType|string|true|none|保留字段|none| -|parentId|string|true|none|父级id|none| -|order|integer|true|none|排序|none| -|locale|string|true|none|国际化键|none| - -

User

- - - - - - -```json -{ - "id": "string", - "name": "string", - "email": "string", - "password": "string", - "role": [ - { - "id": "string", - "name": "string", - "permission": [ - { - "id": 0, - "desc": "string", - "name": "string" - } - ], - "menus": [ - { - "id": 0, - "name": "string", - "order": 0, - "parentId": 0, - "menuType": "string", - "icon": "string", - "component": "string", - "path": "string", - "locale": "string" - } - ] - } - ], - "department": "string", - "employeeType": "string", - "probationENd": "string", - "robationDuration": "string", - "protocolStart": "string", - "protocolEnd": "string", - "address": "string", - "status": 0, - "createTime": "string", - "updateTime": "string", - "create_time": "string", - "salt": "string", - "update_time": "string" -} - -``` - -### 属性 - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|id|string|true|none|数据库自增id|none| -|name|string|true|none|用户名|none| -|email|string|true|none||登录邮箱| -|password|string|true|none|密码|none| -|role|[[Role](#schemarole)]|true|none|角色|none| -|department|string|true|none||1.1.0以前的遗留字段| -|employeeType|string|true|none||1.1.0以前的遗留字段| -|probationENd|string|true|none||1.1.0以前的遗留字段| -|robationDuration|string|true|none||1.1.0以前的遗留字段| -|protocolStart|string|true|none||1.1.0以前的遗留字段| -|protocolEnd|string|true|none||1.1.0以前的遗留字段| -|address|string|true|none||1.1.0以前的遗留字段| -|status|number|true|none|帐号状态|none| -|createTime|string|true|none||none| -|updateTime|string|true|none||none| -|create_time|string|true|none||none| -|salt|string|true|none|bcrypt盐|none| -|update_time|string|true|none||none| - -

Role

- - - - - - -```json -{ - "id": "string", - "name": "string", - "permission": [ - { - "id": 0, - "desc": "string", - "name": "string" - } - ], - "menus": [ - { - "id": 0, - "name": "string", - "order": 0, - "parentId": 0, - "menuType": "string", - "icon": "string", - "component": "string", - "path": "string", - "locale": "string" - } - ] -} - -``` - -### 属性 - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|id|string|true|none|角色id|none| -|name|string|true|none|角色名|none| -|permission|[[Permission](#schemapermission)]|true|none|权限|none| -|menus|[[Menu](#schemamenu)]|true|none|菜单|none| - -

Menu

- - - - - - -```json -{ - "id": 0, - "name": "string", - "order": 0, - "parentId": 0, - "menuType": "string", - "icon": "string", - "component": "string", - "path": "string", - "locale": "string" -} - -``` - -### 属性 - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|id|number|true|none|菜单id|none| -|name|string|true|none|菜单名|none| -|order|number|true|none|排序|none| -|parentId|number|false|none|父级id|none| -|menuType|string|true|none|保留字段|none| -|icon|string|false|none|图标名|none| -|component|string|true|none|组件名|none| -|path|string|true|none|路由路径|none| -|locale|string|true|none|国际化键|none| - -

Permission

- - - - - - -```json -{ - "id": 0, - "desc": "string", - "name": "string" -} - -``` - -### 属性 - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|id|number|true|none|权限ID|none| -|desc|string|true|none|权限介绍|none| -|name|string|true|none|权限键|none| - -

PaginationOptionsRoutingLabels

- - - - - - -```json -{ - "limitLabel": "string", - "pageLabel": "string" -} - -``` - -### 属性 - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|limitLabel|string|true|none||none| -|pageLabel|string|true|none||none| - - - - - - - - -```json -{ - "first": "string", - "previous": "string", - "next": "string", - "last": "string" -} - -``` - -### 属性 - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|first|string|false|none||none| -|previous|string|false|none||none| -|next|string|false|none||none| -|last|string|false|none||none| - -

PaginationMeta

- - - - - - -```json -{ - "itemCount": 0, - "totalItems": 0, - "itemsPerPage": 0, - "currentPage": 0 -} - -``` - -### 属性 - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|itemCount|number|false|none||none| -|totalItems|number|false|none||none| -|itemsPerPage|number|false|none||none| -|currentPage|number|false|none||none| - -

I18

- - - - - - -```json -{ - "id": 0, - "lang": { - "id": 0, - "name": "string", - "i18": [ - { - "id": 0, - "lang": { - "id": 0, - "name": "string", - "i18": [ - null - ] - }, - "key": "string", - "content": "string" - } - ] - }, - "key": "string", - "content": "string" -} - -``` - -### 属性 - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|id|number|true|none|国际化字段的自增id|none| -|lang|[Lang](#schemalang)|true|none|国际化字段对应的语言信息|none| -|key|string|true|none|国际化字段的键|none| -|content|string|true|none|国际化字段的实际内容|none| - -

Lang

- - - - - - -```json -{ - "id": 0, - "name": "string", - "i18": [ - { - "id": 0, - "lang": { - "id": 0, - "name": "string", - "i18": [ - { - "id": null, - "lang": null, - "key": null, - "content": null - } - ] - }, - "key": "string", - "content": "string" - } - ] -} - -``` - -### 属性 - -|名称|类型|必选|约束|中文名|说明| -|---|---|---|---|---|---| -|id|number|true|none|语言ID|none| -|name|string|true|none|语言名|none| -|i18|[[I18](#schemai18)]|true|none|对应的国际化词条|none| - diff --git a/docs/backend-design.md b/docs/backend-design.md deleted file mode 100644 index 315bbe71..00000000 --- a/docs/backend-design.md +++ /dev/null @@ -1,93 +0,0 @@ -# TinyPro 后端设计架构 - -本文档旨在捋清 TinyPro 后端设计架构。 - -## 表设计 - -![](./images/data-table-design.png) - -## 专用术语 - -- 公开接口: 一个不需要Token的接口, 一般是登录接口. -- 非公开接口: 一个需要Token的接口, 当Token过期时必须返回401响应码. 错误报文格式请参考[异常格式](#异常格式) -- 保护接口: 一个需要相应权限的接口, 会从Token中读取用户Email, 如果Token过期则**必须**返回401响应码,如果用户**没有**相应的权限,**必须**返回一个403响应码,错误报文格式请参考[异常格式](#异常格式) - -## 请求示意图 - -### 公开接口请求示意图 - -```mermaid -sequenceDiagram - User->>+Server: 发送请求 - Server->>Server: 是公开接口 - Server->>Server: 处理请求 - Server-->>-User: 返回响应 -``` - -### 非公开接口请求示意图 - -#### 正常响应 - -如果用户Token合法且未过期则不会返回异常 - -```mermaid -sequenceDiagram - User->>+Server: 发送请求 - Server->>Server: 不是公开接口 - Server->>+AuthGuard: 用户Token - AuthGuard->>AuthGuard: Token合法 - AuthGuard->>AuthGuard: Token在Redis中未过期 - AuthGuard->>-Server: 放行 - Server->>Server: 处理请求 - Server-->>-User: 返回响应 -``` - -#### 异常响应 - -如果Token过期或异常则会返回异常响应 - -```mermaid -sequenceDiagram - User->>+Server: 发送请求 - Server->>Server: 不是公开接口 - Server->>+AuthGuard: 用户Token - AuthGuard->>AuthGuard: Token不合法 - AuthGuard-->> -User: 401 Token不合法 - - User->>+Server: 发送请求 - Server->>Server: 不是公开接口 - Server->>+AuthGuard: 用户Token - AuthGuard->>AuthGuard: Token合法 - AuthGuard->>AuthGuard: Token过期 - AuthGuard-->> -User: 401 登陆过期 -``` - -### 保护接口 - -#### 正常响应 - -```mermaid -sequenceDiagram - User->>+Server: 发送请求 - Server->>Server: 不是公开接口 - Server->>AuthGuard: Token - AuthGuard-->>Server: Token合法且未过期 - Server->>PermissionGuard: 是保护接口 - PermissionGuard->>PermissionGuard: 未设定权限或拥有相应权限 - PermissionGuard -->> Server: 放行 - Server ->> Server: 逻辑处理 - Server ->> -User: 响应 -``` - -#### 异常响应 - -```mermaid -sequenceDiagram - User->>+Server: 发送请求 - Server->>Server: 不是公开接口 - Server->>AuthGuard: Token - AuthGuard-->>Server: Token合法且未过期 - Server->>PermissionGuard: 是保护接口 - PermissionGuard->>PermissionGuard: 未拥有权限 - PermissionGuard-->>User: 权限不足 -``` diff --git a/docs/images/data-table-design.png b/docs/images/data-table-design.png deleted file mode 100644 index 6daf0408..00000000 Binary files a/docs/images/data-table-design.png and /dev/null differ diff --git a/docs/images/feature_2012x914.png b/docs/images/feature_2012x914.png deleted file mode 100644 index 07d1b796..00000000 Binary files a/docs/images/feature_2012x914.png and /dev/null differ diff --git a/docs/images/image-20250901183132738.png b/docs/images/image-20250901183132738.png deleted file mode 100644 index fc10f58f..00000000 Binary files a/docs/images/image-20250901183132738.png and /dev/null differ diff --git a/docs/images/image-20250901210454281.png b/docs/images/image-20250901210454281.png deleted file mode 100644 index 1db8be3b..00000000 Binary files a/docs/images/image-20250901210454281.png and /dev/null differ diff --git a/docs/images/tiny-pro-show.png b/docs/images/tiny-pro-show.png deleted file mode 100644 index 16974088..00000000 Binary files a/docs/images/tiny-pro-show.png and /dev/null differ diff --git "a/docs/images/\344\270\272\346\265\213\350\257\225\347\224\250\346\210\267\347\273\221\345\256\232\350\217\234\345\215\225.png" "b/docs/images/\344\270\272\346\265\213\350\257\225\347\224\250\346\210\267\347\273\221\345\256\232\350\217\234\345\215\225.png" deleted file mode 100644 index 7ad93f5e..00000000 Binary files "a/docs/images/\344\270\272\346\265\213\350\257\225\347\224\250\346\210\267\347\273\221\345\256\232\350\217\234\345\215\225.png" and /dev/null differ diff --git "a/docs/images/\345\217\252\345\213\276\351\200\211\346\265\213\350\257\225\351\241\265\351\235\242\345\215\263\345\217\257.png" "b/docs/images/\345\217\252\345\213\276\351\200\211\346\265\213\350\257\225\351\241\265\351\235\242\345\215\263\345\217\257.png" deleted file mode 100644 index 9a3319fb..00000000 Binary files "a/docs/images/\345\217\252\345\213\276\351\200\211\346\265\213\350\257\225\351\241\265\351\235\242\345\215\263\345\217\257.png" and /dev/null differ diff --git "a/docs/images/\345\267\246\344\276\247\346\265\213\350\257\225\351\241\265\351\235\242 - \344\270\255\346\226\207.png" "b/docs/images/\345\267\246\344\276\247\346\265\213\350\257\225\351\241\265\351\235\242 - \344\270\255\346\226\207.png" deleted file mode 100644 index b8cf3262..00000000 Binary files "a/docs/images/\345\267\246\344\276\247\346\265\213\350\257\225\351\241\265\351\235\242 - \344\270\255\346\226\207.png" and /dev/null differ diff --git "a/docs/images/\345\267\246\344\276\247\346\265\213\350\257\225\351\241\265\351\235\242 - \350\213\261\346\226\207.png" "b/docs/images/\345\267\246\344\276\247\346\265\213\350\257\225\351\241\265\351\235\242 - \350\213\261\346\226\207.png" deleted file mode 100644 index c95cc120..00000000 Binary files "a/docs/images/\345\267\246\344\276\247\346\265\213\350\257\225\351\241\265\351\235\242 - \350\213\261\346\226\207.png" and /dev/null differ diff --git "a/docs/images/\346\226\260\345\242\236\346\235\203\351\231\220.png" "b/docs/images/\346\226\260\345\242\236\346\235\203\351\231\220.png" deleted file mode 100644 index def75ee2..00000000 Binary files "a/docs/images/\346\226\260\345\242\236\346\235\203\351\231\220.png" and /dev/null differ diff --git "a/docs/images/\346\226\260\345\242\236\346\235\203\351\231\220\346\210\220\345\212\237.png" "b/docs/images/\346\226\260\345\242\236\346\235\203\351\231\220\346\210\220\345\212\237.png" deleted file mode 100644 index 4515e7c7..00000000 Binary files "a/docs/images/\346\226\260\345\242\236\346\235\203\351\231\220\346\210\220\345\212\237.png" and /dev/null differ diff --git "a/docs/images/\346\226\260\345\242\236\350\257\215\346\235\241.png" "b/docs/images/\346\226\260\345\242\236\350\257\215\346\235\241.png" deleted file mode 100644 index 4b22e755..00000000 Binary files "a/docs/images/\346\226\260\345\242\236\350\257\215\346\235\241.png" and /dev/null differ diff --git "a/docs/images/\346\234\200\347\273\210\351\241\265\351\235\242\346\225\210\346\236\234.png" "b/docs/images/\346\234\200\347\273\210\351\241\265\351\235\242\346\225\210\346\236\234.png" deleted file mode 100644 index 800366fd..00000000 Binary files "a/docs/images/\346\234\200\347\273\210\351\241\265\351\235\242\346\225\210\346\236\234.png" and /dev/null differ diff --git "a/docs/images/\346\235\203\351\231\220\347\273\221\345\256\232\345\261\225\347\244\272.png" "b/docs/images/\346\235\203\351\231\220\347\273\221\345\256\232\345\261\225\347\244\272.png" deleted file mode 100644 index 24d67b10..00000000 Binary files "a/docs/images/\346\235\203\351\231\220\347\273\221\345\256\232\345\261\225\347\244\272.png" and /dev/null differ diff --git "a/docs/images/\346\237\245\347\234\213\350\217\234\345\215\225\351\241\265.png" "b/docs/images/\346\237\245\347\234\213\350\217\234\345\215\225\351\241\265.png" deleted file mode 100644 index 03a563ce..00000000 Binary files "a/docs/images/\346\237\245\347\234\213\350\217\234\345\215\225\351\241\265.png" and /dev/null differ diff --git "a/docs/images/\346\265\213\350\257\225\347\224\250\346\210\267\347\231\273\345\275\225.png" "b/docs/images/\346\265\213\350\257\225\347\224\250\346\210\267\347\231\273\345\275\225.png" deleted file mode 100644 index 073a6a68..00000000 Binary files "a/docs/images/\346\265\213\350\257\225\347\224\250\346\210\267\347\231\273\345\275\225.png" and /dev/null differ diff --git "a/docs/images/\346\267\273\345\212\240\350\217\234\345\215\225.png" "b/docs/images/\346\267\273\345\212\240\350\217\234\345\215\225.png" deleted file mode 100644 index f71b7255..00000000 Binary files "a/docs/images/\346\267\273\345\212\240\350\217\234\345\215\225.png" and /dev/null differ diff --git "a/docs/images/\346\267\273\345\212\240\350\247\222\350\211\262\345\256\214\345\205\250\344\275\223.png" "b/docs/images/\346\267\273\345\212\240\350\247\222\350\211\262\345\256\214\345\205\250\344\275\223.png" deleted file mode 100644 index a7a67071..00000000 Binary files "a/docs/images/\346\267\273\345\212\240\350\247\222\350\211\262\345\256\214\345\205\250\344\275\223.png" and /dev/null differ diff --git "a/docs/images/\347\202\271\345\207\273\346\267\273\345\212\240\350\257\215\346\235\241.png" "b/docs/images/\347\202\271\345\207\273\346\267\273\345\212\240\350\257\215\346\235\241.png" deleted file mode 100644 index d675cbbe..00000000 Binary files "a/docs/images/\347\202\271\345\207\273\346\267\273\345\212\240\350\257\215\346\235\241.png" and /dev/null differ diff --git "a/docs/images/\347\231\273\351\231\206\346\265\213\350\257\225\350\264\246\345\217\267.png" "b/docs/images/\347\231\273\351\231\206\346\265\213\350\257\225\350\264\246\345\217\267.png" deleted file mode 100644 index 6bd527b5..00000000 Binary files "a/docs/images/\347\231\273\351\231\206\346\265\213\350\257\225\350\264\246\345\217\267.png" and /dev/null differ diff --git "a/docs/images/\347\273\221\345\256\232\346\235\203\351\231\220.png" "b/docs/images/\347\273\221\345\256\232\346\235\203\351\231\220.png" deleted file mode 100644 index 9a2f59a9..00000000 Binary files "a/docs/images/\347\273\221\345\256\232\346\235\203\351\231\220.png" and /dev/null differ diff --git "a/docs/images/\347\273\221\345\256\232\350\217\234\345\215\225.png" "b/docs/images/\347\273\221\345\256\232\350\217\234\345\215\225.png" deleted file mode 100644 index e53497ad..00000000 Binary files "a/docs/images/\347\273\221\345\256\232\350\217\234\345\215\225.png" and /dev/null differ diff --git "a/docs/images/\347\273\221\345\256\232\350\247\222\350\211\262.png" "b/docs/images/\347\273\221\345\256\232\350\247\222\350\211\262.png" deleted file mode 100644 index dfdc75e4..00000000 Binary files "a/docs/images/\347\273\221\345\256\232\350\247\222\350\211\262.png" and /dev/null differ diff --git "a/docs/images/\351\200\200\345\207\272\347\231\273\345\275\225.png" "b/docs/images/\351\200\200\345\207\272\347\231\273\345\275\225.png" deleted file mode 100644 index ad41cff5..00000000 Binary files "a/docs/images/\351\200\200\345\207\272\347\231\273\345\275\225.png" and /dev/null differ diff --git "a/docs/images/\351\200\211\346\213\251\350\257\255\350\250\200.png" "b/docs/images/\351\200\211\346\213\251\350\257\255\350\250\200.png" deleted file mode 100644 index b474b1fb..00000000 Binary files "a/docs/images/\351\200\211\346\213\251\350\257\255\350\250\200.png" and /dev/null differ diff --git a/docs/tiny-pro-backend-dev-guideline.md b/docs/tiny-pro-backend-dev-guideline.md deleted file mode 100644 index 7f8e801f..00000000 --- a/docs/tiny-pro-backend-dev-guideline.md +++ /dev/null @@ -1,241 +0,0 @@ -# TinyPro 后端开发指南 - -在阅读本指南前,我们假设您已经阅读过[Nest.js官方文档](https://docs.nestjs.com/)并能够独立本机启动`MySQL`与`Redis`的能力。 - -## 项目初始化 - -[快速开始](./tiny-pro.md) - -## 后端启动 - -开发阶段通常不会使用docker进行启动,更多的是本地启动。首先我们要配置环境变量文件, 也就是`.env`文件. - -```properties -# 数据库IP (一般是本地) -DATABASE_HOST = 'localhost' -# 数据库端口 -DATABASE_PORT = 3306 -# 数据库用户名 -DATABASE_USERNAME = 'root' -# 数据库密码 -DATABASE_PASSWORD = 'root' -# 数据库名 (请确保该库存在) -DATABASE_NAME = 'ospp-nest' -# 请阅读: https://www.typeorm.org/migrations -# 线上环境请关闭 -DATABASE_SYNCHRONIZE = 'true' -DATABASE_AUTOLOADENTITIES = 'true' -# jwt secret -AUTH_SECRET = 'secret' -# ACCESS_TOKEN在Token中的有效时间 (秒) -REDIS_SECONDS = 7200 -# redis ip -REDIS_HOST = 'localhost' -# redis 端口 -REDIS_PORT = 6379 -# token过期时间 -EXPIRES_IN = '2h' -# 分页默认起始页 (一般可以不修改) -PAGINATION_PAGE = 1 -# 分页默认大小 -PAGINATION_LIMIT = 10 -# 刷新令牌有效时间 (毫秒) -REFRESH_TOKEN_TTL = 604800000 -# 至多有多少个设备可以同时在线 -DEVICE_LIMIT=1 -``` - -### 开发前检查清单 - -- [ ] 后端项目已被初始化 -- [ ] `.env`文件中`DATABASE_HOST`**是开发环境** -- [ ] `.env`文件中`DATABASE_NAME`为开发库 -- [ ] `.env`文件中`DATABASE_NAME`存在 -- [ ] `.env`文件中`DATABASE_SYNCHRONIZE`为`true` -- [ ] `.env`文件中`REDIS_HOST`**是开发环境** -- [ ] MySQL服务可以正常访问 -- [ ] Redis服务可以正常访问 -- [ ] `dist`目录被删除 (可选,如果你不需要测试初始化数据的话) - -配置好文件后您可以运行`npm run start:dev`来运行后端服务。当出现下述字样时,表示后端启动成功。 - -``` -LOG [NestApplication] Nest application successfully started +11ms -Application is running on: http://[::1]:3000 -``` - -### 生成迁移文件 - -有时,我们需要改动数据库结构。在修改完成后**必须**执行`pnpm run migrate:gen`来生成迁移文件。在运行该命令期间,请确保开发环境数据库可以访问。 - -#### 修改表结构 - -假设我们修改了 `User` 表. 它位于 `nestJs/libs/models/src` 下. - -```diff -export class User { - @PrimaryGeneratedColumn() - id: number; - @Column() - name: string; - @Column() -+ nickName: string; -} -``` - -#### 运行迁移文件生成指令 - -1. 请确保你在`.env`文件中设置的`DATABASE_HOST`为开发数据库。 -2. 运行 `pnpm run migrate:gen` -3. 当出现`Success! Migration file created at migrations/1752296660591-TinyPro.js`命令后则表示歉意文件生成成功 -4. 运行 `pnpm run mirgate:run`指令或`node migrate.js`来应用迁移文件。当出现了 `Now you can safely launched the project` 字样。表示迁移文件已经被安全的应用到了数据库中。 - -## 初始化数据 - -有些时候我们需要自动初始化一些数据(比如前端的默认国际化字段). 这些逻辑**均需**写在`App.module.ts`中`AppModule`类中的`onModuleInit`函数中。 - -## 国际化 - -> 这里的国际化指的是报错信息的国际化 - -后端采用的是`nestjs-i18n`依赖库。国际化词条放在`i18n//xxx.json`下 - -``` -i18n - enUS - exception.json - validation.json - zhCN - exception.json - validation.json -``` - -目前仅支持`enUS`与`zhCN`两种语言,且`fallback`为`enUS`. - -### 报错时候使用国际化词条 - -后端服务遵循`Restful`规范,可以直接抛出错误使用HttpStatusCode来代替错误代码。如果需要使用国际化词条,请确保该词条已经存在于`enUS|zhCN/exception.json`文件内。假设有一个服务`PolicyService`需要抛出一个`409`错误。 - -1. 添加国际化词条 -2. 在服务中注入`I18nService` -3. 使用该词条 - -```json -// zhCN/exception.json -{ - // 前面不做修改 - "policy":{ - "exists": "Policy已存在" - } -} -``` - -```ts -import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { I18nTranslations } from '../.generate/i18n.generated'; -import { I18nContext, I18nService } from 'nestjs-i18n'; -@Injectable() -export class PolicyService { - constructor( - private readonly i18n: I18nService - ) {} - createPolicy(){ - const exists = ...; - if (exists){ - throw new HttpException( - this.i18n.translate('exception.policy.exists', { - lang: I18nContext.current().lang, - }), - HttpStatus.CONFLICT // 409 - ) - } - //.... - } -} -``` - -## 接口权限管理 - -### Token管理 - -凡是**没有**被`Public`修饰器修饰的接口,均会被`auth/auth.guard.ts`进行校验,如果**token不存在**、**token过期**、**token不合法**,均不允许访问。 - -### 权限控制 - -如果一个接口没有被`Permission`修饰器进行修饰,那么这个接口是**允许**所有**已经登录**的用户访问。如果一个接口**被**`Permission`修饰器进行修饰,那么该接口**仅允许**拥有该权限的用户访问,其余用户会返回**403**错误代码 - -默认`admin`用户存在超级权限`(*)`, 拥有该权限且已经登陆的用户可以访问任何接口。 - -例如 - -```ts -@Controller('/policy') -export class PolicyController { - @Get('/list') - async getPolicy(){} -} -``` - -上述代码中`GET /policy/list`是一个不公开,不受保护的接口。我们可以使用`Permission`修饰器对他进行权限认证,当且仅当用户角色存在`policy::get::list`权限时才放行 - -```ts -@Controller('/policy') -export class PolicyController { - @Get('/list') - @Permission('policy::get::list') - async getPolicy(){} -} -``` - -这样一来`GET /policy/list`就只允许拥有`policy::get::list`权限的角色访问,其余角色访问则会返回一个403错误 - -但有些时候我们需要一个接口允许未登陆的用户访问。例如我们在登陆的时候经常需要获取免责声明,那么我们就可以写一个`GET /policy`接口,用于获取一个免责声明的法律条文。 - -所以我们可以添加如下 - -```ts -@Controller('/policy') -export class PolicyController { - @Get('/list') - @Permission('policy::get::list') - async getPolicies(){} - @Get('/') - @Public() - async getPolicy(){} -} -``` - -这样一来`GET /policy/list`接口只允许**登录**且**拥有policy::get::list**权限的角色访问。`GET /policy`接口则允许**未登陆**的**所有角色**进行访问。 - -如果未来的某一天,我们需要让`/policy/*`都允许未登录的用户访问,那么我们可以这么写 - -```ts -@Public() -@Controller('/policy') -export class PolicyController { - @Get('/list') - async getPolicies(){} - @Get('/') - async getPolicy(){} -} -``` - -这样一来,所有的policy接口都可以被未登录的用户访问了 - -## 遇到困难? - -加官方小助手微信 opentiny-official,加入技术交流群 - -## 常见问题 - -### 打包速度慢 - -请阅读[SWC](https://docs.nestjs.com/recipes/swc) - -### 提示 `Lock file exists, if you want init agin, please remove dist or dist/lock` - -为了避免重复初始化,系统会在第一次初始化的时候在`dist`目录下新建`app/lock`文件,如果您需要再次初始化,那么请您删除`dist/app`或者直接删除`dist`文件夹 - -### docker 部署时数据库超时 - -`docker-compose.yaml`实际上配置了`depends_on`字段,但`mysql`镜像并没有提供对应的健康检查。如果服务挂掉,可以等待`mysql`启动成功后手动重启后端服务 diff --git a/docs/tiny-pro-front-dev-guideline.md b/docs/tiny-pro-front-dev-guideline.md deleted file mode 100644 index 8dd8600c..00000000 --- a/docs/tiny-pro-front-dev-guideline.md +++ /dev/null @@ -1,213 +0,0 @@ -# TinyPro 前端开发指南 - -在阅读本文时,我们假设您已经学习过了Vue3. 在开始二次开发前, 我们需要先启动`tiny-pro`后端. 请参阅[TinyPro 快速上手](./tiny-pro.md) - -## 页面开发 - -### 第一步、新建页面 - -首先我们在`tiny-pro/web/src/views`下新建一个`test-page`目录, 该目录下只有一个`index.vue`文件。目录结构如下图所示 - -``` -web - src - views - test-page - index.vue -``` - -```html - - -``` - -### 第二步、创建国际化词条 - -点击 `系统管理 > 国际化管理` 打开国际化管理页面。在 `系统管理 > 国际化管理` 页面中单击 `增加词条` 按钮。弹出modal应如下所示 - -![](./images/点击添加词条.png) - -在该modal中,我们将词条Key定义为了`test::page::title`, 词条内容为 `测试页面`。 点击 `词条语言` 下的下拉框,选择`zhCN`(简体中文) - -![](./images/选择语言.png) - -选择好语言后单击 `添加词条` 按钮即可成功将Key 为 `test::page::title` 的简体中文国际化词条添加到数据库中。 - -### 第三步、绑定菜单 - -点击`系统管理 > 查看菜单`打开菜单管理页面, 在 `系统管理 > 查看菜单` 中点击 `添加菜单` - -![](./images/添加菜单.png) - -- 名称 - - 这里一般为英文, 在开发的时候对应的是该路由的id -- 优先级 - - 在菜单中排列的优先级,优先级越高越靠近上方(浏览器顶部) -- 父级菜单 - - 如果设置了父级菜单,则会作为父级菜单的子集 -- 图标 - - 菜单中的图标,必选 -- 组件 - - 组件名称,在该实例中是`test-page/index.vue`。填写该表单项的时候不能包含`src/views`前缀! -- 国际化 - - 菜单的国际化文本 - -点击 `确认` 按钮后, `Modal`会自动关闭。 - -![](./images/绑定菜单.png) - -点击 `系统管理 > 查看角色` 来到角色管理页面 - -点击`绑定菜单`按钮,本实例将菜单绑定到了`admin`用户。勾选`测试页面`后,单击`确认修改`按钮(右下角)后,在左侧菜单便会出现测试页面菜单项 - -![](./images/左侧测试页面%20-%20中文.png) - - -### 第四步、正式开发 - -现在单击 `测试页面` 菜单项会发现只有一个银色的 `hello-world` 字样。接下来我们进入正式开发阶段。 - -我们将在页面中添加一个一级标题,一个按钮,与**两行**文本,点击按钮后,第一行文本自增,第二行文本会在自增的基础上*2. 请将`tiny-pro/web/src/views/test-page/index.vue`替换为如下代码 - -```html - - - -``` - -最终效果如下 - -![](./images/最终页面效果.png) - -## 权限管理 - -本章将会进行组件级别的权限管理。我们依然沿用上一章的测试页面。这一章我们将创建一个`test-role`角色,绑定给一个`test`用户。并且在测试页面中,为`Count * 2`这个元素增加一个`test::page::double::text`权限,并且我们并不给`test-role`用户绑定该权限。 - -### 新增权限 - -请点击 `系统管理 > 查看权限` 来到权限管理页面。在 `系统管理 > 查看权限` 页面中单击 `添加权限` 按钮。并按照图示填写信息 - -![](./images/新增权限.png) - -单击 `确认` 按钮后上方会出现 `表单提交成功` 字样。表明权限已经成功添加到了数据库中。接下里我们需要新增角色 - -### 新增角色 - - -点击 `系统管理 > 查看角色` 来到角色管理页面。在角色管理页面中点击 `添加角色` 按钮。并按照图示填写信息 - -![](./images/绑定权限.png) - -注意!一定不要拥有**test::page::double::text**权限!! - -点击 `确认` 后, 弹出框将会自动关闭. 接下来我们需要创建用户 - -### 新增用户 - -点击 `系统管理 > 查看用户`来到用户管理页面。在用户管理页面中点击 `添加用户` 按钮。并按照图示填写信息 - -![](./images/添加角色完全体.png) - -之后我们需要为`test-role`角色来绑定菜单,我们可以只绑定一个`测试页面`菜单 - -![](./images/为测试用户绑定菜单.png) - -### 修改页面 - -现在我们来到`tiny-pro/web/src/views/test-page/index.vue`中,将文件中的代码替换为如下代码 - -```html - - - -``` - -### 登陆测试用户 - -点击提交后,弹窗将会自动关闭。现在将鼠标放到右上角头像上,在弹出框中选择 `退出登录`。按照下图是输入信息 - -![](./images/测试用户登录.png) - -因为我们只给`test-role`绑定了一个`测试页面`的路由,所以会跳转到测试页面,可以发现原本要出现的`Count * 2`如今已经不存在了。 - -![](./images/权限绑定展示.png) - -## 遇到困难? - -加官方小助手微信 opentiny-official,加入技术交流群 - -## 环境变量 - -所有的环境变量应当以`VITE_`作为前缀. 例如 `VITE_XXX` - -|名称|类型|简介|状态 -|:--|:--|:--|:--:| -|VITE_CONTEXT|String|路由前缀|启用中 -|VITE_BASE_API|String|axios请求api时所携带的前缀, 主要用于解决浏览器跨域问题.|启用中 -|VITE_SERVER_HOST|String|开发时, Vite反代到哪个后端|启用中 -|VITE_MOCK_HOST|String|Mock服务|废弃 -|VITE_USE_MOCK|Boolean|是否启用Mock服务|启用中 -|VITE_MOCK_IGNORE|String|使用英文逗号分隔, 标明哪些api不被mock|启用中 -|VITE_MOCK_SERVER_HOST|String|追加在`VITE_BASE_API`后面, Mock服务被集成到了后端,以`${VITE_BASE_API}${VITE_MOCK_SERVER_HOST}`开头的请求都会被发送到后端的mock服务|启用中 -|VITE_OUT_DIR|String|产物最终输出位置, 相对于命令执行目录|启用中 - - -## 常见问题 - -### 前端跨域问题如何解决 - -对于开发环境来说,可以直接修改`dev-server`的`proxy`. 例如`vite`工具的`server.proxy` - -### 代码无法提交 - -您可以选择移除husky或根据[Angular 规范](https://zj-git-guide.readthedocs.io/zh-cn/latest/message/Angular%E6%8F%90%E4%BA%A4%E4%BF%A1%E6%81%AF%E8%A7%84%E8%8C%83/)书写commit信息 - -### 页面部署后刷新404 - -请移步[Vue Router服务器部署指南](https://router.vuejs.org/guide/essentials/history-mode.html#Example-Server-Configurations) diff --git a/docs/tiny-pro-mobile-adaptation.md b/docs/tiny-pro-mobile-adaptation.md deleted file mode 100644 index 713f47b6..00000000 --- a/docs/tiny-pro-mobile-adaptation.md +++ /dev/null @@ -1,201 +0,0 @@ -# Tiny Pro 移动端适配说明 - -本文档概述 Tiny Pro 在移动端上的适配基线、 工具与用法、样式与编码策略、常用代码片段等方面内容,便于在新增页面或组件时保持一致的移动体验与质量。 - -## 1. 适配基线 - -- **断点规范(UnoCSS)** - - 配置位置:`template/tinyvue/uno.config.ts` - - ```ts - const breakpoints = { - sm: '641px', // 手机(小屏,iPhone SE、Mini 等) - md: '769px', // 平板(竖屏,iPad Mini、iPad 等) - lg: '1025px', // 平板横屏 / 小型笔记本(iPad 横屏、Surface Go) - xl: '1367px', // 笔记本主流分辨率(MacBook Air、13寸笔记本) - '2xl': '1441px', // 高分辨率笔记本(MacBook Pro 14、部分高清显示器) - '3xl': '1921px', // 全高清显示器 / 桌面大屏(1080p 显示器) - } - ``` - - 为了保持PC端的使用体验,采用了“PC端优先”的适配策略。而UnoCSS 默认支持的是 `sm:`, `md:` 这类 min-width 媒体查询。,所以在`uno.config.ts`定义了max-:` 变体,以支持 max-width 媒体查询,例如:`max-sm:` → `@media (max-width: 640px)`。 - - - -## 2. 工具与用法 - -- **UnoCSS 原子类 + 断点前缀**: - - - 最小宽度断点:`sm:`, `md:`, `lg:` 等,例如:`sm:px-4 md:px-6 lg:px-8`。 - - 最大宽度断点:`max-sm:`, `max-md:` 等,例如:`max-sm:text-center`。 - - Attributify 语法可选:`
`。 - -- **响应式 Hook**(`template/tinyvue/src/hooks/responsive.ts`): - - - `useResponsive({ sm: 375, md: 768, lg: 1024 })` 返回 `sm/md/lg` 三个 `ref`,用于在脚本层判断断点。 - - `useResponsiveSize()` 提供 `gridSize`、`modalSize`:在较小屏幕下使表单/弹层更紧凑,`modalSize` 小屏为 `100%`,大屏为 `768px`。 - - ```ts - const { sm, md, lg } = useResponsive() - const { gridSize, modalSize } = useResponsiveSize() - ``` - -- **常用逻辑** - - `gridSize`:小屏 → `mini`,大屏 → `medium` - - `modalSize`:小屏 → `100%`,大屏 → `768px` - - - -## 3. 样式与编码策略 - -- **优先级** - - 简单场景:使用 UnoCSS 原子类。 - - - 复杂样式:使用 Less 媒体查询。 - -- **布局与滚动** - - 首页及核心业务模块完成适配,小屏模式下侧边栏默认收起、导航栏折叠,确保主要内容可见。 - - 页面主要容器避免横向滚动,必要时在小屏下开启局部横向滚动。 - - 表格与大区块在不同断点下自动调整宽度、栅格与间距,小屏下支持横向滚动;分页与密度支持响应式控制。 - -- **图表自适应** - - 图表组件接入 `resize` 监听,在侧边栏展开/收起、窗口缩放、语言切换等场景下保持自适应。 - - - 小屏下使用 `vw` 宽度与较小字号,保证图表展示效果与可读性。 - - -- **表单与模态框** - - - 接入 `useResponsiveSize()`,控制弹窗在小屏下铺满显示,大屏保持固定宽度。 - - - 表单项在不同断点下动态调整排布与间距,优化触控体验。 - - -- **导航与交互** - - - 小屏下隐藏导航栏非关键元素,操作聚合到"折叠菜单"。 - - - 移动端默认收起侧边菜单栏,提升主要内容展示区域。 - - -- **性能优化** - - 在 `responsive.ts` 中对 `resize` 事件处理增加节流机制,避免窗口缩放等场景下的频繁无效渲染。 - - - -## 4. 常用代码片段 - -1. 基于栅格系统 + 响应式断点工具类,通过为 tiny-row 和 tiny-col 添加不同屏幕宽度下的样式规则,实现自适应布局: - -```vue - - - ··· - ··· - ··· - - - -``` -```vue -
- -
-``` - -2. 基于 响应式工具类 + 自定义响应式 Hook,解决(1)对话框宽度自适应;(2)表格尺寸和密度自适应;(3)逻辑层响应式控制 - -```vue - - - -``` - -```vue - - - -``` - -3. 通过 `useResponsive` 获取屏幕断点状态 `sm/md/lg`,如:在模板中结合 `v-if="!lg"` 控制分隔线的渲染,从而实现了小屏下纵向菜单才显示分隔线的效果 - -```vue - - - -``` - - - -## 5. 测试与验证 - -### 5.1 自动化校验(Playwright) - -#### 测试用例 - -本项目主要通过Playwright E2E自动化测试、移动端模拟器和真实移动端浏览器验证的方式,对TinyPro前端在移动端的适配效果进行了系统性验证。测试内容涵盖了页面布局、交互体验、业务流程及异常处理,确保移动端的使用体验与桌面端保持一致,并符合预期的设计目标。以下为部分关键测试用例及其验证结果: - -| 测试用例编号 | 测试目标 | 测试方法 / 操作步骤 | 预期结果 | 实际结果 | 验证结论 | -| ------------ | -------------------------- | ------------------------------------------------------------ | ---------------------------------------------------------- | -------- | -------- | -| TC-01 | 移动端侧边菜单栏折叠与展开 | 模拟 iPhone 12 viewport,点击侧边栏按钮,断言侧边栏是否可见 | 移动端侧边菜单栏默认隐藏,点击按钮后正常显示,动画执行顺畅 | 符合预期 | 通过 | -| TC-02 | 移动端列表横向滚动加载 | 模拟 iPhone 12 viewport,滚动列表到底部,检查新增 DOM 元素出现 | 新数据正常追加,滚动条无抖动 | 符合预期 | 通过 | -| TC-03 | 移动端表格 size 为 mini | 模拟 iPhone 12 viewport,进入“监控页”,查看表格的样式以及 class | 移动端表格的 class 包含 size_mini,布局更紧凑 | 符合预期 | 通过 | -| TC-04 | 移动端模态框响应式布局 | 模拟 iPhone 12 viewport,点击“查看菜单”页的“创建菜单”按钮 | 移动端模态框宽度为 100% | 符合预期 | 通过 | -| TC-05 | 导航栏折叠菜单交互验证 | 模拟 iPhone 12 viewport,在移动端点击折叠菜单,断言二级菜单是否可见 | 非关键操作聚合到折叠菜单中,菜单展开/收起正常 | 符合预期 | 通过 | - -### 5.2 人工验证 - -- 开发者工具设备模拟(iPhone/Android 多机型)。 -- 关键页面(登录、首页、表格、图表、表单/弹层)在真机检查。 - -| 测试用例编号 | 测试目标 | 测试方法 / 操作步骤 | 预期结果 | 实际结果 | 验证结论 | -| ------------ | ----------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -------- | -------- | -| TC-06 | 图表在移动端的自适应 | 手动模拟 iPhone 12 viewport,切换侧边栏收起/展开,检查图表宽度 | 图表随容器宽度变化自适应,无溢出,label 字号在小屏正常显示 | 符合预期 | 通过 | -| TC-07 | 页面横向滚动可控性 | 手动模拟 iPhone 12 viewport,检查页面是否出现整体横向滚动 | 页面主容器无横向滚动条,仅在表格/大区块等必要区域允许横向滚动 | 符合预期 | 通过 | -| TC-08 | 响应式性能(resize 节流验证) | 手动拖动浏览器宽度,检查页面重绘/卡顿情况 | 页面可平滑缩放,无明显卡顿或频繁无效渲染 | 符合预期 | 通过 | - -**测试结论:**通过上述测试验证,移动端的主要功能、页面适配和交互均符合设计预期,未发现严重问题。结合Playwright自动化用例的覆盖和实际设备验证结果,可以确认TinyPro在移动端环境下具备良好的稳定性与可用性。 - - - -## 6. 扩展与规范化 - -- 如需新增或调整断点: - - UnoCSS:修改 `template/tinyvue/uno.config.ts` 中的 `breakpoints`,同时考虑是否需要对应 `max-:` 变体。 -- 统一实践: - - 优先使用 Uno 原子类进行简单的响应式布局;当样式逻辑复杂时再落到 Less 媒体查询。 - - 以“桌面端优先”为设计基线:先实现 PC 布局,再通过断点类覆盖到平板和手机,保持布局逻辑的可读性。 - - 组件脚本层通过 `useResponsive` 控制功能性差异(如图表尺寸、分页大小、模态框尺寸等等)。 - - 在`template\tinyvue\src\hooks\responsive.ts`中封装统一的响应式 Hook,提升复用性与可维护性。 - diff --git a/docs/tiny-pro-spring-boot.md b/docs/tiny-pro-spring-boot.md deleted file mode 100644 index fe4e7e12..00000000 --- a/docs/tiny-pro-spring-boot.md +++ /dev/null @@ -1,348 +0,0 @@ -# TinyPro 后端开发指南 - -在阅读本指南前,我们假设您已经阅读过[Springboot官方文档]((https://spring.io/projects/spring-boot))并能够独立本机启动`MySQL`与`Redis`的能力。 - -## 项目初始化 - -[快速开始](./tiny-pro.md) - -## 后端启动 - -开发阶段通常不会使用docker进行启动,更多的是本地启动。首先我们要配置环境变量文件, 也就是`application.properties`文件. - -```properties -# ====================== 服务基本配置 ====================== - -# 设置 Spring Boot 应用启动后监听的端口号为 3000 -# 即你可以通过 http://localhost:3000 访问你的服务 -server.port=3000 - -# ====================== 数据库连接配置(MySQL)====================== - -# 数据库连接URL:连接到本地 MySQL 服务,数据库名为 login,端口 3306 -# 参数说明: -# - allowMultiQueries=true:允许一条SQL语句中包含多个查询(比如多个SELECT) -# - serverTimezone=GMT%2B8:设置服务器时区为东八区(北京时间),%2B 是URL编码的 + -# - useUnicode=true&characterEncoding=utf8:使用 Unicode 和 UTF-8 编码,防止中文乱码 -# - autoReconnect=true:连接异常时自动尝试重连 -# - allowPublicKeyRetrieval=true:允许客户端获取服务端公钥(MySQL 8+ 可能需要,解决某些认证问题) -# - useSSL=false:不启用 SSL 加密(开发环境常用,生产环境建议开启) -spring.datasource.url=jdbc:mysql://localhost:3306/login?allowMultiQueries=true&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&allowPublicKeyRetrieval=true&useSSL=false - -# 数据库用户名,通常是安装 MySQL 时的默认超级用户 -spring.datasource.username=root - -# 数据库密码,生产环境中请务必修改为复杂密码,并不要直接写在代码中! -spring.datasource.password=111111 - -# 指定使用的 JDBC 驱动类,这里是 MySQL 8 的驱动类 -spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver - -# ====================== HikariCP 连接池配置(高性能连接池,默认使用)====================== - -# 连接池的名称,用于监控和日志中识别 -spring.datasource.hikari.pool-name=HikariCPDatasource - -# 连接池中保持的最小空闲连接数,即使没有请求也会维持这么多连接 -spring.datasource.hikari.minimum-idle=5 - -# 空闲连接的超时时间(毫秒),超过这个时间空闲连接会被释放,默认是 10 分钟,这里设为 3 分钟 (180000ms) -spring.datasource.hikari.idle-timeout=180000 - -# 连接池中允许的最大连接数,控制同时能从池中拿出的最大连接数量 -spring.datasource.hikari.maximum-pool-size=10 - -# 是否自动提交事务,默认为 true;可根据业务需求调整 -spring.datasource.hikari.auto-commit=true - -# 连接的最大存活时间(毫秒),超时后会被销毁并新建,这里设为 3 分钟 -spring.datasource.hikari.max-lifetime=180000 - -# 连接超时时间(毫秒),即从池中获取一个连接的最长等待时间,超时将抛出异常 -spring.datasource.hikari.connection-timeout=30000 - -# ====================== MyBatis-Plus 配置(ORM 框架)====================== - -# 指定 MyBatis-Plus 的 Mapper XML 文件所在的位置,这里放在 resources/mappers/ 目录下 -mybatis-plus.mapper-locations=classpath:mappers/*.xml - -# 指定实体类(POJO)所在的包路径,MyBatis-Plus 会为这些类创建别名,简化 XML 中的引用 -mybatis-plus.type-aliases-package=com.TinyPro.entity.po - -# 开启数据库字段下划线转驼峰命名规则,例如数据库字段 user_name 会映射为 Java 属性 userName -mybatis-plus.configuration.map-underscore-to-camel-case=true - -# 设置 MyBatis 枚举类型处理器为 EnumOrdinalTypeHandler,即存储枚举的 ordinal(序号)而非 name -mybatis-plus.configuration.default-enum-type-handler=org.apache.ibatis.type.EnumOrdinalTypeHandler - -# ====================== JPA / Hibernate 配置(可选,如果你同时用了 JPA)====================== - -# Hibernate 自动更新数据库表结构(根据实体类变化自动调整表,开发环境可用,生产环境慎用!) -# 可选值:none, validate, update, create, create-drop -spring.jpa.hibernate.ddl-auto=update - -# 指定数据库方言为 MySQL 8,确保 Hibernate 生成符合 MySQL 8 的 SQL -spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect - -# 再次明确指定 Hibernate 使用的 MySQL 方言(与上面一样,有些版本可能需要) -spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL8Dialect - -# 指定使用 InnoDB 存储引擎(MySQL 常用引擎,支持事务等高级功能) -spring.jpa.properties.hibernate.dialect.storage_engine=innodb - -# 是否对所有标识符(如表名、字段名)使用全局引号包裹,一般保持 false 即可,某些特殊场景可能需要 -spring.jpa.properties.hibernate.globally_quoted_identifiers=true - -# 指定 Hibernate 的物理命名策略为标准的(不对表名/字段名做任何转换) -spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl - -# ====================== JWT 配置(JSON Web Token,用于身份认证)====================== - -# JWT 密钥,用于对 Token 进行签名和验证,生产环境一定要使用复杂且保密的密钥! -# 这里是示例值 "0Zi4SA==" -jwt.secret=0Zi4SA== - -# ====================== Redis 配置(缓存/会话等)====================== - -# Redis 服务器主机地址,这里为本机 -spring.data.redis.host=localhost - -# Redis 服务端口,默认是 6379 -spring.data.redis.port=6379 - -# 表示是否开启展示模式,默认为 true -reject.start=true -``` - -### 开发前检查清单 - -- [ ] 后端项目已被初始化 -- [ ] `application.properties`文件中`server.port`**是开发环境** -- [ ] `application.properties`文件中`spring.datasource`为开发库的配置 -- [ ] application.properties文件中`spring.data.redis`**是开发环境** -- [ ] MySQL服务可以正常访问 -- [ ] Redis服务可以正常访问 -- [ ] `data`目录被删除 (可选,如果你不需要测试初始化数据的话) - -配置好文件后您可以运行`mvn spring-boot:run`来运行后端服务。当出现下述字样时,表示后端启动成功。 - -``` -2025-08-29T02:11:32.039+08:00 INFO 19572 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 3000 (http) with context path '' -2025-08-29T02:11:32.060+08:00 INFO 19572 --- [ main] com.TinyPro.TinyProApplication : Started TinyProApplication in 12.558 seconds (process running for 13.067) -2025-08-29T02:11:32.079+08:00 INFO 19572 --- [ main] com.TinyPro.DataInitializer : [✅ TEST] MySQL 数据库连接成功!数据库状态正常。 -2025-08-29T02:11:33.434+08:00 INFO 19572 --- [ main] com.TinyPro.DataInitializer : [✅ TEST] Redis 连接成功!缓存服务状态正常。 -2025-08-29T02:11:33.435+08:00 WARN 19572 --- [ main] com.TinyPro.DataInitializer : Lock file exists, if you want init again, please remove data/lock - -``` - -### Docker启动配置 - -在docker中, 可以通过修改 environment 项来覆盖 application.yaml 中的参数. - -在一些企业中, 数据库可能不会部署在容器中. 这个时候我们可以通过 environment 来覆盖 spring.datasource.url 参数. 现在我们假设数据库地址是 「mysql://node-a:3306/tiny-pro」 - -那么我们可以把 docker-compose 中 environment 下的 SPRING_DATASOURCE_URL 参数填写为 「jdbc:mysql://node-a:3306/tiny-pro?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC」 - -理论上所有在 `application.yaml` 中出现的配置项都可以在 `environment ` 中覆盖,只是需要将 「.」 转为 「_」 - -#### [Docker 最全的Docker-compose快速部署](https://developer.aliyun.com/article/914404) - -## 初始化数据 - -有些时候我们需要自动初始化一些数据(比如前端的默认国际化字段). 这些逻辑**均需**写在DataInitializer类里面。 - -```java -// 初始化国际化信息 -initI18n(); - -// 初始化权限 -initPermissions(); - -// 初始化菜单 -initMenus(); - -// 初始化角色 -Role role = initRole(); - -// 初始化用户 -initUser(role); - -// 创建锁文件 -lockFile.getParentFile().mkdirs(); -Files.createFile(lockFile.toPath()); -``` - -主要负责对于初始化的修改 - -## 国际化 - -> 这里的国际化指的是报错信息的国际化 - -后端采用的是`spring-context`依赖库。国际化词条放在`resources/i18n/messages_zh_CN或者en_US.properties`下 - -``` -resources - i18n - messages_zh_CN.properties - messages_en_US.properties` -``` - -目前仅支持`enUS`与`zhCN`两种语言 - -### 报错时候使用国际化词条 - -后端服务遵循`Restful`规范,可以直接抛出错误使用HttpStatusCode来代替错误代码。如果需要使用国际化词条,请确保该词条已经存在于``resources/i18n/messages_zh_CN.properties``文件内。假设有一个服务`PolicyService`需要抛出一个`409`错误。 - -1. 添加国际化词条 -2. 在服务中注入`I18nService` -3. 使用该词条 - -```json -// messages_zh_CN.properties -policy.exists = Policy已存在 - -``` - -```java -package com.TinyPro.service.imp; - -import com.TinyPro.entity.po.Employee; - -import com.TinyPro.exception.BusinessException; -import com.TinyPro.jpa.IEmployeRepository; -import com.TinyPro.service.IEmployeeService; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.Optional; - -@Service -public class PolicyService implements IPolicyService { - @Autowired - private IPolicyRepository iPolicyRepository; - - @Override - public List list(List query) { - try { - List allById = iPolicyRepository.findAllById(query); - return allById; - } catch (Exception e) { - throw new BusinessException("policy.exists", HttpStatus.CONFLICT,null);//这块就是抛出异常然后由全局异常捕获 - } - } -``` - -## 接口权限管理 - -### Token管理 - -凡是**没有**被`Public`修饰器修饰的接口,均会被`filter/UserGuardsFilter.java`进行校验,如果**token不存在**、**token过期**、**token不合法**,均不允许访问。 - -### 权限控制 - -如果一个接口没有被`PermissionAnnotation`修饰器进行修饰,那么这个接口是**允许**所有**已经登录**的用户访问。如果一个接口**被**`Permission`修饰器进行修饰,那么该接口**仅允许**拥有该权限的用户访问,其余用户会返回**403**错误代码 - -默认`admin`用户存在超级权限`(*)`, 拥有该权限且已经登陆的用户可以访问任何接口。 - -例如 - -```java -@RestController -@RequestMapping("/policy") -public class LangController { - @Autowired - private ILangService langService; - - @GetMapping("/list") - public ResponseEntity createLang(@RequestBody @Valid CreateLangDto createLangDto) { - return this.langService.create(createLangDto); - } - -``` - -上述代码中`GET /policy/list`是一个不公开,不受保护的接口。我们可以使用`PermissionAnnotation`修饰器对他进行权限认证,当且仅当用户角色存在`policy::get::list`权限时才放行 - -```java -@RestController -@RequestMapping("/policy") -public class LangController { - @Autowired - private ILangService langService; - - @GetMapping("/list") - @PermissionAnnotation("policy::get::list") - public ResponseEntity createLang(@RequestBody @Valid CreateLangDto createLangDto) { - return this.langService.create(createLangDto); - } -``` - -这样一来`GET /policy/list`就只允许拥有`policy::get::list`权限的角色访问,其余角色访问则会返回一个403错误 - -但有些时候我们需要一个接口允许未登陆的用户访问。例如我们在登陆的时候经常需要获取免责声明,那么我们就可以写一个`GET /policy`接口,用于获取一个免责声明的法律条文。 - -所以我们可以添加如下 - -```java -@RestController -@RequestMapping("/policy") -public class LangController { - @Autowired - private ILangService langService; - - @GetMapping("/list") - @PermissionAnnotation("policy::get::list") - public ResponseEntity createLang(@RequestBody @Valid CreateLangDto createLangDto) { - return this.langService.create(createLangDto); - } - @IsPublic() - @GetMapping("/list") - public ResponseEntity createLang(@RequestBody @Valid CreateLangDto createLangDto) { - return this.langService.create(createLangDto); - } -``` - -这样一来`GET /policy/list`接口只允许**登录**且**拥有policy::get::list**权限的角色访问。`GET /policy`接口则允许**未登陆**的**所有角色**进行访问。 - -如果未来的某一天,我们需要让`/policy/*`都允许未登录的用户访问,那么我们可以这么写 - -```java -@IsPublic() -@RestController -@RequestMapping("/policy") -public class LangController { - @Autowired - private ILangService langService; - - @GetMapping("/list") - @PermissionAnnotation("policy::get::list") - public ResponseEntity createLang(@RequestBody @Valid CreateLangDto createLangDto) { - return this.langService.create(createLangDto); - } -``` - -这样一来,所有的policy接口都可以被未登录的用户访问了 - -## 当出现messages_zh_CN.properties(编码问题) - -点击File -> settings -> File Encodings -> 选择UTF-8和with BOM under Windows, with no BOM otherwise这个选项,做到全局的UTF-8的配置 - -1. 点击Settings - -image-20250901190434427 - -2. 搜索File Encodings(选择对应的选项) - -image-20250901190522736 - -## 遇到困难? - -加官方小助手微信 opentiny-official,加入技术交流群 - -## 常见问题 - -### 提示 `Lock file exists, if you want init agin, please remove dist or dist/lock` - -为了避免重复初始化,系统会在第一次初始化的时候在`data`目录下新建`lock`文件,如果您需要再次初始化,那么请您删除`data/lock`或者直接删除`data`文件夹 diff --git a/docs/tiny-pro.md b/docs/tiny-pro.md deleted file mode 100644 index 643fdcd6..00000000 --- a/docs/tiny-pro.md +++ /dev/null @@ -1,240 +0,0 @@ -# Tiny Pro 快速启动 - -## 环境准备 - -请确保您安装了`nodejs`, `npm`, `tiny-cli` - -```bash -tiny init pro -``` - -运行上述代码后按照提示输入配置 - -``` -? 请输入项目名称: tiny-pro -? 请输入项目描述: 基于TinyPro套件创建的中后台系统 -* 请选择您希望使用的客户端技术栈: vue -* 请选择您希望使用的服务端技术栈: Nest.js -* 请选择你想要的构建工具: Vite -* 请确保已安装数据库服务(参考文档 -https://www.opentiny.design/tiny-cli/docs/toolkits/pro#database): -已完成数据库服务安装,开始配置 -* 请选择数据库类型: MySql -* 请输入数据库地址: localhost -* 请输入数据库端口: 3306 -* 请输入数据库名称: ospp-nest -* 请输入登录用户名: root -* 请输入密码: [hidden] -``` - -初始化完成后,项目结构应该为 - -``` -tiny-pro - nestJs # 后端服务 - web # 前端服务 -``` - -## 后端启动 - -后端服务支持`docker启动`与`命令启动`, 执行操作前请先确保所处位置为`tiny-pro/nestJS` - -### Docker启动 - -在运行`docker compose up -d`之前,请先修改`.env`环境变量文件,示例如下 - -```properties -# 数据库IP -DATABASE_HOST = 'mysql' -# 数据库端口 -DATABASE_PORT = 3306 -# 数据库用户名 -DATABASE_USERNAME = 'root' -# 数据库密码 -DATABASE_PASSWORD = 'root' -# 数据库名 (请确保该库存在) -DATABASE_NAME = 'ospp-nest' -# 请阅读: https://www.typeorm.org/migrations -# 线上环境请关闭 -DATABASE_SYNCHRONIZE = false -DATABASE_AUTOLOADENTITIES = true -# jwt secret -AUTH_SECRET = 'secret' -REDIS_SECONDS = 7200 -# redis ip -REDIS_HOST = 'redis' -# redis 端口 -REDIS_PORT = 6379 -# token过期时间 -EXPIRES_IN = '2h' -# 分页默认起始页 (一般可以不修改) -PAGINATION_PAGE = 1 -# 分页默认大小 -PAGINATION_LIMIT = 10 -``` - -修改完`.env`文件后,请执行`docker compose up -d` - - -当执行`docker ps`中`STATUS`列均为`Up`时,表示后端启动成功 - -``` -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -b76f3ebebe81 nestjs-back "docker-entrypoint.s…" 12 minutes ago Up 11 minutes 0.0.0.0:3000->3000/tcp nestjs-back-1 -32ae9982b96a redis "docker-entrypoint.s…" 12 minutes ago Up 12 minutes 0.0.0.0:6379->6379/tcp nestjs-redis-1 -94f3b55b7b2b mysql:8 "docker-entrypoint.s…" 12 minutes ago Up 12 minutes 0.0.0.0:3306->3306/tcp, 33060/tcp nestjs-mysql-1 -``` - -### 命令启动 - -#### 依赖安装 - -```bash -npm i -``` - -#### 环境准备 - -#### 安装MySQL - -请参考[MySQL 8.0安装](https://dev.mysql.com/doc/mysql-installation-excerpt/8.0/en/windows-installation.html) - -#### 安装Redis服务 - -请参考[Redis 官方手册](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-windows/) - -请确保您的机器已经安装了`Mysql`与`Redis`服务。接下来,我们需要配置`.env`环境变量文件。`.env`文件示例如下 - -```properties -# 数据库IP -DATABASE_HOST = 'localhost' -# 数据库端口 -DATABASE_PORT = 3306 -# 数据库用户名 -DATABASE_USERNAME = 'root' -# 数据库密码 -DATABASE_PASSWORD = 'root' -# 数据库名 (请确保该库存在) -DATABASE_NAME = 'ospp-nest' -# 请阅读: https://www.typeorm.org/migrations -# 线上环境请关闭 -DATABASE_SYNCHRONIZE = false -DATABASE_AUTOLOADENTITIES = true -# jwt secret -AUTH_SECRET = 'secret' -REDIS_SECONDS = 7200 -# redis ip -REDIS_HOST = 'localhost' -# redis 端口 -REDIS_PORT = 6379 -# token过期时间 -EXPIRES_IN = '2h' -# 分页默认起始页 (一般可以不修改) -PAGINATION_PAGE = 1 -# 分页默认大小 -PAGINATION_LIMIT = 10 -``` - -#### 启动项目 - -在启动项目前请您做好如下检查 - -- [ ] MySQL服务可以正常访问 -- [ ] Redis服务可以正常访问 -- [ ] MySQL中存在`.env`文件中`DATABASE_NAME`字段定义的数据库,且该数据库为空 -- [ ] 您已经运行了 `node migrate.js` 命令且出现了 `Now you can safely launched the project` 字样。 -- [ ] `.env`文件中`DATABASE_SYNCHRONIZE`为`false` -- [ ] `tiny-pro`后端依赖已经安装 - -完成上述检查后,您可以在`tiny-pro/nestJs`下执行`npm run start`. - -``` -LOG [NestApplication] Nest application successfully started +11ms -Application is running on: http://[::1]:3000 -``` - -当出现上述文本时候即为后端启动成功。 - -## 前端启动 - -### 依赖安装 - -```bash -cd tiny-pro/web -npm i -``` - -### 项目启动 - -在项目启动前,请您确保后端服务已经启动成功,且可以正常访问。我们列出了一个启动前检查清单,您可以对照检查清单来进行启动前检查 - -- [ ] 后端服务启动成功 -- [ ] 前端依赖安装完成 -- [ ] `npm run mock`启动mock服务 - -上述列表全部检查完成后,运行 `npm run start` 即可启动前端服务,浏览器会自行打开项目,当出现下图时则代表启动成功。 - -![启动成功](./images/tiny-pro-show.png) - -## 前端打包 - -对于前端项目打包,只需要执行`npm run build`即可 - -## 后端打包 - -### 命令打包 - -运行`npm run build`即可 - -### docker打包 - -> 这里只阐述默认 tiny-pro 后端打包,如果您进行了修改(例如增加了某些node-gyp依赖,请修改`dockerfile`手动安装`node-gyp`系统级前置依赖) - -运行 `docker build -t tinypro:latest` 即可 - -## 遇到困难? - -加官方小助手微信 opentiny-official,加入技术交流群 - -## 常见问题 - -### 后端docker启动时出现 `Error response from daemon: Ports are not available: exposing port TCP 0.0.0.0:3306 -> 0.0.0.0:0: listen tcp 0.0.0.0:3306: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.` - -这是因为宿主机的某些应用占用了`3306`端口, 请先释放宿主机该端口后手动启动MySQL服务 - -### 后端使用`docker compose up -d`启动时候,出现I/O timeout错误 - -这主要是因为网络问题,您可以手动执行下述命令 - -``` -docker pull node:alpine -docker pull node:lts -``` - -输入完上述命令后再次执行`docker compose up -d`即可。 - - -### 提示 `Lock file exists, if you want init agin, please remove dist or dist/lock` - -为了避免重复初始化,系统会在第一次初始化的时候在`dist`目录下新建`app/lock`文件,如果您需要再次初始化,那么请您删除`dist/app`或者直接删除`dist`文件夹 - -### docker 部署时数据库超时 - -在新版本中我们加入了 `wait4x` 来检查 `mysql` 容器情况。但这并不能完全避免因为 `mysql` 启动过慢而导致的容器启动失败。在业务容器中我们设定的轮询时间为2s, 最多等待60s. 如果超时请按照如下检查表逐一排查 - -1. MySQL容器是否启动成功? -2. MySQL容器是否初始化成功? -3. 业务容器环境变量是否正确? - - -### 前端跨域问题如何解决 - -对于开发环境来说,可以直接修改`dev-server`的`proxy`. 例如`vite`工具的`server.proxy` - -### 代码无法提交 - -您可以选择移除husky或根据[Angular 规范](https://zj-git-guide.readthedocs.io/zh-cn/latest/message/Angular%E6%8F%90%E4%BA%A4%E4%BF%A1%E6%81%AF%E8%A7%84%E8%8C%83/)书写commit信息 - -### 页面部署后刷新404 - -请移步[Vue Router服务器部署指南](https://router.vuejs.org/guide/essentials/history-mode.html#Example-Server-Configurations) diff --git a/template/nestJs/.env.example b/template/nestJs/.env.example index cbe3a26e..9f571eac 100644 --- a/template/nestJs/.env.example +++ b/template/nestJs/.env.example @@ -17,3 +17,5 @@ MOCK_REGEX = '/mock' REFRESH_TOKEN_TTL = 604800000 # 至多有多少个设备可以同时在线 DEVICE_LIMIT=1 +# 是否启用演示模式 +PREVIEW_MODE=true diff --git a/template/nestJs/docker-compose.yml b/template/nestJs/docker-compose.yml index e6a28652..d3f9636d 100644 --- a/template/nestJs/docker-compose.yml +++ b/template/nestJs/docker-compose.yml @@ -30,6 +30,6 @@ services: - mysql - redis volumes: - - ./data:/APP/dist/data + - ./data:/builder/dist/data diff --git a/template/nestJs/dockerfile b/template/nestJs/dockerfile index ecffd35c..ea2aefc4 100644 --- a/template/nestJs/dockerfile +++ b/template/nestJs/dockerfile @@ -23,6 +23,6 @@ ENV PAGINATION_LIMIT=10 ENV DATABASE_NAME="" EXPOSE 3000 -VOLUME [ "./dist/data" ] +VOLUME [ "/builder/dist/data" ] CMD ["sh", "endpoint.sh"] diff --git a/template/nestJs/endpoint.sh b/template/nestJs/endpoint.sh index c880e0ee..9c90ab2b 100644 --- a/template/nestJs/endpoint.sh +++ b/template/nestJs/endpoint.sh @@ -1 +1,3 @@ -wait4x mysql "${DATABASE_USERNAME}:${DATABASE_PASSWORD}@tcp(mysql:3306)/${DATABASE_NAME}" --timeout=60s --interval=2s -- node migrate.js && node dist/main.js +#!/bin/sh + +wait4x mysql "${DATABASE_USERNAME}:${DATABASE_PASSWORD}@tcp(mysql:3306)/${DATABASE_NAME}" --timeout=60s --interval=2s -- node ./migrate.js && node ./dist/main.js diff --git a/template/nestJs/migrations/1764502806299-TinyPro.js b/template/nestJs/migrations/1764502806299-TinyPro.js new file mode 100644 index 00000000..303dd297 --- /dev/null +++ b/template/nestJs/migrations/1764502806299-TinyPro.js @@ -0,0 +1,14 @@ +const { MigrationInterface, QueryRunner } = require('typeorm'); +class TinyPro1764502806240 { + name = 'TinyPro1764502806240'; + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE \`application\` (\`id\` int NOT NULL AUTO_INCREMENT, \`name\` varchar(255) NOT NULL, \`description\` varchar(255) NOT NULL, \`icon\` varchar(255) NOT NULL, \`tag\` varchar(255) NOT NULL, \`classify\` varchar(255) NOT NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB` + ); + } + async down(queryRunner) { + await queryRunner.query(`DROP TABLE \`application\``); + } +} + +module.exports = TinyPro1764502806240; diff --git a/template/nestJs/src/app.module.ts b/template/nestJs/src/app.module.ts index a20150ac..dede7be8 100644 --- a/template/nestJs/src/app.module.ts +++ b/template/nestJs/src/app.module.ts @@ -37,7 +37,7 @@ import { HealthCheckController } from './health-check.controller'; import { ApplicationModule } from './application/application.module'; import { ApplicationService } from './application/application.service'; import { applicationData } from './application/init/data'; -import { CONFIG_SCHEMA } from './config-schema'; +import { CONFIG_SCHEMA, Configure } from './config-schema'; @Module({ imports: [ @@ -89,7 +89,8 @@ export class AppModule implements OnModuleInit { private menu: MenuService, private lang: I18LangService, private i18: I18Service, - private application: ApplicationService + private application: ApplicationService, + private cfg: ConfigService ) {} async onModuleInit() { const ROOT = __dirname; @@ -97,6 +98,12 @@ export class AppModule implements OnModuleInit { if (!existsSync(data)) { mkdirSync(data); } + const IS_PREVIEW_MOD = this.cfg.get('PREVIEW_MODE'); + if (IS_PREVIEW_MOD) { + Logger.warn('You are currently in demonstration mode. All additions, deletions, and modifications request will be rejected'); + Logger.warn('If you want to disable the demo mode, please set the `PREVIEW_MODE` environment variable to `false`') + Logger.warn('Alternatively, you can create an `.env` file and set `PREVIEV_MODE` to `false`') + } const LOCK_FILE = join(data, 'lock'); if (existsSync(LOCK_FILE)) { Logger.warn( diff --git a/template/nestJs/src/config-schema.ts b/template/nestJs/src/config-schema.ts index 94f80800..04ff9dfb 100644 --- a/template/nestJs/src/config-schema.ts +++ b/template/nestJs/src/config-schema.ts @@ -19,6 +19,7 @@ export type Configure = { MOCK_REGEX:string; REFRESH_TOKEN_TTL:number; DEVICE_LIMIT:number; + PREVIEW_MODE: boolean; } export const CONFIG_SCHEMA = Joi.object({ @@ -39,5 +40,6 @@ export const CONFIG_SCHEMA = Joi.object({ GLOBAL_PREFIX: Joi.string(), MOCK_REGEX: Joi.string(), REFRESH_TOKEN_TTL: Joi.number(), - DEVICE_LIMIT: Joi.number() + DEVICE_LIMIT: Joi.number(), + PREVIEW_MODE: Joi.bool().default(true) }) diff --git a/template/nestJs/src/public/reject.guard.ts b/template/nestJs/src/public/reject.guard.ts index a59a325c..5e9f8fcd 100644 --- a/template/nestJs/src/public/reject.guard.ts +++ b/template/nestJs/src/public/reject.guard.ts @@ -8,11 +8,20 @@ import { import { Reflector } from '@nestjs/core'; import { I18nTranslations } from '../.generate/i18n.generated'; import { I18nContext } from 'nestjs-i18n'; +import { ConfigService } from '@nestjs/config'; +import { Configure } from '../config-schema'; @Injectable() export class RejectRequestGuard implements CanActivate { - constructor(private readonly reflector: Reflector) {} + constructor( + private readonly reflector: Reflector, + private readonly cfg: ConfigService + ) { + } async canActivate(ctx: ExecutionContext): Promise { + if (!this.cfg.get('PREVIEW_MODE')) { + return true; + } const i18n = I18nContext.current(); const rejectRequest = this.reflector.getAllAndOverride('reject', [ ctx.getHandler(),