4.7 横切机制
概述
审计字段盖章、软删除改写、租户隔离三类横切逻辑不在任何业务代码中出现——它们统一在 gdb 模型钩子层(internal/model/hooks)执行。配合中间件链与 Bind 包装层,构成本项目的横切机制全景。
横切机制全景
text
请求横切(中间件链) 数据横切(模型钩子) 契约横切(Bind 包装层)
CORS Insert 盖章 权限校验(gmeta permission)
Response(panic/元信息) Update 盖章 参数解析(gvalid)
RateLimit Delete → 软删 信封写出
Demo del_flag=0 注入 OpenAPI 补注册
JWT(身份) tenant_id 注入
Tenant(状态)
OperationLog(采集)全局钩子(internal/model/hooks/hooks.go)
钩子经 dao.{X}.Ctx(ctx) 构造模型时挂载(自定义模板 dao_index.tpl):
| 钩子 | 触发 | 行为 |
|---|---|---|
| Insert | 任意 Insert | 主键交数据库自增(不填 id);补 del_flag=0、create_time、create_user(ctx 用户名,回退真实姓名);租户级表自动携带 tenant_id |
| Update | 任意 Update | 自动盖章 update_time / update_user(仅 map 数据生效) |
| Delete | 任意 Delete | 物理删除一律改写为 UPDATE del_flag=1 |
租户盖章规则(Insert)
text
租户用户插入未显式指定 tenant_id → 填自身(防逃逸)
超管未显式指定 → 填 tenant.defaultId(默认 1)
显式指定具体值 → 从显式值
显式 0 → 建平台共享行查询条件注入(hooks.ApplyCtx)
text
所有表: WHERE del_flag = 0
租户级表且非平台上下文:WHERE tenant_id IN (0, 当前租户)
超管(ctx tenant_id=0):不过滤,全量可见租户级表清单(tenantTables,9 张)
text
goframe_user / role / dept / level / position / article / category / notice / link新增租户级表必须同步修改 hooks.go 的 tenantTables(代码生成器不自动维护此清单)。漏加的后果是租户数据互相可见。
两层行级过滤的叠加关系
text
第一层(硬规则,不可配置):租户隔离
dao.Ctx / hooks.ApplyCtx 注入 tenant_id IN(0,自身)
先于一切业务过滤,任何模块不可绕过
第二层(可配置):角色数据范围(data_scope 四档)
ModuleSpec.DataScope 挂点,在 applyFilters 链末环叠加:
Like → Eq → Keyword → Filter → DataScope
共享工厂 ApplyDataScope(deptColumn),模块一行声明即生效请求横切:中间件与包装层分工
text
中间件链(跨路由共性) Bind 包装层(路由级个性)
认证状态(401) 权限码校验(403,读 gmeta)
租户状态(1005/1006) 参数解析与校验(gvalid)
限流(429)/演示(1015) 信封写出
日志采集(不短路) OpenAPI 元数据权限校验不设独立中间件——因为它需要每路由不同的权限码,放在读路由元数据的包装层最自然(与日志注解同源,"注解即事实")。
日志横切(OperationLog)
text
gmeta log 标签 → 路由元数据 → 中间件组装 entry(脱敏 param/result)
→ 有界通道 2048(满则丢弃告警,不阻塞业务)
→ 单 worker 攒批(100 条或 1s flush)
→ LogSink(operation_log 模块 init 注册)→ INSERT goframe_operation_log脱敏规则:param 中 password/oldPassword/newPassword/salt/access_token/refresh_token → ***;result 截断 8000 字节(UTF-8 边界回退)。
配置横切:环境变量覆盖(utility/config)
text
启动入口 cmd.Run 最先执行 config.RegisterEnvOverride()
→ 包装默认文件适配器为 env-first 适配器(gcfg.Adapter 官方扩展点)
→ 全部 Get/MustGet 读取点零改动获得覆盖能力(含 gdb/gredis 经整节点读连接配置)text
优先级 GF_ 环境变量 > 配置文件(12-Factor;未设环境变量时行为不变)
键名 配置路径大写下划线 + GF_ 前缀(database.default.link → GF_DATABASE_DEFAULT_LINK)
空值 环境变量空串视为未设置,不清空文件有效值
范围 仅覆盖配置文件中已存在的键(新增键先在 config.yaml 声明)典型场景:容器部署注入 GF_DATABASE_DEFAULT_LINK / GF_FILE_UPLOADDIR / GF_JWT_SECRET,镜像内 config.yaml 保持默认不动(同一镜像可部署多环境)。键名规则与 Docker 注入方式见 2.5 配置文件说明。
横切开发纪律
- 新增横切逻辑优先问:属于请求横切(中间件)/ 数据横切(钩子)/ 契约横切(Bind)/ 配置横切(utility/config)哪一层?不要散落在业务代码。
- 修改钩子必须全表回归——它影响 25 张表的所有写入。
- 钩子内禁止业务分支判断(如"某模块跳过盖章")——确有需要走逃生舱(显式值优先于自动盖章)。
- 缓存类横切(字典/身份/租户摘要)必须有明确失效点或短 TTL 兜底(见 4.8 缓存架构设计)。
- 业务代码读配置一律用
g.Cfg().Get/MustGet,不要用GetWithEnv(文件优先,环境变量永不生效)。