Files
tyapi-server/api.mdc

2954 lines
86 KiB
Plaintext
Raw Normal View History

2025-07-02 16:17:59 +08:00
# TYAPI Server API 开发规范
## 🏗️ 项目架构概览
本项目采用 **DDD领域驱动设计** + **Clean Architecture** + **事件驱动架构**,基于 Gin 框架构建的企业级后端 API 服务。
## 📋 目录结构规范
```
internal/
├── domains/ # 领域层
│ └── user/ # 用户领域
│ ├── dto/ # 数据传输对象
│ ├── entities/ # 实体
│ ├── events/ # 领域事件
│ ├── handlers/ # HTTP处理器
│ ├── repositories/ # 仓储接口实现
│ ├── routes/ # 路由配置
│ ├── services/ # 领域服务
│ └── validators/ # 验证器
├── shared/ # 共享基础设施
│ ├── interfaces/ # 接口定义
│ ├── middleware/ # 中间件
│ ├── http/ # HTTP基础组件
│ └── ...
└── config/ # 配置管理
```
## 🎯 业务分层架构
### 1. 控制器层 (Handlers)
```go
// internal/domains/user/handlers/user_handler.go
type UserHandler struct {
userService *services.UserService // 注入领域服务
response interfaces.ResponseBuilder // 统一响应构建器
validator interfaces.RequestValidator // 请求验证器
logger *zap.Logger // 结构化日志
jwtAuth *middleware.JWTAuthMiddleware // JWT认证
}
// 标准CRUD处理器方法
func (h *UserHandler) Create(c *gin.Context) {
var req dto.CreateUserRequest
// 1. 请求验证
if err := h.validator.BindAndValidate(c, &req); err != nil {
return // 验证器已处理响应
}
// 2. 调用领域服务
user, err := h.userService.Create(c.Request.Context(), &req)
if err != nil {
h.logger.Error("Failed to create user", zap.Error(err))
h.response.BadRequest(c, err.Error())
return
}
// 3. 统一响应格式
response := dto.FromEntity(user)
h.response.Created(c, response, "User created successfully")
}
```
### 2. 服务层 (Services)
```go
// internal/domains/user/services/user_service.go
type UserService struct {
repo *repositories.UserRepository // 数据访问
eventBus interfaces.EventBus // 事件总线
logger *zap.Logger // 日志
}
func (s *UserService) Create(ctx context.Context, req *dto.CreateUserRequest) (*entities.User, error) {
// 1. 业务规则验证
if err := s.validateCreateUser(req); err != nil {
return nil, err
}
// 2. 实体创建
user := entities.NewUser(req.Username, req.Email, req.Password)
// 3. 数据持久化
if err := s.repo.Create(ctx, user); err != nil {
return nil, err
}
// 4. 发布领域事件
event := events.NewUserCreatedEvent(user.ID, user.Username, user.Email)
s.eventBus.PublishAsync(ctx, event)
return user, nil
}
```
### 3. 仓储层 (Repositories)
```go
// internal/domains/user/repositories/user_repository.go
type UserRepository struct {
db *gorm.DB // 数据库连接
cache interfaces.CacheService // 缓存服务
logger *zap.Logger // 日志
}
func (r *UserRepository) Create(ctx context.Context, user *entities.User) error {
// 使用事务确保数据一致性
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Create(user).Error; err != nil {
return err
}
// 清除相关缓存
r.cache.Delete(ctx, fmt.Sprintf("user:count"))
return nil
})
}
```
### 4. DTO 层 (数据传输对象)
```go
// internal/domains/user/dto/user_dto.go
type CreateUserRequest struct {
Username string `json:"username" binding:"required,min=3,max=50" validate:"username"`
Email string `json:"email" binding:"required,email" validate:"email"`
Password string `json:"password" binding:"required,min=8" validate:"password"`
DisplayName string `json:"display_name" binding:"max=100"`
}
type UserResponse struct {
ID string `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
DisplayName string `json:"display_name"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// 实体转换函数
func FromEntity(user *entities.User) *UserResponse {
return &UserResponse{
ID: user.ID,
Username: user.Username,
Email: user.Email,
DisplayName: user.DisplayName,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
}
}
```
## 🛣️ 路由配置规范
### 1. DDD 领域路由设计模式
```go
// internal/domains/user/routes/user_routes.go
type UserRoutes struct {
handler *handlers.UserHandler
jwtAuth *middleware.JWTAuthMiddleware
}
func (r *UserRoutes) RegisterRoutes(router *gin.Engine) {
// API版本组
v1 := router.Group("/api/v1")
// 🏢 用户域路由组 - 按域名组织
users := v1.Group("/users")
{
// 🌍 公开路由(不需要认证)
users.POST("/send-code", r.handler.SendCode) // 发送验证码
users.POST("/register", r.handler.Register) // 用户注册
users.POST("/login", r.handler.Login) // 用户登录
// 🔐 需要认证的路由
authenticated := users.Group("")
authenticated.Use(r.jwtAuth.Handle())
{
authenticated.GET("/me", r.handler.GetProfile) // 获取当前用户信息
authenticated.PUT("/me/password", r.handler.ChangePassword) // 修改密码
// 未来扩展示例:
// authenticated.PUT("/me", r.handler.UpdateProfile) // 更新用户信息
// authenticated.DELETE("/me", r.handler.DeleteAccount) // 删除账户
// authenticated.GET("/me/sessions", r.handler.GetSessions) // 获取登录会话
}
}
// 📱 SMS验证码域路由组如果需要单独管理SMS
sms := v1.Group("/sms")
{
sms.POST("/send", r.handler.SendCode) // 发送验证码
// 未来可以添加:
// sms.POST("/verify", r.handler.VerifyCode) // 验证验证码
}
}
```
### 2. DDD 多域路由架构
```go
// 按域组织路由,支持横向扩展
func (r *UserRoutes) RegisterRoutes(router *gin.Engine) {
v1 := router.Group("/api/v1")
// 👥 用户域
users := v1.Group("/users")
// 📦 订单域
orders := v1.Group("/orders")
// 🛍️ 商品域
products := v1.Group("/products")
// 💰 支付域
payments := v1.Group("/payments")
}
// 多级权限路由分层
users := v1.Group("/users")
{
// Level 1: 公开路由
users.POST("/register", r.handler.Register)
users.POST("/login", r.handler.Login)
// Level 2: 用户认证路由
authenticated := users.Group("")
authenticated.Use(r.jwtAuth.Handle())
{
authenticated.GET("/me", r.handler.GetProfile)
}
// Level 3: 管理员路由
admin := users.Group("/admin")
admin.Use(r.jwtAuth.Handle(), r.adminAuth.Handle())
{
admin.GET("", r.handler.AdminList)
admin.DELETE("/:id", r.handler.AdminDelete)
}
}
```
### 3. DDD 路由命名最佳实践
#### ✅ **推荐做法** - 领域导向设计:
```go
// 🏢 按业务域划分路由
/api/v1/users/* # 用户域的所有操作
/api/v1/orders/* # 订单域的所有操作
/api/v1/products/* # 商品域的所有操作
/api/v1/payments/* # 支付域的所有操作
// 📋 资源操作使用名词复数
POST /api/v1/users/register # 用户注册
POST /api/v1/users/login # 用户登录
GET /api/v1/users/me # 获取当前用户
PUT /api/v1/users/me/password # 修改当前用户密码
// 🔗 体现资源关系的嵌套路径
GET /api/v1/users/me/orders # 获取当前用户的订单
GET /api/v1/orders/123/items # 获取订单的商品项目
POST /api/v1/products/456/reviews # 为商品添加评论
```
#### ❌ **避免的做法** - 技术导向设计:
```go
// ❌ 技术导向路径
/api/v1/auth/* # 混合了多个域的认证操作
/api/v1/service/* # 不明确的服务路径
/api/v1/api/* # 冗余的api前缀
// ❌ 动词路径
/api/v1/getUserInfo # 应该用 GET /users/me
/api/v1/changeUserPassword # 应该用 PUT /users/me/password
/api/v1/deleteUserAccount # 应该用 DELETE /users/me
// ❌ 混合域概念
/api/v1/userorders # 应该分离为 /users/me/orders
/api/v1/authprofile # 应该分离为 /users/me
```
## 🔐 权限控制体系
### 1. JWT 认证中间件
```go
// 强制认证中间件
type JWTAuthMiddleware struct {
config *config.Config
logger *zap.Logger
}
// 可选认证中间件(支持游客访问)
type OptionalAuthMiddleware struct {
jwtAuth *JWTAuthMiddleware
}
// 使用方式
protected.Use(r.jwtAuth.Handle()) // 强制认证
public.Use(r.optionalAuth.Handle()) // 可选认证
```
### 2. 权限验证模式
```go
// 在Handler中获取当前用户
func (h *UserHandler) getCurrentUserID(c *gin.Context) string {
userID, exists := c.Get("user_id")
if !exists {
return ""
}
return userID.(string)
}
// 权限检查示例
func (h *UserHandler) UpdateProfile(c *gin.Context) {
userID := h.getCurrentUserID(c)
if userID == "" {
h.response.Unauthorized(c, "User not authenticated")
return
}
// 业务逻辑...
}
```
### 3. 权限级别定义
- **Public**: 公开接口,无需认证
- **User**: 需要用户登录
- **Admin**: 需要管理员权限
- **Owner**: 需要资源所有者权限
## 📝 API 响应规范
### 1. 统一响应格式 (APIResponse 结构)
```go
// 标准API响应结构
type APIResponse struct {
Success bool `json:"success"` // 操作是否成功
Message string `json:"message"` // 响应消息(中文)
Data interface{} `json:"data,omitempty"` // 响应数据
Errors interface{} `json:"errors,omitempty"` // 错误详情
Pagination *PaginationMeta `json:"pagination,omitempty"` // 分页信息
Meta map[string]interface{} `json:"meta,omitempty"` // 元数据
RequestID string `json:"request_id"` // 请求追踪ID
Timestamp int64 `json:"timestamp"` // Unix时间戳
}
// 分页元数据结构
type PaginationMeta struct {
Page int `json:"page"` // 当前页码
PageSize int `json:"page_size"` // 每页大小
Total int64 `json:"total"` // 总记录数
TotalPages int `json:"total_pages"` // 总页数
HasNext bool `json:"has_next"` // 是否有下一页
HasPrev bool `json:"has_prev"` // 是否有上一页
}
```
### 2. 成功响应格式示例
```json
// 查询成功响应 (200 OK)
{
"success": true,
"message": "获取成功",
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"phone": "13800138000",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
},
"request_id": "req_123456789",
"timestamp": 1704067200
}
// 创建成功响应 (201 Created)
{
"success": true,
"message": "用户注册成功",
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"phone": "13800138000",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
},
"request_id": "req_123456789",
"timestamp": 1704067200
}
// 登录成功响应 (200 OK)
{
"success": true,
"message": "登录成功",
"data": {
"user": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"phone": "13800138000",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
},
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 86400,
"login_method": "password"
},
"request_id": "req_123456789",
"timestamp": 1704067200
}
// 分页响应 (200 OK)
{
"success": true,
"message": "获取成功",
"data": [
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"phone": "13800138000",
"created_at": "2024-01-01T00:00:00Z"
}
],
"pagination": {
"page": 1,
"page_size": 10,
"total": 100,
"total_pages": 10,
"has_next": true,
"has_prev": false
},
"request_id": "req_123456789",
"timestamp": 1704067200
}
```
### 3. 错误响应格式示例
```json
// 参数验证错误 (400 Bad Request)
{
"success": false,
"message": "请求参数错误",
"errors": {
"phone": ["手机号必须为11位数字"],
"password": ["密码长度至少6位"],
"confirm_password": ["确认密码必须与密码一致"]
},
"request_id": "req_123456789",
"timestamp": 1704067200
}
// 验证码错误 (422 Unprocessable Entity)
{
"success": false,
"message": "验证失败",
"errors": {
"phone": ["手机号必须为11位数字"],
"code": ["验证码必须为6位数字"]
},
"request_id": "req_123456789",
"timestamp": 1704067200
}
// 业务逻辑错误 (400 Bad Request)
{
"success": false,
"message": "手机号已存在",
"request_id": "req_123456789",
"timestamp": 1704067200
}
// 认证错误 (401 Unauthorized)
{
"success": false,
"message": "用户未登录或token已过期",
"request_id": "req_123456789",
"timestamp": 1704067200
}
// 权限错误 (403 Forbidden)
{
"success": false,
"message": "权限不足,无法访问此资源",
"request_id": "req_123456789",
"timestamp": 1704067200
}
// 资源不存在 (404 Not Found)
{
"success": false,
"message": "请求的资源不存在",
"request_id": "req_123456789",
"timestamp": 1704067200
}
// 资源冲突 (409 Conflict)
{
"success": false,
"message": "手机号已被注册",
"request_id": "req_123456789",
"timestamp": 1704067200
}
// 限流错误 (429 Too Many Requests)
{
"success": false,
"message": "请求过于频繁,请稍后再试",
"meta": {
"retry_after": "60s"
},
"request_id": "req_123456789",
"timestamp": 1704067200
}
// 服务器错误 (500 Internal Server Error)
{
"success": false,
"message": "服务器内部错误",
"request_id": "req_123456789",
"timestamp": 1704067200
}
```
### 🚦 限流中间件和 TooManyRequests 详解
#### 限流配置
```yaml
# config.yaml 限流配置
ratelimit:
requests: 1000 # 每个时间窗口允许的请求数
window: 60s # 时间窗口大小
burst: 200 # 突发请求允许数
```
#### 限流中间件实现
```go
// RateLimitMiddleware 限流中间件(修复后的版本)
type RateLimitMiddleware struct {
config *config.Config
response interfaces.ResponseBuilder // ✅ 使用统一响应格式
limiters map[string]*rate.Limiter
mutex sync.RWMutex
}
// Handle 限流处理逻辑
func (m *RateLimitMiddleware) Handle() gin.HandlerFunc {
return func(c *gin.Context) {
clientID := m.getClientID(c) // 获取客户端ID通常是IP地址
limiter := m.getLimiter(clientID)
if !limiter.Allow() {
// 添加限流头部信息
c.Header("X-RateLimit-Limit", fmt.Sprintf("%d", m.config.RateLimit.Requests))
c.Header("X-RateLimit-Window", m.config.RateLimit.Window.String())
c.Header("Retry-After", "60")
// ✅ 使用统一的TooManyRequests响应格式修复前是c.JSON
m.response.TooManyRequests(c, "请求过于频繁,请稍后再试")
c.Abort()
return
}
c.Next()
}
}
```
#### 多层限流保护
```go
// 🔹 1. 全局IP限流中间件层
// 通过RateLimitMiddleware自动处理返回429状态码
// 🔹 2. 短信发送限流(业务层)
func (s *SMSCodeService) checkRateLimit(ctx context.Context, phone string) error {
// 最小发送间隔检查
lastSentKey := fmt.Sprintf("sms:last_sent:%s", phone)
if lastSent exists && now.Sub(lastSent) < s.config.RateLimit.MinInterval {
return fmt.Errorf("请等待 %v 后再试", s.config.RateLimit.MinInterval)
}
// 每小时发送限制
hourlyKey := fmt.Sprintf("sms:hourly:%s:%s", phone, now.Format("2006010215"))
if hourlyCount >= s.config.RateLimit.HourlyLimit {
return fmt.Errorf("每小时最多发送 %d 条短信", s.config.RateLimit.HourlyLimit)
}
return nil
}
// 🔹 3. Handler层限流错误处理
func (h *UserHandler) SendSMSCode(c *gin.Context) {
err := h.smsCodeService.SendCode(ctx, &req)
if err != nil {
// 检查是否是限流错误
if strings.Contains(err.Error(), "请等待") ||
strings.Contains(err.Error(), "最多发送") {
// ✅ 使用TooManyRequests响应
h.response.TooManyRequests(c, err.Error())
return
}
h.response.BadRequest(c, err.Error())
return
}
h.response.Success(c, nil, "验证码发送成功")
}
```
#### 限流响应示例
**中间件层限流**(全局 IP 限流):
```json
{
"success": false,
"message": "请求过于频繁,请稍后再试",
"meta": {
"retry_after": "60s"
},
"request_id": "req_123456789",
"timestamp": 1704067200
}
```
**业务层限流**(短信发送限流):
```json
{
"success": false,
"message": "请等待 60 秒后再试",
"meta": {
"retry_after": "60s"
},
"request_id": "req_123456789",
"timestamp": 1704067200
}
```
#### TooManyRequests 使用场景
- 🚫 **全局限流**: IP 请求频率限制
- 📱 **短信限流**: 验证码发送频率限制
- 🔐 **登录限流**: 防止暴力破解
- 📧 **邮件限流**: 邮件发送频率限制
- <20><> **搜索限流**: 防止恶意搜索
### 4. ResponseBuilder 响应构建器使用
```go
// 成功响应
h.response.Success(c, data, "获取成功")
h.response.Created(c, data, "创建成功")
// 客户端错误响应
h.response.BadRequest(c, "请求参数错误", validationErrors)
h.response.Unauthorized(c, "用户未登录或token已过期")
h.response.Forbidden(c, "权限不足,无法访问此资源")
h.response.NotFound(c, "请求的资源不存在")
h.response.Conflict(c, "手机号已被注册")
h.response.ValidationError(c, validationErrors)
h.response.TooManyRequests(c, "请求过于频繁,请稍后再试")
// 服务器错误响应
h.response.InternalError(c, "服务器内部错误")
// 分页响应
h.response.Paginated(c, data, pagination)
// 自定义响应
h.response.CustomResponse(c, statusCode, data)
```
### 5. 错误处理分层架构
```go
// 1. Handler层 - HTTP错误处理
func (h *UserHandler) Register(c *gin.Context) {
var req dto.RegisterRequest
// 验证请求参数
if err := h.validator.BindAndValidate(c, &req); err != nil {
return // 验证器已处理响应,直接返回
}
// 调用业务服务
user, err := h.userService.Register(c.Request.Context(), &req)
if err != nil {
h.logger.Error("用户注册失败", zap.Error(err))
// 根据错误类型返回相应响应
switch {
case strings.Contains(err.Error(), "手机号已存在"):
h.response.Conflict(c, "手机号已被注册")
case strings.Contains(err.Error(), "验证码错误"):
h.response.BadRequest(c, "验证码错误或已过期")
default:
h.response.InternalError(c, "注册失败,请稍后重试")
}
return
}
// 成功响应
response := dto.FromEntity(user)
h.response.Created(c, response, "用户注册成功")
}
// 2. 验证器层 - 参数验证错误
func (v *RequestValidator) BindAndValidate(c *gin.Context, dto interface{}) error {
// 绑定请求体
if err := c.ShouldBindJSON(dto); err != nil {
v.response.BadRequest(c, "请求体格式错误", err.Error())
return err
}
// 验证数据
if err := v.validator.Struct(dto); err != nil {
validationErrors := v.formatValidationErrors(err)
v.response.ValidationError(c, validationErrors)
return err
}
return nil
}
// 3. 业务服务层 - 业务逻辑错误
func (s *UserService) Register(ctx context.Context, req *dto.RegisterRequest) (*entities.User, error) {
// 验证手机号格式
if !s.isValidPhone(req.Phone) {
return nil, fmt.Errorf("手机号格式不正确")
}
// 检查手机号是否已存在
if err := s.checkPhoneDuplicate(ctx, req.Phone); err != nil {
return nil, fmt.Errorf("手机号已存在")
}
// 验证验证码
if err := s.smsCodeService.VerifyCode(ctx, req.Phone, req.Code, entities.SMSSceneRegister); err != nil {
return nil, fmt.Errorf("验证码错误或已过期")
}
// 创建用户...
return user, nil
}
```
## 🔄 RESTful API 设计规范
### 1. DDD 架构下的 URL 设计规范
```bash
# 🏢 领域驱动的资源设计
GET /api/v1/users/me # 获取当前用户信息
PUT /api/v1/users/me # 更新当前用户信息
DELETE /api/v1/users/me # 删除当前用户账户
# 🔐 认证相关操作(仍在用户域内)
POST /api/v1/users/register # 用户注册
POST /api/v1/users/login # 用户登录
POST /api/v1/users/logout # 用户登出
POST /api/v1/users/send-code # 发送验证码
# 📱 SMS验证码域操作
POST /api/v1/sms/send # 发送验证码
POST /api/v1/sms/verify # 验证验证码
# 🔗 子资源嵌套(当前用户的资源)
GET /api/v1/users/me/orders # 获取当前用户的订单
GET /api/v1/users/me/favorites # 获取当前用户的收藏
POST /api/v1/users/me/favorites # 添加收藏
DELETE /api/v1/users/me/favorites/:id # 删除收藏
# 🛍️ 跨域资源关系
GET /api/v1/orders/123 # 获取订单详情
GET /api/v1/orders/123/items # 获取订单商品
POST /api/v1/products/456/reviews # 为商品添加评论
# 🎯 特殊操作使用动词(在对应域内)
PUT /api/v1/users/me/password # 修改密码
POST /api/v1/orders/123/cancel # 取消订单
POST /api/v1/payments/123/refund # 退款操作
```
### 2. DDD 多域 API 路径设计示例
```bash
# 👥 用户域 (User Domain)
POST /api/v1/users/register # 用户注册
POST /api/v1/users/login # 用户登录
GET /api/v1/users/me # 获取当前用户
PUT /api/v1/users/me # 更新用户信息
PUT /api/v1/users/me/password # 修改密码
GET /api/v1/users/me/sessions # 获取登录会话
# 📦 订单域 (Order Domain)
GET /api/v1/orders # 获取订单列表
POST /api/v1/orders # 创建订单
GET /api/v1/orders/:id # 获取订单详情
PUT /api/v1/orders/:id # 更新订单
POST /api/v1/orders/:id/cancel # 取消订单
GET /api/v1/orders/:id/items # 获取订单商品
# 🛍️ 商品域 (Product Domain)
GET /api/v1/products # 获取商品列表
POST /api/v1/products # 创建商品
GET /api/v1/products/:id # 获取商品详情
PUT /api/v1/products/:id # 更新商品
GET /api/v1/products/:id/reviews # 获取商品评论
POST /api/v1/products/:id/reviews # 添加商品评论
# 💰 支付域 (Payment Domain)
POST /api/v1/payments # 创建支付
GET /api/v1/payments/:id # 获取支付状态
POST /api/v1/payments/:id/refund # 申请退款
# 📱 通知域 (Notification Domain)
GET /api/v1/notifications # 获取通知列表
PUT /api/v1/notifications/:id/read # 标记通知为已读
POST /api/v1/sms/send # 发送短信验证码
```
### 3. HTTP 状态码规范
```bash
# ✅ 成功响应 (2xx)
200 OK # 查询成功 (GET /api/v1/users/me)
201 Created # 创建成功 (POST /api/v1/users/register)
204 No Content # 删除成功 (DELETE /api/v1/users/me)
# ❌ 客户端错误 (4xx)
400 Bad Request # 请求参数错误
401 Unauthorized # 未认证 (需要登录)
403 Forbidden # 无权限 (登录但权限不足)
404 Not Found # 资源不存在
422 Unprocessable Entity # 业务验证失败
429 Too Many Requests # 请求频率限制
# ⚠️ 服务器错误 (5xx)
500 Internal Server Error # 服务器内部错误
502 Bad Gateway # 网关错误
503 Service Unavailable # 服务不可用
```
### 4. 状态码在 DDD 架构中的应用
```go
// 用户域状态码示例
func (h *UserHandler) Login(c *gin.Context) {
// 参数验证失败
if err := h.validator.BindAndValidate(c, &req); err != nil {
// 422 Unprocessable Entity
return
}
user, err := h.userService.Login(ctx, &req)
if err != nil {
switch {
case errors.Is(err, domain.ErrUserNotFound):
h.response.NotFound(c, "用户不存在") // 404
case errors.Is(err, domain.ErrInvalidPassword):
h.response.Unauthorized(c, "密码错误") // 401
case errors.Is(err, domain.ErrUserBlocked):
h.response.Forbidden(c, "账户已被禁用") // 403
default:
h.response.InternalError(c, "登录失败") // 500
}
return
}
h.response.Success(c, user, "登录成功") // 200
}
func (h *UserHandler) Register(c *gin.Context) {
user, err := h.userService.Register(ctx, &req)
if err != nil {
switch {
case errors.Is(err, domain.ErrPhoneExists):
h.response.Conflict(c, "手机号已存在") // 409
case errors.Is(err, domain.ErrInvalidCode):
h.response.BadRequest(c, "验证码错误") // 400
default:
h.response.InternalError(c, "注册失败") // 500
}
return
}
h.response.Created(c, user, "注册成功") // 201
}
```
## ✅ 数据验证规范
### 1. 结构体标签验证 (中文提示)
```go
// 用户注册请求验证
type RegisterRequest struct {
Phone string `json:"phone" binding:"required,len=11" example:"13800138000"`
Password string `json:"password" binding:"required,min=6,max=128" example:"password123"`
ConfirmPassword string `json:"confirm_password" binding:"required,eqfield=Password" example:"password123"`
Code string `json:"code" binding:"required,len=6" example:"123456"`
}
// 用户登录请求验证
type LoginWithPasswordRequest struct {
Phone string `json:"phone" binding:"required,len=11" example:"13800138000"`
Password string `json:"password" binding:"required" example:"password123"`
}
// 修改密码请求验证
type ChangePasswordRequest struct {
OldPassword string `json:"old_password" binding:"required" example:"oldpassword123"`
NewPassword string `json:"new_password" binding:"required,min=6,max=128" example:"newpassword123"`
ConfirmNewPassword string `json:"confirm_new_password" binding:"required,eqfield=NewPassword" example:"newpassword123"`
Code string `json:"code" binding:"required,len=6" example:"123456"`
}
```
### 2. 官方中文翻译包集成
项目集成了 `github.com/go-playground/validator/v10/translations/zh` 官方中文翻译包,自动提供专业的中文验证错误消息。
**集成优势:**
- ✅ **官方支持**: 使用 validator 官方维护的中文翻译
- ✅ **专业翻译**: 所有标准验证规则都有准确的中文翻译
- ✅ **自动更新**: 跟随 validator 版本自动获得新功能的中文支持
- ✅ **智能结合**: 官方翻译 + 自定义字段名映射,提供最佳用户体验
- ✅ **兼容性好**: 保持与现有 API 接口的完全兼容
```go
// 创建支持中文翻译的验证器
func NewRequestValidatorZh(response interfaces.ResponseBuilder) interfaces.RequestValidator {
// 创建验证器实例
validate := validator.New()
// 创建中文locale
zhLocale := zh.New()
uni := ut.New(zhLocale, zhLocale)
// 获取中文翻译器
trans, _ := uni.GetTranslator("zh")
// 注册官方中文翻译
zh_translations.RegisterDefaultTranslations(validate, trans)
// 注册自定义验证器和翻译
registerCustomValidatorsZh(validate, trans)
return &RequestValidatorZh{
validator: validate,
translator: trans,
response: response,
}
}
// 手机号验证器
func validatePhone(fl validator.FieldLevel) bool {
phone := fl.Field().String()
if phone == "" {
return true // 空值由required标签处理
}
// 中国手机号验证11位以1开头
matched, _ := regexp.MatchString(`^1[3-9]\d{9}$`, phone)
return matched
}
// 用户名验证器
func validateUsername(fl validator.FieldLevel) bool {
username := fl.Field().String()
if username == "" {
return true // 空值由required标签处理
}
// 用户名规则3-30字符字母数字下划线不能数字开头
if len(username) < 3 || len(username) > 30 {
return false
}
matched, _ := regexp.MatchString(`^[a-zA-Z][a-zA-Z0-9_]*$`, username)
return matched
}
// 强密码验证器
func validateStrongPassword(fl validator.FieldLevel) bool {
password := fl.Field().String()
if password == "" {
return true // 空值由required标签处理
}
// 密码强度至少8位包含大小写字母和数字
if len(password) < 8 {
return false
}
hasUpper := regexp.MustCompile(`[A-Z]`).MatchString(password)
hasLower := regexp.MustCompile(`[a-z]`).MatchString(password)
hasDigit := regexp.MustCompile(`\d`).MatchString(password)
return hasUpper && hasLower && hasDigit
}
```
### 3. 自定义验证器和翻译注册
```go
// 注册自定义验证器和中文翻译
func registerCustomValidatorsZh(v *validator.Validate, trans ut.Translator) {
// 注册手机号验证器
v.RegisterValidation("phone", validatePhoneZh)
v.RegisterTranslation("phone", trans, func(ut ut.Translator) error {
return ut.Add("phone", "{0}必须是有效的手机号", true)
}, func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("phone", fe.Field())
return t
})
// 注册用户名验证器
v.RegisterValidation("username", validateUsernameZh)
v.RegisterTranslation("username", trans, func(ut ut.Translator) error {
return ut.Add("username", "{0}格式不正确,只能包含字母、数字、下划线,且不能以数字开头", true)
}, func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("username", fe.Field())
return t
})
// 注册密码强度验证器
v.RegisterValidation("strong_password", validateStrongPasswordZh)
v.RegisterTranslation("strong_password", trans, func(ut ut.Translator) error {
return ut.Add("strong_password", "{0}强度不足必须包含大小写字母和数字且不少于8位", true)
}, func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("strong_password", fe.Field())
return t
})
}
// 智能错误格式化(官方翻译 + 自定义字段名)
func (v *RequestValidatorZh) formatValidationErrorsZh(err error) map[string][]string {
errors := make(map[string][]string)
if validationErrors, ok := err.(validator.ValidationErrors); ok {
for _, fieldError := range validationErrors {
fieldName := v.getFieldNameZh(fieldError)
// 使用官方翻译器获取中文错误消息
errorMessage := fieldError.Translate(v.translator)
// 替换字段名为中文显示名称
fieldDisplayName := v.getFieldDisplayName(fieldError.Field())
if fieldDisplayName != fieldError.Field() {
errorMessage = strings.ReplaceAll(errorMessage, fieldError.Field(), fieldDisplayName)
}
if _, exists := errors[fieldName]; !exists {
errors[fieldName] = []string{}
}
errors[fieldName] = append(errors[fieldName], errorMessage)
}
}
return errors
}
```
### 4. 中文翻译效果对比
**标准验证规则** (官方翻译)
```json
{
"success": false,
"message": "验证失败",
"errors": {
"phone": ["手机号必须是有效的手机号"],
"email": ["email必须是一个有效的邮箱"],
"password": ["password长度必须至少为8个字符"],
"confirm_password": ["ConfirmPassword必须等于Password"],
"age": ["age必须大于或等于18"]
}
}
```
**自定义验证规则** (自定义翻译)
```json
{
"success": false,
"message": "验证失败",
"errors": {
"username": [
"用户名格式不正确,只能包含字母、数字、下划线,且不能以数字开头"
],
"password": ["密码强度不足必须包含大小写字母和数字且不少于8位"]
}
}
```
**优化后的用户体验**
通过字段名映射,最终用户看到的是:
```json
{
"success": false,
"message": "验证失败",
"errors": {
"phone": ["手机号必须是有效的手机号"],
"email": ["邮箱必须是一个有效的邮箱"],
"password": ["密码长度必须至少为8个字符"],
"confirm_password": ["确认密码必须等于密码"],
"age": ["年龄必须大于或等于18"]
}
}
```
### 4. 验证器使用示例
```go
func (h *UserHandler) Register(c *gin.Context) {
var req dto.RegisterRequest
// 验证器会自动处理错误响应,返回中文错误信息
if err := h.validator.BindAndValidate(c, &req); err != nil {
return // 验证失败,已返回带中文提示的错误响应
}
// 继续业务逻辑...
}
// 验证失败时的响应示例
{
"success": false,
"message": "验证失败",
"errors": {
"phone": ["手机号 长度必须为 11 位"],
"password": ["密码 长度不能少于 6 位"],
"confirm_password": ["确认密码 必须与 密码 一致"],
"code": ["验证码 长度必须为 6 位"]
},
"request_id": "req_123456789",
"timestamp": 1704067200
}
```
## 📊 分页和查询规范
### 1. 分页参数
```go
type UserListRequest struct {
Page int `form:"page" binding:"min=1"`
PageSize int `form:"page_size" binding:"min=1,max=100"`
Sort string `form:"sort"` // 排序字段
Order string `form:"order"` // asc/desc
Search string `form:"search"` // 搜索关键词
Filters map[string]interface{} `form:"filters"` // 过滤条件
}
```
### 2. 查询接口设计
```
GET /api/v1/users?page=1&page_size=20&sort=created_at&order=desc&search=john
```
## 🔧 中间件使用规范
### 1. 全局中间件(按优先级)
```go
// internal/container/container.go - RegisterMiddlewares
router.RegisterMiddleware(requestID) // 95 - 请求ID
router.RegisterMiddleware(security) // 85 - 安全头部
router.RegisterMiddleware(responseTime) // 75 - 响应时间
router.RegisterMiddleware(cors) // 70 - CORS
router.RegisterMiddleware(rateLimit) // 65 - 限流
router.RegisterMiddleware(requestLogger) // 80 - 请求日志
```
### 2. 路由级中间件
```go
// 认证中间件
protected.Use(r.jwtAuth.Handle())
// 可选认证中间件
public.Use(r.optionalAuth.Handle())
// 自定义中间件
adminRoutes.Use(r.adminAuth.Handle())
```
## 🎯 错误处理规范
### 1. 业务错误分类 (中文错误码和消息)
```go
// 业务错误结构
type BusinessError struct {
Code string `json:"code"` // 错误码
Message string `json:"message"` // 中文错误消息
Details interface{} `json:"details,omitempty"` // 错误详情
}
// 用户域错误码定义
const (
// 用户相关错误
ErrUserNotFound = "USER_NOT_FOUND" // 用户不存在
ErrUserExists = "USER_EXISTS" // 用户已存在
ErrPhoneExists = "PHONE_EXISTS" // 手机号已存在
ErrInvalidCredentials = "INVALID_CREDENTIALS" // 登录凭据无效
ErrInvalidPassword = "INVALID_PASSWORD" // 密码错误
ErrUserBlocked = "USER_BLOCKED" // 用户被禁用
// 验证码相关错误
ErrInvalidCode = "INVALID_CODE" // 验证码错误
ErrCodeExpired = "CODE_EXPIRED" // 验证码已过期
ErrCodeUsed = "CODE_USED" // 验证码已使用
ErrCodeSendTooFrequent = "CODE_SEND_TOO_FREQUENT" // 验证码发送过于频繁
// 请求相关错误
ErrValidationFailed = "VALIDATION_FAILED" // 参数验证失败
ErrInvalidRequest = "INVALID_REQUEST" // 请求格式错误
ErrMissingParam = "MISSING_PARAM" // 缺少必需参数
// 权限相关错误
ErrUnauthorized = "UNAUTHORIZED" // 未认证
ErrForbidden = "FORBIDDEN" // 权限不足
ErrTokenExpired = "TOKEN_EXPIRED" // Token已过期
ErrTokenInvalid = "TOKEN_INVALID" // Token无效
// 系统相关错误
ErrInternalServer = "INTERNAL_SERVER_ERROR" // 服务器内部错误
ErrServiceUnavailable = "SERVICE_UNAVAILABLE" // 服务不可用
ErrRateLimitExceeded = "RATE_LIMIT_EXCEEDED" // 请求频率超限
)
// 错误消息映射(中文)
var ErrorMessages = map[string]string{
// 用户相关
ErrUserNotFound: "用户不存在",
ErrUserExists: "用户已存在",
ErrPhoneExists: "手机号已被注册",
ErrInvalidCredentials: "用户名或密码错误",
ErrInvalidPassword: "密码错误",
ErrUserBlocked: "账户已被禁用,请联系客服",
// 验证码相关
ErrInvalidCode: "验证码错误",
ErrCodeExpired: "验证码已过期,请重新获取",
ErrCodeUsed: "验证码已使用,请重新获取",
ErrCodeSendTooFrequent: "验证码发送过于频繁,请稍后再试",
// 请求相关
ErrValidationFailed: "请求参数验证失败",
ErrInvalidRequest: "请求格式错误",
ErrMissingParam: "缺少必需参数",
// 权限相关
ErrUnauthorized: "用户未登录或登录已过期",
ErrForbidden: "权限不足,无法访问此资源",
ErrTokenExpired: "登录已过期,请重新登录",
ErrTokenInvalid: "登录信息无效,请重新登录",
// 系统相关
ErrInternalServer: "服务器内部错误,请稍后重试",
ErrServiceUnavailable: "服务暂时不可用,请稍后重试",
ErrRateLimitExceeded: "请求过于频繁,请稍后再试",
}
// 创建业务错误
func NewBusinessError(code string, details ...interface{}) *BusinessError {
message := ErrorMessages[code]
if message == "" {
message = "未知错误"
}
err := &BusinessError{
Code: code,
Message: message,
}
if len(details) > 0 {
err.Details = details[0]
}
return err
}
// 实现error接口
func (e *BusinessError) Error() string {
return e.Message
}
```
### 2. 错误处理模式示例
```go
// 服务层错误处理
func (s *UserService) GetByID(ctx context.Context, id string) (*entities.User, error) {
user, err := s.repo.GetByID(ctx, id)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, NewBusinessError(ErrUserNotFound)
}
s.logger.Error("获取用户失败", zap.Error(err), zap.String("user_id", id))
return nil, NewBusinessError(ErrInternalServer)
}
return user, nil
}
func (s *UserService) Register(ctx context.Context, req *dto.RegisterRequest) (*entities.User, error) {
// 检查手机号是否已存在
existingUser, err := s.repo.FindByPhone(ctx, req.Phone)
if err == nil && existingUser != nil {
return nil, NewBusinessError(ErrPhoneExists)
}
// 验证验证码
if err := s.smsCodeService.VerifyCode(ctx, req.Phone, req.Code, entities.SMSSceneRegister); err != nil {
if strings.Contains(err.Error(), "expired") {
return nil, NewBusinessError(ErrCodeExpired)
}
return nil, NewBusinessError(ErrInvalidCode)
}
// 创建用户...
return user, nil
}
// Handler层错误处理
func (h *UserHandler) GetProfile(c *gin.Context) {
userID := h.getCurrentUserID(c)
if userID == "" {
h.response.Unauthorized(c, ErrorMessages[ErrUnauthorized])
return
}
user, err := h.userService.GetByID(c.Request.Context(), userID)
if err != nil {
if bizErr, ok := err.(*BusinessError); ok {
switch bizErr.Code {
case ErrUserNotFound:
h.response.NotFound(c, bizErr.Message)
case ErrUnauthorized:
h.response.Unauthorized(c, bizErr.Message)
case ErrForbidden:
h.response.Forbidden(c, bizErr.Message)
default:
h.response.InternalError(c, bizErr.Message)
}
} else {
h.logger.Error("获取用户信息失败", zap.Error(err))
h.response.InternalError(c, ErrorMessages[ErrInternalServer])
}
return
}
response := dto.FromEntity(user)
h.response.Success(c, response, "获取用户信息成功")
}
// 登录错误处理示例
func (h *UserHandler) LoginWithPassword(c *gin.Context) {
var req dto.LoginWithPasswordRequest
if err := h.validator.BindAndValidate(c, &req); err != nil {
return // 验证器已处理响应
}
user, err := h.userService.LoginWithPassword(c.Request.Context(), &req)
if err != nil {
h.logger.Error("用户登录失败", zap.Error(err), zap.String("phone", req.Phone))
if bizErr, ok := err.(*BusinessError); ok {
switch bizErr.Code {
case ErrUserNotFound:
h.response.NotFound(c, "手机号未注册")
case ErrInvalidPassword:
h.response.Unauthorized(c, "密码错误")
case ErrUserBlocked:
h.response.Forbidden(c, bizErr.Message)
default:
h.response.BadRequest(c, bizErr.Message)
}
} else {
h.response.InternalError(c, "登录失败,请稍后重试")
}
return
}
// 生成JWT token...
h.response.Success(c, loginResponse, "登录成功")
}
```
### 3. 统一错误响应格式
```go
// 错误响应中间件
func ErrorHandlerMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
// 检查是否有未处理的错误
if len(c.Errors) > 0 {
err := c.Errors.Last().Err
if bizErr, ok := err.(*BusinessError); ok {
// 业务错误
c.JSON(getHTTPStatus(bizErr.Code), gin.H{
"success": false,
"message": bizErr.Message,
"error_code": bizErr.Code,
"details": bizErr.Details,
"request_id": c.GetString("request_id"),
"timestamp": time.Now().Unix(),
})
} else {
// 系统错误
c.JSON(500, gin.H{
"success": false,
"message": ErrorMessages[ErrInternalServer],
"error_code": ErrInternalServer,
"request_id": c.GetString("request_id"),
"timestamp": time.Now().Unix(),
})
}
}
}
}
// 根据错误码获取HTTP状态码
func getHTTPStatus(errorCode string) int {
statusMap := map[string]int{
ErrValidationFailed: 400, // Bad Request
ErrInvalidRequest: 400,
ErrMissingParam: 400,
ErrInvalidCode: 400,
ErrPhoneExists: 409, // Conflict
ErrUserExists: 409,
ErrUnauthorized: 401, // Unauthorized
ErrTokenExpired: 401,
ErrTokenInvalid: 401,
ErrInvalidCredentials: 401,
ErrForbidden: 403, // Forbidden
ErrUserBlocked: 403,
ErrUserNotFound: 404, // Not Found
ErrCodeSendTooFrequent: 429, // Too Many Requests
ErrRateLimitExceeded: 429,
ErrInternalServer: 500, // Internal Server Error
ErrServiceUnavailable: 503, // Service Unavailable
}
if status, exists := statusMap[errorCode]; exists {
return status
}
return 500 // 默认服务器错误
}
```
## 📈 日志记录规范
### 1. 结构化日志 (中文日志消息)
```go
// 成功日志
h.logger.Info("用户注册成功",
zap.String("user_id", user.ID),
zap.String("phone", user.Phone),
zap.String("request_id", c.GetString("request_id")))
h.logger.Info("用户登录成功",
zap.String("user_id", user.ID),
zap.String("phone", user.Phone),
zap.String("login_method", "password"),
zap.String("ip_address", c.ClientIP()),
zap.String("request_id", c.GetString("request_id")))
h.logger.Info("验证码发送成功",
zap.String("phone", req.Phone),
zap.String("scene", string(req.Scene)),
zap.String("request_id", c.GetString("request_id")))
// 错误日志
h.logger.Error("用户注册失败",
zap.Error(err),
zap.String("phone", req.Phone),
zap.String("error_type", "business_logic"),
zap.String("request_id", c.GetString("request_id")))
h.logger.Error("数据库操作失败",
zap.Error(err),
zap.String("operation", "create_user"),
zap.String("table", "users"),
zap.String("request_id", c.GetString("request_id")))
h.logger.Error("外部服务调用失败",
zap.Error(err),
zap.String("service", "sms_service"),
zap.String("action", "send_code"),
zap.String("phone", req.Phone),
zap.String("request_id", c.GetString("request_id")))
// 警告日志
h.logger.Warn("验证码重复发送",
zap.String("phone", req.Phone),
zap.String("scene", string(req.Scene)),
zap.Int("retry_count", retryCount),
zap.String("request_id", c.GetString("request_id")))
h.logger.Warn("异常登录尝试",
zap.String("phone", req.Phone),
zap.String("ip_address", c.ClientIP()),
zap.String("user_agent", c.GetHeader("User-Agent")),
zap.Int("attempt_count", attemptCount),
zap.String("request_id", c.GetString("request_id")))
// 调试日志
h.logger.Debug("开始处理用户注册请求",
zap.String("phone", req.Phone),
zap.String("request_id", c.GetString("request_id")))
```
### 2. 日志级别使用规范
- **Debug**: 详细的调试信息(开发环境)
- 请求参数详情
- 中间步骤状态
- 性能指标数据
- **Info**: 重要的业务信息(生产环境)
- 用户操作成功记录
- 系统状态变更
- 业务流程关键节点
- **Warn**: 需要关注但不影响主功能的问题
- 重试操作
- 降级处理
- 资源使用超预期
- **Error**: 影响功能的错误信息
- 业务逻辑错误
- 数据库操作失败
- 外部服务调用失败
### 3. 日志上下文信息规范
```go
// 必需字段
- request_id: 请求追踪ID
- user_id: 用户ID如果已认证
- action: 操作类型
- timestamp: 时间戳(自动添加)
// 可选字段
- phone: 手机号(敏感信息需脱敏)
- ip_address: 客户端IP
- user_agent: 用户代理
- error_type: 错误类型分类
- duration: 操作耗时
- service: 服务名称
- method: 请求方法
- path: 请求路径
// 脱敏处理示例
func maskPhone(phone string) string {
if len(phone) != 11 {
return phone
}
return phone[:3] + "****" + phone[7:]
}
h.logger.Info("用户登录成功",
zap.String("phone", maskPhone(user.Phone)), // 138****8000
zap.String("user_id", user.ID),
zap.String("request_id", c.GetString("request_id")))
```
## 🧪 测试规范
### 1. 单元测试
```go
func TestUserService_Create(t *testing.T) {
// 使用testify进行测试
assert := assert.New(t)
// Mock依赖
mockRepo := &mocks.UserRepository{}
mockEventBus := &mocks.EventBus{}
service := services.NewUserService(mockRepo, mockEventBus, logger)
// 测试用例...
user, err := service.Create(ctx, req)
assert.NoError(err)
assert.NotNil(user)
}
```
### 2. 集成测试
```go
func TestUserHandler_Create(t *testing.T) {
// 设置测试环境
router := setupTestRouter()
// 发送测试请求
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/api/v1/users", bytes.NewBuffer(jsonData))
router.ServeHTTP(w, req)
// 验证响应
assert.Equal(t, 201, w.Code)
}
```
## 🚀 新增业务领域开发指南
### 1. 创建新领域
```bash
# 1. 创建领域目录结构
mkdir -p internal/domains/product/{dto,entities,events,handlers,repositories,routes,services,validators}
# 2. 复制用户领域作为模板
cp -r internal/domains/user/* internal/domains/product/
# 3. 修改包名和结构体名称
```
### 2. 注册到依赖注入容器
```go
// internal/container/container.go
fx.Provide(
// Product domain
NewProductRepository,
NewProductService,
NewProductHandler,
NewProductRoutes,
),
fx.Invoke(
RegisterProductRoutes,
),
```
### 3. 添加路由注册
```go
func RegisterProductRoutes(
router *http.GinRouter,
productRoutes *routes.ProductRoutes,
) {
productRoutes.RegisterRoutes(router.GetEngine())
productRoutes.RegisterPublicRoutes(router.GetEngine())
productRoutes.RegisterAdminRoutes(router.GetEngine())
}
```
## 🚀 DDD 新域开发指南
### 1. 创建新业务域
```bash
# 1. 创建领域目录结构(以订单域为例)
mkdir -p internal/domains/order/{dto,entities,events,handlers,repositories,routes,services}
# 2. 复制用户域作为模板
cp -r internal/domains/user/* internal/domains/order/
# 3. 批量替换包名和结构体名称
```
### 2. 定义领域实体和 DTO
```go
// internal/domains/order/entities/order.go
type Order struct {
ID string `json:"id" gorm:"primaryKey"`
UserID string `json:"user_id" gorm:"not null"`
TotalAmount float64 `json:"total_amount"`
Status Status `json:"status"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// internal/domains/order/dto/order_dto.go
type CreateOrderRequest struct {
Items []OrderItem `json:"items" binding:"required,dive"`
}
type OrderResponse struct {
ID string `json:"id" example:"123e4567-e89b-12d3-a456-426614174000"`
UserID string `json:"user_id" example:"user-123"`
TotalAmount float64 `json:"total_amount" example:"99.99"`
Status string `json:"status" example:"pending"`
CreatedAt time.Time `json:"created_at" example:"2024-01-01T00:00:00Z"`
}
```
### 3. 配置领域路由
```go
// internal/domains/order/routes/order_routes.go
func (r *OrderRoutes) RegisterRoutes(router *gin.Engine) {
v1 := router.Group("/api/v1")
// 📦 订单域路由组
orders := v1.Group("/orders")
{
// 公开查询(可选认证)
orders.GET("/:id/public", r.handler.GetPublicOrder)
// 需要认证的路由
authenticated := orders.Group("")
authenticated.Use(r.jwtAuth.Handle())
{
authenticated.GET("", r.handler.List) // GET /api/v1/orders
authenticated.POST("", r.handler.Create) // POST /api/v1/orders
authenticated.GET("/:id", r.handler.GetByID) // GET /api/v1/orders/:id
authenticated.PUT("/:id", r.handler.Update) // PUT /api/v1/orders/:id
authenticated.POST("/:id/cancel", r.handler.Cancel) // POST /api/v1/orders/:id/cancel
authenticated.GET("/:id/items", r.handler.GetItems) // GET /api/v1/orders/:id/items
}
}
}
```
### 4. 注册到依赖注入容器
```go
// internal/container/container.go
fx.Provide(
// User domain
repositories.NewUserRepository,
services.NewUserService,
handlers.NewUserHandler,
routes.NewUserRoutes,
// Order domain - 新增
order_repositories.NewOrderRepository,
order_services.NewOrderService,
order_handlers.NewOrderHandler,
order_routes.NewOrderRoutes,
),
fx.Invoke(
RegisterUserRoutes,
RegisterOrderRoutes, // 新增
),
// 添加路由注册函数
func RegisterOrderRoutes(
router *http.GinRouter,
orderRoutes *order_routes.OrderRoutes,
) {
orderRoutes.RegisterRoutes(router.GetEngine())
}
```
### 5. 跨域关系处理
```go
// 用户订单关系 - 在用户域添加
func (r *UserRoutes) RegisterRoutes(router *gin.Engine) {
users := v1.Group("/users")
authenticated := users.Group("")
authenticated.Use(r.jwtAuth.Handle())
{
authenticated.GET("/me", r.handler.GetProfile)
// 添加用户相关的订单操作
authenticated.GET("/me/orders", r.handler.GetUserOrders) // 获取用户订单
authenticated.GET("/me/orders/stats", r.handler.GetOrderStats) // 订单统计
}
}
// 或者在订单域处理用户关系
func (h *OrderHandler) List(c *gin.Context) {
userID := h.getCurrentUserID(c) // 从JWT中获取用户ID
orders, err := h.orderService.GetUserOrders(ctx, userID)
// ... 业务逻辑
}
```
## 📖 Swagger/OpenAPI 文档集成指南
### 1. 新增接口 Swagger 文档支持
#### 为 Handler 方法添加 Swagger 注释
```go
// @Summary 接口简短描述(必需)
// @Description 接口详细描述(可选)
// @Tags 标签分组(推荐)
// @Accept json
// @Produce json
// @Security Bearer # 如果需要JWT认证
// @Param request body dto.RequestStruct true "请求参数描述"
// @Param id path string true "路径参数描述"
// @Param page query int false "查询参数描述"
// @Success 200 {object} dto.ResponseStruct "成功响应描述"
// @Failure 400 {object} map[string]interface{} "错误响应描述"
// @Router /api/v1/your-endpoint [post]
func (h *YourHandler) YourMethod(c *gin.Context) {
// Handler实现
}
```
#### Swagger 注释语法详解
```go
// 基础注释
// @Summary 接口摘要(在文档列表中显示)
// @Description 详细描述(支持多行)
// @Tags 标签分组用于在UI中分组显示
// 请求/响应格式
// @Accept 接受的内容类型json, xml, plain, html, mpfd, x-www-form-urlencoded
// @Produce 响应的内容类型json, xml, plain, html
// 安全认证
// @Security Bearer # JWT认证
// @Security ApiKeyAuth # API Key认证
// @Security BasicAuth # 基础认证
// 参数定义
// @Param name location type required "description" Enums(A,B,C) default(A)
// location: query, path, header, body, formData
// type: string, number, integer, boolean, array, object
// required: true, false
// 响应定义
// @Success code {type} model "description"
// @Failure code {type} model "description"
// code: HTTP状态码
// type: object, array, string, number, boolean
// model: 响应模型如dto.UserResponse
// 路由定义
// @Router path [method]
// method: get, post, put, delete, patch, head, options
```
### 2. 完整示例:订单域接口文档
```go
// CreateOrder 创建订单
// @Summary 创建新订单
// @Description 根据购物车内容创建新的订单,支持多商品下单
// @Tags 订单管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param request body dto.CreateOrderRequest true "创建订单请求"
// @Success 201 {object} dto.OrderResponse "订单创建成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 422 {object} map[string]interface{} "业务验证失败"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/orders [post]
func (h *OrderHandler) CreateOrder(c *gin.Context) {
// 实现代码
}
// GetOrderList 获取订单列表
// @Summary 获取当前用户的订单列表
// @Description 分页获取当前用户的订单列表,支持按状态筛选和关键词搜索
// @Tags 订单管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param page query int false "页码" default(1) minimum(1)
// @Param page_size query int false "每页数量" default(20) minimum(1) maximum(100)
// @Param status query string false "订单状态" Enums(pending,paid,shipped,delivered,cancelled)
// @Param search query string false "搜索关键词"
// @Success 200 {object} dto.OrderListResponse "订单列表"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/orders [get]
func (h *OrderHandler) GetOrderList(c *gin.Context) {
// 实现代码
}
// UpdateOrder 更新订单
// @Summary 更新订单信息
// @Description 更新指定订单的部分信息,如收货地址、备注等
// @Tags 订单管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param id path string true "订单ID" Format(uuid)
// @Param request body dto.UpdateOrderRequest true "更新订单请求"
// @Success 200 {object} dto.OrderResponse "订单更新成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 403 {object} map[string]interface{} "无权限操作此订单"
// @Failure 404 {object} map[string]interface{} "订单不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/orders/{id} [put]
func (h *OrderHandler) UpdateOrder(c *gin.Context) {
// 实现代码
}
```
### 3. DTO 结构体文档化
#### 为请求/响应结构体添加文档标签
```go
// CreateOrderRequest 创建订单请求
type CreateOrderRequest struct {
Items []OrderItem `json:"items" binding:"required,dive" example:"[{\"product_id\":\"123\",\"quantity\":2}]"`
DeliveryAddress string `json:"delivery_address" binding:"required,max=200" example:"北京市朝阳区xxx街道xxx号"`
PaymentMethod string `json:"payment_method" binding:"required,oneof=alipay wechat" example:"alipay"`
Remark string `json:"remark" binding:"max=500" example:"请尽快发货"`
} // @name CreateOrderRequest
// OrderResponse 订单响应
type OrderResponse struct {
ID string `json:"id" example:"123e4567-e89b-12d3-a456-426614174000"`
UserID string `json:"user_id" example:"user-123"`
OrderNo string `json:"order_no" example:"ORD20240101001"`
Status OrderStatus `json:"status" example:"pending"`
TotalAmount float64 `json:"total_amount" example:"299.99"`
PaymentMethod string `json:"payment_method" example:"alipay"`
DeliveryAddress string `json:"delivery_address" example:"北京市朝阳区xxx街道xxx号"`
Items []OrderItem `json:"items"`
CreatedAt time.Time `json:"created_at" example:"2024-01-01T00:00:00Z"`
UpdatedAt time.Time `json:"updated_at" example:"2024-01-01T00:00:00Z"`
} // @name OrderResponse
// OrderItem 订单商品项
type OrderItem struct {
ProductID string `json:"product_id" example:"prod-123"`
ProductName string `json:"product_name" example:"iPhone 15 Pro"`
Quantity int `json:"quantity" example:"1"`
Price float64 `json:"price" example:"999.99"`
Subtotal float64 `json:"subtotal" example:"999.99"`
} // @name OrderItem
// OrderListResponse 订单列表响应
type OrderListResponse struct {
Orders []OrderResponse `json:"orders"`
Pagination Pagination `json:"pagination"`
} // @name OrderListResponse
// Pagination 分页信息
type Pagination struct {
Page int `json:"page" example:"1"`
PageSize int `json:"page_size" example:"20"`
Total int `json:"total" example:"150"`
TotalPages int `json:"total_pages" example:"8"`
} // @name Pagination
```
#### 枚举类型文档化
```go
// OrderStatus 订单状态
type OrderStatus string
const (
OrderStatusPending OrderStatus = "pending" // 待支付
OrderStatusPaid OrderStatus = "paid" // 已支付
OrderStatusShipped OrderStatus = "shipped" // 已发货
OrderStatusDelivered OrderStatus = "delivered" // 已送达
OrderStatusCancelled OrderStatus = "cancelled" // 已取消
)
// 为枚举添加Swagger文档
// @Description 订单状态
// @Enum pending,paid,shipped,delivered,cancelled
```
### 4. 文档生成和更新流程
#### 标准工作流程
```bash
# 1. 编写/修改Handler方法添加Swagger注释
vim internal/domains/order/handlers/order_handler.go
# 2. 编写/修改DTO结构体添加example标签
vim internal/domains/order/dto/order_dto.go
# 3. 重新生成Swagger文档
make docs
# 或直接使用命令
swag init -g cmd/api/main.go -o docs/swagger
# 4. 重启项目
go run cmd/api/main.go
# 5. 访问文档查看效果
open http://localhost:8080/swagger/index.html
```
#### 快速开发脚本
```bash
# 创建docs脚本scripts/update-docs.sh
#!/bin/bash
echo "🔄 Updating Swagger documentation..."
# 生成文档
make docs
if [ $? -eq 0 ]; then
echo "✅ Swagger documentation updated successfully!"
echo "📖 View at: http://localhost:8080/swagger/index.html"
else
echo "❌ Failed to update documentation"
exit 1
fi
# 重启开发服务器(可选)
if [ "$1" = "--restart" ]; then
echo "🔄 Restarting development server..."
pkill -f "go run cmd/api/main.go"
nohup go run cmd/api/main.go > /dev/null 2>&1 &
echo "🚀 Development server restarted!"
fi
```
### 5. 文档质量检查清单
#### 必需元素检查
- [ ] **@Summary**: 简洁明了的接口描述
- [ ] **@Description**: 详细的功能说明
- [ ] **@Tags**: 正确的分组标签
- [ ] **@Router**: 正确的路径和 HTTP 方法
- [ ] **@Accept/@Produce**: 正确的内容类型
- [ ] **@Security**: 认证要求(如需要)
#### 参数文档检查
- [ ] **路径参数**: 所有{id}等路径参数都有@Param
- [ ] **查询参数**: 分页、筛选等参数都有@Param
- [ ] **请求体**: 复杂请求有@Param body 定义
- [ ] **示例值**: 所有参数都有 realistic 的 example
#### 响应文档检查
- [ ] **成功响应**: @Success 定义了正确的状态码和模型
- [ ] **错误响应**: @Failure 覆盖了主要的错误场景
- [ ] **响应模型**: DTO 结构体有完整的 json 标签和 example
- [ ] **状态码**: 符合 RESTful 规范
### 6. 高级文档特性
#### 自定义响应模型
```go
// 为复杂响应创建专门的文档模型
type APIResponse struct {
Success bool `json:"success" example:"true"`
Data interface{} `json:"data"`
Message string `json:"message" example:"操作成功"`
RequestID string `json:"request_id" example:"req-123"`
Timestamp int64 `json:"timestamp" example:"1640995200"`
} // @name APIResponse
// 在Handler中使用
// @Success 200 {object} APIResponse{data=dto.OrderResponse} "成功响应"
```
#### 分组和版本管理
```go
// 使用一致的标签分组
// @Tags 用户认证 # 认证相关接口
// @Tags 用户管理 # 用户CRUD接口
// @Tags 订单管理 # 订单相关接口
// @Tags 商品管理 # 商品相关接口
// @Tags 系统管理 # 系统功能接口
// 版本控制
// @Router /api/v1/users [post] # V1版本
// @Router /api/v2/users [post] # V2版本向后兼容
```
### 7. 常见问题和解决方案
#### 问题 1文档生成失败
```bash
# 检查Swagger注释语法
swag init -g cmd/api/main.go -o docs/swagger --parseDependency
# 常见错误:
# - 缺少@Router注释
# - HTTP方法写错必须小写
# - 路径格式不正确
# - 缺少必需的包导入
```
#### 问题 2模型没有正确显示
```bash
# 确保结构体有正确的标签
type UserRequest struct {
Name string `json:"name" example:"张三"` # json标签必需
} // @name UserRequest # 显式命名(可选)
# 确保包被正确解析
swag init -g cmd/api/main.go -o docs/swagger --parseDependency --parseInternal
```
#### 问题 3认证测试失败
```go
// 确保安全定义正确
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
// @description Type "Bearer" followed by a space and JWT token.
// 在接口中正确使用
// @Security Bearer
```
### 8. 持续集成中的文档检查
```bash
# CI脚本示例.github/workflows/docs.yml
name: API Documentation Check
on: [push, pull_request]
jobs:
docs-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.23
- name: Install swag
run: go install github.com/swaggo/swag/cmd/swag@latest
- name: Generate docs
run: make docs
- name: Check docs are up to date
run: |
if [[ `git status --porcelain docs/` ]]; then
echo "Documentation is out of date. Please run 'make docs'"
exit 1
fi
```
## 📚 最佳实践总结
### 🏗️ 架构设计原则
1. **领域驱动设计**: 按业务域组织代码和 API 路径,避免技术导向设计
2. **单一职责原则**: 每个层只负责自己的职责,保持清晰的边界分离
3. **依赖注入管理**: 使用 Uber FX 进行依赖管理,支持模块化扩展
4. **接口隔离原则**: 定义清晰的接口边界,便于测试和扩展
### 📋 API 设计规范
5. **统一响应格式**: 标准化的 API 响应结构和中文错误提示
6. **RESTful 路径设计**: 语义化路径清晰表达业务意图
7. **多层数据验证**: 从 DTO 到业务规则的完整验证链
8. **中文化用户体验**: 所有面向用户的消息都使用中文
### 🔧 技术实现规范
9. **结构化日志记录**: 使用 Zap 记录中文结构化日志,便于监控和调试
10. **智能缓存策略**: 合理使用 Redis 缓存提升系统性能
11. **事件驱动架构**: 使用领域事件解耦业务逻辑,支持异步处理
12. **错误处理分层**: 统一的业务错误码和 HTTP 状态码映射
### 📖 开发协作规范
13. **文档优先开发**: 编写接口时同步维护 Swagger 文档,确保文档和代码一致性
14. **完整测试覆盖**: 单元测试、集成测试和端到端测试
15. **代码审查机制**: 确保代码质量和规范一致性
16. **持续集成部署**: 自动化构建、测试和部署流程
### 🚀 性能和扩展性
17. **数据库事务管理**: 合理使用数据库事务确保数据一致性
18. **请求限流保护**: 防止恶意请求和系统过载
19. **监控和告警**: 完整的应用性能监控和业务指标收集
20. **水平扩展支持**: 微服务架构支持横向扩展
## 🔄 配置管理
### 1. 环境配置
```yaml
# config.yaml (开发环境)
server:
port: "8080"
mode: "debug"
# config.prod.yaml (生产环境)
server:
port: "8080"
mode: "release"
```
### 2. 环境变量覆盖
```bash
# 优先级: 环境变量 > 配置文件 > 默认值
export ENV=production
export DB_HOST=prod-database
export JWT_SECRET=secure-jwt-secret
```
## 📋 当前项目 API 接口清单
### 👥 用户域 (User Domain)
```bash
# 🌍 公开接口(无需认证)
POST /api/v1/users/send-code # 发送验证码
POST /api/v1/users/register # 用户注册
POST /api/v1/users/login # 用户登录
# 🔐 认证接口需要JWT Token
GET /api/v1/users/me # 获取当前用户信息
PUT /api/v1/users/me/password # 修改密码
```
### 📱 SMS 验证码域
```bash
# 🌍 公开接口
POST /api/v1/sms/send # 发送验证码与users/send-code相同
```
### 🔧 系统接口
```bash
# 🌍 健康检查
GET /health # 系统健康状态
GET /health/detailed # 详细健康状态
```
### 📊 请求示例
#### 发送验证码
```bash
curl -X POST http://localhost:8080/api/v1/users/send-code \
-H "Content-Type: application/json" \
-d '{
"phone": "13800138000",
"scene": "register"
}'
# 响应示例
{
"success": true,
"message": "验证码发送成功",
"data": {
"message": "验证码已发送到您的手机",
"expires_at": "2024-01-01T00:05:00Z"
},
"request_id": "req_123456789",
"timestamp": 1704067200
}
```
#### 用户注册
```bash
curl -X POST http://localhost:8080/api/v1/users/register \
-H "Content-Type: application/json" \
-d '{
"phone": "13800138000",
"password": "password123",
"confirm_password": "password123",
"code": "123456"
}'
# 响应示例
{
"success": true,
"message": "用户注册成功",
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"phone": "13800138000",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
},
"request_id": "req_123456789",
"timestamp": 1704067200
}
```
#### 密码登录
```bash
curl -X POST http://localhost:8080/api/v1/users/login-password \
-H "Content-Type: application/json" \
-d '{
"phone": "13800138000",
"password": "password123"
}'
# 响应示例
{
"success": true,
"message": "登录成功",
"data": {
"user": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"phone": "13800138000",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
},
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 86400,
"login_method": "password"
},
"request_id": "req_123456789",
"timestamp": 1704067200
}
```
#### 短信验证码登录
```bash
curl -X POST http://localhost:8080/api/v1/users/login-sms \
-H "Content-Type: application/json" \
-d '{
"phone": "13800138000",
"code": "123456"
}'
# 响应示例同密码登录login_method为"sms"
```
#### 获取当前用户信息
```bash
curl -X GET http://localhost:8080/api/v1/users/me \
-H "Authorization: Bearer <your-jwt-token>"
# 响应示例
{
"success": true,
"message": "获取用户信息成功",
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"phone": "13800138000",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
},
"request_id": "req_123456789",
"timestamp": 1704067200
}
```
#### 修改密码
```bash
curl -X PUT http://localhost:8080/api/v1/users/me/password \
-H "Authorization: Bearer <your-jwt-token>" \
-H "Content-Type: application/json" \
-d '{
"old_password": "oldpassword123",
"new_password": "newpassword123",
"confirm_new_password": "newpassword123",
"code": "123456"
}'
# 响应示例
{
"success": true,
"message": "密码修改成功",
"data": null,
"request_id": "req_123456789",
"timestamp": 1704067200
}
```
#### 错误响应示例
```bash
# 参数验证失败
{
"success": false,
"message": "请求参数验证失败",
"errors": {
"phone": ["手机号 长度必须为 11 位"],
"password": ["密码 长度不能少于 6 位"]
},
"request_id": "req_123456789",
"timestamp": 1704067200
}
# 业务逻辑错误
{
"success": false,
"message": "手机号已被注册",
"request_id": "req_123456789",
"timestamp": 1704067200
}
# 认证失败
{
"success": false,
"message": "用户未登录或登录已过期",
"request_id": "req_123456789",
"timestamp": 1704067200
}
```
### 🔄 响应格式示例
#### 成功响应
```json
// 用户注册成功
{
"success": true,
"message": "用户注册成功",
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"phone": "13800138000",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
},
"request_id": "req_123456789",
"timestamp": 1704067200
}
// 用户登录成功
{
"success": true,
"message": "登录成功",
"data": {
"user": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"phone": "13800138000",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
},
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 86400,
"login_method": "password"
},
"request_id": "req_123456789",
"timestamp": 1704067200
}
// 发送验证码成功
{
"success": true,
"message": "验证码发送成功",
"data": {
"message": "验证码已发送",
"expires_at": "2024-01-01T00:05:00Z"
},
"request_id": "req_123456789",
"timestamp": 1704067200
}
```
#### 错误响应
```json
// 参数验证失败
{
"success": false,
"message": "请求参数验证失败",
"errors": {
"phone": ["手机号 长度必须为 11 位"],
"password": ["密码 长度不能少于 6 位"],
"code": ["验证码 长度必须为 6 位"]
},
"request_id": "req_123456789",
"timestamp": 1704067200
}
// 业务逻辑错误
{
"success": false,
"message": "手机号已被注册",
"request_id": "req_123456789",
"timestamp": 1704067200
}
// 认证失败
{
"success": false,
"message": "用户未登录或登录已过期",
"request_id": "req_123456789",
"timestamp": 1704067200
}
// 验证码错误
{
"success": false,
"message": "验证码错误或已过期",
"request_id": "req_123456789",
"timestamp": 1704067200
}
```
---
遵循以上规范,可以确保 API 开发的一致性、可维护性和扩展性。
2025-07-11 21:05:58 +08:00
# TYAPI Server 企业级高级特性完整集成指南
## 🚀 **高级特性完整解决方案实施完成**
本项目现已成功集成所有企业级高级特性,提供完整的可观测性、弹性恢复和分布式事务能力。所有组件均已通过编译验证和容器集成。
## 📊 **已完整集成的高级特性**
### 1. **🔍 分布式链路追踪 (Distributed Tracing)**
**技术栈**: OpenTelemetry + OTLP 导出器
**支持后端**: Jaeger、Zipkin、Tempo、任何 OTLP 兼容系统
**状态**: ✅ **完全集成**
```yaml
# 配置示例 (config.yaml)
monitoring:
tracing_enabled: true
tracing_endpoint: "http://localhost:4317" # OTLP gRPC endpoint
sample_rate: 0.1
```
**核心特性**:
- ✅ HTTP 请求自动追踪中间件
- ✅ 数据库操作追踪
- ✅ 缓存操作追踪
- ✅ 自定义业务操作追踪
- ✅ TraceID/SpanID 自动传播
- ✅ 生产级批处理导出
- ✅ 容器生命周期管理
**使用示例**:
```go
// 自动HTTP追踪已在所有路由启用
// 每个HTTP请求都会创建完整的追踪链路
// 自定义业务操作追踪
ctx, span := tracer.StartSpan(ctx, "business.user_registration")
defer span.End()
// 数据库操作追踪
ctx, span := tracer.StartDBSpan(ctx, "SELECT", "users", "WHERE phone = ?")
defer span.End()
// 缓存操作追踪
ctx, span := tracer.StartCacheSpan(ctx, "GET", "user:cache:123")
defer span.End()
```
### 2. **📈 指标监控 (Metrics Collection)**
**技术栈**: Prometheus + 自定义业务指标
**导出端点**: `/metrics` (Prometheus 格式)
**状态**: ✅ **完全集成**
**自动收集指标**:
```
# HTTP请求指标
http_requests_total{method="GET",path="/api/v1/users",status="200"} 1523
http_request_duration_seconds{method="GET",path="/api/v1/users"} 0.045
# 业务指标
business_user_created_total{source="register"} 245
business_user_login_total{platform="web",status="success"} 1892
business_sms_sent_total{type="verification",provider="aliyun"} 456
# 系统指标
active_users_total 1024
database_connections_active 12
cache_operations_total{operation="get",result="hit"} 8745
```
**自定义指标注册**:
```go
// 注册自定义计数器
metrics.RegisterCounter("custom_events_total", "Custom events counter", []string{"event_type", "source"})
// 记录指标
metrics.IncrementCounter("custom_events_total", map[string]string{
"event_type": "user_action",
"source": "web",
})
```
### 3. **🛡️ 弹性恢复 (Resilience)**
#### 3.1 **熔断器 (Circuit Breaker)**
**状态**: ✅ **完全集成**
```go
// 使用熔断器保护服务调用
err := circuitBreaker.Execute("user-service", func() error {
return userService.GetUserByID(ctx, userID)
})
// 批量执行保护
err := circuitBreaker.ExecuteBatch("batch-operation", []func() error{
func() error { return service1.Call() },
func() error { return service2.Call() },
})
```
**特性**:
- ✅ 故障阈值自动检测
- ✅ 半开状态自动恢复
- ✅ 实时状态监控
- ✅ 多种失败策略
#### 3.2 **重试机制 (Retry)**
**状态**: ✅ **完全集成**
```go
// 快速重试(适用于网络抖动)
err := retryer.ExecuteWithQuickRetry(ctx, "api-call", func() error {
return httpClient.Call()
})
// 标准重试(适用于业务操作)
err := retryer.ExecuteWithStandardRetry(ctx, "db-operation", func() error {
return db.Save(data)
})
// 耐心重试(适用于最终一致性)
err := retryer.ExecuteWithPatientRetry(ctx, "sync-operation", func() error {
return syncService.Sync()
})
```
### 4. **🔄 分布式事务 (Saga Pattern)**
**状态**: ✅ **完全集成**
```go
// 创建分布式事务
saga := sagaManager.CreateSaga("user-registration-001", "用户注册流程")
// 添加事务步骤
saga.AddStep("create-user",
// 正向操作
func(ctx context.Context, data interface{}) error {
return userService.CreateUser(ctx, data)
},
// 补偿操作
func(ctx context.Context, data interface{}) error {
return userService.DeleteUser(ctx, data)
})
saga.AddStep("send-welcome-email",
func(ctx context.Context, data interface{}) error {
return emailService.SendWelcome(ctx, data)
},
func(ctx context.Context, data interface{}) error {
return emailService.SendCancellation(ctx, data)
})
// 执行事务
err := saga.Execute(ctx, userData)
```
**支持特性**:
- ✅ 自动补偿机制
- ✅ 步骤重试策略
- ✅ 事务状态跟踪
- ✅ 并发控制
### 5. **🪝 事件钩子系统 (Hook System)**
**状态**: ✅ **完全集成**
```go
// 注册业务事件钩子
hookSystem.OnUserCreated("metrics-collector", hooks.PriorityHigh, func(ctx context.Context, user interface{}) error {
return businessMetrics.RecordUserCreated("register")
})
hookSystem.OnUserCreated("welcome-email", hooks.PriorityNormal, func(ctx context.Context, user interface{}) error {
return emailService.SendWelcome(ctx, user)
})
// 触发事件(在业务代码中)
results, err := hookSystem.TriggerUserCreated(ctx, newUser)
```
**钩子类型**:
- ✅ 同步钩子(阻塞执行)
- ✅ 异步钩子(后台执行)
- ✅ 优先级控制
- ✅ 超时保护
- ✅ 错误策略(继续/停止/收集)
## 🏗️ **架构集成图**
```
┌─────────────────────────────────────────────────────────────┐
│ HTTP 请求层 │
├─────────────────────────────────────────────────────────────┤
│ 追踪中间件 → 指标中间件 → 限流中间件 → 认证中间件 │
├─────────────────────────────────────────────────────────────┤
│ 业务处理层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Handler │ │ Service │ │ Repository │ │
│ │ + 钩子 │ │ + 重试 │ │ + 熔断器 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 基础设施层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 链路追踪 │ │ 指标收集 │ │ 分布式事务 │ │
│ │ (OpenTel) │ │(Prometheus) │ │ (Saga) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
## 🛠️ **使用指南**
### **启动验证**
1. **编译验证**
```bash
go build ./cmd/api
```
2. **启动应用**
```bash
./api
```
3. **检查指标端点**
```bash
curl http://localhost:8080/metrics
```
4. **检查健康状态**
```bash
curl http://localhost:8080/health
```
### **配置示例**
```yaml
# config.yaml 完整高级特性配置
app:
name: "tyapi-server"
version: "1.0.0"
env: "production"
monitoring:
# 链路追踪配置
tracing_enabled: true
tracing_endpoint: "http://jaeger:4317"
sample_rate: 0.1
# 指标收集配置
metrics_enabled: true
metrics_endpoint: "/metrics"
resilience:
# 熔断器配置
circuit_breaker_enabled: true
failure_threshold: 5
timeout: 30s
# 重试配置
retry_enabled: true
max_retries: 3
retry_delay: 100ms
saga:
# 分布式事务配置
default_timeout: 30s
max_retries: 3
enable_persistence: false
hooks:
# 钩子系统配置
default_timeout: 30s
track_duration: true
error_strategy: "continue"
```
## 📋 **监控仪表板**
### **推荐监控栈**
1. **链路追踪**: Jaeger UI
- 地址: `http://localhost:16686`
- 查看完整请求链路
2. **指标监控**: Prometheus + Grafana
- Prometheus: `http://localhost:9090`
- Grafana: `http://localhost:3000`
3. **应用指标**: 内置指标端点
- 地址: `http://localhost:8080/metrics`
### **关键监控指标**
```yaml
# 告警规则建议
groups:
- name: tyapi-server
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.1
- alert: CircuitBreakerOpen
expr: circuit_breaker_state{state="open"} > 0
- alert: SagaFailure
expr: rate(saga_failed_total[5m]) > 0.05
- alert: HighLatency
expr: histogram_quantile(0.95, http_request_duration_seconds) > 1
```
## 🔧 **性能优化建议**
### **生产环境配置**
1. **追踪采样率**: 建议设置为 0.01-0.1 (1%-10%)
2. **指标收集**: 启用所有核心指标,按需启用业务指标
3. **熔断器阈值**: 根据服务 SLA 调整失败阈值
4. **钩子超时**: 设置合理的钩子执行超时时间
### **扩展性考虑**
1. **水平扩展**: 所有组件都支持多实例部署
2. **状态无关**: 追踪和指标数据通过外部系统存储
3. **配置热更新**: 支持运行时配置调整
## 🎯 **最佳实践**
### **链路追踪**
- 在关键业务操作中主动创建 Span
- 使用有意义的操作名称
- 添加重要的标签和属性
### **指标收集**
- 合理设置指标标签,避免高基数
- 定期清理不再使用的指标
- 使用直方图记录耗时分布
### **弹性设计**
- 在外部服务调用时使用熔断器
- 对瞬时失败使用重试机制
- 设计优雅降级策略
### **事件钩子**
- 保持钩子函数简单快速
- 使用异步钩子处理耗时操作
- 合理设置钩子优先级
## 🔍 **故障排查**
### **常见问题**
1. **追踪数据丢失**
- 检查 OTLP 端点连接性
- 确认采样率配置
- 查看应用日志中的追踪错误
2. **指标不更新**
- 验证 Prometheus 抓取配置
- 检查指标端点可访问性
- 确认指标注册成功
3. **熔断器异常触发**
- 检查失败阈值设置
- 分析下游服务健康状态
- 调整超时时间
## 🏆 **集成完成状态**
| 特性模块 | 实现状态 | 容器集成 | 中间件 | 配置支持 | 文档完整度 |
| ---------- | -------- | -------- | ------------- | -------- | ---------- |
| 链路追踪 | ✅ 100% | ✅ 完成 | ✅ 已集成 | ✅ 完整 | ✅ 完整 |
| 指标监控 | ✅ 100% | ✅ 完成 | ✅ 已集成 | ✅ 完整 | ✅ 完整 |
| 熔断器 | ✅ 100% | ✅ 完成 | ⚠️ 手动集成 | ✅ 完整 | ✅ 完整 |
| 重试机制 | ✅ 100% | ✅ 完成 | ⚠️ 手动集成 | ✅ 完整 | ✅ 完整 |
| 分布式事务 | ✅ 100% | ✅ 完成 | ⚠️ 手动集成 | ✅ 完整 | ✅ 完整 |
| 钩子系统 | ✅ 100% | ✅ 完成 | ⚠️ 应用级集成 | ✅ 完整 | ✅ 完整 |
## 🎉 **总结**
TYAPI Server 现已完成所有企业级高级特性的完整集成:
✅ **已完成的核心能力**
- 分布式链路追踪 (OpenTelemetry + OTLP)
- 全方位指标监控 (Prometheus + 业务指标)
- 多层次弹性恢复 (熔断器 + 重试机制)
- 分布式事务管理 (Saga 模式)
- 灵活事件钩子系统
✅ **生产就绪特性**
- 完整的容器依赖注入
- 自动化中间件集成
- 优雅的生命周期管理
- 完善的配置系统
- 详细的监控指标
✅ **开发体验**
- 编译零错误
- 热插拔组件设计
- 丰富的使用示例
- 完整的故障排查指南
现在您的 TYAPI Server 已经具备了企业级产品的所有核心监控和弹性能力!🚀