@pylonts/dsl 1.1.4 → 1.1.6
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/dist/controller.d.ts +27 -0
- package/dist/controller.js +11 -0
- package/dist/convert.d.ts +18 -6
- package/dist/convert.js +12 -2
- package/dist/curd.d.ts +0 -2
- package/dist/curd.js +7 -0
- package/dist/dao.d.ts +111 -2
- package/dist/dao.js +28 -2
- package/dist/db.js +3 -0
- package/dist/dsl.d.ts +24 -1
- package/dist/dsl.js +16 -0
- package/dist/dto.d.ts +6 -2
- package/dist/dto.js +20 -9
- package/dist/endpoint.d.ts +15 -0
- package/dist/endpoint.js +3 -0
- package/dist/exception.d.ts +14 -0
- package/dist/exception.js +9 -0
- package/dist/field-rule.d.ts +20 -0
- package/dist/field-rule.js +19 -0
- package/dist/flow.d.ts +24 -2
- package/dist/flow.js +16 -4
- package/dist/index.d.ts +7 -0
- package/dist/index.js +9 -0
- package/dist/mermaid-driver.js +32 -3
- package/dist/method.d.ts +11 -0
- package/dist/method.js +3 -0
- package/dist/mysql-driver.js +8 -1
- package/dist/provider.d.ts +6 -11
- package/dist/provider.js +2 -2
- package/dist/service.d.ts +16 -6
- package/dist/service.js +13 -2
- package/dist/third-service.d.ts +75 -0
- package/dist/third-service.js +96 -0
- package/dist/typebox-driver.d.ts +6 -0
- package/dist/typebox-driver.js +76 -14
- package/dist/utils.d.ts +25 -10
- package/dist/utils.js +28 -11
- package/docs/curd.md +110 -110
- package/docs/dto.md +9 -2
- package/docs/table.md +135 -135
- package/docs/third-service.md +122 -0
- package/package.json +4 -1
- package/src/action.ts +10 -10
- package/src/asset.ts +62 -62
- package/src/bases.ts +29 -29
- package/src/component.ts +21 -21
- package/src/controller.ts +40 -0
- package/src/convert.ts +34 -7
- package/src/curd.ts +7 -2
- package/src/dao.ts +162 -3
- package/src/db.ts +5 -0
- package/src/dsl.ts +49 -1
- package/src/dto.ts +25 -9
- package/src/endpoint.ts +18 -0
- package/src/event.ts +12 -12
- package/src/exception.ts +28 -0
- package/src/field-rule.ts +47 -0
- package/src/flow.ts +143 -103
- package/src/index.ts +11 -1
- package/src/mermaid-driver.ts +31 -3
- package/src/method.ts +20 -0
- package/src/mock.ts +12 -12
- package/src/mysql-driver.ts +8 -2
- package/src/navigation.ts +28 -28
- package/src/page-def.ts +79 -79
- package/src/page-flow.ts +153 -153
- package/src/page.ts +76 -76
- package/src/popup.ts +25 -25
- package/src/project.ts +97 -97
- package/src/provider.ts +7 -12
- package/src/ref.ts +18 -18
- package/src/route.ts +11 -11
- package/src/service.ts +28 -6
- package/src/third-service.ts +186 -0
- package/src/typebox-driver.ts +264 -192
- package/src/utils.ts +54 -17
- package/dist/check-inheritance.d.ts +0 -9
- package/dist/check-inheritance.js +0 -58
package/docs/curd.md
CHANGED
|
@@ -1,111 +1,111 @@
|
|
|
1
|
-
# 管理端 CRUD 页面标准(CurdSchema)
|
|
2
|
-
|
|
3
|
-
CurdSchema 是**管理端专用**(`FrontAppSchema.type === 'admin'`)的 CRUD 页面标准:绑定一张实体表 + 一个管理端 app,描述列表页与新增/编辑/详情动作页生成所需的全部页面语义。一条 CurdSchema = 列表页(+ 动作页)的生成规格。
|
|
4
|
-
|
|
5
|
-
页面定义文件按实体组织:`{project}/pages/{entity}.curd.ts`(与 `schema/*.table.ts` 平级)。
|
|
6
|
-
|
|
7
|
-
**CurdSchema 只依赖 table schema(`Field` 实例),不挂钩 DTO(`DtoMessage`)**——DTO 由生成器按标准从 `columns` 推导。
|
|
8
|
-
|
|
9
|
-
## 定义
|
|
10
|
-
|
|
11
|
-
```ts
|
|
12
|
-
import { defineCurd } from '@pylonts/dsl';
|
|
13
|
-
|
|
14
|
-
export const orderCurd = defineCurd('order-curd', {
|
|
15
|
-
description: '订单管理',
|
|
16
|
-
app: webAdmin, // 所属管理端(project.config.ts 的 FrontAppSchema 共享实例)
|
|
17
|
-
table: order, // 绑定实体表(共享实例)
|
|
18
|
-
title: '订单管理',
|
|
19
|
-
section: '订单管理', // 必填:sidebar 分组名
|
|
20
|
-
actions: [defineAction('EXPORT', '导出订单')], // 额外操作按钮
|
|
21
|
-
actionPages: {
|
|
22
|
-
add: { mode: 'modal', columns: [order.columns.order_no, order.columns.mer_id] },
|
|
23
|
-
update: { mode: 'modal', columns: [order.columns.id, order.columns.order_no] },
|
|
24
|
-
detail: { mode: 'route', columns: [order.columns.id, order.columns.order_no, order.columns.amount] },
|
|
25
|
-
},
|
|
26
|
-
list: {
|
|
27
|
-
columns: [order.columns.id, order.columns.order_no, merchant.columns.name], // 可跨表
|
|
28
|
-
keyword: { columns: [order.columns.order_no] }, // 模糊搜索(本表字段)
|
|
29
|
-
orderBy: { column: order.columns.id, direction: 'desc' },
|
|
30
|
-
searchFields: [{ field: order.columns.mer_id }, { field: order.columns.order_no, op: 'like' }],
|
|
31
|
-
columnTitles: { order_no: '订单号', name: '商户名称' }, // Field.name → 文案
|
|
32
|
-
},
|
|
33
|
-
});
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## 字段
|
|
37
|
-
|
|
38
|
-
| 字段 | 类型 | 说明 |
|
|
39
|
-
|---|---|---|
|
|
40
|
-
| `app` | `FrontAppSchema` | 所属管理端(共享实例,`type` 必须为 `'admin'`) |
|
|
41
|
-
| `table` | `TableSchema` | 绑定实体表(共享实例) |
|
|
42
|
-
| `title` | `string` | 列表页中文标题 |
|
|
43
|
-
| `section` | `string` | **必填**:sidebar 分组名(`pylonts gen curd` 据此生成路由注册的 `section` 字段) |
|
|
44
|
-
| `actions?` | `ActionSchema[]` | 页面额外可执行动作(标准 CRUD 之外,如导出、审核) |
|
|
45
|
-
| `actionPages?` | `{ add? / update? / detail? }` | 动作页:`{ mode: 'modal' \| 'route'; columns: Field[] }` |
|
|
46
|
-
| `list` | `CurdListConfig` | 列表页配置(必填) |
|
|
47
|
-
|
|
48
|
-
### ActionPage
|
|
49
|
-
|
|
50
|
-
| 字段 | 类型 | 说明 |
|
|
51
|
-
|---|---|---|
|
|
52
|
-
| `mode` | `'modal' \| 'route'` | 弹窗或独立路由 |
|
|
53
|
-
| `columns` | `Field[]` | 该页面渲染的字段,**必填非空**——前端要显示的字段必须全部显式列出 |
|
|
54
|
-
|
|
55
|
-
### CurdListConfig
|
|
56
|
-
|
|
57
|
-
| 字段 | 类型 | 说明 |
|
|
58
|
-
|---|---|---|
|
|
59
|
-
| `columns` | `Field[]` | 列表列,**必填非空**——前端要显示的字段必须全部显式列出;可含跨表字段(见下) |
|
|
60
|
-
| `keyword?` | `{ columns: Field[] }` | 模糊搜索,columns 必须是**本表字段实例** |
|
|
61
|
-
| `orderBy` | `{ column: Field; direction: 'asc' \| 'desc' }` | 默认排序,**必填**,column 与 direction 都必填;column 必须是**本表字段实例** |
|
|
62
|
-
| `searchFields?` | `{ field: Field; op?: Operator }[]` | 搜索条件字段,op 默认 `'eq'`,可选 `eq/gt/gte/lt/lte/like/ne` |
|
|
63
|
-
| `columnTitles?` | `Record<string, string>` | 列标题覆盖:`Field.name` → 中文文案 |
|
|
64
|
-
|
|
65
|
-
## 跨表字段
|
|
66
|
-
|
|
67
|
-
`columns` / `searchFields` 里的 `Field` 实例可指向**本表或其他表**的列——列表列与搜索条件因此可以显示关联表字段(如订单列表显示商户名称):
|
|
68
|
-
|
|
69
|
-
```ts
|
|
70
|
-
list: {
|
|
71
|
-
columns: [order.columns.id, order.columns.order_no, merchant.columns.name], // 跨表:字段指向 merchant.name
|
|
72
|
-
}
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## 默认与校验
|
|
76
|
-
|
|
77
|
-
- `list.columns` / `actionPages.*.columns` **必填非空**(不允许省略、不允许空数组)——前端显示什么必须显式定义
|
|
78
|
-
- `list.orderBy` **必填**,`column` 与 `direction` 都必填(规格:默认主键 desc 由定义方显式写出)
|
|
79
|
-
- 运行时校验(`defineCurd`,仿 `defineTable` 强校验风格):
|
|
80
|
-
- `app.type` 必须为 `'admin'`,否则抛错
|
|
81
|
-
- 所有 `columns` 非空,否则抛错
|
|
82
|
-
- `list.keyword.columns` / `list.orderBy.column` 必须属于 `table`,否则抛错
|
|
83
|
-
- `list.columns` / `list.searchFields` 允许跨表,**不校验归属**
|
|
84
|
-
- 生成时校验(curd 生成器,`DtoSchemaGen.add` / `update`):
|
|
85
|
-
- 表配置 `autoIncrement` 或 `generator`(主键由服务端生成)时,`actionPages.add.columns` **不允许包含主键字段**,否则抛错——AddRequest 不携带服务端生成的主键
|
|
86
|
-
- `actionPages.update.columns` **必须包含主键字段**,否则抛错——UpdateRequest 靠主键定位记录
|
|
87
|
-
|
|
88
|
-
## DTO 推导(生成器约定)
|
|
89
|
-
|
|
90
|
-
DTO 由 curd 生成器从 `CurdSchema` 按标准命名推导,页面语义不持有 DTO 实例:
|
|
91
|
-
|
|
92
|
-
| DTO | 命名 | 字段来源 |
|
|
93
|
-
|---|---|---|
|
|
94
|
-
| Row | `{Pascal}Row` | `list.columns` |
|
|
95
|
-
| AddRequest | `{Pascal}AddRequest` | `actionPages.add.columns` |
|
|
96
|
-
| UpdateRequest | `{Pascal}UpdateRequest` | `actionPages.update.columns` |
|
|
97
|
-
| DetailRequest | `{Pascal}DetailRequest` | 主键 |
|
|
98
|
-
| DetailResponse | `{Pascal}DetailResponse` | `actionPages.detail.columns` |
|
|
99
|
-
|
|
100
|
-
## 与旧 PageConfig 的差异
|
|
101
|
-
|
|
102
|
-
| PageConfig(旧方案,已废弃) | CurdSchema |
|
|
103
|
-
|---|---|
|
|
104
|
-
| `module: string` | 由 `app` 推导(后端模块 == app 1:1) |
|
|
105
|
-
| `schema: 'bd'` 字符串 | `table: TableSchema` 实例(类型安全) |
|
|
106
|
-
| `operations: { label, action }` | `actions: ActionSchema[]` |
|
|
107
|
-
| `detail.mode` 单例 | `actionPages.detail.mode` |
|
|
108
|
-
| `forms.add / forms.update` | `actionPages.add / actionPages.update` |
|
|
109
|
-
| `keyword` / `orderBy` / `columnTitles` | `list.keyword` / `list.orderBy` / `list.columnTitles`(列改字段实例引用) |
|
|
110
|
-
| `naming` | 去掉(DTO 命名是生成器约定,非页面语义) |
|
|
1
|
+
# 管理端 CRUD 页面标准(CurdSchema)
|
|
2
|
+
|
|
3
|
+
CurdSchema 是**管理端专用**(`FrontAppSchema.type === 'admin'`)的 CRUD 页面标准:绑定一张实体表 + 一个管理端 app,描述列表页与新增/编辑/详情动作页生成所需的全部页面语义。一条 CurdSchema = 列表页(+ 动作页)的生成规格。
|
|
4
|
+
|
|
5
|
+
页面定义文件按实体组织:`{project}/pages/{entity}.curd.ts`(与 `schema/*.table.ts` 平级)。
|
|
6
|
+
|
|
7
|
+
**CurdSchema 只依赖 table schema(`Field` 实例),不挂钩 DTO(`DtoMessage`)**——DTO 由生成器按标准从 `columns` 推导。
|
|
8
|
+
|
|
9
|
+
## 定义
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { defineCurd } from '@pylonts/dsl';
|
|
13
|
+
|
|
14
|
+
export const orderCurd = defineCurd('order-curd', {
|
|
15
|
+
description: '订单管理',
|
|
16
|
+
app: webAdmin, // 所属管理端(project.config.ts 的 FrontAppSchema 共享实例)
|
|
17
|
+
table: order, // 绑定实体表(共享实例)
|
|
18
|
+
title: '订单管理',
|
|
19
|
+
section: '订单管理', // 必填:sidebar 分组名
|
|
20
|
+
actions: [defineAction('EXPORT', '导出订单')], // 额外操作按钮
|
|
21
|
+
actionPages: {
|
|
22
|
+
add: { mode: 'modal', columns: [order.columns.order_no, order.columns.mer_id] },
|
|
23
|
+
update: { mode: 'modal', columns: [order.columns.id, order.columns.order_no] },
|
|
24
|
+
detail: { mode: 'route', columns: [order.columns.id, order.columns.order_no, order.columns.amount] },
|
|
25
|
+
},
|
|
26
|
+
list: {
|
|
27
|
+
columns: [order.columns.id, order.columns.order_no, merchant.columns.name], // 可跨表
|
|
28
|
+
keyword: { columns: [order.columns.order_no] }, // 模糊搜索(本表字段)
|
|
29
|
+
orderBy: { column: order.columns.id, direction: 'desc' },
|
|
30
|
+
searchFields: [{ field: order.columns.mer_id }, { field: order.columns.order_no, op: 'like' }],
|
|
31
|
+
columnTitles: { order_no: '订单号', name: '商户名称' }, // Field.name → 文案
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 字段
|
|
37
|
+
|
|
38
|
+
| 字段 | 类型 | 说明 |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| `app` | `FrontAppSchema` | 所属管理端(共享实例,`type` 必须为 `'admin'`) |
|
|
41
|
+
| `table` | `TableSchema` | 绑定实体表(共享实例) |
|
|
42
|
+
| `title` | `string` | 列表页中文标题 |
|
|
43
|
+
| `section` | `string` | **必填**:sidebar 分组名(`pylonts gen curd` 据此生成路由注册的 `section` 字段) |
|
|
44
|
+
| `actions?` | `ActionSchema[]` | 页面额外可执行动作(标准 CRUD 之外,如导出、审核) |
|
|
45
|
+
| `actionPages?` | `{ add? / update? / detail? }` | 动作页:`{ mode: 'modal' \| 'route'; columns: Field[] }` |
|
|
46
|
+
| `list` | `CurdListConfig` | 列表页配置(必填) |
|
|
47
|
+
|
|
48
|
+
### ActionPage
|
|
49
|
+
|
|
50
|
+
| 字段 | 类型 | 说明 |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| `mode` | `'modal' \| 'route'` | 弹窗或独立路由 |
|
|
53
|
+
| `columns` | `Field[]` | 该页面渲染的字段,**必填非空**——前端要显示的字段必须全部显式列出 |
|
|
54
|
+
|
|
55
|
+
### CurdListConfig
|
|
56
|
+
|
|
57
|
+
| 字段 | 类型 | 说明 |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| `columns` | `Field[]` | 列表列,**必填非空**——前端要显示的字段必须全部显式列出;可含跨表字段(见下) |
|
|
60
|
+
| `keyword?` | `{ columns: Field[] }` | 模糊搜索,columns 必须是**本表字段实例** |
|
|
61
|
+
| `orderBy` | `{ column: Field; direction: 'asc' \| 'desc' }` | 默认排序,**必填**,column 与 direction 都必填;column 必须是**本表字段实例** |
|
|
62
|
+
| `searchFields?` | `{ field: Field; op?: Operator }[]` | 搜索条件字段,op 默认 `'eq'`,可选 `eq/gt/gte/lt/lte/like/ne` |
|
|
63
|
+
| `columnTitles?` | `Record<string, string>` | 列标题覆盖:`Field.name` → 中文文案 |
|
|
64
|
+
|
|
65
|
+
## 跨表字段
|
|
66
|
+
|
|
67
|
+
`columns` / `searchFields` 里的 `Field` 实例可指向**本表或其他表**的列——列表列与搜索条件因此可以显示关联表字段(如订单列表显示商户名称):
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
list: {
|
|
71
|
+
columns: [order.columns.id, order.columns.order_no, merchant.columns.name], // 跨表:字段指向 merchant.name
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 默认与校验
|
|
76
|
+
|
|
77
|
+
- `list.columns` / `actionPages.*.columns` **必填非空**(不允许省略、不允许空数组)——前端显示什么必须显式定义
|
|
78
|
+
- `list.orderBy` **必填**,`column` 与 `direction` 都必填(规格:默认主键 desc 由定义方显式写出)
|
|
79
|
+
- 运行时校验(`defineCurd`,仿 `defineTable` 强校验风格):
|
|
80
|
+
- `app.type` 必须为 `'admin'`,否则抛错
|
|
81
|
+
- 所有 `columns` 非空,否则抛错
|
|
82
|
+
- `list.keyword.columns` / `list.orderBy.column` 必须属于 `table`,否则抛错
|
|
83
|
+
- `list.columns` / `list.searchFields` 允许跨表,**不校验归属**
|
|
84
|
+
- 生成时校验(curd 生成器,`DtoSchemaGen.add` / `update`):
|
|
85
|
+
- 表配置 `autoIncrement` 或 `generator`(主键由服务端生成)时,`actionPages.add.columns` **不允许包含主键字段**,否则抛错——AddRequest 不携带服务端生成的主键
|
|
86
|
+
- `actionPages.update.columns` **必须包含主键字段**,否则抛错——UpdateRequest 靠主键定位记录
|
|
87
|
+
|
|
88
|
+
## DTO 推导(生成器约定)
|
|
89
|
+
|
|
90
|
+
DTO 由 curd 生成器从 `CurdSchema` 按标准命名推导,页面语义不持有 DTO 实例:
|
|
91
|
+
|
|
92
|
+
| DTO | 命名 | 字段来源 |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| Row | `{Pascal}Row` | `list.columns` |
|
|
95
|
+
| AddRequest | `{Pascal}AddRequest` | `actionPages.add.columns` |
|
|
96
|
+
| UpdateRequest | `{Pascal}UpdateRequest` | `actionPages.update.columns` |
|
|
97
|
+
| DetailRequest | `{Pascal}DetailRequest` | 主键 |
|
|
98
|
+
| DetailResponse | `{Pascal}DetailResponse` | `actionPages.detail.columns` |
|
|
99
|
+
|
|
100
|
+
## 与旧 PageConfig 的差异
|
|
101
|
+
|
|
102
|
+
| PageConfig(旧方案,已废弃) | CurdSchema |
|
|
103
|
+
|---|---|
|
|
104
|
+
| `module: string` | 由 `app` 推导(后端模块 == app 1:1) |
|
|
105
|
+
| `schema: 'bd'` 字符串 | `table: TableSchema` 实例(类型安全) |
|
|
106
|
+
| `operations: { label, action }` | `actions: ActionSchema[]` |
|
|
107
|
+
| `detail.mode` 单例 | `actionPages.detail.mode` |
|
|
108
|
+
| `forms.add / forms.update` | `actionPages.add / actionPages.update` |
|
|
109
|
+
| `keyword` / `orderBy` / `columnTitles` | `list.keyword` / `list.orderBy` / `list.columnTitles`(列改字段实例引用) |
|
|
110
|
+
| `naming` | 去掉(DTO 命名是生成器约定,非页面语义) |
|
|
111
111
|
| DTO 引用(`request` / `fields` / `DtoFields`) | 去掉(DTO 由生成器推导,页面只依赖 table) |
|
package/docs/dto.md
CHANGED
|
@@ -9,7 +9,9 @@ DTO 描述接口出入参。方向决定可选性规则,由各方向工厂设
|
|
|
9
9
|
| `buildQuery` | query | 全部可选 |
|
|
10
10
|
| `buildPk` | pk | 主键字段必填,其他字段可选 |
|
|
11
11
|
|
|
12
|
-
##
|
|
12
|
+
## 从字段集合投影
|
|
13
|
+
|
|
14
|
+
`from(source, fields)` 接受两类字段集合源:**表**(`TableSchema`)或**第三方方法消息**(`ThirdMethodSchema`,见 [third-service.md](./third-service.md)),投影字段包装为 DTO 字段,字段实例与源共享,`name/schema` 保持指向源。
|
|
13
15
|
|
|
14
16
|
```ts
|
|
15
17
|
import { buildInput, buildQuery, dtoField, from } from '@pylonts/dsl';
|
|
@@ -28,9 +30,14 @@ buildQuery('OrderPageQuery', {
|
|
|
28
30
|
|
|
29
31
|
// 主键:按 id 取详情
|
|
30
32
|
buildPk('OrderDetailRequest', from(order, [order.columns.id]));
|
|
33
|
+
|
|
34
|
+
// 转发:透传第三方方法消息(wire-format 字段名保持协议原样,不转 camelCase)
|
|
35
|
+
buildOutput('BalanceResult', from(queryBalance.results, [queryBalance.results.fields.balance]));
|
|
31
36
|
```
|
|
32
37
|
|
|
33
|
-
|
|
38
|
+
- **表源**:DTO 字段名转 camelCase(`mer_id` → `merId`),与 DB 列名(snake_case)分离。
|
|
39
|
+
- **第三方方法源**:字段名即线格式协议名(`out_trade_no`、`appId`),保持不变。
|
|
40
|
+
- `from()` 本身不做任何可选性推断——推断在各方向工厂,且按字段的 schema 判断:共享实体列按列规则推断(Rule A),wire 自有字段保留声明值。
|
|
34
41
|
|
|
35
42
|
## 独立字段
|
|
36
43
|
|
package/docs/table.md
CHANGED
|
@@ -1,136 +1,136 @@
|
|
|
1
|
-
# 定义表 (TableSchema)
|
|
2
|
-
|
|
3
|
-
## 字段类型
|
|
4
|
-
|
|
5
|
-
| 构建器 | 类型 | jsType | MySQL 列 | 备注 |
|
|
6
|
-
|---|---|---|---|---|
|
|
7
|
-
| `stringField` | string | string | VARCHAR | 必填 `maxLength` |
|
|
8
|
-
| `textField` | text | string | TEXT | |
|
|
9
|
-
| `intField` | integer | number | INT | |
|
|
10
|
-
| `bigintField` | bigint | string | BIGINT | 传输层走 string 保精度 |
|
|
11
|
-
| `decimalField` | decimal | string | DECIMAL | 必填 `precision` / `scale`,传输层走 string 避免浮点误差 |
|
|
12
|
-
| `booleanField` | boolean | boolean | TINYINT(1) | |
|
|
13
|
-
| `dateField` | date | Date | DATE | |
|
|
14
|
-
| `timeField` | time | string | TIME | |
|
|
15
|
-
| `datetimeField` | datetime | Date | DATETIME | |
|
|
16
|
-
| `enumField` | enum | string / number | VARCHAR(20) / TINYINT | 引用共享枚举定义,见 [enum.md](./enum.md) |
|
|
17
|
-
| `jsonField` | json | object | JSON | |
|
|
18
|
-
|
|
19
|
-
通用扩展属性(构建器参数):`label`(中文标签)、`description`、`optional`、`readOnly`、`default`。
|
|
20
|
-
|
|
21
|
-
**`optional` 默认语义(MySQL 惯例)**:不写 `optional` 或写 `optional: true` → 列可空,DDL 不渲染 `NOT NULL`;写 `optional: false` → 列必填(`NOT NULL`)。业务上必填的列必须显式声明。
|
|
22
|
-
|
|
23
|
-
## 定义表
|
|
24
|
-
|
|
25
|
-
```ts
|
|
26
|
-
import { bigintField, defineTable, decimalField, stringField } from '@pylonts/dsl';
|
|
27
|
-
|
|
28
|
-
const id = bigintField({ readOnly: true, label: '主键' });
|
|
29
|
-
|
|
30
|
-
export const order = defineTable('order', {
|
|
31
|
-
description: '订单',
|
|
32
|
-
autoIncrement: id,
|
|
33
|
-
columns: {
|
|
34
|
-
id,
|
|
35
|
-
order_no: stringField({ label: '订单号', maxLength: 32, optional: false }),
|
|
36
|
-
amount: decimalField({ precision: 18, scale: 2, label: '金额', optional: false }),
|
|
37
|
-
},
|
|
38
|
-
primaryKey: id,
|
|
39
|
-
});
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
- 字段名从 map key 反写,`columns` 里的 key 就是列名。
|
|
43
|
-
- 字段实例不可跨表复用(复用同一字段实例会抛错),枚举除外。
|
|
44
|
-
|
|
45
|
-
## 主键生成策略
|
|
46
|
-
|
|
47
|
-
`autoIncrement` 与 `generator` 互斥,二者选一:
|
|
48
|
-
|
|
49
|
-
| 属性 | 含义 | 例子 |
|
|
50
|
-
|---|---|---|
|
|
51
|
-
| `autoIncrement` | 引用自增主键字段,数据库负责生成值(MySQL `AUTO_INCREMENT`)。设了该属性的字段在 DTO 中自动标记为 optional(写入时不需要传) | `autoIncrement: id` |
|
|
52
|
-
| `generator` | 主键由业务侧生成(非数据库自增),告诉下游工具用哪个 ID 生成器 | `generator: 'snowflake'` |
|
|
53
|
-
|
|
54
|
-
```ts
|
|
55
|
-
// 数据库自增主键
|
|
56
|
-
export const t1 = defineTable('t1', {
|
|
57
|
-
autoIncrement: id,
|
|
58
|
-
columns: { id: bigintField({ readOnly: true, label: '主键' }) },
|
|
59
|
-
primaryKey: id,
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// 业务生成主键(snowflake)
|
|
63
|
-
export const t2 = defineTable('t2', {
|
|
64
|
-
generator: 'snowflake',
|
|
65
|
-
columns: { id: bigintField({ readOnly: true, label: '主键' }) },
|
|
66
|
-
primaryKey: id,
|
|
67
|
-
});
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## 索引
|
|
71
|
-
|
|
72
|
-
```ts
|
|
73
|
-
indexes: [
|
|
74
|
-
{ name: 'uk_uuid', columns: c_uuid, unique: true },
|
|
75
|
-
{ columns: [c_enum, c_date] }, // 名字缺省时 = 字段名 join '_'
|
|
76
|
-
],
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
## 外键与短语检查链
|
|
80
|
-
|
|
81
|
-
短语统一定义在 `schema/_dictionary.ts`(见 [dictionary.md](./dictionary.md)),表文件从那里 import:
|
|
82
|
-
|
|
83
|
-
```ts
|
|
84
|
-
// schema/_dictionary.ts
|
|
85
|
-
import { defineEntityPhrase } from '@pylonts/dsl';
|
|
86
|
-
|
|
87
|
-
export const bd = defineEntityPhrase({ name: 'bd', label: 'BD推广员', description: '线下拓展商户的推广人员' });
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
```ts
|
|
91
|
-
// schema/bd.table.ts
|
|
92
|
-
import { bigintField, defineTable } from '@pylonts/dsl';
|
|
93
|
-
import { bd as bdPhrase } from './_dictionary';
|
|
94
|
-
|
|
95
|
-
const bdId = bigintField({ readOnly: true, label: 'BD ID' });
|
|
96
|
-
|
|
97
|
-
export const bd = defineTable('bd', {
|
|
98
|
-
description: 'BD',
|
|
99
|
-
phrase: bdPhrase, // 链接词典条目:本表归属的实体
|
|
100
|
-
columns: { id: bdId },
|
|
101
|
-
primaryKey: bdId,
|
|
102
|
-
});
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
```ts
|
|
106
|
-
// schema/audit.table.ts
|
|
107
|
-
import { bigintField, defineTable } from '@pylonts/dsl';
|
|
108
|
-
import { bd } from './bd.table';
|
|
109
|
-
|
|
110
|
-
const auditBdId = bigintField({ label: 'BD' });
|
|
111
|
-
|
|
112
|
-
export const audit = defineTable('audit', {
|
|
113
|
-
description: '审核',
|
|
114
|
-
columns: {
|
|
115
|
-
bd_id: auditBdId, // 列名必须 = 短语 + '_' + 被引用字段名
|
|
116
|
-
},
|
|
117
|
-
foreignKeys: {
|
|
118
|
-
fk_audit_bd: { columns: auditBdId, references: bd.columns.id },
|
|
119
|
-
},
|
|
120
|
-
});
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
**规则(defineTable 时强制检查)**:外键字段名必须等于 `被引用表.phrase.name + "_" + 被引用字段名`。即引用 `bd.id` 的字段必须叫 `bd_id`——`bd` 来自词典(权威短语),`id` 是 `bd` 表主键。
|
|
124
|
-
|
|
125
|
-
**短语口径**:`defineEntityPhrase`(实体短语)解释的**就是短语本身**——`name` 即短语词干(如 `mer`),不是实体全名。引用 `merchant` 实体的字段用短语 `mer`(`mer_id`),**不用长语**(`merchant_id`)。短语要短(mer / bd / amt 三字母左右),语义由 `label`/`description` 解释。`TableSchema.phrase` 只接受实体短语(`defineEntityPhrase` 产物);业务短语(`defineBusinessPhrase`)用于字段命名后缀校验,见 [field-check.md](../../lint/docs/field-check.md)。
|
|
126
|
-
|
|
127
|
-
- 被引用表未定义 `phrase` → 抛错(检查链要求每个被引用表都有短语)。
|
|
128
|
-
- 命名不匹配 → 抛错并提示期望名,例如:
|
|
129
|
-
`foreign key bad: field must be named bd_id (phrase bd + id), got merchant_id`
|
|
130
|
-
- 关联表不需要 `phrase`。
|
|
131
|
-
|
|
132
|
-
> **外键是逻辑作用**:`foreignKeys` 用于定义期命名强校验与关系表达,**DDL 默认不渲染物理 FOREIGN KEY 约束**(`pylonts gen sql init` 不传 `generateForeignKeys`)。数据完整性由 Service/DAO 层保证;如需物理约束,调用 `buildCreateTableSql(schema, { generateForeignKeys: true })`。
|
|
133
|
-
|
|
134
|
-
## 生成 SQL
|
|
135
|
-
|
|
1
|
+
# 定义表 (TableSchema)
|
|
2
|
+
|
|
3
|
+
## 字段类型
|
|
4
|
+
|
|
5
|
+
| 构建器 | 类型 | jsType | MySQL 列 | 备注 |
|
|
6
|
+
|---|---|---|---|---|
|
|
7
|
+
| `stringField` | string | string | VARCHAR | 必填 `maxLength` |
|
|
8
|
+
| `textField` | text | string | TEXT | |
|
|
9
|
+
| `intField` | integer | number | INT | |
|
|
10
|
+
| `bigintField` | bigint | string | BIGINT | 传输层走 string 保精度 |
|
|
11
|
+
| `decimalField` | decimal | string | DECIMAL | 必填 `precision` / `scale`,传输层走 string 避免浮点误差 |
|
|
12
|
+
| `booleanField` | boolean | boolean | TINYINT(1) | |
|
|
13
|
+
| `dateField` | date | Date | DATE | |
|
|
14
|
+
| `timeField` | time | string | TIME | |
|
|
15
|
+
| `datetimeField` | datetime | Date | DATETIME | |
|
|
16
|
+
| `enumField` | enum | string / number | VARCHAR(20) / TINYINT | 引用共享枚举定义,见 [enum.md](./enum.md) |
|
|
17
|
+
| `jsonField` | json | object | JSON | |
|
|
18
|
+
|
|
19
|
+
通用扩展属性(构建器参数):`label`(中文标签)、`description`、`optional`、`readOnly`、`default`。
|
|
20
|
+
|
|
21
|
+
**`optional` 默认语义(MySQL 惯例)**:不写 `optional` 或写 `optional: true` → 列可空,DDL 不渲染 `NOT NULL`;写 `optional: false` → 列必填(`NOT NULL`)。业务上必填的列必须显式声明。
|
|
22
|
+
|
|
23
|
+
## 定义表
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { bigintField, defineTable, decimalField, stringField } from '@pylonts/dsl';
|
|
27
|
+
|
|
28
|
+
const id = bigintField({ readOnly: true, label: '主键' });
|
|
29
|
+
|
|
30
|
+
export const order = defineTable('order', {
|
|
31
|
+
description: '订单',
|
|
32
|
+
autoIncrement: id,
|
|
33
|
+
columns: {
|
|
34
|
+
id,
|
|
35
|
+
order_no: stringField({ label: '订单号', maxLength: 32, optional: false }),
|
|
36
|
+
amount: decimalField({ precision: 18, scale: 2, label: '金额', optional: false }),
|
|
37
|
+
},
|
|
38
|
+
primaryKey: id,
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- 字段名从 map key 反写,`columns` 里的 key 就是列名。
|
|
43
|
+
- 字段实例不可跨表复用(复用同一字段实例会抛错),枚举除外。
|
|
44
|
+
|
|
45
|
+
## 主键生成策略
|
|
46
|
+
|
|
47
|
+
`autoIncrement` 与 `generator` 互斥,二者选一:
|
|
48
|
+
|
|
49
|
+
| 属性 | 含义 | 例子 |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| `autoIncrement` | 引用自增主键字段,数据库负责生成值(MySQL `AUTO_INCREMENT`)。设了该属性的字段在 DTO 中自动标记为 optional(写入时不需要传) | `autoIncrement: id` |
|
|
52
|
+
| `generator` | 主键由业务侧生成(非数据库自增),告诉下游工具用哪个 ID 生成器 | `generator: 'snowflake'` |
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
// 数据库自增主键
|
|
56
|
+
export const t1 = defineTable('t1', {
|
|
57
|
+
autoIncrement: id,
|
|
58
|
+
columns: { id: bigintField({ readOnly: true, label: '主键' }) },
|
|
59
|
+
primaryKey: id,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// 业务生成主键(snowflake)
|
|
63
|
+
export const t2 = defineTable('t2', {
|
|
64
|
+
generator: 'snowflake',
|
|
65
|
+
columns: { id: bigintField({ readOnly: true, label: '主键' }) },
|
|
66
|
+
primaryKey: id,
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 索引
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
indexes: [
|
|
74
|
+
{ name: 'uk_uuid', columns: c_uuid, unique: true },
|
|
75
|
+
{ columns: [c_enum, c_date] }, // 名字缺省时 = 字段名 join '_'
|
|
76
|
+
],
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## 外键与短语检查链
|
|
80
|
+
|
|
81
|
+
短语统一定义在 `schema/_dictionary.ts`(见 [dictionary.md](./dictionary.md)),表文件从那里 import:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
// schema/_dictionary.ts
|
|
85
|
+
import { defineEntityPhrase } from '@pylonts/dsl';
|
|
86
|
+
|
|
87
|
+
export const bd = defineEntityPhrase({ name: 'bd', label: 'BD推广员', description: '线下拓展商户的推广人员' });
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
// schema/bd.table.ts
|
|
92
|
+
import { bigintField, defineTable } from '@pylonts/dsl';
|
|
93
|
+
import { bd as bdPhrase } from './_dictionary';
|
|
94
|
+
|
|
95
|
+
const bdId = bigintField({ readOnly: true, label: 'BD ID' });
|
|
96
|
+
|
|
97
|
+
export const bd = defineTable('bd', {
|
|
98
|
+
description: 'BD',
|
|
99
|
+
phrase: bdPhrase, // 链接词典条目:本表归属的实体
|
|
100
|
+
columns: { id: bdId },
|
|
101
|
+
primaryKey: bdId,
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
// schema/audit.table.ts
|
|
107
|
+
import { bigintField, defineTable } from '@pylonts/dsl';
|
|
108
|
+
import { bd } from './bd.table';
|
|
109
|
+
|
|
110
|
+
const auditBdId = bigintField({ label: 'BD' });
|
|
111
|
+
|
|
112
|
+
export const audit = defineTable('audit', {
|
|
113
|
+
description: '审核',
|
|
114
|
+
columns: {
|
|
115
|
+
bd_id: auditBdId, // 列名必须 = 短语 + '_' + 被引用字段名
|
|
116
|
+
},
|
|
117
|
+
foreignKeys: {
|
|
118
|
+
fk_audit_bd: { columns: auditBdId, references: bd.columns.id },
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**规则(defineTable 时强制检查)**:外键字段名必须等于 `被引用表.phrase.name + "_" + 被引用字段名`。即引用 `bd.id` 的字段必须叫 `bd_id`——`bd` 来自词典(权威短语),`id` 是 `bd` 表主键。
|
|
124
|
+
|
|
125
|
+
**短语口径**:`defineEntityPhrase`(实体短语)解释的**就是短语本身**——`name` 即短语词干(如 `mer`),不是实体全名。引用 `merchant` 实体的字段用短语 `mer`(`mer_id`),**不用长语**(`merchant_id`)。短语要短(mer / bd / amt 三字母左右),语义由 `label`/`description` 解释。`TableSchema.phrase` 只接受实体短语(`defineEntityPhrase` 产物);业务短语(`defineBusinessPhrase`)用于字段命名后缀校验,见 [field-check.md](../../lint/docs/field-check.md)。
|
|
126
|
+
|
|
127
|
+
- 被引用表未定义 `phrase` → 抛错(检查链要求每个被引用表都有短语)。
|
|
128
|
+
- 命名不匹配 → 抛错并提示期望名,例如:
|
|
129
|
+
`foreign key bad: field must be named bd_id (phrase bd + id), got merchant_id`
|
|
130
|
+
- 关联表不需要 `phrase`。
|
|
131
|
+
|
|
132
|
+
> **外键是逻辑作用**:`foreignKeys` 用于定义期命名强校验与关系表达,**DDL 默认不渲染物理 FOREIGN KEY 约束**(`pylonts gen sql init` 不传 `generateForeignKeys`)。数据完整性由 Service/DAO 层保证;如需物理约束,调用 `buildCreateTableSql(schema, { generateForeignKeys: true })`。
|
|
133
|
+
|
|
134
|
+
## 生成 SQL
|
|
135
|
+
|
|
136
136
|
见 [driver.md](./driver.md)。
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# 定义第三方服务(ThirdServiceSchema)
|
|
2
|
+
|
|
3
|
+
第三方服务适配器契约(如微信支付 tenpay、短信、文件存储)。`defineThirdService` 声明适配器类契约:构造函数配置 + 方法列表。
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { defineFieldRule, defineThirdService } from '@pylonts/dsl';
|
|
7
|
+
import { wx } from '../project.config';
|
|
8
|
+
|
|
9
|
+
const balance = intField({ optional: false, label: '余额(分)' });
|
|
10
|
+
|
|
11
|
+
// Rule = name + two named ends. One rule per semantic (defining fenYuan twice throws).
|
|
12
|
+
const fenYuan = defineFieldRule({
|
|
13
|
+
name: 'fenYuan',
|
|
14
|
+
ends: { fen: {}, yuan: {} },
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export const wxPayService = defineThirdService({
|
|
18
|
+
schema: wx,
|
|
19
|
+
name: 'WxPayService',
|
|
20
|
+
description: '微信支付服务(tenpay APIv2)',
|
|
21
|
+
methods: [
|
|
22
|
+
{
|
|
23
|
+
name: 'queryBalance',
|
|
24
|
+
args: { name: 'QueryBalanceArgs', fields: { openid: user.columns.openid } },
|
|
25
|
+
results: {
|
|
26
|
+
name: 'QueryBalanceResult',
|
|
27
|
+
fields: {
|
|
28
|
+
balance,
|
|
29
|
+
},
|
|
30
|
+
refs: [
|
|
31
|
+
{ field: balance, ref: order.columns.amount, convert: { rule: fenYuan, end: fenYuan.ends.fen } },
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## ThirdMethodSchema:方法的 args / results
|
|
40
|
+
|
|
41
|
+
每个方法的 `args` / `results` 各是一个 `ThirdMethodSchema`——**字段集合容器**,与 `TableSchema.columns` 同构,但字段是线格式(wire-format)字段。结构上它是 `DtoMessage` 的字段集合对应物。
|
|
42
|
+
|
|
43
|
+
| 属性 | 说明 |
|
|
44
|
+
|------|------|
|
|
45
|
+
| `name` | 消息名(如 `QueryBalanceResult`),生成产物的类型名 |
|
|
46
|
+
| `fields` | 线格式字段 map,key 即协议字段名(`out_trade_no`、`appId` 原样保留) |
|
|
47
|
+
| `refs` | 同事实变体链接(见下) |
|
|
48
|
+
| `schema` | 反向指针,指向所属 method(构建器写入) |
|
|
49
|
+
|
|
50
|
+
`defineThirdMethod` 写回自有字段的 `name/schema`(与 `defineTable` 同一惯例)。
|
|
51
|
+
|
|
52
|
+
## 字段与本地实体的关系
|
|
53
|
+
|
|
54
|
+
两个通道,按"同一事实"的表达方式选择:
|
|
55
|
+
|
|
56
|
+
### 同一概念且类型一致 → 共享实例
|
|
57
|
+
|
|
58
|
+
直接复用本地实体列实例,类型/语义/默认值自动跟随实体,DTO 投影继承全部语义(与 `from(table)` 完全同构):
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
args: {
|
|
62
|
+
name: 'QueryBalanceArgs',
|
|
63
|
+
fields: {
|
|
64
|
+
openid: user.columns.openid, // 共享实例,schema 仍指向 t_user
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
前提是线格式字段名与实体列名一致(协议恰好也叫 `openid`)。名字不同时(协议叫 `userId` 而列叫 `user_id`),不共享实例,走 refs。
|
|
70
|
+
|
|
71
|
+
### 同一概念但类型/格式不同(变体)→ 自有字段 + refs
|
|
72
|
+
|
|
73
|
+
声明自有线格式类型,再挂一条 `refs` 链接指向实体列。规则先定义一次:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
// 规则 = 名称 + 两端(具名 map)。同一语义全局只允许一条(重复定义抛错)。
|
|
77
|
+
const fenYuan = defineFieldRule({
|
|
78
|
+
name: 'fenYuan',
|
|
79
|
+
ends: { fen: {}, yuan: {} },
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const totalFee = intField({ optional: false, label: '金额(分)' });
|
|
83
|
+
|
|
84
|
+
fields: {
|
|
85
|
+
total_fee: totalFee,
|
|
86
|
+
},
|
|
87
|
+
refs: [
|
|
88
|
+
{
|
|
89
|
+
field: totalFee, // 本地定义(本消息的 wire 字段)
|
|
90
|
+
ref: order.columns.amount, // 其他定义(表列或其他消息字段)
|
|
91
|
+
convert: { rule: fenYuan, end: fenYuan.ends.fen },
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- `field`:本消息的 wire 字段实例(构建器校验必须是本消息字段)
|
|
97
|
+
- `ref`:其他 schema 的字段实例(表列或其他消息字段,构建器校验不得是本消息字段)
|
|
98
|
+
- `convert`(可选):绑定一条规则到这对字段——仅当两字段需要转化时声明,纯关联不需要
|
|
99
|
+
- `rule`:`FieldRuleSchema`——规则 = 名称 + 两端(如 `fenYuan` 的 `fen`/`yuan` 端)。加密/脱敏/换算统一为规则名维度,`defineFieldRule` 按名称查重,同一语义只声明一次
|
|
100
|
+
- `end`:`field` 所站的端——引用 `rule.ends.fen` / `rule.ends.yuan`(具名引用,无索引魔法;构建器按实例校验),`ref` 自动占另一端——不再重复声明 from/to
|
|
101
|
+
- 生成器将来为这对字段产出两个方向的函数(field 端→ref 端 与 ref 端→field 端)
|
|
102
|
+
|
|
103
|
+
## 嵌套字段
|
|
104
|
+
|
|
105
|
+
线格式字段支持递归嵌套,用 `arrayField` / `objectField`(Field 体系,非表列):
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
fields: {
|
|
109
|
+
payer_info: objectField({
|
|
110
|
+
properties: {
|
|
111
|
+
openid: stringField({ optional: false, maxLength: 64 }),
|
|
112
|
+
},
|
|
113
|
+
}),
|
|
114
|
+
coupons: arrayField({ items: intField() }),
|
|
115
|
+
},
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
表列不支持这两个类型(`buildCreateTableSql` 直接报错)。
|
|
119
|
+
|
|
120
|
+
## DTO 转发
|
|
121
|
+
|
|
122
|
+
DTO 通过 `from(thirdMethod, fields)` 投影第三方消息字段,构建转发引用(透传不复制)。线格式字段名保持协议原样,不做 camelCase。见 [dto.md](./dto.md#从字段集合投影)。
|