befly 3.9.11 → 3.9.13
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/docs/README.md +85 -0
- package/docs/addon.md +512 -0
- package/docs/api.md +1368 -0
- package/docs/cipher.md +580 -0
- package/docs/config.md +638 -0
- package/docs/examples.md +799 -0
- package/docs/hook.md +754 -0
- package/docs/logger.md +495 -0
- package/docs/plugin.md +978 -0
- package/docs/quickstart.md +331 -0
- package/docs/sync.md +586 -0
- package/docs/table.md +765 -0
- package/docs/validator.md +588 -0
- package/hooks/parser.ts +31 -10
- package/package.json +3 -3
- package/router/api.ts +11 -2
- package/sync/syncDb/apply.ts +1 -1
- package/sync/syncDb/constants.ts +0 -11
- package/sync/syncDb/helpers.ts +0 -1
- package/sync/syncDb/table.ts +2 -2
- package/sync/syncDb/tableCreate.ts +3 -3
- package/tests/syncDb-constants.test.ts +1 -23
- package/tests/syncDb-helpers.test.ts +0 -1
- package/types/api.d.ts +12 -0
- package/types/database.d.ts +0 -2
package/docs/table.md
ADDED
|
@@ -0,0 +1,765 @@
|
|
|
1
|
+
# 表结构定义指南
|
|
2
|
+
|
|
3
|
+
> 本文档详细介绍 Befly 框架的表结构定义规范,包括字段类型、约束、校验规则及数据库同步机制。
|
|
4
|
+
|
|
5
|
+
## 目录
|
|
6
|
+
|
|
7
|
+
- [表结构定义指南](#表结构定义指南)
|
|
8
|
+
- [目录](#目录)
|
|
9
|
+
- [核心概念](#核心概念)
|
|
10
|
+
- [表定义文件](#表定义文件)
|
|
11
|
+
- [文件存放位置](#文件存放位置)
|
|
12
|
+
- [表名生成规则](#表名生成规则)
|
|
13
|
+
- [字段定义](#字段定义)
|
|
14
|
+
- [完整字段结构](#完整字段结构)
|
|
15
|
+
- [必填属性](#必填属性)
|
|
16
|
+
- [可选属性](#可选属性)
|
|
17
|
+
- [字段类型](#字段类型)
|
|
18
|
+
- [string - 字符串](#string---字符串)
|
|
19
|
+
- [number - 数字](#number---数字)
|
|
20
|
+
- [text - 长文本](#text---长文本)
|
|
21
|
+
- [array_string - 字符串数组](#array_string---字符串数组)
|
|
22
|
+
- [array_text - 长文本数组](#array_text---长文本数组)
|
|
23
|
+
- [类型映射表](#类型映射表)
|
|
24
|
+
- [约束与索引](#约束与索引)
|
|
25
|
+
- [index - 普通索引](#index---普通索引)
|
|
26
|
+
- [unique - 唯一约束](#unique---唯一约束)
|
|
27
|
+
- [nullable - 可空约束](#nullable---可空约束)
|
|
28
|
+
- [unsigned - 无符号约束](#unsigned---无符号约束)
|
|
29
|
+
- [正则验证](#正则验证)
|
|
30
|
+
- [内置正则](#内置正则)
|
|
31
|
+
- [自定义正则](#自定义正则)
|
|
32
|
+
- [系统字段](#系统字段)
|
|
33
|
+
- [保留字段列表](#保留字段列表)
|
|
34
|
+
- [State 状态值](#state-状态值)
|
|
35
|
+
- [校验规则](#校验规则)
|
|
36
|
+
- [文件名规范](#文件名规范)
|
|
37
|
+
- [字段名规范](#字段名规范)
|
|
38
|
+
- [类型联动校验](#类型联动校验)
|
|
39
|
+
- [数据库同步](#数据库同步)
|
|
40
|
+
- [同步命令](#同步命令)
|
|
41
|
+
- [同步流程](#同步流程)
|
|
42
|
+
- [变更检测](#变更检测)
|
|
43
|
+
- [安全机制](#安全机制)
|
|
44
|
+
- [完整示例](#完整示例)
|
|
45
|
+
- [用户表定义](#用户表定义)
|
|
46
|
+
- [角色表定义](#角色表定义)
|
|
47
|
+
- [文章表定义](#文章表定义)
|
|
48
|
+
- [最佳实践](#最佳实践)
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 核心概念
|
|
53
|
+
|
|
54
|
+
### 表定义文件
|
|
55
|
+
|
|
56
|
+
Befly 使用 JSON 文件定义数据库表结构。每个 JSON 文件对应一张数据库表,文件中的每个键值对定义一个字段。
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"fieldKey": {
|
|
61
|
+
"name": "字段标签",
|
|
62
|
+
"type": "string",
|
|
63
|
+
"max": 100
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 文件存放位置
|
|
69
|
+
|
|
70
|
+
| 类型 | 目录位置 | 表名前缀 |
|
|
71
|
+
| -------- | ------------------------------ | --------------------- |
|
|
72
|
+
| 项目表 | `项目根目录/tables/` | 无前缀 |
|
|
73
|
+
| Addon 表 | `packages/addon{Name}/tables/` | `addon_{addon_name}_` |
|
|
74
|
+
|
|
75
|
+
### 表名生成规则
|
|
76
|
+
|
|
77
|
+
表名由文件名自动转换生成:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
文件名(小驼峰) → 数据库表名(下划线)
|
|
81
|
+
|
|
82
|
+
userProfile.json → user_profile # 项目表
|
|
83
|
+
admin.json → addon_admin_admin # addonAdmin 的表
|
|
84
|
+
loginLog.json → addon_admin_login_log # addonAdmin 的表
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 字段定义
|
|
90
|
+
|
|
91
|
+
### 完整字段结构
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
interface FieldDefinition {
|
|
95
|
+
/** 字段标签/描述(必填) */
|
|
96
|
+
name: string;
|
|
97
|
+
/** 字段类型(必填) */
|
|
98
|
+
type: 'string' | 'number' | 'text' | 'array_string' | 'array_text';
|
|
99
|
+
/** 字段详细说明 */
|
|
100
|
+
detail?: string;
|
|
101
|
+
/** 最小值/最小长度 */
|
|
102
|
+
min?: number | null;
|
|
103
|
+
/** 最大值/最大长度 */
|
|
104
|
+
max?: number | null;
|
|
105
|
+
/** 默认值 */
|
|
106
|
+
default?: any;
|
|
107
|
+
/** 是否创建索引 */
|
|
108
|
+
index?: boolean;
|
|
109
|
+
/** 是否唯一 */
|
|
110
|
+
unique?: boolean;
|
|
111
|
+
/** 是否允许为空 */
|
|
112
|
+
nullable?: boolean;
|
|
113
|
+
/** 是否无符号(仅 number 类型) */
|
|
114
|
+
unsigned?: boolean;
|
|
115
|
+
/** 正则验证 */
|
|
116
|
+
regexp?: string | null;
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 必填属性
|
|
121
|
+
|
|
122
|
+
| 属性 | 类型 | 说明 |
|
|
123
|
+
| ------ | -------- | ------------------------------------------------------------------------------ |
|
|
124
|
+
| `name` | `string` | 字段标签,用于 API 返回和错误提示,支持中文、字母、数字、空格、下划线、短横线 |
|
|
125
|
+
| `type` | `string` | 字段类型,必须为 `string`、`number`、`text`、`array_string`、`array_text` 之一 |
|
|
126
|
+
|
|
127
|
+
### 可选属性
|
|
128
|
+
|
|
129
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
130
|
+
| ---------- | ---------------- | ------- | ------------------------------------ |
|
|
131
|
+
| `detail` | `string` | `''` | 字段详细说明 |
|
|
132
|
+
| `min` | `number \| null` | `0` | 最小值(number)或最小长度(string) |
|
|
133
|
+
| `max` | `number \| null` | `100` | 最大值(number)或最大长度(string) |
|
|
134
|
+
| `default` | `any` | `null` | 默认值 |
|
|
135
|
+
| `index` | `boolean` | `false` | 是否创建普通索引 |
|
|
136
|
+
| `unique` | `boolean` | `false` | 是否唯一约束 |
|
|
137
|
+
| `nullable` | `boolean` | `false` | 是否允许 NULL |
|
|
138
|
+
| `unsigned` | `boolean` | `true` | 是否无符号(仅 number 类型有效) |
|
|
139
|
+
| `regexp` | `string \| null` | `null` | 正则验证规则(内置或自定义) |
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 字段类型
|
|
144
|
+
|
|
145
|
+
### string - 字符串
|
|
146
|
+
|
|
147
|
+
用于存储定长或短文本,映射到数据库 `VARCHAR` 类型。
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
{
|
|
151
|
+
"username": {
|
|
152
|
+
"name": "用户名",
|
|
153
|
+
"type": "string",
|
|
154
|
+
"min": 2,
|
|
155
|
+
"max": 30,
|
|
156
|
+
"regexp": "@alphanumeric_"
|
|
157
|
+
},
|
|
158
|
+
"email": {
|
|
159
|
+
"name": "邮箱",
|
|
160
|
+
"type": "string",
|
|
161
|
+
"max": 100,
|
|
162
|
+
"default": "",
|
|
163
|
+
"regexp": "@email"
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**约束规则:**
|
|
169
|
+
|
|
170
|
+
- `max` 必须为数字,范围 1 ~ 65535
|
|
171
|
+
- `min` 用于输入验证,不影响数据库结构
|
|
172
|
+
- 默认值为空字符串 `''`
|
|
173
|
+
|
|
174
|
+
### number - 数字
|
|
175
|
+
|
|
176
|
+
用于存储整数,映射到数据库 `BIGINT` 类型。
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"age": {
|
|
181
|
+
"name": "年龄",
|
|
182
|
+
"type": "number",
|
|
183
|
+
"min": 0,
|
|
184
|
+
"max": 150,
|
|
185
|
+
"default": 0
|
|
186
|
+
},
|
|
187
|
+
"roleId": {
|
|
188
|
+
"name": "角色ID",
|
|
189
|
+
"type": "number",
|
|
190
|
+
"min": 1,
|
|
191
|
+
"max": 999999999999999,
|
|
192
|
+
"index": true
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**约束规则:**
|
|
198
|
+
|
|
199
|
+
- `min` 和 `max` 用于输入验证
|
|
200
|
+
- `unsigned` 默认为 `true`(无符号,仅 MySQL 有效)
|
|
201
|
+
- 默认值为 `0`
|
|
202
|
+
|
|
203
|
+
### text - 长文本
|
|
204
|
+
|
|
205
|
+
用于存储大段文本,映射到数据库 `MEDIUMTEXT`(MySQL)或 `TEXT`(PostgreSQL)。
|
|
206
|
+
|
|
207
|
+
```json
|
|
208
|
+
{
|
|
209
|
+
"content": {
|
|
210
|
+
"name": "文章内容",
|
|
211
|
+
"type": "text"
|
|
212
|
+
},
|
|
213
|
+
"description": {
|
|
214
|
+
"name": "描述",
|
|
215
|
+
"type": "text",
|
|
216
|
+
"nullable": true
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**约束规则:**
|
|
222
|
+
|
|
223
|
+
- `min` 和 `max` 必须为 `null`(不限制长度)
|
|
224
|
+
- `default` 必须为 `null`(MySQL TEXT 不支持默认值)
|
|
225
|
+
- 不支持创建索引
|
|
226
|
+
|
|
227
|
+
### array_string - 字符串数组
|
|
228
|
+
|
|
229
|
+
用于存储字符串数组,以 JSON 格式存入 `VARCHAR` 字段。
|
|
230
|
+
|
|
231
|
+
```json
|
|
232
|
+
{
|
|
233
|
+
"tags": {
|
|
234
|
+
"name": "标签",
|
|
235
|
+
"type": "array_string",
|
|
236
|
+
"max": 500
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**约束规则:**
|
|
242
|
+
|
|
243
|
+
- `max` 指定存储 JSON 字符串的最大长度
|
|
244
|
+
- 默认值为 `'[]'`(空数组 JSON)
|
|
245
|
+
- 存储格式:`["tag1", "tag2", "tag3"]`
|
|
246
|
+
|
|
247
|
+
### array_text - 长文本数组
|
|
248
|
+
|
|
249
|
+
用于存储大型数组数据,以 JSON 格式存入 `MEDIUMTEXT` 字段。
|
|
250
|
+
|
|
251
|
+
```json
|
|
252
|
+
{
|
|
253
|
+
"menus": {
|
|
254
|
+
"name": "菜单权限",
|
|
255
|
+
"type": "array_text"
|
|
256
|
+
},
|
|
257
|
+
"apis": {
|
|
258
|
+
"name": "接口权限",
|
|
259
|
+
"type": "array_text"
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**约束规则:**
|
|
265
|
+
|
|
266
|
+
- `min` 和 `max` 必须为 `null`
|
|
267
|
+
- `default` 必须为 `null`
|
|
268
|
+
- 存储格式:JSON 数组字符串
|
|
269
|
+
|
|
270
|
+
### 类型映射表
|
|
271
|
+
|
|
272
|
+
| Befly 类型 | MySQL | PostgreSQL | SQLite |
|
|
273
|
+
| -------------- | -------------- | ------------------- | --------- |
|
|
274
|
+
| `string` | `VARCHAR(max)` | `character varying` | `TEXT` |
|
|
275
|
+
| `number` | `BIGINT` | `BIGINT` | `INTEGER` |
|
|
276
|
+
| `text` | `MEDIUMTEXT` | `TEXT` | `TEXT` |
|
|
277
|
+
| `array_string` | `VARCHAR(max)` | `character varying` | `TEXT` |
|
|
278
|
+
| `array_text` | `MEDIUMTEXT` | `TEXT` | `TEXT` |
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## 约束与索引
|
|
283
|
+
|
|
284
|
+
### index - 普通索引
|
|
285
|
+
|
|
286
|
+
为字段创建普通索引,加速查询。
|
|
287
|
+
|
|
288
|
+
```json
|
|
289
|
+
{
|
|
290
|
+
"roleId": {
|
|
291
|
+
"name": "角色ID",
|
|
292
|
+
"type": "number",
|
|
293
|
+
"index": true
|
|
294
|
+
},
|
|
295
|
+
"status": {
|
|
296
|
+
"name": "状态",
|
|
297
|
+
"type": "string",
|
|
298
|
+
"max": 20,
|
|
299
|
+
"index": true
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
**生成的索引名:** `idx_{字段名}`
|
|
305
|
+
|
|
306
|
+
### unique - 唯一约束
|
|
307
|
+
|
|
308
|
+
确保字段值在表中唯一。
|
|
309
|
+
|
|
310
|
+
```json
|
|
311
|
+
{
|
|
312
|
+
"username": {
|
|
313
|
+
"name": "用户名",
|
|
314
|
+
"type": "string",
|
|
315
|
+
"max": 30,
|
|
316
|
+
"unique": true
|
|
317
|
+
},
|
|
318
|
+
"email": {
|
|
319
|
+
"name": "邮箱",
|
|
320
|
+
"type": "string",
|
|
321
|
+
"max": 100,
|
|
322
|
+
"unique": true
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### nullable - 可空约束
|
|
328
|
+
|
|
329
|
+
允许字段存储 NULL 值。
|
|
330
|
+
|
|
331
|
+
```json
|
|
332
|
+
{
|
|
333
|
+
"deletedAt": {
|
|
334
|
+
"name": "删除时间",
|
|
335
|
+
"type": "number",
|
|
336
|
+
"nullable": true
|
|
337
|
+
},
|
|
338
|
+
"remark": {
|
|
339
|
+
"name": "备注",
|
|
340
|
+
"type": "string",
|
|
341
|
+
"max": 500,
|
|
342
|
+
"nullable": true
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
**注意:** 默认 `nullable: false`,字段不允许为 NULL。
|
|
348
|
+
|
|
349
|
+
### unsigned - 无符号约束
|
|
350
|
+
|
|
351
|
+
仅对 `number` 类型有效,仅 MySQL 支持。
|
|
352
|
+
|
|
353
|
+
```json
|
|
354
|
+
{
|
|
355
|
+
"amount": {
|
|
356
|
+
"name": "金额",
|
|
357
|
+
"type": "number",
|
|
358
|
+
"unsigned": true
|
|
359
|
+
},
|
|
360
|
+
"balance": {
|
|
361
|
+
"name": "余额(可负)",
|
|
362
|
+
"type": "number",
|
|
363
|
+
"unsigned": false
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
**注意:** 默认 `unsigned: true`。
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## 正则验证
|
|
373
|
+
|
|
374
|
+
### 内置正则
|
|
375
|
+
|
|
376
|
+
使用 `@` 前缀引用内置正则规则:
|
|
377
|
+
|
|
378
|
+
| 规则名 | 说明 | 示例 |
|
|
379
|
+
| -------------------- | -------------------------- | ------------------ |
|
|
380
|
+
| `@email` | 邮箱格式 | `user@example.com` |
|
|
381
|
+
| `@phone` | 手机号(中国) | `13800138000` |
|
|
382
|
+
| `@alphanumeric` | 字母和数字 | `abc123` |
|
|
383
|
+
| `@alphanumeric_` | 字母、数字、下划线 | `user_123` |
|
|
384
|
+
| `@alphanumericDash_` | 字母、数字、下划线、短横线 | `role-code_123` |
|
|
385
|
+
|
|
386
|
+
```json
|
|
387
|
+
{
|
|
388
|
+
"email": {
|
|
389
|
+
"name": "邮箱",
|
|
390
|
+
"type": "string",
|
|
391
|
+
"max": 100,
|
|
392
|
+
"regexp": "@email"
|
|
393
|
+
},
|
|
394
|
+
"username": {
|
|
395
|
+
"name": "用户名",
|
|
396
|
+
"type": "string",
|
|
397
|
+
"max": 30,
|
|
398
|
+
"regexp": "@alphanumeric_"
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### 自定义正则
|
|
404
|
+
|
|
405
|
+
直接使用正则表达式字符串:
|
|
406
|
+
|
|
407
|
+
```json
|
|
408
|
+
{
|
|
409
|
+
"roleType": {
|
|
410
|
+
"name": "角色类型",
|
|
411
|
+
"type": "string",
|
|
412
|
+
"max": 5,
|
|
413
|
+
"regexp": "^(admin|user)$"
|
|
414
|
+
},
|
|
415
|
+
"code": {
|
|
416
|
+
"name": "编码",
|
|
417
|
+
"type": "string",
|
|
418
|
+
"max": 20,
|
|
419
|
+
"regexp": "^[A-Z]{2}[0-9]{4}$"
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## 系统字段
|
|
427
|
+
|
|
428
|
+
### 保留字段列表
|
|
429
|
+
|
|
430
|
+
以下字段由系统自动创建和管理,**不能**在表定义中使用:
|
|
431
|
+
|
|
432
|
+
| 字段名 | 类型 | 说明 |
|
|
433
|
+
| ------------ | --------- | ------------------------ |
|
|
434
|
+
| `id` | `BIGINT` | 主键 ID,自增 |
|
|
435
|
+
| `created_at` | `BIGINT` | 创建时间戳(毫秒) |
|
|
436
|
+
| `updated_at` | `BIGINT` | 更新时间戳(毫秒) |
|
|
437
|
+
| `deleted_at` | `BIGINT` | 删除时间戳(软删除标记) |
|
|
438
|
+
| `state` | `TINYINT` | 状态字段 |
|
|
439
|
+
|
|
440
|
+
**系统自动创建的索引:**
|
|
441
|
+
|
|
442
|
+
- `idx_created_at`
|
|
443
|
+
- `idx_updated_at`
|
|
444
|
+
- `idx_state`
|
|
445
|
+
|
|
446
|
+
### State 状态值
|
|
447
|
+
|
|
448
|
+
| 值 | 常量名 | 说明 |
|
|
449
|
+
| --- | ---------- | --------- |
|
|
450
|
+
| `0` | `DISABLED` | 禁用 |
|
|
451
|
+
| `1` | `ENABLED` | 正常/启用 |
|
|
452
|
+
| `2` | `DELETED` | 已删除 |
|
|
453
|
+
|
|
454
|
+
---
|
|
455
|
+
|
|
456
|
+
## 校验规则
|
|
457
|
+
|
|
458
|
+
### 文件名规范
|
|
459
|
+
|
|
460
|
+
表定义文件必须使用**小驼峰命名**:
|
|
461
|
+
|
|
462
|
+
```
|
|
463
|
+
✓ user.json
|
|
464
|
+
✓ userProfile.json
|
|
465
|
+
✓ loginLog.json
|
|
466
|
+
|
|
467
|
+
✗ User.json # 首字母大写
|
|
468
|
+
✗ user_profile.json # 下划线
|
|
469
|
+
✗ user-profile.json # 短横线
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
特殊文件(以下划线开头)用于通用字段定义:
|
|
473
|
+
|
|
474
|
+
```
|
|
475
|
+
✓ _common.json # 通用字段定义
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### 字段名规范
|
|
479
|
+
|
|
480
|
+
字段标签(`name` 属性)支持:
|
|
481
|
+
|
|
482
|
+
- 中文字符
|
|
483
|
+
- 英文字母
|
|
484
|
+
- 数字
|
|
485
|
+
- 空格
|
|
486
|
+
- 下划线 `_`
|
|
487
|
+
- 短横线 `-`
|
|
488
|
+
|
|
489
|
+
```json
|
|
490
|
+
{
|
|
491
|
+
"field1": { "name": "用户名称" }, // ✓
|
|
492
|
+
"field2": { "name": "User Name" }, // ✓
|
|
493
|
+
"field3": { "name": "用户-名称_123" } // ✓
|
|
494
|
+
}
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### 类型联动校验
|
|
498
|
+
|
|
499
|
+
不同字段类型有特定的约束规则:
|
|
500
|
+
|
|
501
|
+
| 类型 | min/max 要求 | default 要求 |
|
|
502
|
+
| -------------- | ---------------- | -------------------- |
|
|
503
|
+
| `string` | `max` 必须为数字 | 可为任意字符串 |
|
|
504
|
+
| `number` | 可为数字 | 必须为 `null` 或数字 |
|
|
505
|
+
| `text` | 必须为 `null` | 必须为 `null` |
|
|
506
|
+
| `array_string` | `max` 必须为数字 | 默认为 `'[]'` |
|
|
507
|
+
| `array_text` | 必须为 `null` | 必须为 `null` |
|
|
508
|
+
|
|
509
|
+
---
|
|
510
|
+
|
|
511
|
+
## 数据库同步
|
|
512
|
+
|
|
513
|
+
### 同步命令
|
|
514
|
+
|
|
515
|
+
```bash
|
|
516
|
+
# 同步所有表
|
|
517
|
+
bun run sync:db
|
|
518
|
+
|
|
519
|
+
# 同步指定表
|
|
520
|
+
bun run sync:db --table=user
|
|
521
|
+
|
|
522
|
+
# 计划模式(仅输出 SQL,不执行)
|
|
523
|
+
bun run sync:db --plan
|
|
524
|
+
|
|
525
|
+
# 强制模式(执行危险变更,如字段长度收缩)
|
|
526
|
+
bun run sync:db --force
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### 同步流程
|
|
530
|
+
|
|
531
|
+
```
|
|
532
|
+
1. 校验表定义文件(checkTable)
|
|
533
|
+
├─ 文件名格式校验
|
|
534
|
+
├─ 字段类型校验
|
|
535
|
+
├─ 约束规则校验
|
|
536
|
+
└─ 保留字段检查
|
|
537
|
+
|
|
538
|
+
2. 建立数据库连接
|
|
539
|
+
└─ 检查数据库版本(MySQL 8+, PostgreSQL 17+)
|
|
540
|
+
|
|
541
|
+
3. 扫描表定义文件
|
|
542
|
+
├─ 项目 tables 目录
|
|
543
|
+
└─ 各 addon 的 tables 目录
|
|
544
|
+
|
|
545
|
+
4. 对比并应用变更
|
|
546
|
+
├─ 表不存在 → 创建表
|
|
547
|
+
└─ 表已存在 → 对比字段变化
|
|
548
|
+
├─ 新增字段 → ADD COLUMN
|
|
549
|
+
├─ 修改字段 → MODIFY COLUMN
|
|
550
|
+
└─ 索引变更 → CREATE/DROP INDEX
|
|
551
|
+
|
|
552
|
+
5. 清理 Redis 缓存
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### 变更检测
|
|
556
|
+
|
|
557
|
+
同步时检测以下变更:
|
|
558
|
+
|
|
559
|
+
| 变更类型 | 说明 | 自动执行 |
|
|
560
|
+
| -------- | ---------------------------- | ---------- |
|
|
561
|
+
| 长度扩展 | VARCHAR 长度增加 | ✓ |
|
|
562
|
+
| 长度收缩 | VARCHAR 长度减少 | 需 --force |
|
|
563
|
+
| 类型宽化 | INT → BIGINT, VARCHAR → TEXT | ✓ |
|
|
564
|
+
| 类型变更 | 其他类型变更 | ✗ 禁止 |
|
|
565
|
+
| 默认值 | 默认值变更 | ✓ |
|
|
566
|
+
| 注释 | 字段注释变更 | ✓ |
|
|
567
|
+
| 索引 | 添加/删除索引 | ✓ |
|
|
568
|
+
|
|
569
|
+
### 安全机制
|
|
570
|
+
|
|
571
|
+
1. **禁止危险类型变更**:不允许从 `BIGINT` 改为 `VARCHAR` 等不兼容变更
|
|
572
|
+
2. **长度收缩保护**:默认跳过长度收缩,避免数据截断
|
|
573
|
+
3. **计划模式**:`--plan` 仅输出 SQL 不执行,用于审查
|
|
574
|
+
4. **保留字段保护**:不允许定义系统保留字段
|
|
575
|
+
|
|
576
|
+
---
|
|
577
|
+
|
|
578
|
+
## 完整示例
|
|
579
|
+
|
|
580
|
+
### 用户表定义
|
|
581
|
+
|
|
582
|
+
```json
|
|
583
|
+
// tables/user.json
|
|
584
|
+
{
|
|
585
|
+
"nickname": {
|
|
586
|
+
"name": "昵称",
|
|
587
|
+
"type": "string",
|
|
588
|
+
"min": 1,
|
|
589
|
+
"max": 50
|
|
590
|
+
},
|
|
591
|
+
"username": {
|
|
592
|
+
"name": "用户名",
|
|
593
|
+
"type": "string",
|
|
594
|
+
"min": 2,
|
|
595
|
+
"max": 30,
|
|
596
|
+
"unique": true,
|
|
597
|
+
"regexp": "@alphanumeric_"
|
|
598
|
+
},
|
|
599
|
+
"password": {
|
|
600
|
+
"name": "密码",
|
|
601
|
+
"type": "string",
|
|
602
|
+
"min": 6,
|
|
603
|
+
"max": 500
|
|
604
|
+
},
|
|
605
|
+
"email": {
|
|
606
|
+
"name": "邮箱",
|
|
607
|
+
"type": "string",
|
|
608
|
+
"max": 100,
|
|
609
|
+
"unique": true,
|
|
610
|
+
"regexp": "@email"
|
|
611
|
+
},
|
|
612
|
+
"phone": {
|
|
613
|
+
"name": "手机号",
|
|
614
|
+
"type": "string",
|
|
615
|
+
"max": 20,
|
|
616
|
+
"regexp": "@phone"
|
|
617
|
+
},
|
|
618
|
+
"avatar": {
|
|
619
|
+
"name": "头像",
|
|
620
|
+
"type": "string",
|
|
621
|
+
"max": 500
|
|
622
|
+
},
|
|
623
|
+
"roleId": {
|
|
624
|
+
"name": "角色ID",
|
|
625
|
+
"type": "number",
|
|
626
|
+
"min": 1,
|
|
627
|
+
"index": true
|
|
628
|
+
},
|
|
629
|
+
"lastLoginTime": {
|
|
630
|
+
"name": "最后登录时间",
|
|
631
|
+
"type": "number",
|
|
632
|
+
"default": 0
|
|
633
|
+
},
|
|
634
|
+
"lastLoginIp": {
|
|
635
|
+
"name": "最后登录IP",
|
|
636
|
+
"type": "string",
|
|
637
|
+
"max": 50
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
### 角色表定义
|
|
643
|
+
|
|
644
|
+
```json
|
|
645
|
+
// tables/role.json
|
|
646
|
+
{
|
|
647
|
+
"name": {
|
|
648
|
+
"name": "角色名称",
|
|
649
|
+
"type": "string",
|
|
650
|
+
"min": 2,
|
|
651
|
+
"max": 50,
|
|
652
|
+
"index": true
|
|
653
|
+
},
|
|
654
|
+
"code": {
|
|
655
|
+
"name": "角色编码",
|
|
656
|
+
"type": "string",
|
|
657
|
+
"min": 2,
|
|
658
|
+
"max": 50,
|
|
659
|
+
"unique": true,
|
|
660
|
+
"regexp": "@alphanumericDash_"
|
|
661
|
+
},
|
|
662
|
+
"description": {
|
|
663
|
+
"name": "角色描述",
|
|
664
|
+
"type": "string",
|
|
665
|
+
"max": 200
|
|
666
|
+
},
|
|
667
|
+
"menus": {
|
|
668
|
+
"name": "菜单权限",
|
|
669
|
+
"type": "array_text"
|
|
670
|
+
},
|
|
671
|
+
"apis": {
|
|
672
|
+
"name": "接口权限",
|
|
673
|
+
"type": "array_text"
|
|
674
|
+
},
|
|
675
|
+
"sort": {
|
|
676
|
+
"name": "排序",
|
|
677
|
+
"type": "number",
|
|
678
|
+
"default": 0
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
### 文章表定义
|
|
684
|
+
|
|
685
|
+
```json
|
|
686
|
+
// tables/article.json
|
|
687
|
+
{
|
|
688
|
+
"title": {
|
|
689
|
+
"name": "标题",
|
|
690
|
+
"type": "string",
|
|
691
|
+
"min": 1,
|
|
692
|
+
"max": 200,
|
|
693
|
+
"index": true
|
|
694
|
+
},
|
|
695
|
+
"summary": {
|
|
696
|
+
"name": "摘要",
|
|
697
|
+
"type": "string",
|
|
698
|
+
"max": 500
|
|
699
|
+
},
|
|
700
|
+
"content": {
|
|
701
|
+
"name": "内容",
|
|
702
|
+
"type": "text"
|
|
703
|
+
},
|
|
704
|
+
"authorId": {
|
|
705
|
+
"name": "作者ID",
|
|
706
|
+
"type": "number",
|
|
707
|
+
"index": true
|
|
708
|
+
},
|
|
709
|
+
"categoryId": {
|
|
710
|
+
"name": "分类ID",
|
|
711
|
+
"type": "number",
|
|
712
|
+
"index": true
|
|
713
|
+
},
|
|
714
|
+
"tags": {
|
|
715
|
+
"name": "标签",
|
|
716
|
+
"type": "array_string",
|
|
717
|
+
"max": 500
|
|
718
|
+
},
|
|
719
|
+
"viewCount": {
|
|
720
|
+
"name": "阅读数",
|
|
721
|
+
"type": "number",
|
|
722
|
+
"default": 0
|
|
723
|
+
},
|
|
724
|
+
"publishedAt": {
|
|
725
|
+
"name": "发布时间",
|
|
726
|
+
"type": "number",
|
|
727
|
+
"default": 0,
|
|
728
|
+
"index": true
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
---
|
|
734
|
+
|
|
735
|
+
## 最佳实践
|
|
736
|
+
|
|
737
|
+
1. **合理选择字段类型**
|
|
738
|
+
- 短文本(< 65535 字符)使用 `string`
|
|
739
|
+
- 长文本(文章、JSON 大对象)使用 `text`
|
|
740
|
+
- ID、时间戳、数值使用 `number`
|
|
741
|
+
|
|
742
|
+
2. **设置合理的长度限制**
|
|
743
|
+
- 用户名:30 ~ 50
|
|
744
|
+
- 邮箱:100 ~ 150
|
|
745
|
+
- URL/路径:500 ~ 1000
|
|
746
|
+
- 标题:100 ~ 200
|
|
747
|
+
|
|
748
|
+
3. **善用索引**
|
|
749
|
+
- 经常作为查询条件的字段添加 `index: true`
|
|
750
|
+
- 需要唯一性的字段使用 `unique: true`
|
|
751
|
+
- 避免对长文本字段创建索引
|
|
752
|
+
|
|
753
|
+
4. **使用正则验证**
|
|
754
|
+
- 用户输入使用内置正则(`@email`、`@phone` 等)
|
|
755
|
+
- 枚举值使用自定义正则(`^(a|b|c)$`)
|
|
756
|
+
|
|
757
|
+
5. **默认值设计**
|
|
758
|
+
- 数值字段默认为 `0`
|
|
759
|
+
- 字符串字段默认为 `''`
|
|
760
|
+
- 时间戳字段默认为 `0`
|
|
761
|
+
|
|
762
|
+
6. **文件组织**
|
|
763
|
+
- 一张表对应一个 JSON 文件
|
|
764
|
+
- 文件名使用小驼峰
|
|
765
|
+
- 相关表放在同一目录
|