Files
easygoadmin-wiki/EasyGoAdmin_GoFrame_EleVue/zh/architecture/base-classes.md
T
2026-10-20 21:19:44 +08:00

112 lines
6.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 |
| 逃生舱 | 手写方法(完全自由) | 多重继承/桥接方法 |