Files
photography/backend/CLAUDE.md
xujiang 39a42695d3 refactor: 重构后端架构为 go-zero 框架,优化项目结构
主要变更:
- 采用 go-zero 框架替代 Gin,提升开发效率
- 重构项目结构,API 文件模块化组织
- 将 model 移至 api/internal/model 目录
- 移除 common 包,改为标准 pkg 目录结构
- 实现统一的仓储模式,支持配置驱动数据库切换
- 简化测试策略,专注 API 集成测试
- 更新 CLAUDE.md 文档,提供详细的开发指导

技术栈更新:
- 框架: Gin → go-zero v1.6.0+
- 代码生成: 引入 goctl 工具
- 架构模式: 四层架构 → go-zero 三层架构 (Handler→Logic→Model)
- 项目布局: 遵循 Go 社区标准和 go-zero 最佳实践
2025-07-10 15:05:52 +08:00

34 KiB
Raw Blame History

Backend API Service - CLAUDE.md

本文件为 Claude Code 在后端 API 服务模块中工作时提供指导。

🎯 模块概览

这是一个基于 Go + go-zero 框架的 REST API 后端服务,利用 go-zero 生态工具快速开发,采用 go-zero 推荐的项目架构。

主要特性

  • 🏗️ go-zero 标准架构 (API → Logic → Model)
  • 🚀 代码自动生成 (通过 .api 文件和 goctl 工具)
  • 📊 多数据库支持 (PostgreSQL/SQLite 通过配置切换 + Redis)
  • 🔐 JWT 认证 + 中间件系统
  • 📁 文件上传和存储管理
  • 🔗 链路追踪和监控
  • 🛡️ 熔断、限流、负载均衡
  • 📚 API 文档自动生成

技术栈

  • 语言: Go 1.23+
  • 框架: go-zero v1.6.0+
  • 数据库: PostgreSQL/SQLite (通过配置切换) + Redis (缓存)
  • ORM: GORM v1.30.0 (统一数据访问)
  • 认证: JWT (内置 JWT 支持)
  • 日志: go-zero 内置日志系统
  • 配置: go-zero 配置系统
  • 工具: goctl (代码生成工具)
  • 容器化: Docker + Docker Compose

📁 简洁架构设计

go-zero 项目结构 (优化版)

backend/
├── CLAUDE.md                          # 📋 当前文件 - 后端总览
├── go.mod                             # Go 模块文件
├── go.sum                             # 依赖锁定文件
├── Makefile                           # 构建脚本
├── api/                               # 🌐 API 服务模块
│   ├── CLAUDE.md                      # API 模块开发指导
│   ├── desc/                          # 📝 API 定义文件目录
│   │   ├── photography.api            # 📋 主 API 文件 (导入其他模块)
│   │   ├── user.api                   # 用户接口定义
│   │   ├── photo.api                  # 照片接口定义
│   │   ├── category.api               # 分类接口定义
│   │   ├── auth.api                   # 认证接口定义
│   │   └── common.api                 # 公共类型定义
│   ├── etc/                           # ⚙️ 配置文件
│   │   ├── photography-api.yaml       # 主配置文件
│   │   ├── photography-dev.yaml       # 开发环境配置
│   │   └── photography-prod.yaml      # 生产环境配置
│   ├── internal/                      # 📦 内部模块
│   │   ├── config/                    # 配置结构
│   │   │   └── config.go              # 配置定义
│   │   ├── handler/                   # 🎯 处理器 (goctl 自动生成)
│   │   │   ├── user/                  # 用户处理器
│   │   │   ├── photo/                 # 照片处理器
│   │   │   ├── category/              # 分类处理器
│   │   │   └── auth/                  # 认证处理器
│   │   ├── logic/                     # 🧠 业务逻辑 (goctl 自动生成)
│   │   │   ├── user/                  # 用户业务逻辑
│   │   │   ├── photo/                 # 照片业务逻辑
│   │   │   ├── category/              # 分类业务逻辑
│   │   │   └── auth/                  # 认证业务逻辑
│   │   ├── svc/                       # 🔧 服务上下文
│   │   │   └── servicecontext.go      # 服务上下文定义
│   │   ├── types/                     # 📦 类型定义 (goctl 自动生成)
│   │   │   └── types.go               # 请求/响应类型
│   │   ├── middleware/                # 🛡️ 中间件
│   │   │   ├── auth.go                # 认证中间件
│   │   │   ├── cors.go                # CORS 中间件
│   │   │   └── logger.go              # 日志中间件
│   │   └── model/                     # 📊 数据模型模块 (内部)
│   │       ├── CLAUDE.md              # 数据模型开发指导
│   │       ├── sql/                   # SQL 定义文件
│   │       │   ├── user.sql           # 用户表结构
│   │       │   ├── photo.sql          # 照片表结构
│   │       │   └── category.sql       # 分类表结构
│   │       ├── user.go                # 用户模型 (goctl 自动生成)
│   │       ├── photo.go               # 照片模型 (goctl 自动生成)
│   │       ├── category.go            # 分类模型 (goctl 自动生成)
│   │       └── vars.go                # 模型变量定义
│   └── photography.go                 # 🚀 服务入口 (goctl 自动生成)
├── pkg/                               # 📦 可导出包 (业务解耦)
│   ├── CLAUDE.md                      # 公共包开发指导
│   ├── errorx/                        # 错误处理包
│   │   └── errorx.go                  # 统一错误定义
│   ├── response/                      # 响应处理包
│   │   └── response.go                # 统一响应格式
│   ├── utils/                         # 通用工具包
│   │   ├── jwt/                       # JWT 工具
│   │   │   └── jwt.go                 # JWT 实现
│   │   ├── hash/                      # 哈希工具
│   │   │   └── hash.go                # 哈希实现
│   │   ├── file/                      # 文件处理工具
│   │   │   └── file.go                # 文件处理实现
│   │   └── database/                  # 数据库工具
│   │       └── database.go            # 数据库连接工厂
│   └── constants/                     # 常量定义包
│       └── constants.go               # 全局常量定义
├── configs/                           # 📋 配置文件目录
│   ├── sql/                           # SQL 初始化文件
│   │   ├── init.sql                   # 数据库初始化
│   │   └── seed.sql                   # 种子数据
│   └── docker/                        # Docker 相关配置
│       ├── Dockerfile                 # Docker 镜像定义
│       ├── docker-compose.yml         # 本地开发环境
│       └── docker-compose.prod.yml    # 生产环境配置
├── scripts/                           # 🛠️ 脚本目录
│   ├── build.sh                       # 构建脚本
│   ├── deploy.sh                      # 部署脚本
│   └── gen-code.sh                    # 代码生成脚本
├── deploy/                            # 🚀 部署配置
│   ├── k8s/                           # Kubernetes 配置
│   └── systemd/                       # Systemd 配置
└── tests/                             # 🧪 测试模块 (简化)
    ├── CLAUDE.md                      # 测试开发指导
    ├── api_test.go                    # API 集成测试
    └── benchmark_test.go              # 性能测试

go-zero 三层架构

🎯 Handler 层 (api/internal/handler/)

  • 职责: HTTP 请求处理、参数验证、响应封装
  • 特点: 由 goctl 工具自动生成,专注于 HTTP 协议处理
  • 文件: 按业务模块分组的处理器文件

🧠 Logic 层 (api/internal/logic/)

  • 职责: 业务逻辑处理、数据处理、服务编排
  • 特点: 由 goctl 工具生成框架,开发者填充业务逻辑
  • 文件: 按业务模块分组的逻辑文件

📊 Model 层 (model/)

  • 职责: 数据模型定义、数据库操作
  • 特点: 由 goctl 工具从 SQL 文件自动生成
  • 文件: 按数据表生成的模型文件

go-zero 设计原则

  1. 代码生成驱动: 通过 .api 文件和 goctl 工具自动生成代码
  2. 配置驱动: 所有配置统一管理,支持多环境热加载
  3. 中间件体系: 丰富的内置中间件,支持自定义扩展
  4. 错误处理: 统一的错误处理和响应格式
  5. 服务治理: 内置熔断、限流、负载均衡等微服务组件
  6. 链路追踪: 内置 OpenTelemetry 支持,便于问题排查
  7. 数据库无关: 统一的模型接口,支持多种数据库类型

🚀 快速开始

环境准备

# 1. 安装 Go 1.23+ 
go version

# 2. 安装 goctl 工具
go install github.com/zeromicro/go-zero/tools/goctl@latest

# 3. 验证安装
goctl --version

项目初始化

# 1. 创建项目目录
mkdir photography-backend && cd photography-backend

# 2. 初始化 Go 模块
go mod init photography-backend

# 3. 创建 API 定义文件
mkdir -p api/desc

快速开发流程

# 1. 定义 API 接口 (api/photography.api)
# 2. 生成 API 服务代码
goctl api go -api api/photography.api -dir api/

# 3. 定义数据库表结构 (model/*.sql)
# 4. 生成数据模型代码
goctl model mysql ddl -src model/user.sql -dir model/

# 5. 启动开发服务器
cd api && go run photography.go -f etc/photography-dev.yaml

开发模式

  • 快速开发: 使用 SQLite 进行本地开发,无需额外数据库
  • 生产模式: 使用 PostgreSQL + Redis完整的生产环境配置
  • 测试模式: 使用内存数据库,用于单元测试和集成测试

🔧 go-zero 开发规范

API 文件组织规范

主 API 文件 (api/desc/photography.api)

syntax = "v1"

info(
    title: "Photography API"
    desc: "摄影作品集 API 服务"
    author: "Developer"
    email: "dev@example.com"
    version: "v1.0.0"
)

// 导入其他模块的 API 定义
import "common.api"
import "auth.api"
import "user.api"
import "photo.api"
import "category.api"

公共类型定义 (api/desc/common.api)

syntax = "v1"

// 公共响应结构
type BaseResponse {
    Code    int    `json:"code"`
    Message string `json:"message"`
}

type PageRequest {
    Page     int `form:"page,default=1"`
    PageSize int `form:"page_size,default=10"`
}

type PageResponse {
    Total int64 `json:"total"`
    Page  int   `json:"page"`
    Size  int   `json:"size"`
}

用户模块 API (api/desc/user.api)

syntax = "v1"

import "common.api"

// 用户管理
@server(
    group: user
    prefix: /api/v1/users
    jwt: Auth
)
service photography-api {
    @doc "获取用户列表"
    @handler getUserList
    get / (GetUserListReq) returns (GetUserListResp)
    
    @doc "创建用户"
    @handler createUser
    post / (CreateUserReq) returns (CreateUserResp)
    
    @doc "获取用户详情"
    @handler getUser
    get /:id (GetUserReq) returns (GetUserResp)
}

type GetUserListReq {
    PageRequest
    Keyword string `form:"keyword,optional"`
}

type GetUserListResp {
    BaseResponse
    Data UserListData `json:"data"`
}

type UserListData {
    PageResponse
    Users []User `json:"users"`
}

type User {
    Id       int64  `json:"id"`
    Username string `json:"username"`
    Email    string `json:"email"`
    Avatar   string `json:"avatar"`
    Status   int    `json:"status"`
    CreateAt int64  `json:"create_at"`
}

代码生成规范

  1. API 服务生成:
    # 从主 API 文件生成代码
    goctl api go -api api/desc/photography.api -dir api/
    
  2. 数据模型生成:
    # 从 SQL DDL 生成模型到 internal/model
    goctl model mysql ddl -src api/internal/model/sql/user.sql -dir api/internal/model/
    
  3. 代码更新: 重新生成时保持手动修改的业务逻辑
  4. 文件组织: API 按模块分文件model 放在 internal 目录
  5. 命名规范: 遵循 go-zero 的命名约定

业务逻辑开发规范

// Logic 层开发示例 (api/internal/logic/user/getuserlistlogic.go)
type GetUserListLogic struct {
    logx.Logger
    ctx    context.Context
    svcCtx *svc.ServiceContext
}

func NewGetUserListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserListLogic {
    return &GetUserListLogic{
        Logger: logx.WithContext(ctx),
        ctx:    ctx,
        svcCtx: svcCtx,
    }
}

func (l *GetUserListLogic) GetUserList(req *types.GetUserListReq) (resp *types.GetUserListResp, err error) {
    // 业务逻辑处理
    users, total, err := l.svcCtx.UserModel.FindList(l.ctx, req.Page, req.PageSize, req.Keyword)
    if err != nil {
        return nil, errorx.NewDefaultError("获取用户列表失败")
    }

    return &types.GetUserListResp{
        Code:    200,
        Message: "success",
        Data: types.UserList{
            Total: total,
            Users: users,
        },
    }, nil
}

数据模型开发规范

// api/internal/model/user.go (由 goctl 自动生成)
type User struct {
    Id       int64     `db:"id"`
    Username string    `db:"username"`
    Email    string    `db:"email"`
    Avatar   string    `db:"avatar"`
    Status   int64     `db:"status"`
    CreateAt time.Time `db:"create_at"`
    UpdateAt time.Time `db:"update_at"`
}

// 自定义方法 (在生成的模型中添加)
func (u *UserModel) FindList(ctx context.Context, page, pageSize int, keyword string) ([]*User, int64, error) {
    query := fmt.Sprintf("SELECT %s FROM %s WHERE 1=1", userRows, u.table)
    args := []interface{}{}
    
    if keyword != "" {
        query += " AND (username LIKE ? OR email LIKE ?)"
        args = append(args, "%"+keyword+"%", "%"+keyword+"%")
    }
    
    // 获取总数
    countQuery := strings.Replace(query, userRows, "COUNT(*)", 1)
    var total int64
    err := u.conn.QueryRowCtx(ctx, &total, countQuery, args...)
    if err != nil {
        return nil, 0, err
    }
    
    // 分页查询
    query += " ORDER BY id DESC LIMIT ? OFFSET ?"
    args = append(args, pageSize, (page-1)*pageSize)
    
    var users []*User
    err = u.conn.QueryRowsCtx(ctx, &users, query, args...)
    return users, total, err
}

// SQL DDL 文件示例 (api/internal/model/sql/user.sql)
/*
CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) NOT NULL DEFAULT '' COMMENT '用户名',
  `email` varchar(255) NOT NULL DEFAULT '' COMMENT '邮箱',
  `avatar` varchar(255) NOT NULL DEFAULT '' COMMENT '头像',
  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态1-正常0-禁用',
  `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_username` (`username`),
  UNIQUE KEY `idx_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
*/

中间件开发规范

// api/internal/middleware/auth.go
type AuthMiddleware struct {
    secret string
}

func NewAuthMiddleware(secret string) *AuthMiddleware {
    return &AuthMiddleware{secret: secret}
}

func (m *AuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // JWT 验证逻辑
        token := r.Header.Get("Authorization")
        if token == "" {
            httpx.ErrorCtx(r.Context(), w, errorx.NewCodeError(401, "未授权"))
            return
        }
        
        // 解析和验证 JWT
        claims, err := jwt.ParseToken(token, m.secret)
        if err != nil {
            httpx.ErrorCtx(r.Context(), w, errorx.NewCodeError(401, "无效的令牌"))
            return
        }
        
        // 将用户信息存入上下文
        ctx := context.WithValue(r.Context(), "userId", claims.UserId)
        next(w, r.WithContext(ctx))
    })
}

统一响应格式

// pkg/response/response.go
type Response struct {
    Code    int         `json:"code"`
    Message string      `json:"message"`
    Data    interface{} `json:"data,omitempty"`
}

func Success(data interface{}) *Response {
    return &Response{
        Code:    200,
        Message: "success",
        Data:    data,
    }
}

func Error(code int, message string) *Response {
    return &Response{
        Code:    code,
        Message: message,
    }
}

错误处理规范

// pkg/errorx/errorx.go
type CodeError struct {
    Code int    `json:"code"`
    Msg  string `json:"msg"`
}

func (e *CodeError) Error() string {
    return e.Msg
}

func NewCodeError(code int, msg string) error {
    return &CodeError{Code: code, Msg: msg}
}

func NewDefaultError(msg string) error {
    return NewCodeError(500, msg)
}

// pkg/constants/constants.go - 错误码定义
const (
    UserNotFoundError     = 10001
    InvalidParameterError = 10002
    PermissionDeniedError = 10003
    InternalError         = 50001
)

// 在 logic 中使用
func (l *GetUserLogic) GetUser(req *types.GetUserReq) (*types.GetUserResp, error) {
    user, err := l.svcCtx.UserModel.FindOne(l.ctx, req.Id)
    if err != nil {
        if err == model.ErrNotFound {
            return nil, errorx.NewCodeError(constants.UserNotFoundError, "用户不存在")
        }
        return nil, errorx.NewDefaultError("获取用户信息失败")
    }
    
    return &types.GetUserResp{
        Code:    200,
        Message: "success",
        Data:    user,
    }, nil
}

配置管理规范

# api/etc/photography-dev.yaml
Name: photography-api
Host: 0.0.0.0
Port: 8080
Mode: dev

# 数据库配置 (支持多种数据库类型)
Database:
  Type: sqlite                    # postgres, sqlite
  Host: localhost                 # PostgreSQL
  Port: 5432
  Name: photography
  User: postgres
  Password: password
  SSLMode: disable
  FilePath: ./data/photography.db # SQLite

# Redis 配置
Redis:
  Host: localhost:6379
  Password: ""
  DB: 0

# JWT 配置
Auth:
  AccessSecret: your-access-secret
  AccessExpire: 86400  # 24小时

# 日志配置
Log:
  ServiceName: photography-api
  Mode: console
  Level: info

数据库配置结构

// api/internal/config/config.go
type Config struct {
    rest.RestConf
    Database DatabaseConfig
    Redis    RedisConfig
    Auth     AuthConfig
}

type DatabaseConfig struct {
    Type     string `json:",default=sqlite"`
    Host     string `json:",default=localhost"`
    Port     int    `json:",default=5432"`
    Name     string `json:",default=photography"`
    User     string `json:",default=postgres"`
    Password string `json:",optional"`
    SSLMode  string `json:",default=disable"`
    FilePath string `json:",default=./data/photography.db"`
}

type RedisConfig struct {
    Host     string `json:",default=localhost:6379"`
    Password string `json:",optional"`
    DB       int    `json:",default=0"`
}

type AuthConfig struct {
    AccessSecret string `json:",default=your-access-secret"`
    AccessExpire int64  `json:",default=86400"`
}

📊 统一仓储架构设计

数据库无关设计原则

项目采用统一的仓储接口,通过配置文件切换不同的数据库实现,业务逻辑完全与数据库类型解耦。

仓储接口设计

// 统一仓储接口
type UserRepository interface {
    GetByID(ctx context.Context, id uint) (*entity.User, error)
    Create(ctx context.Context, user *entity.User) error
    Update(ctx context.Context, user *entity.User) error
    Delete(ctx context.Context, id uint) error
    List(ctx context.Context, opts *dto.ListOptions) ([]*entity.User, int64, error)
    GetByEmail(ctx context.Context, email string) (*entity.User, error)
    GetByUsername(ctx context.Context, username string) (*entity.User, error)
}

// 数据库实现 (database/user_repository.go)
type userRepository struct {
    db     *gorm.DB
    logger logger.Logger
}

func NewUserRepository(db *gorm.DB, logger logger.Logger) UserRepository {
    return &userRepository{
        db:     db,
        logger: logger,
    }
}

// 所有数据库都使用相同的实现
func (r *userRepository) GetByID(ctx context.Context, id uint) (*entity.User, error) {
    var user entity.User
    err := r.db.WithContext(ctx).First(&user, id).Error
    if err != nil {
        if errors.Is(err, gorm.ErrRecordNotFound) {
            return nil, ErrUserNotFound
        }
        return nil, err
    }
    return &user, nil
}

配置驱动的数据库切换

// 配置结构
type DatabaseConfig struct {
    Type     string `mapstructure:"type"`     // "postgres", "sqlite"
    Host     string `mapstructure:"host"`
    Port     int    `mapstructure:"port"`
    Name     string `mapstructure:"name"`
    User     string `mapstructure:"user"`
    Password string `mapstructure:"password"`
    SSLMode  string `mapstructure:"ssl_mode"`
    FilePath string `mapstructure:"file_path"` // SQLite 文件路径
}

// 数据库连接工厂
func NewDatabase(config *DatabaseConfig) (*gorm.DB, error) {
    var dsn string
    var dialector gorm.Dialector

    switch config.Type {
    case "postgres":
        dsn = fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=%s",
            config.Host, config.User, config.Password, config.Name, config.Port, config.SSLMode)
        dialector = postgres.Open(dsn)
    case "sqlite":
        dsn = config.FilePath
        dialector = sqlite.Open(dsn)
    default:
        return nil, fmt.Errorf("unsupported database type: %s", config.Type)
    }

    db, err := gorm.Open(dialector, &gorm.Config{
        Logger: logger.Default.LogMode(logger.Info),
    })
    if err != nil {
        return nil, err
    }

    return db, nil
}

功能差异处理策略

// 高级功能接口 (可选实现)
type AdvancedUserRepository interface {
    UserRepository
    // PostgreSQL 特有功能
    FullTextSearch(ctx context.Context, query string) ([]*entity.User, error)
    BulkUpsert(ctx context.Context, users []*entity.User) error
}

// 实现中处理差异
func (r *userRepository) FullTextSearch(ctx context.Context, query string) ([]*entity.User, error) {
    // 检查数据库类型
    if r.db.Dialector.Name() == "postgres" {
        // PostgreSQL 全文搜索
        var users []*entity.User
        err := r.db.WithContext(ctx).
            Where("to_tsvector('simple', name || ' ' || email) @@ plainto_tsquery('simple', ?)", query).
            Find(&users).Error
        return users, err
    }
    
    // SQLite 降级为 LIKE 搜索
    var users []*entity.User
    err := r.db.WithContext(ctx).
        Where("name LIKE ? OR email LIKE ?", "%"+query+"%", "%"+query+"%").
        Find(&users).Error
    return users, err
}

主要实体

  • User: 用户信息和权限
  • Photo: 照片信息和元数据
  • Category: 照片分类
  • Tag: 照片标签
  • Album: 相册管理

关系设计

User (1:N) Photo
Photo (N:M) Category
Photo (N:M) Tag
User (1:N) Album
Album (N:M) Photo

🔐 认证和授权

JWT 认证流程

  1. 用户登录 → 验证凭据
  2. 生成 JWT Token (AccessToken + RefreshToken)
  3. 客户端携带 Token 访问受保护资源
  4. 服务器验证 Token 有效性

权限角色

  • Admin: 系统管理员 (所有权限)
  • Editor: 内容编辑者 (内容管理)
  • User: 普通用户 (查看权限)

🧪 简化测试策略

测试类型 (简化)

  • API 集成测试: 测试完整的 API 请求响应流程
  • 性能测试: 基本的接口响应时间测试

测试工具 (最小化)

  • Go Testing: 内置测试框架
  • httptest: HTTP 服务测试
  • go-zero 测试工具: 内置测试支持

测试示例

// tests/api_test.go
func TestGetUserList(t *testing.T) {
    // 使用 httptest 测试 API
    req := httptest.NewRequest("GET", "/api/v1/users?page=1&page_size=10", nil)
    w := httptest.NewRecorder()
    
    // 调用 handler
    handler := getUserListHandler()
    handler.ServeHTTP(w, req)
    
    // 验证响应
    assert.Equal(t, 200, w.Code)
    
    var resp types.GetUserListResp
    err := json.Unmarshal(w.Body.Bytes(), &resp)
    assert.NoError(t, err)
    assert.Equal(t, 200, resp.Code)
}

// tests/benchmark_test.go
func BenchmarkGetUserList(b *testing.B) {
    for i := 0; i < b.N; i++ {
        req := httptest.NewRequest("GET", "/api/v1/users", nil)
        w := httptest.NewRecorder()
        handler := getUserListHandler()
        handler.ServeHTTP(w, req)
    }
}

📚 API 文档

文档生成

  • Swagger/OpenAPI: 自动生成 API 文档
  • Postman Collection: 接口测试集合
  • README: 快速开始指南

文档维护

  • 接口变更时同步更新文档
  • 提供完整的请求/响应示例
  • 包含错误码和处理说明

🚀 部署配置

环境变量

# 数据库配置 (PostgreSQL)
DB_TYPE=postgres
DB_HOST=localhost
DB_PORT=5432
DB_NAME=photography
DB_USER=postgres
DB_PASSWORD=password
DB_SSLMODE=disable

# 数据库配置 (SQLite)
DB_TYPE=sqlite
DB_FILE_PATH=./data/photography.db

# JWT 配置
JWT_SECRET=your-secret-key
JWT_EXPIRES_IN=24h

# 文件存储
STORAGE_TYPE=local
STORAGE_PATH=./uploads

Docker 部署

# 构建镜像
make build-image

# 启动服务
make prod-up

# 查看日志
make logs

📋 常用命令

goctl 代码生成命令

# 生成 API 服务 (从主 API 文件)
goctl api go -api api/desc/photography.api -dir api/

# 生成数据模型 (从 SQL DDL)
goctl model mysql ddl -src api/internal/model/sql/user.sql -dir api/internal/model/

# 生成数据模型 (从数据库)
goctl model mysql datasource -url="user:password@tcp(localhost:3306)/database" -table="user" -dir api/internal/model/

# 生成 Docker 文件
goctl docker -go photography.go

# 生成 Kubernetes 配置
goctl kube deploy -name photography-api -namespace default -image photography-api:latest -o deploy.yaml

# 一键生成脚本 (scripts/gen-code.sh)
#!/bin/bash
echo "生成 API 代码..."
goctl api go -api api/desc/photography.api -dir api/

echo "生成数据模型..."
goctl model mysql ddl -src api/internal/model/sql/user.sql -dir api/internal/model/
goctl model mysql ddl -src api/internal/model/sql/photo.sql -dir api/internal/model/
goctl model mysql ddl -src api/internal/model/sql/category.sql -dir api/internal/model/

echo "代码生成完成!"

开发命令

# 启动开发服务器
go run api/photography.go -f api/etc/photography-dev.yaml

# 代码格式化
go fmt ./...

# 代码检查
go vet ./...

# 测试
go test ./tests/...

# 构建
go build -o bin/photography-api api/photography.go

# 使用 Makefile
make dev      # 启动开发服务器
make build    # 构建二进制文件
make gen      # 生成代码
make test     # 运行测试

数据库命令 (简化)

# 执行 SQL 文件
sqlite3 data/photography.db < api/internal/model/sql/user.sql

# 或者使用 PostgreSQL
psql -U postgres -d photography -f api/internal/model/sql/user.sql

# 初始化所有表
cat api/internal/model/sql/*.sql | sqlite3 data/photography.db

Docker 命令

# 构建镜像
docker build -t photography-api .

# 运行容器
docker run -p 8080:8080 photography-api

# 使用 docker-compose
docker-compose up -d

🔍 问题排查

常见问题

  1. 数据库连接失败: 检查配置文件和环境变量
  2. JWT 验证失败: 检查密钥配置和 Token 格式
  3. 文件上传失败: 检查存储配置和权限设置
  4. API 响应慢: 检查数据库查询和缓存配置

日志查看

# 查看应用日志
tail -f logs/app.log

# 查看错误日志
tail -f logs/error.log

# 查看访问日志
tail -f logs/access.log

🎯 模块工作指南

根据工作内容选择模块

🌐 API 接口开发

cd api/
# 参考 api/CLAUDE.md

适用场景:

  • API 定义文件编写 (api/desc/*.api)
  • Handler 处理器开发 (api/internal/handler/)
  • Logic 业务逻辑实现 (api/internal/logic/)
  • 中间件开发 (api/internal/middleware/)

📊 数据模型开发

cd api/internal/model/
# 参考 api/internal/model/CLAUDE.md

适用场景:

  • SQL 表结构设计 (api/internal/model/sql/)
  • 数据模型生成和自定义方法
  • 数据访问层实现

📦 公共包开发

cd pkg/
# 参考 pkg/CLAUDE.md

适用场景:

  • 错误处理 (pkg/errorx/)
  • 响应格式 (pkg/response/)
  • 通用工具 (pkg/utils/)
  • 全局常量 (pkg/constants/)

🧪 测试开发

cd tests/
# 参考 tests/CLAUDE.md

适用场景: API 集成测试、性能基准测试

🐳 部署配置

cd configs/docker/
# 参考 configs/docker/README.md

适用场景: Docker 镜像构建、容器化部署、环境配置

go-zero 开发流程 (优化版)

1. 设计 API 接口

# 1.1 编辑公共类型 api/desc/common.api
# 1.2 编辑具体模块 api/desc/user.api, api/desc/photo.api
# 1.3 在主文件中导入 api/desc/photography.api

2. 创建数据表结构

# 2.1 设计 SQL DDL api/internal/model/sql/user.sql
# 2.2 生成数据模型
goctl model mysql ddl -src api/internal/model/sql/user.sql -dir api/internal/model/

3. 生成代码框架

# 3.1 使用脚本一键生成
chmod +x scripts/gen-code.sh && ./scripts/gen-code.sh

# 3.2 或手动生成
goctl api go -api api/desc/photography.api -dir api/

4. 实现业务逻辑

# 4.1 在 api/internal/logic/ 中实现具体的业务逻辑
# 4.2 在 api/internal/svc/servicecontext.go 中注入依赖
# 4.3 在 pkg/ 中开发通用工具和错误处理

5. 配置和测试

# 5.1 配置数据库连接 api/etc/photography-dev.yaml
# 5.2 运行服务
go run api/photography.go -f api/etc/photography-dev.yaml

# 5.3 编写和运行测试
go test ./tests/...

🔄 最佳实践

go-zero 开发流程

  1. API 优先设计: 先定义 .api 文件,明确接口契约
  2. 代码生成驱动: 使用 goctl 工具生成框架代码
  3. 业务逻辑专注: 在 Logic 层专注实现业务逻辑
  4. 配置驱动: 通过配置文件管理不同环境
  5. 简化测试: 专注 API 集成测试,减少复杂度

代码质量 (简化)

  • go fmt: 使用标准格式化工具
  • go vet: 运行静态分析检查
  • API 测试: 确保接口功能正常
  • 配置验证: 确保不同环境配置正确

go-zero 优势

  • 快速开发: 通过代码生成大幅提升开发效率
  • 生态丰富: 内置中间件、链路追踪、服务治理
  • 配置简单: YAML 配置文件,易于理解和维护
  • 部署方便: 单二进制文件,容器化友好

📈 项目状态

已完成功能

  • go-zero 架构设计
  • 统一数据库模式 (配置驱动切换)
  • JWT 认证中间件
  • API 代码生成框架
  • Docker 部署支持
  • 基础 CRUD 接口

开发中功能

  • 🔄 API 接口实现 (通过 goctl 快速生成)
  • 🔄 数据模型完善
  • 🔄 简化测试用例

计划中功能

  • 📋 文件上传功能
  • 📋 缓存集成
  • 📋 链路追踪
  • 📋 性能监控

🔧 开发环境

必需工具

  • Go 1.23+: 编程语言
  • goctl: go-zero 代码生成工具
  • SQLite/PostgreSQL: 数据库 (可配置切换)
  • Redis (可选): 缓存数据库
  • Docker (可选): 容器化部署

推荐工具

  • GoLand/VSCode: 代码编辑器 (支持 .api 文件语法高亮)
  • Postman/curl: API 测试
  • SQLite Browser/DBeaver: 数据库管理

💡 go-zero 开发技巧

快速开发技巧

  • 模板化开发: 使用 goctl 模板快速生成标准代码
  • 配置热更新: 修改配置文件无需重启服务
  • 内置中间件: 使用 go-zero 内置的限流、熔断等中间件
  • 链路追踪: 开启内置的链路追踪,便于问题定位

性能优化 (内置支持)

  • 连接池: go-zero 自动管理数据库连接池
  • 缓存集成: 简单配置即可集成 Redis 缓存
  • 负载均衡: 内置多种负载均衡算法
  • 服务发现: 支持多种服务发现机制

安全防护 (中间件)

  • JWT 中间件: 内置 JWT 认证中间件
  • CORS 中间件: 跨域资源共享支持
  • 限流中间件: 防止接口被恶意调用
  • 参数验证: 通过 .api 文件定义自动验证

部署优化

  • 单二进制: 编译生成单个可执行文件
  • Docker 友好: 通过 goctl 生成 Dockerfile
  • 配置外置: 通过环境变量或配置文件管理
  • 健康检查: 内置健康检查接口

🚀 快速上手总结

  1. 安装 goctl: go install github.com/zeromicro/go-zero/tools/goctl@latest
  2. 创建 API 文件: 定义接口和数据结构
  3. 生成代码: goctl api go -api photography.api -dir api/
  4. 实现业务逻辑: 在 Logic 层编写具体实现
  5. 配置数据库: 修改配置文件,支持多种数据库
  6. 启动服务: go run photography.go -f etc/config.yaml

通过 go-zero 生态工具,可以极大地加快开发进度,专注于业务逻辑实现,而无需关心框架细节。

📋 项目结构优化总结

解决的问题

1. API 文件组织问题

原问题: API 文件会不会有多个?是不是应该放一个目录中?

解决方案:

  • 将所有 API 文件统一放在 api/desc/ 目录中
  • 按功能模块分割:user.api, photo.api, category.api, auth.api
  • 使用主文件 photography.api 通过 import 导入各模块
  • 公共类型定义放在 common.api 中,避免重复

2. Model 目录位置问题

原问题: model 是不是应该放到 internal/model 目录下?

解决方案:

  • 将 model 从根目录移至 api/internal/model/
  • SQL 文件组织在 api/internal/model/sql/ 目录
  • 符合 Go 项目中 internal 包的语义(仅内部使用)
  • 与 go-zero 推荐的项目结构保持一致

3. 公共包命名问题

原问题: 不想有 common 包,业务解耦的放在 pkg

解决方案:

  • 移除 common/ 目录,改为 pkg/ 目录
  • 按功能细分:pkg/errorx/, pkg/response/, pkg/utils/, pkg/constants/
  • 符合 Go 社区标准,pkg/ 目录用于可被外部导入的包
  • 更好的包命名,避免泛泛的 "common" 命名

4. 其他结构优化

配置文件整理:

  • Docker 相关配置移至 configs/docker/
  • SQL 初始化文件移至 configs/sql/
  • 脚本文件统一放在 scripts/ 目录

工具包细分:

  • JWT 工具:pkg/utils/jwt/
  • 哈希工具:pkg/utils/hash/
  • 文件工具:pkg/utils/file/
  • 数据库工具:pkg/utils/database/

新结构的优势

1. 🎯 清晰的模块边界

  • API 定义、处理逻辑、数据模型各司其职
  • 内部包和外部包职责分明
  • 按功能域组织,便于团队协作

2. 🚀 高效的开发流程

  • 通过脚本一键生成所有代码框架
  • 模块化的 API 定义,支持并行开发
  • 统一的错误处理和响应格式

3. 🛠️ 易于维护和扩展

  • SQL 文件集中管理,便于数据库版本控制
  • 公共工具包可被多个服务复用
  • 配置文件按环境分离,支持多环境部署

4. 📏 符合 Go 社区标准

  • 遵循 Go 项目布局标准
  • internal 包的正确使用
  • pkg 包的合理组织

这种结构既保持了 go-zero 的开发效率优势,又符合 Go 社区的最佳实践,为项目的长期维护奠定了良好基础。