112 lines
6.7 KiB
Markdown
112 lines
6.7 KiB
Markdown
# 4.6 核心基座体系
|
||
|
||
:::tip 概述
|
||
本项目没有传统"基类继承"——用**组合**替代继承:ModuleSpec 泛型基座(module_spec.go)承担 CRUD 流水线,base.go 共享能力层提供横切函数。本节解释两者的分工与设计纪律。
|
||
:::
|
||
|
||
## 组合替代继承的架构
|
||
|
||
```text
|
||
┌────────────────────────────────────────────────────┐
|
||
│ 模块 logic 文件(example.go) │
|
||
│ var exampleSpec = ModuleSpec[...]{ 声明差异 } │──▶ 委托
|
||
│ func 方法() { exampleSpec.Page(...) } │
|
||
│ func 扩展() { 手写(合法逃生舱)} │
|
||
└───────────────────────┬────────────────────────────┘
|
||
│ 复用
|
||
┌───────────────────────▼────────────────────────────┐
|
||
│ module_spec.go:ModuleSpec 泛型基座 │
|
||
│ applyFilters / readTransform / saveTransform │
|
||
│ checkUniqueRules / FillTextFields / buildTree │
|
||
└───────────────────────┬────────────────────────────┘
|
||
│ 调用
|
||
┌───────────────────────▼────────────────────────────┐
|
||
│ base.go 共享能力层(导出上限 15 个) │
|
||
│ 文件转正 / 富文本 / 字典文本 / 唯一校验 / │
|
||
│ 事务 / 分块查询 / 泛型树 / 临时目录清理 │
|
||
└────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## base.go 能力清单(internal/logic/base.go)
|
||
|
||
| 能力 | 函数 | 一句话职责 |
|
||
|------|------|-----------|
|
||
| 文件字段 | SaveFileFields / SaveMultiFileFields | 批量转正(原地更新 map) |
|
||
| | SaveFileField / SaveMultiFileField | 单值便捷版 |
|
||
| 富文本 | LocalizeContentFields | 存库本地化([IMG_URL] 占位符化) |
|
||
| | ExpandContentFields | 读取还原(**唯一还原出口**) |
|
||
| 枚举文本 | DictGetText | 字典值 → 显示名(进程内缓存 300s) |
|
||
| | SerializeMapOnRead | map 行补 {field}Text |
|
||
| | LogTypeText | 操作日志类型 → 中文(静态表) |
|
||
| 唯一校验 | CheckUnique | UX 快速报错(TOCTOU,真值靠 uk_* 索引) |
|
||
| 事务 | WithTrans | 事务包裹(错误自动回滚) |
|
||
| 分块 | ChunkQuery | 分块遍历(导出防 OOM,默认 500/页) |
|
||
| 树 | BuildTree[T] | 泛型平铺转树 |
|
||
| 清理 | CleanTemp | 清理上传临时日期目录 |
|
||
|
||
架构纪律(文件头约束,真实注释):
|
||
|
||
```text
|
||
1) 按能力分区:文件字段 / 富文本 / 枚举文本 / 唯一校验 / 事务 / 分块查询 / 树
|
||
2) 依赖方向单向:只允许依赖 dao / utility / consts,禁止引用模块文件
|
||
3) 导出函数上限 15 个,超出按能力拆 internal/logic/base/ 子包
|
||
4) CheckUnique 仅为 UX 快速报错(TOCTOU),并发唯一性靠 uk_* 索引
|
||
5) [IMG_URL] 占位符是持久化契约,还原只能走 ExpandContentFields
|
||
```
|
||
|
||
## ModuleSpec 基座流水线(module_spec.go)
|
||
|
||
```text
|
||
Page applyFilters → Count → applyOrder.Page → structRows → readTransform
|
||
→ AfterPageList
|
||
List applyFilters → 排序 → req.limit 上限 → structRows → readTransform
|
||
→ IsTree 时 buildTree(叶子 Children 预置 [] 而非 null)
|
||
Detail Model.Where(id).One → Struct → readTransform → AfterDetail
|
||
Add ToData → BeforeAdd → saveTransform(文件/富文本转正)→ checkUniqueRules
|
||
→ Insert → LastInsertId → AfterAdd → 返回 id
|
||
Update ToData → BeforeUpdate → saveTransform → checkUniqueRules(排除自身)
|
||
→ Update(仅更新 data 中出现的键)→ AfterUpdate
|
||
Delete BeforeDelete → Delete(钩子改写软删)→ AfterDelete
|
||
BatchDelete BeforeBatchDelete → 逐条 BeforeDelete+软删 → AfterBatchDelete
|
||
```
|
||
|
||
readTransform 顺序:FileFields 补 URL → MultiFileFields 补 URL → ContentFields 展开 [IMG_URL] → FillTextFields({field}Text)。
|
||
|
||
## 设计纪律(为什么基座不会腐化)
|
||
|
||
::: warning 基座三铁律
|
||
1. **基座禁止"按模块分支"**——差异只进声明字段与钩子;写不进钩子 → 手写(合法逃生舱)。这条纪律保证基座永远不出现 `if module == "user"` 式代码。
|
||
2. **钩子用具体函数类型**,req any 经 mustAs[T] 断言(失败=编程错误,panic 暴露而非带病运行)。
|
||
3. **钩子内禁止 append、禁止 HTTP/文件 IO**——文件迁移统一在 saveTransform 完成。
|
||
:::
|
||
|
||
applyFilters 固定顺序:`LikeFields → EqFields → KeywordFields → Filter → DataScope`;租户/软删隔离由 dao.Ctx **先于本链**注入(任何模块不可绕过)。
|
||
|
||
## ModuleSpec 声明字段全景
|
||
|
||
```text
|
||
Model func(ctx) *gdb.Model 数据源(通常 dao.Xxx.Ctx)
|
||
LikeFields []string 逐列 LIKE(值取 req 同名驼峰字段,空跳过)
|
||
EqFields []string 等值(零值跳过)
|
||
KeywordFields []string req.keyword 多列 OR(基座保留能力)
|
||
Filter func(ctx, m, req) *gdb.Model 复杂条件逃生舱
|
||
OrderBy / ListOrderBy / OrderableFields 排序(白名单控制前端排序)
|
||
IsTree / ParentOf / ChildSetter 树形
|
||
SerializeMaps map[字段]字典编码 {field}Text 填充
|
||
FileFields / MultiFileFields / ContentFields / FileSaveDir 文件与富文本
|
||
UniqueFields []UniqueRule{Field, Group} 唯一性预检(Group 分组唯一)
|
||
DataScope DataScopeFunc 行级数据权限(ApplyDataScope 接线)
|
||
ToData req → map[string]any 新增/修改共用转换
|
||
钩子 Before/After × Add/Update/Delete/Batch + AfterPageList/AfterDetail
|
||
```
|
||
|
||
## 与"基类方案"的对比
|
||
|
||
| 维度 | 本项目(泛型基座+组合) | 传统基类继承 |
|
||
|------|----------------------|-------------|
|
||
| 差异表达 | 声明字段 + 钩子 | 重写虚方法 |
|
||
| 类型安全 | 泛型三类型参数编译期约束 | 常退化 any/map |
|
||
| 复用粒度 | 函数级(base.go 自由组合) | 类级(受继承链束缚) |
|
||
| 基座污染风险 | 三铁律禁止按模块分支 | 基类易堆积 if-else |
|
||
| 逃生舱 | 手写方法(完全自由) | 多重继承/桥接方法 |
|