@kne/fastify-intl-admin 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +337 -0
- package/index.js +45 -0
- package/libs/controllers/lang-lib.js +165 -0
- package/libs/controllers/lang-type.js +115 -0
- package/libs/models/lang-lib.js +73 -0
- package/libs/models/lang-type.js +67 -0
- package/libs/schema/lang-lib.js +157 -0
- package/libs/schema/lang-type.js +90 -0
- package/libs/services/lang-lib.js +625 -0
- package/libs/services/lang-type.js +381 -0
- package/libs/utils/http-errors.js +48 -0
- package/libs/utils/i18n-file.js +134 -0
- package/libs/utils/list-query.js +58 -0
- package/package.json +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
# fastify-intl-admin
|
|
2
|
+
|
|
3
|
+
### 描述
|
|
4
|
+
|
|
5
|
+
Fastify 业务插件:国际化语言类型与语言库管理(lang-type / lang-lib)
|
|
6
|
+
|
|
7
|
+
### 安装
|
|
8
|
+
|
|
9
|
+
```shell
|
|
10
|
+
npm i --save @kne/fastify-intl-admin
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### 概述
|
|
14
|
+
|
|
15
|
+
### 项目概述
|
|
16
|
+
|
|
17
|
+
`@kne/fastify-intl-admin` 是一个 Fastify 业务插件,提供国际化**语言类型(lang-type)**与**语言库词条(lang-lib)**的管理接口,对接管理端 IntlAdmin 组件。
|
|
18
|
+
|
|
19
|
+
### 核心架构与流程
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
HTTP Request
|
|
23
|
+
↓
|
|
24
|
+
controllers(鉴权 + schema 校验)
|
|
25
|
+
↓
|
|
26
|
+
services(业务逻辑)
|
|
27
|
+
↓
|
|
28
|
+
models(Sequelize / t_ 前缀表)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
插件通过 `@kne/fastify-namespace` 注册为 `intl-admin` 命名空间,默认路由前缀 `/api/v1/intl-admin`。
|
|
32
|
+
|
|
33
|
+
### 核心概念详解
|
|
34
|
+
|
|
35
|
+
#### 语言类型(langType)
|
|
36
|
+
|
|
37
|
+
维护可用语言目录,如 `zh-CN`、`en-US`。字段含名称、编码、参数提示、描述与启停状态。
|
|
38
|
+
|
|
39
|
+
#### 语言库词条(langLib)
|
|
40
|
+
|
|
41
|
+
按 `namespace` + `code` + `locale` 唯一存储译文。接口 list 返回**扁平列表**,树形分组由前端组装。
|
|
42
|
+
|
|
43
|
+
#### 审核状态(reviewStatus)
|
|
44
|
+
|
|
45
|
+
| 场景 | 默认 reviewStatus |
|
|
46
|
+
|------|-------------------|
|
|
47
|
+
| 人工填写条目(`aiGenerate=false`) | `approved` |
|
|
48
|
+
| AI 生成条目(`aiGenerate=true`,非默认语言) | `pending` |
|
|
49
|
+
| 默认语言词条(含 AI 源文) | `approved` |
|
|
50
|
+
| 编辑非默认语言 target | 重置为 `pending` |
|
|
51
|
+
| 编辑默认语言 target | 保持 `approved` |
|
|
52
|
+
|
|
53
|
+
#### AI 生成扩展点
|
|
54
|
+
|
|
55
|
+
通过 `options.generateTargets` 注入:
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
generateTargets: async ({ namespace, code, locales }) => {
|
|
59
|
+
// 返回 { [locale]: targetText }
|
|
60
|
+
return { 'zh-CN': '...', 'en-US': '...' };
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
未配置时,`aiGenerate=true` 的请求会返回 400。
|
|
65
|
+
|
|
66
|
+
### 主要特性
|
|
67
|
+
|
|
68
|
+
- 标准 CRUD:`list` / `create` / `save` / `remove` / `set-status`
|
|
69
|
+
- 语言库批量添加:`add-package`(多 locale)
|
|
70
|
+
- 单条 / 批量审核、启停、删除(`id` 或 `ids`)
|
|
71
|
+
- `getAuthenticate(type)` 按功能注入鉴权
|
|
72
|
+
- 列表筛选兼容顶层 flat 参数与 `filter` 对象
|
|
73
|
+
|
|
74
|
+
### 使用方法
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
const fastify = require('fastify')();
|
|
78
|
+
|
|
79
|
+
await fastify.register(require('@kne/fastify-sequelize'), { /* ... */ });
|
|
80
|
+
await fastify.register(require('@kne/fastify-intl-admin'), {
|
|
81
|
+
prefix: '/api/v1/intl-admin',
|
|
82
|
+
getAuthenticate: type => {
|
|
83
|
+
// 按 type 返回中间件数组,如 langType / langType:manage
|
|
84
|
+
return [];
|
|
85
|
+
},
|
|
86
|
+
generateTargets: async ({ namespace, code, locales, sourceTarget, defaultLocale }) => {
|
|
87
|
+
return Object.fromEntries(locales.map(locale => [locale, `${sourceTarget || code}@${locale}`]));
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
> **关键设计**:数据为全局语言包(无 `tenantId`);树形 UI 由前端把扁平 list 转为 `parentId` 树,后端只负责词条行存储与分页。
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
### 示例
|
|
96
|
+
|
|
97
|
+
### API
|
|
98
|
+
|
|
99
|
+
### 配置项
|
|
100
|
+
|
|
101
|
+
| 属性名 | 类型 | 必填 | 默认值 | 说明 |
|
|
102
|
+
| --- | --- | --- | --- | --- |
|
|
103
|
+
| name | string | 否 | `intl-admin` | 命名空间名 |
|
|
104
|
+
| prefix | string | 否 | `/api/v1/intl-admin` | 路由前缀 |
|
|
105
|
+
| dbTableNamePrefix | string | 否 | `t_` | 表名前缀 |
|
|
106
|
+
| getAuthenticate | function | 否 | `() => []` | `(type) => middleware[]`,type 如 `langType` / `langType:manage` / `langLib` / `langLib:manage` |
|
|
107
|
+
| generateTargets | function | 否 | `null` | AI 生成译文回调;`aiGenerate=true` 时必填 |
|
|
108
|
+
|
|
109
|
+
### 接口
|
|
110
|
+
|
|
111
|
+
统一约定:读 `GET`,写 `POST`;list 返回 `{ pageData, totalCount }`;写操作除 create/add-package 外成功返回 `{}`。
|
|
112
|
+
|
|
113
|
+
列表筛选同时支持:
|
|
114
|
+
|
|
115
|
+
- 顶层:`keyword`、`locale`、`reviewStatus`、`perPage`、`currentPage`
|
|
116
|
+
- 或:`filter.keyword` 等(prompts 标准)
|
|
117
|
+
|
|
118
|
+
#### GET `/lang-type/list`
|
|
119
|
+
|
|
120
|
+
语言类型列表。
|
|
121
|
+
|
|
122
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
|
123
|
+
| --- | --- | --- | --- | --- |
|
|
124
|
+
| keyword | string | 否 | - | 匹配 name / code / description |
|
|
125
|
+
| perPage | number | 否 | `20` | 每页条数 |
|
|
126
|
+
| currentPage | number | 否 | `1` | 页码 |
|
|
127
|
+
|
|
128
|
+
#### POST `/lang-type/create`
|
|
129
|
+
|
|
130
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
131
|
+
| --- | --- | --- | --- |
|
|
132
|
+
| name | string | 是 | 名称 |
|
|
133
|
+
| code | string | 是 | 编码(唯一) |
|
|
134
|
+
| params | string | 否 | 参数提示 |
|
|
135
|
+
| description | string | 否 | 描述 |
|
|
136
|
+
| status | string | 否 | `open` / `closed`,默认 `open` |
|
|
137
|
+
|
|
138
|
+
返回创建的记录。
|
|
139
|
+
|
|
140
|
+
#### POST `/lang-type/save`
|
|
141
|
+
|
|
142
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
143
|
+
| --- | --- | --- | --- |
|
|
144
|
+
| id | number / string | 是 | ID |
|
|
145
|
+
| name | string | 否 | 名称 |
|
|
146
|
+
| params | string | 否 | 参数提示 |
|
|
147
|
+
| description | string | 否 | 描述 |
|
|
148
|
+
|
|
149
|
+
> `code` 不可修改。
|
|
150
|
+
|
|
151
|
+
#### POST `/lang-type/remove`
|
|
152
|
+
|
|
153
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
154
|
+
| --- | --- | --- | --- |
|
|
155
|
+
| id | number / string | 是 | ID |
|
|
156
|
+
|
|
157
|
+
若仍有 `langLib.locale === code` 的词条则拒绝删除。
|
|
158
|
+
|
|
159
|
+
#### POST `/lang-type/set-status`
|
|
160
|
+
|
|
161
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
162
|
+
| --- | --- | --- | --- |
|
|
163
|
+
| id | number / string | 是 | ID |
|
|
164
|
+
| status | string | 是 | `open` / `closed` |
|
|
165
|
+
|
|
166
|
+
#### POST `/lang-type/set-default`
|
|
167
|
+
|
|
168
|
+
将指定语言设为默认(全局唯一,会取消其他默认标记)。
|
|
169
|
+
|
|
170
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
171
|
+
| --- | --- | --- | --- |
|
|
172
|
+
| id | number / string | 是 | ID |
|
|
173
|
+
|
|
174
|
+
> 创建时若当前无任何语言类型,自动 `isDefault=true`。删除默认语言时,会自动将剩余最早创建的一条设为默认。新建语言默认排到列表末尾(`sort` 递增)。
|
|
175
|
+
|
|
176
|
+
#### POST `/lang-type/move`
|
|
177
|
+
|
|
178
|
+
语言类型上移 / 下移(与相邻项交换 `sort`)。
|
|
179
|
+
|
|
180
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
181
|
+
| --- | --- | --- | --- |
|
|
182
|
+
| id | number / string | 是 | ID |
|
|
183
|
+
| direction | string | 是 | `up` / `down` |
|
|
184
|
+
|
|
185
|
+
返回 `{ moved: true|false }`;已在边界时 `moved: false`。
|
|
186
|
+
|
|
187
|
+
#### GET `/lang-lib/list`
|
|
188
|
+
|
|
189
|
+
语言库**扁平**词条列表(不含虚拟父行)。
|
|
190
|
+
|
|
191
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
|
192
|
+
| --- | --- | --- | --- | --- |
|
|
193
|
+
| keyword | string | 否 | - | 匹配 namespace / code / target |
|
|
194
|
+
| locale | string | 否 | - | 语言精确过滤 |
|
|
195
|
+
| reviewStatus | string | 否 | - | `approved` / `pending` / `rejected` |
|
|
196
|
+
| perPage | number | 否 | `20` | 每页条数 |
|
|
197
|
+
| currentPage | number | 否 | `1` | 页码 |
|
|
198
|
+
|
|
199
|
+
返回示例:
|
|
200
|
+
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"pageData": [
|
|
204
|
+
{
|
|
205
|
+
"id": 1,
|
|
206
|
+
"namespace": "global",
|
|
207
|
+
"code": "Hello",
|
|
208
|
+
"locale": "zh-CN",
|
|
209
|
+
"target": "你好",
|
|
210
|
+
"reviewStatus": "approved",
|
|
211
|
+
"status": "open"
|
|
212
|
+
}
|
|
213
|
+
],
|
|
214
|
+
"totalCount": 1
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
#### GET `/lang-lib/messages`
|
|
219
|
+
|
|
220
|
+
运行时语言包(供 `@kne/react-intl` `apis.localeMessage` 使用)。**无需登录**。返回**扁平** `{ [code]: target }`,仅包含 `status=open` 且 `reviewStatus=approved` 的词条。
|
|
221
|
+
|
|
222
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
|
223
|
+
| --- | --- | --- | --- | --- |
|
|
224
|
+
| locale | string | 是 | - | 语言 code |
|
|
225
|
+
| namespace | string | 否 | `global` | 命名空间 |
|
|
226
|
+
|
|
227
|
+
返回示例:
|
|
228
|
+
|
|
229
|
+
```json
|
|
230
|
+
{
|
|
231
|
+
"Hello": "你好",
|
|
232
|
+
"Save": "保存"
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
#### POST `/lang-lib/add-package`
|
|
237
|
+
|
|
238
|
+
批量添加同一 `namespace+code` 下多语言词条。
|
|
239
|
+
|
|
240
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
241
|
+
| --- | --- | --- | --- |
|
|
242
|
+
| namespace | string | 是 | 命名空间 |
|
|
243
|
+
| code | string | 是 | 词条编码 |
|
|
244
|
+
| locales | string[] | 是 | 语言编码列表 |
|
|
245
|
+
| aiGenerate | boolean | 否 | 是否 AI 生成,默认 `false` |
|
|
246
|
+
| sourceTarget | string | AI 时是 | 默认语言源文案 |
|
|
247
|
+
| defaultLocale | string | 否 | 默认语言 code;省略时取 `langType.isDefault`;若在 locales 中则直接写入 `sourceTarget` 且 `approved` |
|
|
248
|
+
| targets | object | 否 | `{ [locale]: target }`,`aiGenerate=false` 时使用 |
|
|
249
|
+
|
|
250
|
+
- 各 locale 须在 langType 中存在且 `status=open`
|
|
251
|
+
- 人工:`reviewStatus=approved`;AI 非默认语言:`pending`;默认语言词条始终 `approved`
|
|
252
|
+
- 返回创建的记录数组
|
|
253
|
+
|
|
254
|
+
#### POST `/lang-lib/save`
|
|
255
|
+
|
|
256
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
257
|
+
| --- | --- | --- | --- |
|
|
258
|
+
| id | number / string | 是 | ID |
|
|
259
|
+
| target | string | 否 | 译文;非默认语言变更后 `reviewStatus` 重置为 `pending`;默认语言保持 `approved` |
|
|
260
|
+
|
|
261
|
+
`namespace` / `code` / `locale` 不可改。
|
|
262
|
+
|
|
263
|
+
#### POST `/lang-lib/remove`
|
|
264
|
+
|
|
265
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
266
|
+
| --- | --- | --- | --- |
|
|
267
|
+
| id | number / string | 否 | 单条 |
|
|
268
|
+
| ids | array | 否 | 批量(与 id 二选一) |
|
|
269
|
+
|
|
270
|
+
#### POST `/lang-lib/set-status`
|
|
271
|
+
|
|
272
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
273
|
+
| --- | --- | --- | --- |
|
|
274
|
+
| id / ids | number / array | 是 | 单条或批量 |
|
|
275
|
+
| status | string | 是 | `open` / `closed` |
|
|
276
|
+
|
|
277
|
+
#### POST `/lang-lib/review`
|
|
278
|
+
|
|
279
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
280
|
+
| --- | --- | --- | --- |
|
|
281
|
+
| id / ids | number / array | 是 | 单条或批量 |
|
|
282
|
+
| reviewStatus | string | 是 | 仅 `approved` / `rejected` |
|
|
283
|
+
| reviewRemark | string | 否 | 备注,最长 500 |
|
|
284
|
+
|
|
285
|
+
### 程序化 API
|
|
286
|
+
|
|
287
|
+
挂载于 `fastify['intl-admin'].services`:
|
|
288
|
+
|
|
289
|
+
| 方法签名 | 说明 |
|
|
290
|
+
| --- | --- |
|
|
291
|
+
| `langType.list(auth, query)` | 语言类型列表 |
|
|
292
|
+
| `langType.create(auth, body)` | 创建 |
|
|
293
|
+
| `langType.save(auth, body)` | 保存 |
|
|
294
|
+
| `langType.remove(auth, body)` | 删除 |
|
|
295
|
+
| `langType.setStatus(auth, body)` | 启停 |
|
|
296
|
+
| `langType.setDefault(auth, body)` | 设为默认语言 |
|
|
297
|
+
| `langLib.list(auth, query)` | 词条扁平列表 |
|
|
298
|
+
| `langLib.addPackage(auth, body)` | 批量添加 |
|
|
299
|
+
| `langLib.save(auth, body)` | 保存 |
|
|
300
|
+
| `langLib.remove(auth, body)` | 删除(id/ids) |
|
|
301
|
+
| `langLib.setStatus(auth, body)` | 启停(id/ids) |
|
|
302
|
+
| `langLib.review(auth, body)` | 审核(id/ids) |
|
|
303
|
+
|
|
304
|
+
首参 `auth` 为 `request.userInfo || {}`(本插件不做租户过滤)。
|
|
305
|
+
|
|
306
|
+
### 数据模型
|
|
307
|
+
|
|
308
|
+
#### langType(表注释:语言类型)
|
|
309
|
+
|
|
310
|
+
| 属性名 | 类型 | 说明 |
|
|
311
|
+
| --- | --- | --- |
|
|
312
|
+
| name | string | 名称 |
|
|
313
|
+
| code | string | 编码(唯一) |
|
|
314
|
+
| params | string | 翻译参数提示 |
|
|
315
|
+
| description | text | 描述 |
|
|
316
|
+
| status | enum | `open` / `closed` |
|
|
317
|
+
| isDefault | boolean | 是否默认语言(全局唯一;首条创建自动为 true) |
|
|
318
|
+
|
|
319
|
+
#### langLib(表注释:语言库词条)
|
|
320
|
+
|
|
321
|
+
| 属性名 | 类型 | 说明 |
|
|
322
|
+
| --- | --- | --- |
|
|
323
|
+
| namespace | string | 命名空间 |
|
|
324
|
+
| code | string | 词条编码 |
|
|
325
|
+
| locale | string | 语言编码 |
|
|
326
|
+
| target | text | 译文 |
|
|
327
|
+
| reviewStatus | enum | `approved` / `pending` / `rejected` |
|
|
328
|
+
| reviewRemark | text | 审核备注 |
|
|
329
|
+
| status | enum | `open` / `closed` |
|
|
330
|
+
|
|
331
|
+
唯一约束:`(namespace, code, locale)`。
|
|
332
|
+
|
|
333
|
+
### 机制说明
|
|
334
|
+
|
|
335
|
+
> **列表兼容**:`normalizeListQuery` 将顶层 `keyword` 等写入 `filter`,与 prompts 的 `filter` 对象约定兼容前端 isNext 扁平 query。
|
|
336
|
+
|
|
337
|
+
> **鉴权**:默认 `getAuthenticate` 返回空数组;宿主应用可按 `langType` / `langLib` / `*:manage` 覆盖。`GET /lang-lib/messages` 固定不挂鉴权,允许未登录访问。
|
package/index.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const fp = require('fastify-plugin');
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
const { ensureHttpErrors } = require('./libs/utils/http-errors');
|
|
4
|
+
|
|
5
|
+
module.exports = fp(async (fastify, options) => {
|
|
6
|
+
ensureHttpErrors(fastify);
|
|
7
|
+
|
|
8
|
+
options = Object.assign(
|
|
9
|
+
{},
|
|
10
|
+
{
|
|
11
|
+
dbTableNamePrefix: 't_',
|
|
12
|
+
name: 'intl-admin',
|
|
13
|
+
prefix: '/api/v1/intl-admin',
|
|
14
|
+
getAuthenticate: () => {
|
|
15
|
+
return [fastify.account.authenticate.user, fastify.account.authenticate.admin];
|
|
16
|
+
},
|
|
17
|
+
getUserModel: () => {
|
|
18
|
+
if (!fastify.account) {
|
|
19
|
+
throw new Error('请先安装 fastify-account 插件或者实现 options.getUserModel');
|
|
20
|
+
}
|
|
21
|
+
return fastify.account.models.user;
|
|
22
|
+
},
|
|
23
|
+
// ({ namespace, code, locales, sourceTarget, defaultLocale }) => Promise<{ [locale]: string }>
|
|
24
|
+
generateTargets: null
|
|
25
|
+
},
|
|
26
|
+
options
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
fastify.register(require('@kne/fastify-namespace'), {
|
|
30
|
+
options,
|
|
31
|
+
name: options.name,
|
|
32
|
+
modules: [
|
|
33
|
+
['controllers', path.resolve(__dirname, './libs/controllers')],
|
|
34
|
+
[
|
|
35
|
+
'models',
|
|
36
|
+
await fastify.sequelize.addModels(path.resolve(__dirname, './libs/models'), {
|
|
37
|
+
prefix: options.dbTableNamePrefix,
|
|
38
|
+
getUserModel: options.getUserModel,
|
|
39
|
+
modelPrefix: options.name
|
|
40
|
+
})
|
|
41
|
+
],
|
|
42
|
+
['services', path.resolve(__dirname, './libs/services')]
|
|
43
|
+
]
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
const fp = require('fastify-plugin');
|
|
2
|
+
const schema = require('../schema/lang-lib');
|
|
3
|
+
|
|
4
|
+
module.exports = fp(async (fastify, options) => {
|
|
5
|
+
const { services } = fastify[options.name];
|
|
6
|
+
const getAuth = type => (options.getAuthenticate ? options.getAuthenticate(type) : []);
|
|
7
|
+
|
|
8
|
+
fastify.get(
|
|
9
|
+
`${options.prefix}/lang-lib/list`,
|
|
10
|
+
{
|
|
11
|
+
onRequest: getAuth('langLib'),
|
|
12
|
+
schema: {
|
|
13
|
+
summary: '语言库词条列表',
|
|
14
|
+
query: schema.listQuery
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
async request => services.langLib.list(request.userInfo || {}, request.query)
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
fastify.get(
|
|
21
|
+
`${options.prefix}/lang-lib/namespaces`,
|
|
22
|
+
{
|
|
23
|
+
onRequest: getAuth('langLib'),
|
|
24
|
+
schema: {
|
|
25
|
+
summary: '语言库命名空间列表'
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
async request => services.langLib.namespaces(request.userInfo || {}, request.query)
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
fastify.post(
|
|
32
|
+
`${options.prefix}/lang-lib/add-package`,
|
|
33
|
+
{
|
|
34
|
+
onRequest: getAuth('langLib:manage'),
|
|
35
|
+
schema: {
|
|
36
|
+
summary: '批量添加语言词条',
|
|
37
|
+
body: schema.addPackageBody
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
async request => services.langLib.addPackage(request.userInfo || {}, request.body)
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
fastify.post(
|
|
44
|
+
`${options.prefix}/lang-lib/save`,
|
|
45
|
+
{
|
|
46
|
+
onRequest: getAuth('langLib:manage'),
|
|
47
|
+
schema: {
|
|
48
|
+
summary: '保存语言词条',
|
|
49
|
+
body: schema.saveBody
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
async request => {
|
|
53
|
+
await services.langLib.save(request.userInfo || {}, request.body);
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
fastify.post(
|
|
59
|
+
`${options.prefix}/lang-lib/remove`,
|
|
60
|
+
{
|
|
61
|
+
onRequest: getAuth('langLib:manage'),
|
|
62
|
+
schema: {
|
|
63
|
+
summary: '删除语言词条',
|
|
64
|
+
body: schema.removeBody
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
async request => {
|
|
68
|
+
await services.langLib.remove(request.userInfo || {}, request.body);
|
|
69
|
+
return {};
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
fastify.post(
|
|
74
|
+
`${options.prefix}/lang-lib/set-status`,
|
|
75
|
+
{
|
|
76
|
+
onRequest: getAuth('langLib:manage'),
|
|
77
|
+
schema: {
|
|
78
|
+
summary: '设置语言词条状态',
|
|
79
|
+
body: schema.setStatusBody
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
async request => {
|
|
83
|
+
await services.langLib.setStatus(request.userInfo || {}, request.body);
|
|
84
|
+
return {};
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
fastify.post(
|
|
89
|
+
`${options.prefix}/lang-lib/review`,
|
|
90
|
+
{
|
|
91
|
+
onRequest: getAuth('langLib:manage'),
|
|
92
|
+
schema: {
|
|
93
|
+
summary: '审核语言词条',
|
|
94
|
+
body: schema.reviewBody
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
async request => {
|
|
98
|
+
await services.langLib.review(request.userInfo || {}, request.body);
|
|
99
|
+
return {};
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
fastify.post(
|
|
104
|
+
`${options.prefix}/lang-lib/regenerate`,
|
|
105
|
+
{
|
|
106
|
+
onRequest: getAuth('langLib:manage'),
|
|
107
|
+
schema: {
|
|
108
|
+
summary: '重新生成待审核词条译文',
|
|
109
|
+
body: schema.regenerateBody
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
async request => {
|
|
113
|
+
await services.langLib.regenerate(request.userInfo || {}, request.body);
|
|
114
|
+
return {};
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
fastify.post(
|
|
119
|
+
`${options.prefix}/lang-lib/copy-to-namespace`,
|
|
120
|
+
{
|
|
121
|
+
onRequest: getAuth('langLib:manage'),
|
|
122
|
+
schema: {
|
|
123
|
+
summary: '复制词条到新命名空间',
|
|
124
|
+
body: schema.copyToNamespaceBody
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
async request => services.langLib.copyToNamespace(request.userInfo || {}, request.body)
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
fastify.get(
|
|
131
|
+
`${options.prefix}/lang-lib/messages`,
|
|
132
|
+
{
|
|
133
|
+
// 运行时语言包:允许未登录访问
|
|
134
|
+
schema: {
|
|
135
|
+
summary: '运行时语言包(扁平 code→文案,无需登录)',
|
|
136
|
+
query: schema.messagesQuery
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
async request => services.langLib.messages(request.userInfo || {}, request.query)
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
fastify.get(
|
|
143
|
+
`${options.prefix}/lang-lib/export`,
|
|
144
|
+
{
|
|
145
|
+
onRequest: getAuth('langLib'),
|
|
146
|
+
schema: {
|
|
147
|
+
summary: '导出语言库 i18n 文件',
|
|
148
|
+
query: schema.exportQuery
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
async request => services.langLib.exportI18n(request.userInfo || {}, request.query)
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
fastify.post(
|
|
155
|
+
`${options.prefix}/lang-lib/import`,
|
|
156
|
+
{
|
|
157
|
+
onRequest: getAuth('langLib:manage'),
|
|
158
|
+
schema: {
|
|
159
|
+
summary: '导入语言库 i18n 文件',
|
|
160
|
+
body: schema.importBody
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
async request => services.langLib.importI18n(request.userInfo || {}, request.body)
|
|
164
|
+
);
|
|
165
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
const fp = require('fastify-plugin');
|
|
2
|
+
const schema = require('../schema/lang-type');
|
|
3
|
+
|
|
4
|
+
module.exports = fp(async (fastify, options) => {
|
|
5
|
+
const { services } = fastify[options.name];
|
|
6
|
+
const getAuth = type => (options.getAuthenticate ? options.getAuthenticate(type) : []);
|
|
7
|
+
|
|
8
|
+
fastify.get(
|
|
9
|
+
`${options.prefix}/lang-type/list`,
|
|
10
|
+
{
|
|
11
|
+
onRequest: getAuth('langType'),
|
|
12
|
+
schema: {
|
|
13
|
+
summary: '语言类型列表',
|
|
14
|
+
query: schema.listQuery
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
async request => services.langType.list(request.userInfo || {}, request.query)
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
fastify.post(
|
|
21
|
+
`${options.prefix}/lang-type/create`,
|
|
22
|
+
{
|
|
23
|
+
onRequest: getAuth('langType:manage'),
|
|
24
|
+
schema: {
|
|
25
|
+
summary: '创建语言类型',
|
|
26
|
+
body: schema.createBody
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
async request => services.langType.create(request.userInfo || {}, request.body)
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
fastify.post(
|
|
33
|
+
`${options.prefix}/lang-type/save`,
|
|
34
|
+
{
|
|
35
|
+
onRequest: getAuth('langType:manage'),
|
|
36
|
+
schema: {
|
|
37
|
+
summary: '保存语言类型',
|
|
38
|
+
body: schema.saveBody
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
async request => {
|
|
42
|
+
await services.langType.save(request.userInfo || {}, request.body);
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
fastify.post(
|
|
48
|
+
`${options.prefix}/lang-type/remove`,
|
|
49
|
+
{
|
|
50
|
+
onRequest: getAuth('langType:manage'),
|
|
51
|
+
schema: {
|
|
52
|
+
summary: '删除语言类型',
|
|
53
|
+
body: schema.removeBody
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
async request => {
|
|
57
|
+
await services.langType.remove(request.userInfo || {}, request.body);
|
|
58
|
+
return {};
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
fastify.post(
|
|
63
|
+
`${options.prefix}/lang-type/set-status`,
|
|
64
|
+
{
|
|
65
|
+
onRequest: getAuth('langType:manage'),
|
|
66
|
+
schema: {
|
|
67
|
+
summary: '设置语言类型状态',
|
|
68
|
+
body: schema.setStatusBody
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
async request => {
|
|
72
|
+
await services.langType.setStatus(request.userInfo || {}, request.body);
|
|
73
|
+
return {};
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
fastify.post(
|
|
78
|
+
`${options.prefix}/lang-type/set-default`,
|
|
79
|
+
{
|
|
80
|
+
onRequest: getAuth('langType:manage'),
|
|
81
|
+
schema: {
|
|
82
|
+
summary: '设置默认语言',
|
|
83
|
+
body: schema.setDefaultBody
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
async request => {
|
|
87
|
+
await services.langType.setDefault(request.userInfo || {}, request.body);
|
|
88
|
+
return {};
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
fastify.post(
|
|
93
|
+
`${options.prefix}/lang-type/move`,
|
|
94
|
+
{
|
|
95
|
+
onRequest: getAuth('langType:manage'),
|
|
96
|
+
schema: {
|
|
97
|
+
summary: '语言类型上移/下移',
|
|
98
|
+
body: schema.moveBody
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
async request => services.langType.move(request.userInfo || {}, request.body)
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
fastify.post(
|
|
105
|
+
`${options.prefix}/lang-type/translate-remaining`,
|
|
106
|
+
{
|
|
107
|
+
onRequest: getAuth('langType:manage'),
|
|
108
|
+
schema: {
|
|
109
|
+
summary: '补译目标语言缺失词条',
|
|
110
|
+
body: schema.translateRemainingBody
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
async request => services.langType.translateRemaining(request.userInfo || {}, request.body)
|
|
114
|
+
);
|
|
115
|
+
});
|