# 4.8 横切机制 :::tip 概述 横切关注点不在业务代码中重复出现——认证、租户、日志、审计、软删分别由中间件链与 GORM 全局钩子在固定位置统一执行。本页给出"每个红线由谁执行"的总图。 ::: ## 红线执行点总览 | 红线 | 执行点 | 时机 | |---|---|---| | 统一信封 + panic 恢复 | Response 中间件 | 响应写出时 | | 身份认证 | JWT 中间件 | 请求进入业务前 | | 租户上下文 | Tenant 中间件 + Scope | 请求级 + SQL 级 | | 权限校验 | Authz 中间件(元数据驱动) | handler 前 | | 操作日志 | OperationLog 中间件 | 请求结束后异步 | | 限流 | RateLimit 中间件 | 认证前 | | 审计字段盖章 | GORM create/update 钩子 | 写 SQL 前 | | 物理删除拦截 | GORM delete 钩子 | 删除 SQL 前 | | 软删过滤 | repository Scope(ApplyCtx) | 查询构建时 | ## 审计盖章(createStamp / updateStamp) ```text Insert 前:补 create_user / create_time / del_flag=0 / tenant_id(租户表) Update 前:盖 update_user / update_time ``` 业务代码**永不**手工赋值审计列——这是钩子存在的意义。create_user/update_user 从 ctx 身份取。 ## 物理删除拦截(blockPhysicalDelete) 一切物理 DELETE 默认被拒绝。确需物理清理的场景(如 LogClean 任务清超期日志): ```go ctx = hooks.WithPhysicalDelete(ctx) // 显式放行,作用域仅此 ctx 链路 ``` ## 查询侧 Scope(ApplyCtx) `repository.Ctx(ctx)` 在 SQL 构建前以 GORM Scope 注入: ```text 所有表: del_flag = 0 租户白名单表: 非平台级身份追加 tenant_id IN (0, 自身租户) ``` 租户判定依据 `hooks.tenantTables` 白名单(9 张表)——**新增租户级表必须同步登记**。 ## 身份与上下文的流动 ```text JWT 中间件 解析 Token → ctx 写入用户身份(loader 经 init 接线) Tenant 中间件 加载租户 → ctx 写入租户身份 ↓ service / repository / 钩子 从 ctx 读身份(盖章、隔离、日志) ``` ctx 是横切数据的唯一载体——不使用全局变量传递请求态数据。 ## 日志的三个层次 | 层次 | 载体 | 内容 | |---|---|---| | 运行日志 | zap(log/ 目录) | 排查用,带 trace_id | | 操作日志 | gin_operation_log 表 | 审计用,谁在何时对什么做了什么 | | 登录日志 | gin_login_log 表 | 登录行为专项 | 操作日志的采集内容由 RouteSpec 声明驱动(Log/LogType),异步落库不阻塞响应。 ## 演示模式(Demo 中间件) `app.demo=true` 时写操作一律拒绝,读操作与登录类公开接口放行——演示环境保护数据的同时不影响体验。 ## 横切的可裁剪性 WireCheck 保证接线缺失只降级不崩:去掉 auth 模块则身份扩展加载跳过,去掉 operation_log 则日志丢弃。裁剪部署形态由启动告警明确提示。 ## 下一步 → [4.9 缓存架构设计](/zh/architecture/cache.html)