Skip to content

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),模块一行声明即生效

详见 5.8 多租户隔离 与 5.9 数据权限。

请求横切:中间件与包装层分工 ​

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(文件优先,环境变量永不生效)。

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