midway-fatcms 0.0.7 → 0.0.9

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.
Files changed (159) hide show
  1. package/.qoder/skills/midway-fatcms/01-quick-start.md +231 -0
  2. package/.qoder/skills/midway-fatcms/02-crud-quick.md +375 -0
  3. package/.qoder/skills/midway-fatcms/03-crud-sharding.md +489 -0
  4. package/.qoder/skills/midway-fatcms/04-condition-operators.md +93 -0
  5. package/.qoder/skills/midway-fatcms/05-configuration.md +290 -0
  6. package/.qoder/skills/midway-fatcms/06-builtin-functions.md +241 -0
  7. package/.qoder/skills/midway-fatcms/07-examples.md +504 -0
  8. package/.qoder/skills/midway-fatcms/SKILL.md +96 -0
  9. package/README.md +9 -9
  10. package/dist/configuration.d.ts +10 -0
  11. package/dist/configuration.js +26 -0
  12. package/dist/controller/base/BaseApiController.d.ts +1 -2
  13. package/dist/controller/base/BaseApiController.js +0 -4
  14. package/dist/controller/gateway/DocGatewayController.js +1 -1
  15. package/dist/controller/helpers.controller.d.ts +6 -0
  16. package/dist/controller/helpers.controller.js +19 -0
  17. package/dist/controller/manage/FlowConfigManageApi.js +4 -2
  18. package/dist/controller/manage/SysConfigMangeApi.js +6 -1
  19. package/dist/controller/manage/UserAccountManageApi.js +7 -2
  20. package/dist/index.d.ts +2 -2
  21. package/dist/index.js +2 -2
  22. package/dist/libs/crud-pro/CrudPro.d.ts +51 -3
  23. package/dist/libs/crud-pro/CrudPro.js +111 -4
  24. package/dist/libs/crud-pro/exceptions.d.ts +7 -0
  25. package/dist/libs/crud-pro/exceptions.js +7 -0
  26. package/dist/libs/crud-pro/interfaces.d.ts +83 -12
  27. package/dist/libs/crud-pro/models/CrudResult.d.ts +116 -0
  28. package/dist/libs/crud-pro/models/CrudResult.js +126 -0
  29. package/dist/libs/crud-pro/models/RequestModel.d.ts +2 -2
  30. package/dist/libs/crud-pro/models/ServiceHub.d.ts +2 -0
  31. package/dist/libs/crud-pro/services/CrudProDataTypeConvertService.d.ts +70 -2
  32. package/dist/libs/crud-pro/services/CrudProDataTypeConvertService.js +205 -13
  33. package/dist/libs/crud-pro/services/CrudProExecuteSqlService.js +36 -2
  34. package/dist/libs/crud-pro/services/CrudProGenSqlCondition.js +8 -4
  35. package/dist/libs/crud-pro/services/CrudProTableMetaService.d.ts +36 -0
  36. package/dist/libs/crud-pro/services/CrudProTableMetaService.js +97 -4
  37. package/dist/libs/crud-pro/services/CurdProServiceHub.d.ts +2 -0
  38. package/dist/libs/crud-pro/services/CurdProServiceHub.js +6 -0
  39. package/dist/libs/crud-pro-quick/CrudProQuick.d.ts +382 -0
  40. package/dist/libs/crud-pro-quick/CrudProQuick.js +689 -0
  41. package/dist/libs/crud-pro-quick/fixSoftDelete.d.ts +30 -0
  42. package/dist/{service/curd → libs/crud-pro-quick}/fixSoftDelete.js +3 -6
  43. package/dist/libs/crud-pro-quick/index.d.ts +36 -0
  44. package/dist/libs/crud-pro-quick/index.js +49 -0
  45. package/dist/libs/crud-pro-quick/models.d.ts +33 -0
  46. package/dist/libs/crud-pro-quick/models.js +2 -0
  47. package/dist/libs/crud-sharding/ShardingBase.d.ts +78 -0
  48. package/dist/libs/crud-sharding/ShardingBase.js +179 -0
  49. package/dist/libs/crud-sharding/ShardingByCustomCrud.d.ts +35 -0
  50. package/dist/libs/crud-sharding/ShardingByCustomCrud.js +297 -0
  51. package/dist/libs/crud-sharding/ShardingByHashCrud.d.ts +38 -0
  52. package/dist/libs/crud-sharding/ShardingByHashCrud.js +86 -0
  53. package/dist/libs/crud-sharding/ShardingByKeyCrud.d.ts +39 -0
  54. package/dist/libs/crud-sharding/ShardingByKeyCrud.js +74 -0
  55. package/dist/libs/crud-sharding/ShardingByTimeCrud.d.ts +66 -0
  56. package/dist/libs/crud-sharding/ShardingByTimeCrud.js +524 -0
  57. package/dist/libs/crud-sharding/ShardingConfig.d.ts +25 -10
  58. package/dist/libs/crud-sharding/ShardingConfig.js +5 -5
  59. package/dist/libs/crud-sharding/ShardingMerger.d.ts +10 -18
  60. package/dist/libs/crud-sharding/ShardingMerger.js +27 -44
  61. package/dist/libs/crud-sharding/ShardingResult.d.ts +33 -0
  62. package/dist/libs/crud-sharding/ShardingResult.js +16 -0
  63. package/dist/libs/crud-sharding/ShardingTableCreator.d.ts +21 -4
  64. package/dist/libs/crud-sharding/ShardingTableCreator.js +193 -59
  65. package/dist/libs/crud-sharding/ShardingUtils.d.ts +48 -0
  66. package/dist/libs/crud-sharding/ShardingUtils.js +122 -1
  67. package/dist/libs/crud-sharding/TIME_COLUMN_CLEAN_SPEC.md +488 -0
  68. package/dist/libs/crud-sharding/index.d.ts +13 -15
  69. package/dist/libs/crud-sharding/index.js +33 -17
  70. package/dist/models/RedisKeys.d.ts +1 -0
  71. package/dist/models/RedisKeys.js +1 -0
  72. package/dist/models/bizmodels.d.ts +2 -6
  73. package/dist/service/SysAppService.d.ts +2 -2
  74. package/dist/service/SysAppService.js +16 -5
  75. package/dist/service/SysConfigService.d.ts +1 -1
  76. package/dist/service/SysConfigService.js +7 -2
  77. package/dist/service/SysDictDataService.js +14 -4
  78. package/dist/service/SysMenuService.js +7 -2
  79. package/dist/service/TableMetaCacheRedisSubscriber.d.ts +31 -0
  80. package/dist/service/TableMetaCacheRedisSubscriber.js +98 -0
  81. package/dist/service/curd/CurdMixService.d.ts +6 -4
  82. package/dist/service/curd/CurdMixService.js +16 -2
  83. package/dist/service/curd/CurdProService.d.ts +149 -29
  84. package/dist/service/curd/CurdProService.js +157 -38
  85. package/dist/service/flow/FlowConfigService.js +7 -2
  86. package/dist/service/flow/FlowInstanceCrudService.js +22 -19
  87. package/package.json +1 -1
  88. package/src/configuration.ts +27 -0
  89. package/src/controller/base/BaseApiController.ts +0 -5
  90. package/src/controller/gateway/DocGatewayController.ts +1 -1
  91. package/src/controller/helpers.controller.ts +15 -0
  92. package/src/controller/manage/CrudStandardDesignApi.ts +4 -3
  93. package/src/controller/manage/FlowConfigManageApi.ts +4 -2
  94. package/src/controller/manage/SysConfigMangeApi.ts +6 -1
  95. package/src/controller/manage/UserAccountManageApi.ts +7 -2
  96. package/src/index.ts +2 -2
  97. package/src/libs/crud-pro/CrudPro.ts +134 -7
  98. package/src/libs/crud-pro/exceptions.ts +8 -0
  99. package/src/libs/crud-pro/interfaces.ts +111 -15
  100. package/src/libs/crud-pro/models/CrudResult.ts +178 -0
  101. package/src/libs/crud-pro/models/RequestModel.ts +2 -2
  102. package/src/libs/crud-pro/models/ServiceHub.ts +4 -0
  103. package/src/libs/crud-pro/services/CrudProDataTypeConvertService.ts +238 -15
  104. package/src/libs/crud-pro/services/CrudProExecuteSqlService.ts +41 -2
  105. package/src/libs/crud-pro/services/CrudProGenSqlCondition.ts +11 -7
  106. package/src/libs/crud-pro/services/CrudProTableMetaService.ts +110 -3
  107. package/src/libs/crud-pro/services/CurdProServiceHub.ts +8 -0
  108. package/src/libs/crud-pro-quick/CrudProQuick.ts +782 -0
  109. package/src/{service/curd → libs/crud-pro-quick}/fixSoftDelete.ts +23 -13
  110. package/src/libs/crud-pro-quick/index.ts +52 -0
  111. package/src/libs/crud-pro-quick/models.ts +35 -0
  112. package/src/libs/crud-sharding/ShardingBase.ts +256 -0
  113. package/src/libs/crud-sharding/ShardingByCustomCrud.ts +329 -0
  114. package/src/libs/crud-sharding/ShardingByHashCrud.ts +111 -0
  115. package/src/libs/crud-sharding/ShardingByKeyCrud.ts +97 -0
  116. package/src/libs/crud-sharding/ShardingByTimeCrud.ts +628 -0
  117. package/src/libs/crud-sharding/ShardingConfig.ts +28 -10
  118. package/src/libs/crud-sharding/ShardingMerger.ts +35 -63
  119. package/src/libs/crud-sharding/ShardingResult.ts +29 -0
  120. package/src/libs/crud-sharding/ShardingTableCreator.ts +214 -71
  121. package/src/libs/crud-sharding/ShardingUtils.ts +137 -0
  122. package/src/libs/crud-sharding/TIME_COLUMN_CLEAN_SPEC.md +488 -0
  123. package/src/libs/crud-sharding/index.ts +30 -16
  124. package/src/models/RedisKeys.ts +1 -0
  125. package/src/models/bizmodels.ts +4 -7
  126. package/src/service/SysAppService.ts +18 -7
  127. package/src/service/SysConfigService.ts +8 -3
  128. package/src/service/SysDictDataService.ts +14 -4
  129. package/src/service/SysMenuService.ts +7 -2
  130. package/src/service/TableMetaCacheRedisSubscriber.ts +105 -0
  131. package/src/service/crudstd/CrudStdService.ts +2 -2
  132. package/src/service/curd/CurdMixService.ts +26 -5
  133. package/src/service/curd/CurdProService.ts +186 -45
  134. package/src/service/flow/FlowConfigService.ts +7 -2
  135. package/src/service/flow/FlowInstanceCrudService.ts +23 -20
  136. package/.qoder/skills/midway-fatcms-crud/SKILL.md +0 -375
  137. package/.qoder/skills/midway-fatcms-crud/examples.md +0 -990
  138. package/.qoder/skills/midway-fatcms-crud/reference.md +0 -568
  139. package/dist/libs/crud-pro/README.md +0 -809
  140. package/dist/libs/crud-pro/README_FUNC.md +0 -193
  141. package/dist/libs/crud-sharding/ROUTING_LOGIC.md +0 -944
  142. package/dist/libs/crud-sharding/ShardingCrudPro.d.ts +0 -363
  143. package/dist/libs/crud-sharding/ShardingCrudPro.js +0 -675
  144. package/dist/libs/crud-sharding/ShardingRouter.d.ts +0 -69
  145. package/dist/libs/crud-sharding/ShardingRouter.js +0 -377
  146. package/dist/models/StandardColumns.d.ts +0 -71
  147. package/dist/models/StandardColumns.js +0 -28
  148. package/dist/service/curd/CrudProQuick.d.ts +0 -190
  149. package/dist/service/curd/CrudProQuick.js +0 -319
  150. package/dist/service/curd/README.md +0 -1100
  151. package/dist/service/curd/fixSoftDelete.d.ts +0 -20
  152. package/src/libs/crud-pro/README.md +0 -809
  153. package/src/libs/crud-pro/README_FUNC.md +0 -193
  154. package/src/libs/crud-sharding/ROUTING_LOGIC.md +0 -944
  155. package/src/libs/crud-sharding/ShardingCrudPro.ts +0 -835
  156. package/src/libs/crud-sharding/ShardingRouter.ts +0 -512
  157. package/src/models/StandardColumns.ts +0 -76
  158. package/src/service/curd/CrudProQuick.ts +0 -360
  159. package/src/service/curd/README.md +0 -1100
@@ -1,1100 +0,0 @@
1
- # Curd 服务层
2
-
3
- Curd 服务层在 CrudPro 库之上封装了**自动上下文注入、标准字段填充、软删除、快捷 CRUD、分表入口和关联数据填充**能力,是业务代码操作数据库的唯一入口。
4
-
5
- > CrudPro 库本身的使用指南见 [libs/crud-pro/README.md](../../libs/crud-pro/README.md),分表路由逻辑见 [libs/crud-sharding/ROUTING_LOGIC.md](../../libs/crud-sharding/ROUTING_LOGIC.md)。
6
-
7
- ## 架构总览
8
-
9
- ```
10
- 业务代码
11
-
12
- ├── CurdMixService(协调入口,CurdProService + 关联数据填充)
13
- │ ├── CurdMixByDictService → relatedType: 'dict'
14
- │ ├── CurdMixBySysConfigService → relatedType: 'sysCfgEnum'
15
- │ ├── CurdMixByAccountService → relatedType: 'accountBasic'
16
- │ ├── CurdMixByWorkbenchService → relatedType: 'workbenchBasic'
17
- │ └── CurdMixByLinkToCustomService → relatedType: 'linkToCustom'
18
-
19
- └── CurdProService(核心封装层)
20
- ├── CrudProQuick → 快捷 CRUD(链式调用)
21
- ├── ShardingCrudPro → 分表 CRUD
22
- ├── fixCfgModel() → 标准字段自动填充
23
- └── fixSoftDelete() → 软删除处理
24
- ```
25
-
26
- 核心工具:`CurdMixUtils` 提供 `copyColumnRelationToRow`、`forEachRowAndColumnsRelation` 等通用方法。
27
-
28
- ## 服务层 vs CrudPro 库
29
-
30
- | 能力 | CrudPro 库 | Curd 服务层 |
31
- |------|-----------|------------|
32
- | SQL 生成与执行 | ✅ | 透传 |
33
- | 事务 / 连接管理 | 需手动注入 | 自动从 Midway Context 注入 |
34
- | visitor(当前用户)| 需手动设置 | 自动从 userSession 转换 |
35
- | 标准字段自动填充 | 需手动配置 updateCfg | `fixCfgModel` 自动注入 |
36
- | 软删除 | 无 | `fixSoftDelete` 自动处理 |
37
- | 快捷 CRUD | 无 | `CrudProQuick` |
38
- | 分表 CRUD | `ShardingCrudPro` 类 | `getShardingCrud()` 工厂方法 |
39
- | 关联数据填充 | columnsRelation 定义 | `CurdMixService` 自动执行 |
40
- | 自定义 SQL 执行 | `executeSQL` | 透传 |
41
-
42
- ## CurdProService
43
-
44
- Midway `@Provide()` 服务,自动注入 `ctx`、数据库配置和全局配置。
45
-
46
- ### 自动上下文注入
47
-
48
- 每次调用 `getCrudPro()` 创建实例时,自动完成:
49
-
50
- - **visitor**:从 `ctx.userSession` 提取用户信息(accountId、nickName、roleCodes、functionCodes、workbenchCode 等)
51
- - **transaction**:从 `ctx.transaction` 获取事务管理器(需配合 `tx.middleware`)
52
- - **logger**:使用 `BaseService.getContextLogger()`
53
- - **contextCfg**:从全局配置注入系统数据库名/类型
54
- - **contextFunc**:`MyContextFunc` 根据数据库名从 Midway Config 中获取连接池
55
-
56
- ### 核心方法
57
-
58
- | 方法 | 说明 |
59
- |------|------|
60
- | `executeCrudByCfg(reqJson, cfgModel)` | 带标准字段填充 + 软删除的 CRUD 执行 |
61
- | `executeCrud(reqJson)` | 从数据库加载配置并执行(method 必填) |
62
- | `executeSQL(sqlCfgModel)` | 直接执行 SQL |
63
- | `getQuickCrud(db, dbType, table?)` | 获取 CrudProQuick 实例 |
64
- | `getShardingCrud(db, dbType, config)` | 获取 ShardingCrudPro 实例 |
65
- | `getCachedCfgByMethod(method)` | 从缓存加载配置 |
66
- | `getAllTableInfos(query, options?)` | 获取表结构信息 |
67
-
68
- ## CrudProQuick
69
-
70
- 面向业务代码的快捷 CRUD 工具,省去手动组装 `cfgModel` 的步骤。所有方法自动继承 `CurdProService` 的上下文(visitor、事务、软删除、标准字段填充等)。
71
-
72
- ### 获取实例
73
-
74
- ```typescript
75
- import { SqlDbType } from '@/libs/crud-pro/models/keys';
76
-
77
- // 方式1:通过 CurdProService(推荐)
78
- const quick = curdProService.getQuickCrud('mydb', SqlDbType.mysql, 't_user');
79
-
80
- // 方式2:通过 CurdMixService(兼容旧版本)
81
- const quick = curdMixService.getBbUtil('mydb', SqlDbType.mysql, 't_user');
82
-
83
- // sqlTable 可在构造时指定,也可在每次调用时覆盖
84
- const quick2 = curdProService.getQuickCrud('mydb', SqlDbType.mysql);
85
- const users = await quick2.getList({ condition: { status: 1 } }, 't_user');
86
- ```
87
-
88
- ### 基础配置
89
-
90
- #### setBaseCfgModel
91
-
92
- 通过 `setBaseCfgModel` 可设置公共配置,后续所有操作自动携带:
93
-
94
- ```typescript
95
- const quick = curdProService.getQuickCrud('mydb', SqlDbType.mysql, 't_order');
96
-
97
- // 设置公共配置
98
- quick.setBaseCfgModel({
99
- maxLimit: 50000, // 最大查询限制
100
- enableStandardUpdateCfg: true, // 启用标准字段自动填充
101
- enableSoftDelete: true, // 启用软删除
102
- enableStandardUpdateCfgCondition: true, // 查询条件自动注入 created_by
103
- });
104
-
105
- // 后续所有操作都会自动携带这些配置
106
- ```
107
-
108
- ### API 分类一览
109
-
110
- #### 查询类方法
111
-
112
- | 方法 | 返回类型 | 说明 |
113
- |------|----------|------|
114
- | `getList(reqJson, sqlTable?)` | `any[]` | 列表查询 |
115
- | `getListPage(reqJson, sqlTable?)` | `ResModelPageQuery` | 分页查询(含 total_count) |
116
- | `getUniqueOne(reqJson, sqlTable?)` | `any \| null` | 期望唯一一条,0条返回null,多条报错 |
117
- | `getOne(reqJson, sqlTable?)` | `any` | 获取第一条(已废弃,建议用 getUniqueOne) |
118
- | `isExist(reqJson, sqlTable?)` | `boolean` | 存在性判断(比 COUNT 高效) |
119
- | `getTotalCount(reqJson, sqlTable?)` | `number` | 统计总数 |
120
-
121
- #### 写入类方法
122
-
123
- | 方法 | 返回类型 | 说明 |
124
- |------|----------|------|
125
- | `insertObject(reqJson, sqlTable?)` | `ResModelAffected` | 插入单条 |
126
- | `batchInsert(reqJson, sqlTable?)` | `ResModelAffected` | 批量插入(推荐大数据量使用) |
127
- | `updateObject(reqJson, sqlTable?)` | `ResModelAffected` | 更新 |
128
- | `insertOrUpdate(reqJson, sqlTable?)` | `ResModelStandard` | 先查询存在性,再决定插入或更新 |
129
- | `insertOnDuplicate(reqJson, uniqueColumn?, sqlTable?)` | `ResModelAffected` | 原生 upsert(效率更高) |
130
- | `deleteObject(reqJson, sqlTable?)` | `ResModelAffected` | 删除(受软删除配置影响) |
131
-
132
- #### 原生 SQL 方法
133
-
134
- | 方法 | 返回类型 | 说明 |
135
- |------|----------|------|
136
- | `executeSQL(sql, args?)` | `any` | 框架封装的 SQL 执行(参数绑定) |
137
- | `executeNativeSQL<T>(sql, args?)` | `T` | 原生 SQL 执行,不做额外处理 |
138
-
139
- ### 请求参数结构
140
-
141
- 所有查询/写入方法都接受 `IRequestModel` 作为参数:
142
-
143
- ```typescript
144
- interface IRequestModel {
145
- condition?: Record<string, any>; // 查询/更新/删除条件
146
- data?: Record<string, any> | Record<string, any>[]; // 插入/更新数据
147
- pageNo?: number; // 页码(从1开始)
148
- pageSize?: number; // 每页条数
149
- orderBy?: string | OrderByItem[]; // 排序规则
150
- columns?: string[]; // 指定返回列(默认全返回)
151
- }
152
- ```
153
-
154
- ### 条件查询详解
155
-
156
- #### 基础条件(自动转 $eq)
157
-
158
- ```typescript
159
- // 简单等值条件 - 自动转换为 $eq
160
- const users = await quick.getList({
161
- condition: { status: 'active', age: 18 }
162
- });
163
- // 等价于: WHERE status = 'active' AND age = 18
164
- ```
165
-
166
- #### 操作符条件
167
-
168
- ```typescript
169
- // 比较操作符
170
- const users = await quick.getList({
171
- condition: {
172
- age: { $gt: 18, $lt: 60 }, // 大于且小于
173
- score: { $gte: 60 }, // 大于等于
174
- level: { $lte: 10 }, // 小于等于
175
- status: { $ne: 'deleted' }, // 不等于
176
- }
177
- });
178
-
179
- // 范围操作符
180
- const orders = await quick.getList({
181
- condition: {
182
- id: { $in: [1, 2, 3, 4, 5] }, // IN 查询
183
- type: { $nin: ['test', 'draft'] }, // NOT IN 查询
184
- amount: { $between: [100, 500] }, // BETWEEN 查询
185
- name: { $like: '%张三%' }, // LIKE 模糊查询
186
- }
187
- });
188
-
189
- // NULL 判断
190
- const users = await quick.getList({
191
- condition: {
192
- deleted_at: { $isNull: true }, // IS NULL
193
- phone: { $isNotNull: true }, // IS NOT NULL
194
- }
195
- });
196
- ```
197
-
198
- #### 组合条件
199
-
200
- ```typescript
201
- // AND 条件(默认)
202
- const users = await quick.getList({
203
- condition: {
204
- $and: [
205
- { status: 'active' },
206
- { age: { $gte: 18 } },
207
- ]
208
- }
209
- });
210
-
211
- // OR 条件
212
- const users = await quick.getList({
213
- condition: {
214
- $or: [
215
- { status: 'pending' },
216
- { status: 'processing' },
217
- ]
218
- }
219
- });
220
-
221
- // AND + OR 组合
222
- const users = await quick.getList({
223
- condition: {
224
- $and: [
225
- { department: 'tech' },
226
- {
227
- $or: [
228
- { level: 'senior' },
229
- { years: { $gte: 5 } },
230
- ]
231
- }
232
- ]
233
- }
234
- });
235
- // 等价于: WHERE department = 'tech' AND (level = 'senior' OR years >= 5)
236
- ```
237
-
238
- ### 排序规则
239
-
240
- ```typescript
241
- // 字符串语法(推荐)
242
- const users = await quick.getList({
243
- condition: { status: 'active' },
244
- orderBy: 'created_at-', // 降序(后缀 - 表示 DESC)
245
- });
246
-
247
- const users2 = await quick.getList({
248
- condition: { status: 'active' },
249
- orderBy: 'age+', // 升序(后缀 + 表示 ASC,可省略)
250
- });
251
-
252
- // 多字段排序
253
- const users3 = await quick.getList({
254
- condition: { status: 'active' },
255
- orderBy: 'department+,age-,created_at-',
256
- // 等价于: ORDER BY department ASC, age DESC, created_at DESC
257
- });
258
-
259
- // 对象数组语法
260
- const users4 = await quick.getList({
261
- condition: { status: 'active' },
262
- orderBy: [
263
- { fieldName: 'department', orderType: 'ASC' },
264
- { fieldName: 'created_at', orderType: 'DESC' },
265
- ]
266
- });
267
- ```
268
-
269
- ### 完整使用示例
270
-
271
- #### 1. 查询操作
272
-
273
- ```typescript
274
- const quick = curdProService.getQuickCrud('mydb', SqlDbType.mysql, 't_user');
275
-
276
- // 基础查询
277
- const users = await quick.getList({
278
- condition: { status: 'active' }
279
- });
280
-
281
- // 条件 + 排序 + 指定字段
282
- const users = await quick.getList({
283
- condition: {
284
- status: 'active',
285
- age: { $gte: 18, $lte: 60 },
286
- },
287
- orderBy: 'created_at-',
288
- columns: ['id', 'name', 'email'], // 只返回指定字段
289
- });
290
-
291
- // 分页查询
292
- const page = await quick.getListPage({
293
- condition: { status: 'active' },
294
- pageNo: 1,
295
- pageSize: 20,
296
- orderBy: 'created_at-',
297
- });
298
- console.log(page.rows); // 当前页数据
299
- console.log(page.total_count); // 总记录数
300
-
301
- // 唯一查询(期望结果只有0或1条)
302
- const user = await quick.getUniqueOne({
303
- condition: { email: 'user@example.com' }
304
- });
305
- // 0条返回 null,多条抛出异常
306
-
307
- // 存在性判断(比 COUNT 查询更高效)
308
- const exists = await quick.isExist({
309
- condition: { phone: '13800138000' }
310
- });
311
-
312
- // 统计总数
313
- const count = await quick.getTotalCount({
314
- condition: { status: 'active' }
315
- });
316
- ```
317
-
318
- #### 2. 写入操作
319
-
320
- ```typescript
321
- // 插入单条
322
- const result = await quick.insertObject({
323
- data: {
324
- name: '张三',
325
- email: 'zhangsan@example.com',
326
- status: 'active',
327
- // 如果启用 enableStandardUpdateCfg,会自动填充 created_by 等字段
328
- }
329
- });
330
- console.log(result.affectedRows); // 1
331
- console.log(result.insertId); // 自增ID
332
-
333
- // 批量插入(效率远高于循环单条插入)
334
- const result = await quick.batchInsert({
335
- data: [
336
- { name: '张三', email: 'zs@example.com' },
337
- { name: '李四', email: 'ls@example.com' },
338
- { name: '王五', email: 'ww@example.com' },
339
- ]
340
- });
341
- console.log(result.affectedRows); // 3
342
-
343
- // 更新(注意:必须带 condition,否则可能全表更新)
344
- const result = await quick.updateObject({
345
- condition: { id: 1 }, // WHERE id = 1
346
- data: {
347
- name: '张三丰',
348
- updated_at: new Date(),
349
- // 如果启用 enableStandardUpdateCfg,会自动填充 modified_by 等字段
350
- }
351
- });
352
- console.log(result.affectedRows); // 1
353
-
354
- // 插入或更新(先查询存在性)
355
- const result = await quick.insertOrUpdate({
356
- condition: { email: 'zhangsan@example.com' }, // 查询条件
357
- data: {
358
- name: '张三',
359
- email: 'zhangsan@example.com',
360
- age: 25,
361
- }
362
- });
363
- // 如果不存在:执行 INSERT
364
- // 如果存在:执行 UPDATE(用 data 更新匹配的记录)
365
-
366
- // 原生 upsert(MySQL: ON DUPLICATE KEY UPDATE)
367
- const result = await quick.insertOnDuplicate(
368
- {
369
- data: {
370
- id: 1,
371
- name: '张三',
372
- count: 1,
373
- }
374
- },
375
- ['id'], // 唯一列(PostgreSQL/SQL Server 必需)
376
- );
377
- // 如果唯一键冲突,执行 UPDATE,否则执行 INSERT
378
- // MySQL 可省略 uniqueColumn,依赖表的唯一索引
379
-
380
- // 删除(受软删除配置影响)
381
- const result = await quick.deleteObject({
382
- condition: { id: 1 }
383
- });
384
- // 如果 enableSoftDelete = true,实际执行 UPDATE 设置 deleted_at
385
- // 如果 enableSoftDelete = false,执行物理 DELETE
386
- ```
387
-
388
- #### 3. 原生 SQL 执行
389
-
390
- ```typescript
391
- import { NativeSqlSelectResult, NativeSqlResultMySQL } from '@/libs/crud-pro/models/ResModel';
392
-
393
- // 执行框架封装的 SQL(带参数绑定、日志等)
394
- const rows = await quick.executeSQL(
395
- 'SELECT * FROM t_user WHERE status = ? AND created_at > ?',
396
- ['active', '2024-01-01']
397
- );
398
-
399
- // 执行原生 SQL(最小封装,返回数据库驱动原始结果)
400
-
401
- // SELECT 查询
402
- const users = await quick.executeNativeSQL<{id: number; name: string}[]>(
403
- 'SELECT id, name FROM t_user WHERE age > ? ORDER BY id DESC LIMIT 10',
404
- [18]
405
- );
406
-
407
- // MySQL INSERT/UPDATE/DELETE
408
- const result = await quick.executeNativeSQL<NativeSqlResultMySQL>(
409
- 'INSERT INTO t_user (name, age) VALUES (?, ?)',
410
- ['张三', 20]
411
- );
412
- console.log(result.insertId); // 自增ID
413
- console.log(result.affectedRows); // 影响行数
414
- console.log(result.changedRows); // 实际修改行数(MySQL特有)
415
- ```
416
-
417
- #### 4. 动态表名切换
418
-
419
- ```typescript
420
- // 构造时不指定表名
421
- const quick = curdProService.getQuickCrud('mydb', SqlDbType.mysql);
422
-
423
- // 每次调用时指定表名
424
- const users = await quick.getList(
425
- { condition: { status: 'active' } },
426
- 't_user' // 第二个参数为表名
427
- );
428
-
429
- const orders = await quick.getList(
430
- { condition: { status: 'paid' } },
431
- 't_order'
432
- );
433
- ```
434
-
435
- ### 高级配置组合示例
436
-
437
- ```typescript
438
- const quick = curdProService.getQuickCrud('mydb', SqlDbType.mysql, 't_article');
439
-
440
- // 组合配置:软删除 + 标准字段填充 + 数据权限
441
- quick.setBaseCfgModel({
442
- maxLimit: 10000,
443
- enableSoftDelete: true, // 软删除
444
- enableStandardUpdateCfg: true, // 自动填充 created_by/modified_by
445
- enableStandardUpdateCfgCondition: ['by'], // 查询条件自动注入 created_by = 当前用户ID
446
- });
447
-
448
- // 此时:
449
- // - insert: 自动填充 created_by 和 deleted_at = 0
450
- // - update: 自动填充 modified_by,条件自动加 created_by = 当前用户ID
451
- // - delete: 实际执行 UPDATE 设置 deleted_at 和 deleted_by
452
- // - query: 自动追加 condition.deleted_at = 0
453
- // - 查询条件: 自动追加 created_by = 当前用户ID(只能查自己的数据)
454
- ```
455
-
456
- ## IRequestCfgModel2 扩展配置
457
-
458
- 服务层在 CrudPro 的 `IRequestCfgModel` 基础上扩展了 `IRequestCfgModel2`:
459
-
460
- ```typescript
461
- interface IRequestCfgModel2 extends IRequestCfgModel {
462
- enableStandardUpdateCfg?: boolean | string[]; // 标准字段自动填充
463
- enableStandardUpdateCfgCondition?: boolean | string[]; // condition 中的标准字段填充
464
- enableSoftDelete?: boolean; // 启用软删除
465
- }
466
- ```
467
-
468
- ### enableStandardUpdateCfg
469
-
470
- 控制 `fixCfgModel` 是否自动向 `updateCfg` 注入标准字段。
471
-
472
- | 值 | 行为 |
473
- |----|------|
474
- | `undefined` / `true` | 默认行为:insert/update 自动填充 `created_by`、`modified_by` 等 |
475
- | `false` | 完全关闭标准字段自动填充 |
476
- | `string[]` | 指定要填充的字段后缀,如 `['by']` 只填充 `created_by`/`modified_by` |
477
- | `['null']` | 等同于空数组,不填充任何字段 |
478
-
479
- ### enableStandardUpdateCfgCondition
480
-
481
- 控制查询/删除条件中是否自动注入 `created_by` 等字段,实现"只能查自己数据"的效果。
482
-
483
- | 值 | 行为 |
484
- |----|------|
485
- | `undefined` / `false` | 不在 condition 中注入 |
486
- | `string[]` | 指定字段后缀,如 `['by']` 自动注入 `condition.created_by = 当前用户ID` |
487
- | `['null']` | 不注入 |
488
-
489
- ### 各操作类型自动填充规则
490
-
491
- | 操作类型 | data 字段 | condition 字段 |
492
- |---------|-----------|---------------|
493
- | INSERT | `created_by`, `created_avatar`, `created_nickname`, `created_account_type` | — |
494
- | UPDATE | `modified_by`, `modified_avatar`, `modified_nickname`, `modified_account_type` | `created_by`, `created_account_type`(限制只改自己的) |
495
- | DELETE | — | `created_by`, `created_account_type`(限制只删自己的) |
496
- | INSERT_OR_UPDATE / INSERT_ON_DUPLICATE_UPDATE | `created_*` + `modified_*` | `created_by`, `created_account_type` |
497
- | QUERY / QUERY_PAGE / QUERY_ONE | — | 由 `enableStandardUpdateCfgCondition` 控制 |
498
-
499
- > 上述 `created_by` / `modified_by` 取自 `visitor.accountId`,其余字段取自 visitor 对应属性。
500
-
501
- ## 软删除
502
-
503
- 当 `cfgModel.enableSoftDelete = true` 时,`fixSoftDelete` 自动改写操作行为:
504
-
505
- | 操作 | 原始行为 | 软删除行为 |
506
- |------|---------|-----------|
507
- | INSERT | 正常插入 | 自动设置 `data.deleted_at = 0` |
508
- | DELETE | 物理删除 | 改为 UPDATE:设置 `deleted_at = 当前时间戳`、`deleted_by = 当前用户ID` |
509
- | QUERY / QUERY_ONE / QUERY_PAGE | 正常查询 | 自动追加 `condition.deleted_at = 0` |
510
-
511
- **前提**:表必须包含 `deleted_at`(number 类型)和 `deleted_by`(string 类型)字段。
512
-
513
- ```typescript
514
- // 启用软删除
515
- const cfgModel: IRequestCfgModel2 = {
516
- method: 'article.delete',
517
- sqlTable: 'article',
518
- sqlSimpleName: KeysOfSimpleSQL.SIMPLE_DELETE,
519
- enableSoftDelete: true,
520
- };
521
-
522
- // 实际执行:UPDATE article SET deleted_at=1712851200000, deleted_by='user123' WHERE id=1
523
- ```
524
-
525
- ## 分表 CRUD 入口
526
-
527
- 通过 `CurdProService.getShardingCrud()` 获取分表操作器:
528
-
529
- ```typescript
530
- import { ShardingType } from '@/libs/crud-sharding';
531
-
532
- const sharding = curdProService.getShardingCrud('mydb', SqlDbType.mysql, {
533
- type: ShardingType.MONTH,
534
- baseTable: 't_order',
535
- timeColumn: 'created_at',
536
- countCache: { ttlSeconds: 300 },
537
- });
538
-
539
- // 后续调用 sharding.insert / query / queryPage 等
540
- // 路由逻辑详见 libs/crud-sharding/ROUTING_LOGIC.md
541
- ```
542
-
543
- ### ShardingCrudPro 完整使用指南
544
-
545
- #### 获取实例
546
-
547
- ```typescript
548
- import { ShardingType } from '@/libs/crud-sharding';
549
-
550
- // 方式1:通过 CurdProService 获取(推荐)
551
- const sharding = curdProService.getShardingCrud('mydb', SqlDbType.mysql, {
552
- type: ShardingType.MONTH, // 按月分表
553
- baseTable: 't_order', // 基础表名
554
- timeColumn: 'created_at', // 时间字段(时间分表必填)
555
- autoCreateTable: true, // 自动创建分表(可选)
556
- countCache: { // COUNT 查询缓存(可选)
557
- ttlSeconds: 300,
558
- maxSize: 1000,
559
- },
560
- });
561
-
562
- // 方式2:通过 CurdMixService 获取
563
- const sharding = curdMixService.getShardingCrud('mydb', SqlDbType.mysql, {
564
- type: ShardingType.HASH,
565
- baseTable: 't_user',
566
- shardingColumn: 'user_id', // 分表字段(HASH/RANGE 必填)
567
- tableCount: 16, // 分表数量
568
- });
569
- ```
570
-
571
- #### 支持的分表策略
572
-
573
- | 策略 | 枚举值 | 说明 | 示例 |
574
- |------|--------|------|------|
575
- | 按年分表 | `ShardingType.YEAR` | 按年份分表 | `order_2024`, `order_2025` |
576
- | 按月分表 | `ShardingType.MONTH` | 按月份分表 | `order_202401`, `order_202402` |
577
- | 按日分表 | `ShardingType.DAY` | 按日期分表 | `order_20240101` |
578
- | 按范围分表 | `ShardingType.RANGE` | 按数值范围分表 | `user_0` ~ `user_99` |
579
- | 按哈希分表 | `ShardingType.HASH` | 按哈希值分表 | `order_01` ~ `order_16` |
580
- | 自定义分表 | `ShardingType.CUSTOM` | 自定义路由规则 | 自定义函数决定 |
581
-
582
- #### 配置选项详解
583
-
584
- ```typescript
585
- interface IShardingConfig {
586
- type: ShardingType; // 分表类型(必填)
587
- baseTable: string; // 基础表名(必填)
588
- timeColumn?: string; // 时间字段(时间分表必填)
589
- shardingColumn?: string; // 分表字段(HASH/RANGE 必填)
590
- tableCount?: number; // 分表数量(HASH/RANGE,默认 16/10)
591
- autoCreateTable?: boolean; // 自动创建分表(默认 false)
592
- tableCreateOptions?: { // 分表创建选项
593
- copyIndexes?: boolean; // 是否复制索引(默认 false)
594
- ignoreForeignKeys?: boolean; // 是否忽略外键(默认 true)
595
- tableOptions?: string; // 表选项(如 MySQL 引擎配置)
596
- };
597
- countCache?: { // COUNT 查询缓存(时间分表)
598
- ttlSeconds?: number; // 缓存有效期(默认 300 秒)
599
- maxSize?: number; // 最大缓存条目(默认 1000)
600
- };
601
- recentTableCount?: number; // 查询无时间范围时返回最近 N 个分表
602
- customRouter?: (ctx) => string | string[]; // 自定义路由函数(CUSTOM 类型)
603
- suffixFormatter?: (suffix) => string; // 自定义后缀格式化
604
- }
605
- ```
606
-
607
- #### API 一览
608
-
609
- | 方法 | 返回类型 | 说明 |
610
- |------|----------|------|
611
- | `setBaseCfg(cfg)` | `this` | 设置基础配置,支持链式调用 |
612
- | `getConfig()` | `IShardingConfig` | 获取分表配置 |
613
- | `insert(reqJson)` | `ExecuteContext` | 插入单条数据(自动路由) |
614
- | `batchInsert(reqJson)` | `IShardingSmartBatchInsertResult` | 批量插入(支持跨分表) |
615
- | `update(reqJson)` | `ExecuteContext` | 更新数据 |
616
- | `delete(reqJson)` | `ExecuteContext` | 删除数据 |
617
- | `insertOrUpdate(reqJson)` | `ExecuteContext` | 插入或更新 |
618
- | `insertOnDuplicateUpdate(reqJson)` | `ExecuteContext` | 原生 upsert |
619
- | `queryOne(reqJson)` | `any \| null` | 查询单条 |
620
- | `query(reqJson)` | `any[]` | 列表查询(多表自动合并) |
621
- | `queryPage(reqJson)` | `IShardingPageQueryResult` | 分页查询(跨分表分页) |
622
- | `queryCount(reqJson)` | `number` | 统计总数 |
623
- | `isExist(reqJson)` | `boolean` | 存在性判断 |
624
-
625
- #### 使用示例
626
-
627
- ##### 1. 时间分表(按月)
628
-
629
- ```typescript
630
- const sharding = curdProService.getShardingCrud('mydb', SqlDbType.mysql, {
631
- type: ShardingType.MONTH,
632
- baseTable: 't_order',
633
- timeColumn: 'created_at',
634
- autoCreateTable: true,
635
- });
636
-
637
- // 插入数据 - 自动路由到对应月份分表
638
- await sharding.insert({
639
- data: {
640
- order_id: 'ORD001',
641
- amount: 100,
642
- created_at: '2024-03-15 10:00:00', // -> 路由到 t_order_202403
643
- },
644
- });
645
-
646
- // 批量插入 - 自动分组并行插入
647
- const result = await sharding.batchInsert({
648
- data: [
649
- { order_id: '001', amount: 100, created_at: '2024-01-15' }, // -> t_order_202401
650
- { order_id: '002', amount: 200, created_at: '2024-01-20' }, // -> t_order_202401
651
- { order_id: '003', amount: 150, created_at: '2024-02-10' }, // -> t_order_202402
652
- { order_id: '004', amount: 300, created_at: '2024-03-05' }, // -> t_order_202403
653
- ],
654
- });
655
- console.log(result.totalAffected); // 4
656
- console.log(result.tableCount); // 3(涉及3个分表)
657
- console.log(result.tableResults); // 各分表插入详情
658
-
659
- // 更新数据 - 需要包含时间字段用于路由
660
- await sharding.update({
661
- condition: { order_id: 'ORD001', created_at: '2024-03-15' },
662
- data: { amount: 200 },
663
- });
664
-
665
- // 列表查询 - 自动合并多表结果(必须传 orderBy)
666
- const orders = await sharding.query({
667
- condition: {
668
- status: 'paid',
669
- created_at: { $gte: '2024-01-01', $lte: '2024-03-31' }, // 时间范围自动路由
670
- },
671
- orderBy: 'created_at DESC', // 必须传,且格式固定为 timeColumn DESC
672
- });
673
-
674
- // 分页查询 - 跨分表分页自动处理
675
- const page = await sharding.queryPage({
676
- condition: { status: 'paid' },
677
- pageNo: 1,
678
- pageSize: 10,
679
- orderBy: 'created_at DESC', // 必须传
680
- });
681
- console.log(page.rows, page.total_count);
682
-
683
- // 查询总数 - 并行查询各分表后汇总
684
- const count = await sharding.queryCount({
685
- condition: { status: 'paid' },
686
- });
687
-
688
- // 查询单条 - 按顺序查询各分表,找到即返回
689
- const order = await sharding.queryOne({
690
- condition: { order_id: 'ORD001' },
691
- });
692
-
693
- // 存在性判断 - 并行查询各分表
694
- const exists = await sharding.isExist({
695
- condition: { order_id: 'ORD001' },
696
- });
697
- ```
698
-
699
- ##### 2. 哈希分表
700
-
701
- ```typescript
702
- const sharding = curdProService.getShardingCrud('mydb', SqlDbType.mysql, {
703
- type: ShardingType.HASH,
704
- baseTable: 't_user',
705
- shardingColumn: 'user_id',
706
- tableCount: 16,
707
- autoCreateTable: true,
708
- });
709
-
710
- // 插入 - 根据 user_id 哈希值路由
711
- await sharding.insert({
712
- data: { user_id: 10001, name: '张三' }, // -> 路由到 t_user_05
713
- });
714
-
715
- // 查询 - 需要提供分表字段用于路由
716
- const user = await sharding.queryOne({
717
- condition: { user_id: 10001 }, // 根据 user_id 确定分表
718
- });
719
-
720
- // 查询多表 - 不带分表字段时可能查询多个分表
721
- const users = await sharding.query({
722
- condition: { status: 'active' }, // 无 user_id,可能扫描多个分表
723
- orderBy: 'created_at DESC',
724
- });
725
- ```
726
-
727
- ##### 3. 范围分表
728
-
729
- ```typescript
730
- const sharding = curdProService.getShardingCrud('mydb', SqlDbType.mysql, {
731
- type: ShardingType.RANGE,
732
- baseTable: 't_log',
733
- shardingColumn: 'log_id',
734
- tableCount: 100,
735
- });
736
-
737
- // log_id 1-9999 路由到 t_log_0,10000-19999 路由到 t_log_1,以此类推
738
- await sharding.insert({
739
- data: { log_id: 15000, content: 'xxx' }, // -> 路由到 t_log_1
740
- });
741
- ```
742
-
743
- ##### 4. 自定义分表规则
744
-
745
- ```typescript
746
- const sharding = curdProService.getShardingCrud('mydb', SqlDbType.mysql, {
747
- type: ShardingType.CUSTOM,
748
- baseTable: 't_data',
749
- customRouter: (context) => {
750
- const { data, condition, config } = context;
751
- // 自定义路由逻辑
752
- const tenantId = data?.tenant_id || condition?.tenant_id;
753
- return `t_data_${tenantId}`; // 按租户ID分表
754
- },
755
- });
756
- ```
757
-
758
- ##### 5. 链式配置
759
-
760
- ```typescript
761
- const sharding = curdProService
762
- .getShardingCrud('mydb', SqlDbType.mysql, {
763
- type: ShardingType.MONTH,
764
- baseTable: 't_order',
765
- timeColumn: 'created_at',
766
- })
767
- .setBaseCfg({
768
- enableStandardUpdateCfg: true,
769
- enableSoftDelete: true,
770
- });
771
- ```
772
-
773
- #### 重要约束
774
-
775
- **时间分表查询约束:**
776
- - 列表查询 (`query`) 和分页查询 (`queryPage`) 必须传 `orderBy` 参数
777
- - `orderBy` 格式必须固定为 `'{timeColumn} DESC'`,如 `'created_at DESC'`
778
- - 这是分表合并算法的要求:按时间倒序的表顺序 + 倒序排序的数据 = 无需内存排序直接合并
779
-
780
- ```typescript
781
- // 正确
782
- await sharding.query({
783
- condition: { status: 'paid' },
784
- orderBy: 'created_at DESC', // 符合约束
785
- });
786
-
787
- // 错误 - 缺少 orderBy
788
- await sharding.query({
789
- condition: { status: 'paid' },
790
- });
791
-
792
- // 错误 - 排序方向错误
793
- await sharding.query({
794
- condition: { status: 'paid' },
795
- orderBy: 'created_at ASC',
796
- });
797
- ```
798
-
799
- **写操作路由约束:**
800
- - `insert` 必须在 `data` 中包含 `timeColumn`(时间分表)或 `shardingColumn`(HASH/RANGE)
801
- - `update`/`delete` 必须在 `condition` 中包含分表字段,或提供足够的时间范围确定单一分表
802
-
803
- ```typescript
804
- // 正确 - 数据包含时间字段
805
- await sharding.insert({
806
- data: { order_id: '001', created_at: '2024-03-15' },
807
- });
808
-
809
- // 错误 - 缺少时间字段,无法路由
810
- await sharding.insert({
811
- data: { order_id: '001' }, // 报错:无法确定目标分表
812
- });
813
- ```
814
-
815
- ## CurdMixService
816
-
817
- ### 关联数据填充执行流程
818
-
819
- 1. `CurdMixService.executeCrudByCfg()` → 调用 `prepare()` 注册 5 个处理器
820
- 2. CrudPro 执行 SQL,返回 `ExecuteContext`
821
- 3. `MyContextFunc.afterExecuteSQLList()` 遍历所有处理器:
822
- - `handleExecuteContextPrepare()` — 预加载关联数据
823
- - `handleExecuteContext()` — 遍历每行,按 `sourceColumn` 取值查关联数据,通过 `targetColumns` 映射写回
824
-
825
- ### linkColumnRelationDatas
826
-
827
- 独立于 CRUD 操作,直接对已有数据行执行关联填充。适用于数据来自缓存、外部接口或其他非 CurdMixService 渠道,但仍需要字典翻译、用户信息等关联数据的场景。
828
-
829
- > ⚠️ 该方法会**原地修改** `rows` 数组中的对象,不会返回新数组。
830
-
831
- #### 方法签名
832
-
833
- ```typescript
834
- linkColumnRelationDatas(rows: any[], param: ILinkColumnRelationParam): Promise<void>
835
- ```
836
-
837
- #### 参数接口
838
-
839
- ```typescript
840
- interface ILinkColumnRelationParam {
841
- columnsRelations: ColumnRelation[]; // 关联配置数组
842
- }
843
- ```
844
-
845
- | 参数 | 类型 | 说明 |
846
- |------|------|------|
847
- | `rows` | `any[]` | 待填充的数据行数组;为空或非数组时直接返回 |
848
- | `param.columnsRelations` | `ColumnRelation[]` | 关联规则配置,与 CRUD 配置中的 `columnsRelation` 格式一致 |
849
-
850
- #### 基础用法
851
-
852
- ```typescript
853
- // 场景:从缓存或其他来源获取了数据行,仍需要关联填充
854
- const rows = [...]; // 任意来源的数据行
855
-
856
- await curdMixService.linkColumnRelationDatas(rows, {
857
- columnsRelations: [
858
- {
859
- relatedType: 'dict',
860
- relatedCode: 'SexEnum',
861
- sourceColumn: 'sex',
862
- targetColumns: [{ from: 'label', to: 'sex_text' }],
863
- },
864
- ],
865
- });
866
- // rows 中的每行会原地追加 sex_text 字段
867
- ```
868
-
869
- #### 多关联类型组合
870
-
871
- ```typescript
872
- const rows = await redisCache.get('order_list');
873
-
874
- await curdMixService.linkColumnRelationDatas(rows, {
875
- columnsRelations: [
876
- // 字典翻译
877
- {
878
- relatedType: 'dict',
879
- relatedCode: 'OrderStatus',
880
- sourceColumn: 'status',
881
- targetColumns: [
882
- { from: 'label', to: 'status_text' },
883
- { from: 'color', to: 'status_color' },
884
- ],
885
- },
886
- // 系统配置枚举
887
- {
888
- relatedType: 'sysCfgEnum',
889
- relatedCode: 'PayMethod',
890
- sourceColumn: 'pay_method',
891
- targetColumns: [
892
- { from: 'label', to: 'pay_method_info.label' },
893
- { from: 'style', to: 'pay_method_info.style' },
894
- ],
895
- },
896
- // 用户信息填充
897
- {
898
- relatedType: 'accountBasic',
899
- sourceColumn: 'created_by',
900
- targetColumns: [], // 空 = 使用默认映射
901
- },
902
- // 工作台信息
903
- {
904
- relatedType: 'workbenchBasic',
905
- sourceColumn: 'workbench_code',
906
- targetColumns: [
907
- { from: 'workbench_domain', to: 'workbench_info.workbench_domain' },
908
- { from: 'workbench_name', to: 'workbench_info.workbench_name' },
909
- ],
910
- },
911
- ],
912
- });
913
- ```
914
-
915
- #### 与 CrudProQuick 配合使用
916
-
917
- ```typescript
918
- const quick = curdProService.getQuickCrud('mydb', SqlDbType.mysql, 't_order');
919
-
920
- // 先用 CrudProQuick 获取原始数据(不带关联)
921
- const rows = await quick.getList({
922
- condition: { status: 'paid' },
923
- });
924
-
925
- // 再用 linkColumnRelationDatas 补充关联数据
926
- await curdMixService.linkColumnRelationDatas(rows, {
927
- columnsRelations: [
928
- {
929
- relatedType: 'dict',
930
- relatedCode: 'OrderStatus',
931
- sourceColumn: 'status',
932
- targetColumns: [{ from: 'label', to: 'status_text' }],
933
- },
934
- {
935
- relatedType: 'accountBasic',
936
- sourceColumn: 'created_by',
937
- targetColumns: [],
938
- },
939
- ],
940
- });
941
- ```
942
-
943
- > **提示**:如果 CRUD 操作本身就需要关联填充,建议直接使用 `CurdMixService.executeCrudByCfg()` 配置 `columnsRelation`,一步完成查询和填充。`linkColumnRelationDatas` 适用于数据已存在的二次填充场景。
944
-
945
- ## ColumnRelation 配置
946
-
947
- ```typescript
948
- interface ColumnRelation {
949
- relatedType: any; // 关联类型
950
- relatedCode?: string; // 关联编码(dict/sysCfgEnum/linkToCustom 时必填)
951
- sourceColumn: string; // 从结果行中取值的字段名
952
- targetColumns?: CopyAttr[]; // 映射规则,[{ from: 'label', to: 'sex_text' }]
953
- }
954
-
955
- interface CopyAttr {
956
- from: string; // 关联数据中的字段名,支持 '*' 表示整体赋值
957
- to: string; // 写入结果行的目标路径(支持嵌套,如 'user_info.name')
958
- }
959
- ```
960
-
961
- ## 关联类型
962
-
963
- ### dict — 数据字典
964
-
965
- 从 `sys_data_dict_item` 表查询字典数据,按 `dict_code` + `value` 匹配。
966
-
967
- ```typescript
968
- const cfgJson: IRequestCfgModel = {
969
- method: 'user.list',
970
- sqlTable: 'sys_user',
971
- sqlSimpleName: KeysOfSimpleSQL.SIMPLE_QUERY,
972
- columnsRelation: [
973
- {
974
- relatedType: 'dict',
975
- relatedCode: 'SexEnum', // 字典编码
976
- sourceColumn: 'sex', // 结果行中的 sex 字段值作为字典 value
977
- targetColumns: [
978
- { from: 'label', to: 'sex_text' }, // 字典 label → sex_text
979
- ],
980
- },
981
- {
982
- relatedType: 'dict',
983
- relatedCode: 'StatusEnum',
984
- sourceColumn: 'status',
985
- targetColumns: [
986
- { from: 'label', to: 'status_text' },
987
- { from: 'color', to: 'status_color' },
988
- ],
989
- },
990
- ],
991
- };
992
- ```
993
-
994
- ### sysCfgEnum — 系统配置枚举
995
-
996
- 从 `sys_configs` 表查询配置,按 `code` 列作为唯一键匹配。
997
-
998
- ```typescript
999
- columnsRelation: [
1000
- {
1001
- relatedType: 'sysCfgEnum',
1002
- relatedCode: 'OrderStatus',
1003
- sourceColumn: 'order_status',
1004
- targetColumns: [
1005
- { from: 'label', to: 'order_status_info.label' },
1006
- { from: 'style', to: 'order_status_info.style' },
1007
- ],
1008
- },
1009
- ]
1010
- ```
1011
-
1012
- ### accountBasic — 用户账号信息
1013
-
1014
- 根据 `sourceColumn` 中的用户 ID,查询用户基本信息(昵称、头像等)。
1015
-
1016
- ```typescript
1017
- columnsRelation: [
1018
- {
1019
- relatedType: 'accountBasic',
1020
- sourceColumn: 'created_by', // 用户 ID 字段
1021
- targetColumns: [], // 空 = 使用默认映射
1022
- },
1023
- ]
1024
- ```
1025
-
1026
- **用户 ID 适配**:支持数字、字符串、JSON 数组等多种 ID 格式,自动通过 `toFatcmsUserAccountId` 转换。
1027
-
1028
- ### workbenchBasic — 工作台站点信息
1029
-
1030
- 根据 `workbench_code` 查询站点信息。
1031
-
1032
- ```typescript
1033
- columnsRelation: [
1034
- {
1035
- relatedType: 'workbenchBasic',
1036
- sourceColumn: 'workbench_code',
1037
- targetColumns: [
1038
- { from: 'workbench_domain', to: 'workbench_info.workbench_domain' },
1039
- { from: 'workbench_name', to: 'workbench_info.workbench_name' },
1040
- ],
1041
- },
1042
- ]
1043
- ```
1044
-
1045
- 支持数组字段(如 `workbench_code_array`),目标路径使用 `$ARRAY_INDEX` 占位符:
1046
-
1047
- ```typescript
1048
- columnsRelation: [
1049
- {
1050
- relatedType: 'workbenchBasic',
1051
- sourceColumn: 'workbench_code_array',
1052
- targetColumns: [
1053
- { from: 'workbench_domain', to: 'workbench_info_array[$ARRAY_INDEX].workbench_domain' },
1054
- { from: 'workbench_name', to: 'workbench_info_array[$ARRAY_INDEX].workbench_name' },
1055
- ],
1056
- },
1057
- ]
1058
- ```
1059
-
1060
- ### linkToCustom — 自定义表关联
1061
-
1062
- 关联任意自定义表,需在 `sys_configs` 中配置关联规则。
1063
-
1064
- ```typescript
1065
- columnsRelation: [
1066
- {
1067
- relatedType: 'linkToCustom',
1068
- relatedCode: 'categoryLinkConfig', // 关联配置编码
1069
- sourceColumn: 'category_id',
1070
- targetColumns: [], // 使用配置中的默认映射
1071
- },
1072
- ]
1073
- ```
1074
-
1075
- ## 标准表字段
1076
-
1077
- 推荐业务表包含以下标准字段,以配合服务层的自动填充和软删除机制:
1078
-
1079
- **常用字段(6 个)**:`deleted_at`、`deleted_by`、`created_by`、`created_at`、`modified_by`、`modified_at`
1080
-
1081
- **完整字段(12 个)**:在常用字段基础上增加 `created_account_type`、`created_avatar`、`created_nickname`、`modified_account_type`、`modified_avatar`、`modified_nickname`
1082
-
1083
- 类型定义位于 `@/models/StandardColumns`:
1084
-
1085
- ```typescript
1086
- import { StandardDataModel, FullStandardDataModel, NotDeletedCondition } from '@/models/StandardColumns';
1087
-
1088
- // 定义业务表数据类型
1089
- interface OrderEntity {
1090
- id: number;
1091
- order_no: string;
1092
- amount: number;
1093
- }
1094
- type Order = StandardDataModel<OrderEntity>; // 自动包含 6 个标准字段
1095
- type OrderFull = FullStandardDataModel<OrderEntity>; // 自动包含 12 个标准字段
1096
-
1097
- // 未删除查询条件
1098
- const condition = { ...NotDeletedCondition, status: 1 };
1099
- // 等价于 { deleted_at: 0, status: 1 }
1100
- ```