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

117 lines
5.5 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.7 横切机制
:::tip 概述
审计字段盖章、软删除改写、租户隔离三类横切逻辑不在任何业务代码中出现——它们统一在 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 多租户隔离](../core/tenant) 与 [5.9 数据权限](../core/data-scope)。
## 请求横切:中间件与包装层分工
```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 配置文件说明](../quickstart/env-vars)。
## 横切开发纪律
+ 新增横切逻辑优先问:属于请求横切(中间件)/ 数据横切(钩子)/ 契约横切(Bind)/ 配置横切(utility/config)哪一层?不要散落在业务代码。
+ 修改钩子必须全表回归——它影响 25 张表的所有写入。
+ 钩子内禁止业务分支判断(如"某模块跳过盖章")——确有需要走逃生舱(显式值优先于自动盖章)。
+ 缓存类横切(字典/身份/租户摘要)必须有明确失效点或短 TTL 兜底(见 [4.8 缓存架构设计](./cache))。
+ 业务代码读配置一律用 `g.Cfg().Get/MustGet`,不要用 `GetWithEnv`(文件优先,环境变量永不生效)。