Files
Inbox/Go项目实战/03_基础设施/01_错误处理/01_基础设施详细设计规格说明书.md
2025-12-11 07:24:36 +08:00

260 lines
11 KiB
Markdown
Raw Permalink 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.

---
tags: []
aliases:
- 🏗️ Infrastructure Design Specifications (v1.2)
- 🏗️ Infrastructure Design Specifications (v1.1)
- 🏗️ Infrastructure Design Specifications (v1.0)
date created: 星期三, 十二月 10日 2025, 9:41:53 上午
date modified: 星期三, 十二月 10日 2025, 10:37:49 上午
---
# 🏗️ Infrastructure Design Specifications (v1.2)
项目名称: Enterprise-CMS-Core
模块: Infrastructure (Error Handling & Response)
版本: 1.2.0 (Refined)
状态: [✅ 已锁定]
---
## 1. 设计目标 (Design Objectives)
本模块旨在构建一套**统一的、类型安全的、框架解耦**的 HTTP 响应与错误处理机制。
- **统一性:** 无论成功与否API 必须返回结构一致的 JSON Envelope。
- **可观测性:** 错误必须携带业务语义ErrorCode而非仅返回 HTTP 500。
- **解耦性:** 业务逻辑层 (Service) 不感知 HTTP 框架 (Gin),仅通过 Go 原生 `error` 接口交互。
- **高内聚:** 错误码定义与错误实体封装在同一包内,减少调用摩擦。
---
## 2. 技术选型基线 (Tech Stack Baseline)
|**组件**|**选型**|**约束说明**|
|---|---|---|
|**HTTP Context**|`github.com/gin-gonic/gin`|仅在 `internal/pkg/app` (Level 1) 和 `handler` 层使用。**严禁**在 `service` 层引入。|
|**Error Handling**|Go Standard Library|使用 Go 1.13+ `errors` (`Is`, `As`, `New`) 和 `fmt.Errorf`。**严禁**引入第三方 error 库 (如 `pkg/errors`)。|
|**Serialization**|`encoding/json`|使用标准库。MVP 阶段暂不引入 `json-iterator`。|
|**Concurrency**|`sync.RWMutex`|用于保护错误码 Map 的并发读取(读多写少场景)。|
| **Tracing** | Gin Keys | Trace ID 必须由前置中间件或网关注入。Gin Context Key 约定为 `"X-Trace-ID"`。 |
---
## 3. 核心设计模式 (Design Patterns)
### 3.1 响应封装Context Object & Factory
采用 **“上下文对象”** 模式对 `gin.Context` 进行封装,提供链式调用的体验。
- **模式:** `app.New(c).Success(data)`
- **优势:** 屏蔽底层框架差异,统一入口。
### 3.2 错误处理:安全与动态机制 (Security & Dynamics)
- **双层信息架构:**
- **User Msg (Safe):** JSON Body 中的 `msg` 字段。**仅**允许返回 `ecode` 中定义的静态文案,或经过白名单过滤的动态参数(如参数名)。
- **Log Detail (Unsafe):** 服务端日志。必须记录完整的 `err.Error()`包含堆栈、SQL 错误、`fmt.Errorf` 包装的底层原因)。
- **动态文案支持:**
- `ecode` 包需提供 `WithMsg(msg string)``WithDetails(args …any)` 方法,用于**安全地**覆盖默认文案。
- **示例:** `return ecode.InvalidParams.WithMsg("Email 格式错误")`
### 3.3 状态码管理Centralized Registry
采用 **“集中式注册表”** 模式。
- **约束:** 所有业务错误码 (Business Code) 必须在 `internal/pkg/ecode` 包中定义为 `const`
- **禁止:** 严禁在业务代码中硬编码数字Magic Number
### 3.4 错误码号段分配Error Code Allocation
结构定义:
错误码采用 5 位数字结构A BB NN
- **A (万位):** 模块/领域 (1=Infra, 2=User, 3=Content…)
- **BB (千百位):** 组件/子模块分类
- **NN (十个位):** 具体错误流水号
#### 1. 基础设施层 (System / Infra) - `10000 - 19999`
针对基础设施,**必须**严格遵守以下二级分类,严禁混用:
|**二级区间 (Sub-Range)**|**组件归属 (Component)**|**典型示例 (Examples)**|
|---|---|---|
|**10000 - 10099**|**Server General**|`10000` (Success), `10001` (Unknown Error), `10002` (Panic Recovered)|
|**10100 - 10199**|**Database (Internal)**|`10100` (DB Connection Lost), `10101` (SQL Syntax Error) - _注意业务查空属业务码不在此列_|
|**10200 - 10299**|**Cache (Redis)**|`10200` (Redis Timeout), `10201` (Key Evicted Unexpectedly)|
|**10300 - 10399**|**Serialization**|`10300` (JSON Marshal Failed), `10301` (Invalid Request Body)|
|**10400 - 10499**|**Middleware/Gateway**|`10400` (Too Many Requests/Rate Limit), `10401` (Route Not Found)|
|**10500 - 10599**|**3rd Party API**|`10500` (External Service Unavailable), `10501` (SMS Send Failed)|
#### 2. 业务模块层 (Business Modules) - `20000+`
业务模块建议参考同等逻辑进行二级划分(由各模块负责人定义,但建议遵循以下范式):
|**一级区间**|**模块**|**二级区间示例**|
|---|---|---|
|**20000 - 29999**|**User / Auth**|`200xx` (基础账户), `201xx` (登录/Token), `202xx` (RBAC 权限), `203xx` (KYC 认证)|
|**30000 - 39999**|**Content (CMS)**|`300xx` (文章), `301xx` (分类/标签), `302xx` (评论), `303xx` (审核流)|
---
## 4. 交互协议与数据流 (Interaction Protocol)
### 4.1 JSON 响应契约 (The Contract)
所有 HTTP 接口返回的 Body 必须符合以下结构:
```JSON
{
"code": 20001, // 业务状态码 (0=成功, 非0=错误)
"msg": "用户已存在", // 开发者提示/用户提示
"data": { }, // 业务数据 (成功时为 Object/Array, 失败时为 null)
"trace_id": "abc-123" // 必填。取值优先级: c.GetHeader("X-Trace-ID") -> c.GetString("X-Trace-ID") -> UUID生成
}
```
### 4.2 HTTP 状态码策略 (Status Code Policy)
本项目采用 **"Hybrid 策略 "**
- **HTTP 200 OK:**
- 所有 **业务逻辑错误** (Code `2xxxx` - `4xxxx`)。
- 前端通过 Body 中的 `code != 0` 判断业务异常。
- _理由:_ 避免网关(如 Nginx拦截 4xx 响应并替换为默认错误页,导致前端拿不到 JSON 数据。
- **HTTP 500 Internal Server Error:**
- 所有 **基础设施错误** (Code `1xxxx`),包括 Panic、数据库断连、Redis 超时。
- _理由:_ 触发云厂商负载均衡器 (LB) 的熔断机制,将流量切出故障节点。
- **HTTP 401/403:**
- 仅用于网关层面的拦截(如 JWT 格式错误),业务层鉴权失败建议走 HTTP 200 + Code `20101`
### 4.3 跨层交互时序 (Cross-Layer Flow)
```mermaid
sequenceDiagram
participant C as Controller (Handler)
participant S as Service (Domain)
participant I as Infra (pkg/app)
participant E as Ecode (pkg/ecode)
C->>I: app.New(c) 初始化
C->>S: Call Business Logic
alt 成功
S-->>C: return (data, nil)
C->>I: app.Success(data)
I-->>Client: JSON {code:0, data:…}
else 失败 (业务错误)
S-->>C: return (nil, ecode.New(20001))
C->>I: app.Error(err)
I->>I: errors.As(err) -> 提取 Code 20001
I-->>Client: JSON {code:20001, msg:"…"}
else 失败 (系统错误)
S-->>C: return (nil, errors.New("DB error"))
C->>I: app.Error(err)
I->>I: errors.As(err) -> 失败 (Fallback)
I-->>Client: JSON {code:50000, msg:"Internal Error"}
end
```
---
## 5. 目录结构与职责 (Directory & Responsibilities)
```Plaintext
internal/
├── middleware/ # [New] 全局中间件
│ ├── recovery.go # Panic 捕获 -> 转换为 ecode.ServerError (50000)
│ └── not_found.go # 404 捕获 -> 转换为 ecode.NotFound (40400)
└── pkg/
├── ecode/ # [Level 0] 错误核心包 (无内部依赖)
│ ├── code.go # const 常量定义 (UserNotFound = 20001)
│ ├── msg.go # 错误码文案映射 (Map & GetMsg)
│ └── error.go # Error 结构体定义 (New, Parse 方法)
└── app/ # [Level 1] HTTP 响应封装 (依赖 gin, ecode)
└── response.go # NewResponse, Success, Error 方法
```
---
## 6. 开发规范与 Linter 规则 (Linting Rules)
1. **包引用原则:**
- `ecode` 包必须保持零依赖(只依赖标准库)。
- `app` 包依赖 `ecode`
2. **Service 层纯净性:**
- `internal/domain/service` 代码中**严禁出现** `import "github.com/gin-gonic/gin"`
- `internal/domain/service` 代码中**严禁出现** `import "enterprise-cms-core/internal/pkg/app"`
- 只允许引入 `internal/pkg/ecode`
3. **错误包装与响应清洗:**
- **Log:** `app.Error(err)` 内部必须将 `err` 的完整堆栈打印到 Zap 日志中。
- **Response:**
-`err` 可被断言为 `*ecode.Error`,则取其 `Msg` 字段返回。
-`err` 仅为普通 `error` (如 DB error)**严禁**直接将其内容返回给前端,必须统一兜底返回 `ecode.ServerError` 的文案("Internal Server Error")。
4. **全局兜底机制 (Global Safety Net):**
- 项目必须在 `internal/middleware` 中实现 `Recovery` 中间件。
- **严禁**让 Gin 默认的 Panic 堆栈直接输出到 HTTP Body。
- **必须**捕获所有 Panic并调用 `app.Error(ecode.ServerError)` 统一输出为符合 JSON 契约的格式 (`{"code": 50000, "msg": "Internal Server Error", …}`)。
---
## 7. 工程化实施标准 (Engineering Standards)
### 7.1 代码风格契约 (Code Style Contract)
为确保代码长期可维护,生成的代码必须严格遵守以下 Go 惯用语 (Idioms)
1. **命名规范:**
- **缩写:** 使用全大写缩写 (如 `ServeHTTP`, `ID`, `URL`),严禁 `Url`, `Id`
- **局部变量:** 保持短小 (如 `ctx`, `err`, `req`),避免 Java 式的长命名 (如 `requestContext`, `errorObject`)。
- **工厂方法:** `ecode` 包内使用 `New()`, `app` 包内使用 `NewResponse()`
2. **代码组织:**
- **Import 分组:** 标准库 -> 第三方库 -> 内部库 (enterprise-cms-core/…)。
- **Guard Clauses:** 优先使用“卫语句”提前返回,减少 `else` 嵌套层级。
### 7.2 注释与文档 (Documentation)
为了提升团队协作效率,所有 Exported (首字母大写) 的类型、函数、常量必须包含符合 GoDoc 规范的**中文注释**。
- **格式规范:** `// FunctionName 中文描述…`
- **关键:** 注释**必须**以函数/变量名开头,且与中文描述之间**保留一个空格**。这是 Go 官方工具链解析文档的标准要求。
- **内容重心:**
- **摘要:** 第一行简明扼要地说明“它是做什么的”。
- **详情 (可选):** 解释 **"Why" (设计意图)** 和 **"Caveats" (副作用/注意事项)**,而非翻译代码逻辑。
- **示例:**
```Go
// Success 向客户端写入标准的 JSON 成功响应。
//
// 注意:
// 1. 无论业务逻辑如何,此方法会将 HTTP 状态码强制设置为 200。
// 2. data 字段若为 nil将序列化为 JSON 的 null。
func (r *Response) Success(data any) { … }
// UserNotFound 表示用户不存在的业务错误码 (20001)。
const UserNotFound = 20001
```
### 7.3 可扩展性设计 (Extensibility Patterns)
为了应对未来需求变更,本模块需采用以下模式:
1. **Functional Options (针对 `app` 包):**
- 构造 `Response` 对象时,应支持 Option 模式,以便未来无需破坏函数签名即可添加新字段(如 TraceID, DebugInfo
- *定义:* `type Option func(*Response)`
- *签名:* `func New(c *gin.Context, opts …Option) *Response`
2. **Interface Segregation (接口隔离):**
- 虽然 `ecode` 是基础值对象,但 `app` 层若涉及复杂逻辑,应定义 `Responder` 接口,方便 Mock 测试。