Files
tyapi-server/api.mdc
2025-07-13 16:36:20 +08:00

2954 lines
86 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 开发的一致性、可维护性和扩展性。
# 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 已经具备了企业级产品的所有核心监控和弹性能力!🚀