创建仓库

This commit is contained in:
2025-12-11 07:24:36 +08:00
commit 0d81c1792d
128 changed files with 15104 additions and 0 deletions

View File

@@ -0,0 +1,259 @@
---
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 测试。

View File

@@ -0,0 +1,247 @@
---
tags: []
aliases:
- 🏗️ AI 辅助基础设施构建 SOP (v2.1) - [错误处理与响应篇]
- 🏗️ AI 辅助基础设施构建 SOP (v2.0) - [错误处理与响应篇]
- 🏗️ AI 辅助基础设施构建 SOP (v1.1) - [错误处理与响应篇]
- 🏗️ AI 辅助基础设施构建 SOP (v1.0) - [错误处理与响应篇]
date created: 星期三, 十二月 10日 2025, 12:34:57 凌晨
date modified: 星期三, 十二月 10日 2025, 11:55:08 中午
---
# 🏗️ AI 辅助基础设施构建 SOP (v2.1) - [错误处理与响应篇]
**核心理念:**
1. **Contract First (契约优先):** 永远先定义对外暴露的 JSON 结构,再写内部 Go 结构体。
2. **DX Driven (体验驱动):** 在实现逻辑前,先写“伪代码”验证调用是否顺手。
3. **Atomic Delivery (原子交付):** 单次交互只生成一个文件,利用“上下文锚点”串联上下文。
---
## 📋 准备工作:变量与架构确认
在使用以下 Prompt 前,请确认上下文:
- `{语言/框架}`: Go 1.24+ / Gin
- `{模块路径}`:
- `internal/pkg/ecode` (Level 0: 错误码 + 错误实体 + 映射逻辑)
- `internal/pkg/app` (Level 1: HTTP 响应封装,依赖 `ecode`)
- `{架构约束}`: `ecode` 包零依赖;`app` 包依赖 `ecode`
---
## Phase 0: 原子化任务拆解 (The MECE Protocol)
**目的:** 将大需求拆解为一组符合 MECE 原则的微任务清单。
### 🤖 拆解者 Prompt (复制使用)
```Markdown
你现在是我的 **Tech Lead (技术负责人)**
我们要实现 `{模块名称}` 模块。为了防止代码生成中断和逻辑混乱,请不要直接开始写代码。
请先执行 **“MECE 任务拆解”**
**1. 架构约束分析:**
- 本模块遵循 Modular Clean Architecture。
- `internal/pkg/ecode`: 包含错误码常量、错误实体结构体、错误文案映射。**严禁依赖上层包**。
- `internal/pkg/app`: 包含 Gin 的 Response 封装。依赖 `ecode`
**2. 原子化切分:**
请将开发工作拆解为 3-5 个“原子任务步”。
- 每个步骤必须针对**单个物理文件**。
- 步骤必须遵循依赖顺序(底层先于上层)。
**3. 输出格式:**
请输出一个 **Markdown Checklist (执行清单)**
格式示例:
- [ ] **Step 1: {文件名}** - {核心职责} (依赖: 无)
- [ ] **Step 2: {文件名}** - {核心职责} (依赖: Step 1)
**模块需求:**
我们需要一套统一的 HTTP 错误处理机制,支持自定义业务错误码,统一返回 JSON 格式。
```
---
## Phase 0.5: API 签名锁定 (API Surface Lock)
**目的:** 在实现具体逻辑前,强制锁定所有 Public 方法的签名,防止实现阶段出现参数不一致。
### 🤖 Prompt 0.5: 生成接口定义
**[发送给 AI]:**
````markdown
在开始写代码前,请先为 `internal/pkg/app` 包定义 **Public API 签名 (Exported Functions)**。
请直接提供 `Responder` 接口定义或核心函数的函数头(无需函数体)。
**要求:**
1. **一致性:** 确认 `context` 参数的位置(建议统一作为第一个参数)。
2. **完整性:** 必须包含 `New`, `Success`, `Error` 以及我们刚才讨论的 `ErrorCtx` (处理 trace_id)。
3. **Go Doc:** 为每个方法写出符合 Go 标准的注释。
**期望输出示例:**
```go
// Response wraps the gin.Context for unified JSON response.
type Response struct { … }
// New creates a new Response wrapper.
func New(c *gin.Context) *Response { … }
// Success sends a successful response with data.
func (r *Response) Success(data any) { … }
```
````
---
## Phase 1: 契约定义 (Contract Definition)
**目的:** 确立“对外口径”。
### 🤖 Prompt 1: 定义 JSON 结构 (复制使用)
```Markdown
你现在是我的 **API 治理专家**。
请设计一套统一的 **HTTP 响应结构 (JSON Envelope)**。
**设计原则:**
1. **统一性:** 无论成功还是失败Body 结构一致。
2. **字段要求:** 必须包含 `code` (int), `msg` (string), `data` (any), `trace_id` (string)。
**任务:**
请给出以下 3 种场景的 JSON 响应示例,并解释设计理由:
- 场景 A: 成功返回对象。
- 场景 B: 成功返回空列表 (明确 `data` 是 `null` 还是 `[]`)。
- 场景 C: 业务错误 (如 Code 20001)。
**[关键补充约束]**
1. **安全性优先:** `app.Error(err)` 处理逻辑中,必须区分**用户可见文案**和**底层调试信息**。若 `err` 包含底层堆栈(如 SQL 错误JSON 中的 `msg` 必须降级显示为 `ecode` 定义的通用文案(如 "Internal Error"),严禁透传底层 Error String。
2. **HTTP 状态码:** 本项目强制执行 **"HTTP 200 OK + Business Code"** 策略。除非 Gin 框架层崩溃,否则 HTTP Status 永远为 200。
3. **Trace ID:** 假设 `c.GetString("trace_id")` 可以获取 ID请在 `app.New(c)` 时将其注入 Response 结构体。
```
---
## Phase 2: 体验验证 (DX Verification)
**目的:** 模拟业务层调用,防止基础设施“反人类”。
### 🤖 Prompt 2: 伪代码验证 (复制使用)
```Markdown
JSON 结构已确认。
假设我们已经有了 `internal/pkg/ecode` 和 `internal/pkg/app`。
请写一段 Gin Handler 的 **伪代码 (Pseudo-code)**,展示开发者该如何使用它们。
**验证重点:**
1. **业务错误:** 如何返回 `ecode.New(20001, "…")`
2. **响应封装:** 如何调用 `app.New(c).Success(data)`
3. **代码简洁性:** 避免大量的 `if err != nil` 重复代码。
请展示最优雅的写法。
```
---
## Phase 3: 迭代式核心实现 (Iterative Implementation)
**核心机制:** 这是一个**循环步骤**。请查看 Phase 0 生成的 Checklist**逐个文件**执行。
### 🔄 循环动作 A: 生成代码
**[用户动作]:** 复制 Checklist 中当前未完成的步骤(例如 "Step 1: 生成 ecode/code.go")。
**[发送 Prompt]:**
```Markdown
我们现在执行 **Step {N}**。
**任务目标:**
{粘贴 Phase 0 Checklist 中的当前步骤描述}
**上下文约束 (严禁修改):**
1. **JSON 契约:** `{粘贴 Phase 1 确认的 JSON}`
2. **DX 规范:** `{粘贴 Phase 2 确认的伪代码}`
3. **依赖控制:** 如果是 `ecode` 包,严禁引用 `app` 或 `gin`。
**输出要求:**
请仅生成该步骤对应的 `{文件名}` 源代码。不要生成测试代码。
**通用代码质量约束 (Linter Rules):**
1. **注释规范:** 所有 Exported (首字母大写) 的结构体、函数、常量必须包含符合 Go Doc 规范的注释。
2. **复杂度控制:** 确保 `gocyclo` (圈复杂度) 低于 10。如果逻辑复杂请拆分为私有函数。
3. **错误检查:** 严禁忽略 error 返回值(如 `json.Marshal`),必须处理或 Log。
4. **Lint 检查:** 生成的代码必须能通过 `errcheck` 和 `staticcheck`。
```
### 🔄 循环动作 B: 上下文锚点 (Context Anchoring)
**[用户动作]:** 代码生成并确认无误后,发送此 Prompt 以建立记忆锚点。
**[发送 Prompt]:**
```Markdown
已确认 `{文件名}` 代码无误。
请将该代码存入你的**短期记忆**,作为后续步骤的上下文依赖。
**不要重复输出它**。我们准备进入下一步。
```
_(重复 A -> B直到所有源码文件生成完毕)_
---
## Phase 4: 极限防御测试 (Extreme Defensive Testing)
**目的:** 模拟“最糟糕”的业务代码调用,确保基础设施不崩。
### 🤖 Prompt 4: 生成红队测试用例
```markdown
所有核心代码已生成。现在请为 `internal/pkg/app/response.go` 编写单元测试 `response_test.go`。
**请覆盖以下 4 个极端场景 (Test Cases):**
1. **Raw Error 降级:**
- **场景:** 传入 `errors.New("db connection broken")` (非 ecode 类型)。
- **断言:** HTTP 状态码为 500 (或 200+Code 50000)Msg 为 "Internal Server Error" (严禁泄漏原始错误信息)。
2. **Double Response 防护:**
- **场景:** 在同一个 Handler 中连续调用 `app.Success()` 两次。
- **断言:** 第二次调用应被忽略或记录 Warning 日志,且不应导致 Panic。
3. **Nil Data 安全:**
- **场景:** 调用 `app.Success(nil)`。
- **断言:** JSON 中的 `data` 字段应为 `null` (或 `{}`,取决于契约),不应 Panic。
4. **并发 Map 读写:**
- **场景:** 启动 100 个 Goroutine 并发调用 `ecode.GetMsg(code)`。
- **断言:** `test -race` 必须通过,无数据竞争。
请输出完整的 Test 代码。
```
---
## Phase 5: 最终验收 (SRE Review)
**目的:** 模拟运维视角审查。
### 🤖 Prompt 5: 找茬模式 (复制使用)
```Markdown
切换角色为 **SRE (站点可靠性工程师)**。
请审查上述所有代码ecode + app
**风险排查:**
1. **Panic 风险:** 是否有未捕获的 Panic 点?
2. **监控盲区:** 当前的 Error Log 是否包含了足够的上下文(如 StackTrace供排查
3. **状态码混淆:** 我们采用了“HTTP 200 + 业务码”模式,请确认这是否会影响网关层的 5xx 告警配置?
请简要列出 2-3 个优化建议。
```

View File

@@ -0,0 +1,161 @@
---
tags: []
aliases:
- "🛡️ 基础设施模块:错误处理与响应系统 (Infra: Error Handling & Response)"
date created: 星期三, 十二月 10日 2025, 12:10:32 中午
date modified: 星期三, 十二月 10日 2025, 12:12:02 中午
---
# 🛡️ 基础设施模块:错误处理与响应系统 (Infra: Error Handling & Response)
## 1\. 模块概述
本模块实现了 **Modular Clean Architecture** 中的基础设施层 (`Level 0` & `Level 1`),提供了一套统一的、安全的、可观测的 HTTP 响应机制。
**核心能力:**
- **统一契约:** 所有 API 响应成功、失败、Panic、404严格遵循 `{code, msg, data, trace_id}` 结构。
- **安全降级:** 自动识别业务错误与系统错误。对系统级错误(如 SQL 失败)进行“掩码”处理,防止敏感信息泄露。
- **可观测性:** 集成 Prometheus 埋点,通过 `X-Biz-Code` 实现业务级监控;全链路 TraceID 自动注入。
- **开发体验:** 提供 `Responder` 接口与工厂模式,支持 Handler 层的依赖注入与 Mock 测试。
-----
## 2\. 文件清单 (File Manifest)
以下代码位于项目根目录 `gitea-aliyun/Klein/enterprise-cms-core/` 下:
### Level 0: 基础领域层 (`internal/pkg/ecode`)
> **依赖:** 零依赖 (仅标准库)
| 文件名 | 类型 | 核心职责 |
| :--- | :--- | :--- |
| `code.go` | Const | **错误码注册表**。定义 `1xxxx` (系统) 和 `2xxxx` (业务) 常量。 |
| `msg.go` | Data | **文案映射**。维护全局 `map[int]string`,提供并发安全的 `GetMsg`。 |
| `error.go` | Struct | **错误实体**。实现 `error` 接口,支持 `WithMsg`/`WithDetails` 扩展。 |
| `ecode_test.go` | Test | 验证并发安全性及不可变性。 |
### Level 1: 应用工具层 (`internal/pkg/app`)
> **依赖:** `gin`, `ecode`
| 文件名 | 类型 | 核心职责 |
| :--- | :--- | :--- |
| `responder.go` | Interface | **接口定义**。定义 `Responder` 接口与 `Factory` 函数类型,用于解耦。 |
| `response.go` | Impl | **核心实现**。封装 Gin Context实现 JSON 序列化、错误清洗、监控埋点。 |
| `options.go` | Pattern | **功能选项**。提供 `WithTraceID` 等扩展配置。 |
| `response_test.go` | Test | 验证 JSON 契约、空指针防御及错误降级逻辑。 |
### Global: 全局中间件 (`internal/middleware`)
> **依赖:** `gin`, `pkg/app`, `pkg/ecode`, `prometheus`
| 文件名 | 类型 | 核心职责 |
| :--- | :--- | :--- |
| `recovery.go` | Safety | **Panic 兜底**。捕获 Panic 并转换为标准 JSON 500 响应。 |
| `not_found.go` | Route | **404 兜底**。将无路由请求转换为标准 JSON 404 响应。 |
| `metrics.go` | Monitor | **业务监控**。采集 `http_requests_total` 指标,包含 `biz_code` 标签。 |
-----
## 3\. 快速上手 (Quick Start)
### 3.1 定义新错误
`internal/pkg/ecode/code.go` 添加常量,并在 `msg.go` 添加文案。
```go
// code.go
const UserBalanceInsufficient = 20005
// msg.go
msg = map[int]string{
// ...
UserBalanceInsufficient: "User Balance Insufficient",
}
```
### 3.2 在 Handler 中使用 (推荐写法)
使用依赖注入的 `app.Factory` 创建响应器,而非直接调用 `app.New`
```go
import (
"github.com/gin-gonic/gin"
"gitea-aliyun/Klein/enterprise-cms-core/internal/pkg/app"
"gitea-aliyun/Klein/enterprise-cms-core/internal/pkg/ecode"
)
type UserHandler struct {
// 注入 Responder 工厂,便于测试 Mock
RespFactory app.Factory
}
func (h *UserHandler) Create(c *gin.Context) {
// 1. 创建响应器
resp := h.RespFactory(c)
// 2. 模拟业务逻辑
if err := h.Service.Create(); err != nil {
// 自动处理错误:如果是业务错误直接返回;如果是系统错误则降级并记录日志
resp.Error(err)
return
}
// 3. 成功响应
resp.Success(gin.H{"status": "created"})
}
```
### 3.3 系统接入 (Main.go)
在 HTTP Server 启动时注册全局中间件。
```go
r := gin.New()
// 1. Recovery (必须最先注册)
r.Use(middleware.Recovery())
// 2. Metrics (监控业务码)
r.Use(middleware.BusinessMetrics())
// ... 注册业务路由 ...
// 3. 404 处理 (最后注册)
r.NoRoute(middleware.NotFound())
```
-----
## 4\. 设计决策说明 (Architecture Decisions)
### A. HTTP 200 Always 策略
- **规则:** 除非网络层崩溃,所有接口(包括业务错误和系统错误)均返回 `HTTP 200 OK`
- **原因:** 防止网关Nginx/ALB拦截非 200 响应并替换 Body确保前端始终能解析 JSON 中的 `code`
### B. 安全掩码 (Security Masking)
- **输入:** `db.Query` 失败返回 `sql: connection refused`
- **输出:** 前端收到 `{ "code": 10000, "msg": "Internal Server Error" }`
- **日志:** 服务端 Error Log 记录原始堆栈。
- **目的:** 杜绝数据库结构、IP 等敏感信息通过报错接口泄露。
### C. 监控指标 (Metrics)
- **指标名:** `http_requests_total`
- **关键标签:** `biz_code` (业务状态码)。
- **SRE 告警:** 请针对 `biz_code >= 10000` (系统错误) 配置告警,而非 HTTP Status Code。
-----
## 5\. 测试指南
本模块已包含完整的单元测试与竞态检测 (Race Detection)。
```bash
# 运行所有测试
go test -v -race ./internal/pkg/...
```

View File

@@ -0,0 +1,27 @@
---
tags: []
date created: 星期三, 十二月 10日 2025, 11:00:25 上午
date modified: 星期三, 十二月 10日 2025, 11:56:10 中午
---
```plaintext
enterprise-cms-core/
├── internal/
│ ├── pkg/
│ │ ├── ecode/ # [Level 0] 基础领域层
│ │ │ ├── code.go # [Const] 纯常量定义 (ErrorCode Registry)
│ │ │ ├── error.go # [Type] 核心结构体定义 (struct Error)
│ │ │ ├── msg.go # [Data] 错误码文案映射 (var msg map[int]string)
│ │ │ └── ecode_test.go # [Test] 单元测试
│ │ │
│ │ └── app/ # [Level 1] 应用工具层
│ │ ├── responder.go # [Interface] 👈 修正点: 定义 type Responder interface
│ │ ├── response.go # [Impl] 定义 type Response struct (实现逻辑)
│ │ ├── options.go # [Pattern] 定义 Functional Options (配置扩展)
│ │ └── response_test.go # [Test] 单元测试
│ │
│ └── middleware/ # [Global]
│ ├── recovery.go # Panic 捕获
│ ├── not_found.go # 404 处理
│ └── metrics.go
```

View File

@@ -0,0 +1,548 @@
---
tags: []
aliases:
- Project Context Aggregation
date created: 星期三, 十二月 10日 2025, 11:10:48 上午
date modified: 星期三, 十二月 10日 2025, 11:12:52 上午
---
# Project Context Aggregation
> Source Items: 3
==== AI 辅助基础设施构建 SOP (v2.1) - 错误处理与响应篇.md ====
```markdown
# 🏗️ AI 辅助基础设施构建 SOP (v2.1) - [错误处理与响应篇]
**核心理念:**
1. **Contract First (契约优先):** 永远先定义对外暴露的 JSON 结构,再写内部 Go 结构体。
2. **DX Driven (体验驱动):** 在实现逻辑前,先写“伪代码”验证调用是否顺手。
3. **Atomic Delivery (原子交付):** 单次交互只生成一个文件,利用“上下文锚点”串联上下文。
---
## 📋 准备工作:变量与架构确认
在使用以下 Prompt 前,请确认上下文:
- `{语言/框架}`: Go 1.24+ / Gin
- `{模块路径}`:
- `internal/pkg/ecode` (Level 0: 错误码 + 错误实体 + 映射逻辑)
- `internal/pkg/app` (Level 1: HTTP 响应封装,依赖 `ecode`)
- `{架构约束}`: `ecode` 包零依赖;`app` 包依赖 `ecode`
---
## Phase 0: 原子化任务拆解 (The MECE Protocol)
**目的:** 将大需求拆解为一组符合 MECE 原则的微任务清单。
### 🤖 拆解者 Prompt (复制使用)
```Markdown
你现在是我的 **Tech Lead (技术负责人)**。
我们要实现 `{模块名称}` 模块。为了防止代码生成中断和逻辑混乱,请不要直接开始写代码。
请先执行 **“MECE 任务拆解”**
**1. 架构约束分析:**
- 本模块遵循 Modular Clean Architecture。
- `internal/pkg/ecode`: 包含错误码常量、错误实体结构体、错误文案映射。**严禁依赖上层包**。
- `internal/pkg/app`: 包含 Gin 的 Response 封装。依赖 `ecode`。
**2. 原子化切分:**
请将开发工作拆解为 3-5 个“原子任务步”。
- 每个步骤必须针对**单个物理文件**。
- 步骤必须遵循依赖顺序(底层先于上层)。
**3. 输出格式:**
请输出一个 **Markdown Checklist (执行清单)**
格式示例:
- [ ] **Step 1: {文件名}** - {核心职责} (依赖: 无)
- [ ] **Step 2: {文件名}** - {核心职责} (依赖: Step 1)
**模块需求:**
我们需要一套统一的 HTTP 错误处理机制,支持自定义业务错误码,统一返回 JSON 格式。
```
---
## Phase 0.5: API 签名锁定 (API Surface Lock)
**目的:** 在实现具体逻辑前,强制锁定所有 Public 方法的签名,防止实现阶段出现参数不一致。
### 🤖 Prompt 0.5: 生成接口定义
**[发送给 AI]:**
````markdown
在开始写代码前,请先为 `internal/pkg/app` 包定义 **Public API 签名 (Exported Functions)**。
请直接提供 `Responder` 接口定义或核心函数的函数头(无需函数体)。
**要求:**
1. **一致性:** 确认 `context` 参数的位置(建议统一作为第一个参数)。
2. **完整性:** 必须包含 `New`, `Success`, `Error` 以及我们刚才讨论的 `ErrorCtx` (处理 trace_id)。
3. **Go Doc:** 为每个方法写出符合 Go 标准的注释。
**期望输出示例:**
```go
// Response wraps the gin.Context for unified JSON response.
type Response struct { … }
// New creates a new Response wrapper.
func New(c *gin.Context) *Response { … }
// Success sends a successful response with data.
func (r *Response) Success(data any) { … }
```
````
---
## Phase 1: 契约定义 (Contract Definition)
**目的:** 确立“对外口径”。
### 🤖 Prompt 1: 定义 JSON 结构 (复制使用)
```Markdown
你现在是我的 **API 治理专家**。
请设计一套统一的 **HTTP 响应结构 (JSON Envelope)**。
**设计原则:**
1. **统一性:** 无论成功还是失败Body 结构一致。
2. **字段要求:** 必须包含 `code` (int), `msg` (string), `data` (any), `trace_id` (string)。
**任务:**
请给出以下 3 种场景的 JSON 响应示例,并解释设计理由:
- 场景 A: 成功返回对象。
- 场景 B: 成功返回空列表 (明确 `data` 是 `null` 还是 `[]`)。
- 场景 C: 业务错误 (如 Code 20001)。
**[关键补充约束]**
1. **安全性优先:** `app.Error(err)` 处理逻辑中,必须区分**用户可见文案**和**底层调试信息**。若 `err` 包含底层堆栈(如 SQL 错误JSON 中的 `msg` 必须降级显示为 `ecode` 定义的通用文案(如 "Internal Error"),严禁透传底层 Error String。
2. **HTTP 状态码:** 本项目强制执行 **"HTTP 200 OK + Business Code"** 策略。除非 Gin 框架层崩溃,否则 HTTP Status 永远为 200。
3. **Trace ID:** 假设 `c.GetString("trace_id")` 可以获取 ID请在 `app.New(c)` 时将其注入 Response 结构体。
```
---
## Phase 2: 体验验证 (DX Verification)
**目的:** 模拟业务层调用,防止基础设施“反人类”。
### 🤖 Prompt 2: 伪代码验证 (复制使用)
```Markdown
JSON 结构已确认。
假设我们已经有了 `internal/pkg/ecode` 和 `internal/pkg/app`。
请写一段 Gin Handler 的 **伪代码 (Pseudo-code)**,展示开发者该如何使用它们。
**验证重点:**
1. **业务错误:** 如何返回 `ecode.New(20001, "…")`
2. **响应封装:** 如何调用 `app.New(c).Success(data)`
3. **代码简洁性:** 避免大量的 `if err != nil` 重复代码。
请展示最优雅的写法。
```
---
## Phase 3: 迭代式核心实现 (Iterative Implementation)
**核心机制:** 这是一个**循环步骤**。请查看 Phase 0 生成的 Checklist**逐个文件**执行。
### 🔄 循环动作 A: 生成代码
**[用户动作]:** 复制 Checklist 中当前未完成的步骤(例如 "Step 1: 生成 ecode/code.go")。
**[发送 Prompt]:**
```Markdown
我们现在执行 **Step {N}**。
**任务目标:**
{粘贴 Phase 0 Checklist 中的当前步骤描述}
**上下文约束 (严禁修改):**
1. **JSON 契约:** `{粘贴 Phase 1 确认的 JSON}`
2. **DX 规范:** `{粘贴 Phase 2 确认的伪代码}`
3. **依赖控制:** 如果是 `ecode` 包,严禁引用 `app` 或 `gin`。
**输出要求:**
请仅生成该步骤对应的 `{文件名}` 源代码。不要生成测试代码。
**通用代码质量约束 (Linter Rules):**
1. **注释规范:** 所有 Exported (首字母大写) 的结构体、函数、常量必须包含符合 Go Doc 规范的注释。
2. **复杂度控制:** 确保 `gocyclo` (圈复杂度) 低于 10。如果逻辑复杂请拆分为私有函数。
3. **错误检查:** 严禁忽略 error 返回值(如 `json.Marshal`),必须处理或 Log。
4. **Lint 检查:** 生成的代码必须能通过 `errcheck` 和 `staticcheck`。
```
### 🔄 循环动作 B: 上下文锚点 (Context Anchoring)
**[用户动作]:** 代码生成并确认无误后,发送此 Prompt 以建立记忆锚点。
**[发送 Prompt]:**
```Markdown
已确认 `{文件名}` 代码无误。
请将该代码存入你的**短期记忆**,作为后续步骤的上下文依赖。
**不要重复输出它**。我们准备进入下一步。
```
_(重复 A -> B直到所有源码文件生成完毕)_
---
## Phase 4: 极限防御测试 (Extreme Defensive Testing)
**目的:** 模拟“最糟糕”的业务代码调用,确保基础设施不崩。
### 🤖 Prompt 4: 生成红队测试用例
```markdown
所有核心代码已生成。现在请为 `internal/pkg/app/response.go` 编写单元测试 `response_test.go`。
**请覆盖以下 4 个极端场景 (Test Cases):**
1. **Raw Error 降级:**
- **场景:** 传入 `errors.New("db connection broken")` (非 ecode 类型)。
- **断言:** HTTP 状态码为 500 (或 200+Code 50000)Msg 为 "Internal Server Error" (严禁泄漏原始错误信息)。
2. **Double Response 防护:**
- **场景:** 在同一个 Handler 中连续调用 `app.Success()` 两次。
- **断言:** 第二次调用应被忽略或记录 Warning 日志,且不应导致 Panic。
3. **Nil Data 安全:**
- **场景:** 调用 `app.Success(nil)`。
- **断言:** JSON 中的 `data` 字段应为 `null` (或 `{}`,取决于契约),不应 Panic。
4. **并发 Map 读写:**
- **场景:** 启动 100 个 Goroutine 并发调用 `ecode.GetMsg(code)`。
- **断言:** `test -race` 必须通过,无数据竞争。
请输出完整的 Test 代码。
```
---
## Phase 5: 最终验收 (SRE Review)
**目的:** 模拟运维视角审查。
### 🤖 Prompt 5: 找茬模式 (复制使用)
```Markdown
切换角色为 **SRE (站点可靠性工程师)**。
请审查上述所有代码ecode + app
**风险排查:**
1. **Panic 风险:** 是否有未捕获的 Panic 点?
2. **监控盲区:** 当前的 Error Log 是否包含了足够的上下文(如 StackTrace供排查
3. **状态码混淆:** 我们采用了“HTTP 200 + 业务码”模式,请确认这是否会影响网关层的 5xx 告警配置?
请简要列出 1-2 个优化建议。
```
```bash
==== 基础设施详细设计规格说明书.md ====
```markdown
---
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 测试。
```bash
==== 错误处理模块文件夹骨架.md ====
```markdown
---
tags: []
date created: 星期三, 十二月 10日 2025, 11:00:25 上午
date modified: 星期三, 十二月 10日 2025, 11:04:26 上午
---
```plaintext
enterprise-cms-core/
├── internal/
│ ├── pkg/
│ │ ├── ecode/ # [Level 0] 基础领域层
│ │ │ ├── code.go # [Const] 纯常量定义 (ErrorCode Registry)
│ │ │ ├── error.go # [Type] 核心结构体定义 (struct Error)
│ │ │ ├── msg.go # [Data] 错误码文案映射 (var msg map[int]string)
│ │ │ └── ecode_test.go # [Test] 单元测试
│ │ │
│ │ └── app/ # [Level 1] 应用工具层
│ │ ├── responder.go # [Interface] 👈 修正点: 定义 type Responder interface
│ │ ├── response.go # [Impl] 定义 type Response struct (实现逻辑)
│ │ ├── options.go # [Pattern] 定义 Functional Options (配置扩展)
│ │ └── response_test.go # [Test] 单元测试
│ │
│ └── middleware/ # [Global]
│ ├── recovery.go # Panic 捕获
│ └── not_found.go # 404 处理
```
```bash

View File

@@ -0,0 +1,38 @@
---
tags: []
aliases:
- "📋 Phase 0: 基础设施构建执行清单 (Execution Checklist)"
date created: 星期三, 十二月 10日 2025, 11:15:25 上午
date modified: 星期三, 十二月 10日 2025, 11:41:27 中午
---
# 📋 Phase 0: 基础设施构建执行清单 (Execution Checklist)
- [ ] **Step 1: `internal/pkg/ecode/code.go`** - **定义错误码注册表**
- **核心职责:** 仅定义 `int` 类型的 `const` 常量。包括基础设施类10000+和业务类20000+)错误码。
- **依赖:** 无 (Root Node)。
- **注意:** 需严格遵循文档中的“五位数字”分段规则。
- [ ] **Step 2: `internal/pkg/ecode/msg.go`** - **定义错误文案映射**
- **核心职责:** 初始化全局 `map[int]string`,提供 `GetMsg(code)` 方法。
- **依赖:** Step 1 (`code.go` 中的常量)。
- **注意:** 使用 `sync.RWMutex` 保护并发读写(虽然主要是读),文案必须是“用户安全”的。
- [ ] **Step 3: `internal/pkg/ecode/error.go`** - **实现核心错误实体**
- **核心职责:** 定义 `struct Error`,实现 `error` 接口,提供 `New()`, `Error()`, `Code()` 等方法。支持 `WithDetails` 等动态扩展。
- **依赖:** Step 1 & Step 2。
- **注意:** 这是 Service 层唯一允许引用的错误对象。
- [ ] **Step 4: `internal/pkg/app/options.go`** - **定义响应配置模式**
- **核心职责:** 定义 `type Option func(*Response)` 及常用的 Option 实现(如 `WithTraceID`)。
- **依赖:** 无(或仅依赖标准库)。
- **注意:** 先于 `response.go` 实现,以便主逻辑直接使用配置项,符合“开闭原则”。
- [ ] **Step 5: `internal/pkg/app/response.go`** - **实现 HTTP 响应封装**
- **核心职责:** 定义 `Response` 结构体,封装 `New`, `Success`, `Error` 方法。处理 JSON 序列化、TraceID 注入、以及将 `error` 接口清洗为 `ecode` 的逻辑。
- **依赖:** Step 3 (`ecode`), Step 4 (`options`), `Gin Context`
- **注意:** 需实现“双层信息架构”日志记录原始错误HTTP Body 返回安全文案。
- [ ] **Step 6: internal/pkg/app/responder.go** - **定义 Responder 接口**
- **核心职责:** 定义 `Responder` 接口,解耦具体实现,方便未来 Mock 测试。
- **依赖:** `app/response.go` (实现关系)。

View File

@@ -0,0 +1,145 @@
---
tags: []
aliases:
- 📦 统一响应结构定义 (The Contract)
date created: 星期三, 十二月 10日 2025, 11:23:15 上午
date modified: 星期三, 十二月 10日 2025, 12:12:46 中午
---
# Phase 1 统一响应结构定义 (The Contract)
## 📦 统一响应结构定义 (The Contract)
所有 HTTP 接口(无论成功与否)必须严格返回以下 JSON 结构:
```JSON
{
"code": 20001, // 业务状态码 (0=成功, 非0=错误)
"msg": "用户已存在", // 用户可见的提示文案 (Safe Message)
"data": { ... }, // 业务数据 payload (成功时返回,失败时通常为 null)
"trace_id": "a1b2-c3d4" // 全链路追踪 ID (必填,用于 SRE 排查)
}
```
---
## 🎨 场景示例与设计理由
### 🟢 场景 A: 成功返回对象 (Single Object)
请求: GET /api/v1/users/1001
HTTP Status: 200 OK
```JSON
{
"code": 0,
"msg": "OK",
"data": {
"user_id": 1001,
"nickname": "TechLead_01",
"avatar": "https://cdn.example.com/u/1001.jpg"
},
"trace_id": "0a1b2c3d-4e5f-6789-1234-567890abcdef"
}
```
**📌 设计理由:**
- **Code 0:** 符合业界惯例(如 Google/Tencent API`0` 明确表示逻辑执行成功。
- **Data 类型:** 返回具体的 Object。
---
### 🟡 场景 B: 成功返回空列表 (Empty List)
请求: GET /api/v1/articles?category=golang (假设该分类下无文章)
HTTP Status: 200 OK
```JSON
{
"code": 0,
"msg": "OK",
"data": {
"list": [],
"total": 0
},
"trace_id": "0a1b2c3d-4e5f-6789-1234-567890abcdef"
}
```
**📌 设计理由:**
- **Data 不为 `null`:** 对于列表型接口,`data` 内部的 `list` 字段必须返回空数组 `[]`,而不是 `null`
- _原因:_ 前端可以直接调用 `.map()``.forEach()` 而无需判空,极大降低前端出现 `Cannot read property 'map' of null` 的崩溃风险。
- **结构一致性:** 即使是列表,建议包裹在 Object 中(如 `{list: [], total: 0}`),方便未来扩展分页字段。
---
### 🔴 场景 C: 业务/系统错误 (Error Handling)
这里我们需要区分 **“预期内的业务错误”** 和 **“预期外的系统错误”**,但在 JSON 表现上它们必须是一致的。
Case C-1: 预期内的业务错误
场景: 用户尝试注册已存在的邮箱。
Service 层返回: ecode.UserAlreadyExist (Code: 20001)
```JSON
{
"code": 20001,
"msg": "用户已存在",
"data": null,
"trace_id": "0a1b2c3d-4e5f-6789-1234-567890abcdef"
}
```
Case C-2: 预期外的系统错误 (触发安全防御)
场景: 数据库突然断连GORM 返回 dial tcp 127.0.0.1:5432: connect: connection refused。
Service 层返回: 原生 error 对象。
```JSON
{
"code": 50000,
"msg": "Internal Server Error", // <--- 严禁显示 "dial tcp ..."
"data": null,
"trace_id": "0a1b2c3d-4e5f-6789-1234-567890abcdef"
}
```
**📌 设计理由:**
- **Security (安全降级):**
- `app.Error(err)` 的内部逻辑必须执行 **Type Assertion (类型断言)**
-`err``*ecode.Error`,则透传其 `msg`Case C-1
-`err` 是普通 `error`Case C-2视为系统级异常。**必须**将 JSON 中的 `msg` 强制重写为 `"Internal Server Error"` 或通用文案防止数据库表结构、IP 地址等敏感信息泄露给攻击者。
- **HTTP 200:** 即使是 Code 50000HTTP Status 依然保持 200。这确保了网关层Nginx/Gateway不会拦截 Body前端始终能解析 JSON 拿到 `code``trace_id` 用于展示和报错。
---
## 🛡️ 关键实现逻辑预告
为了实现上述契约,在接下来的 **Step 5: `internal/pkg/app/response.go`** 中,我们将实现如下核心逻辑:
1. **Trace ID 注入:**`New(c)` 时执行 `r.traceID = c.GetString("trace_id")`
2. **错误清洗:**
```Go
// 伪代码逻辑预览
func (r *Response) Error(err error) {
if e, ok := err.(*ecode.Error); ok {
// 业务错误:直接使用
r.json(e.Code(), e.Msg())
} else {
// 系统错误:记录原始日志,但在 JSON 中降级
log.Error("System Error", zap.Error(err), zap.String("trace_id", r.traceID))
r.json(ecode.ServerErr, "Internal Server Error") // 🔒 安全替换
}
}
```
这套设计已经满足了 **Phase 1** 的所有契约要求。

View File

@@ -0,0 +1,111 @@
---
tags: []
aliases:
- 🛠️ SOP 增强补丁:长代码/多文件分步生成策略
date created: 星期三, 十二月 10日 2025, 9:21:58 上午
date modified: 星期三, 十二月 10日 2025, 9:34:54 上午
---
# 🛠️ SOP 增强补丁:长代码/多文件分步生成策略
核心原则: 原子化交付 (Atomic Delivery)。
不要命令 AI “写完这个模块”。要命令 AI “写完这个文件” 或者 “写完这个结构体的具体方法”。
## 策略一:按物理文件拆分 (File-Level Sharding)
对于基础设施模块,通常可以自然拆分为多个文件。
操作动作:
修改 SOP 的 阶段三,不再一次性要求生成所有文件,而是分轮次请求。
### 🤖 优化后的 Prompt 序列
**第一轮:仅生成错误码定义**
```Markdown
我们先处理 `internal/pkg/code` 包。
请仅生成 `code.go` 文件。
内容包含:
1. package 声明。
2. const 常量定义(错误码)。
3. 暂时不要包含 `GetMsg` 的具体 map 映射逻辑,只定义常量。
```
**第二轮:生成错误码映射**
```Markdown
很好。现在请生成同目录下的 `msg.go` 文件。
内容包含:
1. `var msgFlags = map[int]string{…}` 映射表。
2. `func Text(code int) string` 方法的实现。
注意:请确保引用了 `code.go` 中定义的常量。
```
**第三轮:生成响应结构体**
```Markdown
现在进入 `internal/pkg/app` 包。
请生成 `response.go`
实现 `Response` 结构体定义、`NewResponse` 工厂函数,以及 `Success` 方法。
暂时**不要**实现 `Error` 相关方法,我们下一步单独写。
```
---
## 策略二:骨架先行,血肉填充 (Skeleton First, Flesh Later)
如果单文件(如 `service.go`)依然很长(超过 500 行),使用此策略。先生成接口和空方法,再逐个填充逻辑。
### 🤖 优化后的 Prompt 序列
**第一轮:生成骨架 (The Skeleton)**
```Markdown
我们要实现 `UserService`。由于逻辑复杂,请先生成**骨架代码**。
要求:
1. 定义 Struct 和所有 Method 的签名 (Signature)。
2. Method 内部留空,或仅写 `panic("implement me")`
3. 包含完整的各种 import 和结构体字段注入。
```
**第二轮:填充核心方法 (The Flesh)**
```Markdown
现在,请给出 `Register``Login` 这两个方法的完整实现代码。
请直接输出这两个函数的完整内容,无需重复输出 Struct 定义。
```
---
## 策略三:上下文锚点 (Context Anchoring)
当你完成了第一部分代码(比如 `code.go`),在开始下一部分之前,需要让 AI **“记住但不输出”**,以节省 Token 并防止幻觉。
### 🤖 关键 Prompt (用于连接两个步骤)
```Markdown
[用户动作]:
(将 AI 生成的 code.go 代码复制到本地 IDE确认无误)
[发送给 AI 的指令]:
已确认 `code.go` 代码无误。
现在,请将以下代码作为**“已知上下文”**存入你的短期记忆,**不要重复输出它**,但在后续生成 `msg.go` 时必须严格遵守其中的常量定义。
[粘贴 code.go 的完整代码]
```
---
## ✅ 你的工作流修正建议
回到你当前的 **错误处理模块**,我建议你采用 **“策略一:按物理文件拆分”**。
因为你的 `code` 包和 `app` 包本身就应该物理隔离,避免循环依赖。
**推荐的执行顺序:**
1. **Prompt:** 生成 `internal/pkg/code/err_code.go` (仅包含 const)。
2. **Prompt:** 生成 `internal/pkg/code/err_msg.go` (包含 map 和 Text 方法)。
3. **Prompt:** 生成 `internal/pkg/app/response.go` (包含 Response 结构体和 Success/Error 方法)。

View File

@@ -0,0 +1,63 @@
---
tags: []
aliases:
- 🧩 SOP 补丁MECE 任务拆解协议 (The MECE Protocol)
date created: 星期三, 十二月 10日 2025, 9:26:43 上午
date modified: 星期三, 十二月 10日 2025, 9:30:27 上午
---
# 🧩 SOP 补丁MECE 任务拆解协议 (The MECE Protocol)
**适用场景:** 任何代码行数预估 > 200 行或涉及多个文件交互的大型模块错误处理、RBAC 权限系统、订单状态机)。
**插入位置:** 在原有 SOP 的 **[阶段一:契约定义]** 之前执行。
---
## 阶段 0: 原子化任务拆解 (Atomic Decomposition)
**目的:** 将大需求拆解为一组符合 **MECE 原则 (相互独立,完全穷尽)** 的微任务。确保每个微任务的上下文长度都在 AI 的“舒适区”内,且具备清晰的依赖顺序。
### 🤖 拆解者 Prompt (复制使用)
```Markdown
你现在是我的 **Tech Lead (技术负责人)**
我们要实现 `{模块名称}` 模块。为了防止代码生成中断和逻辑混乱,请不要直接开始写代码。
请先执行 **“MECE 任务拆解”**
**1. 依赖分析:**
分析该模块涉及哪些物理文件?它们之间的依赖关系是什么?(例如B 依赖 A则 A 必须先完成)。
**2. 原子化切分:**
将开发工作拆解为 3-5 个“原子任务步”。
- 每个步骤必须针对**单个物理文件**或**一组紧密相关的函数**。
- 每个步骤必须是独立的,可执行的。
**3. 输出格式:**
请输出一个 **Markdown Checklist (执行清单)**
格式示例:
- [ ] **Step 1: {文件名}** - {核心职责} (依赖: 无)
- [ ] **Step 2: {文件名}** - {核心职责} (依赖: Step 1)
**模块上下文:**
{此处粘贴你的需求或 PRD 片段}
```
---
## ✅ 你的工作流变更 (Workflow Update)
引入此补丁后,你的新工作流变成了:
1. **Phase 0 (New):** 发送拆解 Prompt -> **获得清单**
2. **Phase 1 (User Action):** 选中清单中的 **Step 1** -> 发送 Prompt“请执行 Step 1生成 `code.go`…”。
3. **Phase 2 (User Action):** 拿到代码 -> 存入本地 -> **锚点确认** ("Step 1 已完成,代码如下…")。
4. **Phase 3 (User Action):** 选中清单中的 **Step 2** -> 发送 Prompt“基于 Step 1请执行 Step 2…”。
### 为什么这样做有效?
1. **Token 节省:** AI 在生成 Step 2 时,不需要你在 Prompt 里重新描述 Step 1 的需求,只需要把 Step 1 已经生成的代码贴给它作为 Context 即可。
2. **避免幻觉:** 因为每个 Step 只有一个目标AI 不会“顾头不顾尾”。
3. **断点续传:** 如果 Step 2 生成错了,你只需要重新生成 Step 2而不需要推倒重来。