Skip to content

4.6 核心基座体系 ​

概述

本项目没有传统"基类继承"——用组合替代继承: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)
SerializeMapOnReadmap 行补 {field}Text
LogTypeText操作日志类型 → 中文(静态表)
唯一校验CheckUniqueUX 快速报错(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)。

设计纪律(为什么基座不会腐化) ​

基座三铁律

  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
逃生舱手写方法(完全自由)多重继承/桥接方法

小蚂蚁云团队 · 提供技术支持