# 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. **数据库无关**: 统一的模型接口,支持多种数据库类型 ## 🚀 快速开始 ### 环境准备 ```bash # 1. 安装 Go 1.23+ go version # 2. 安装 goctl 工具 go install github.com/zeromicro/go-zero/tools/goctl@latest # 3. 验证安装 goctl --version ``` ### 项目初始化 ```bash # 1. 创建项目目录 mkdir photography-backend && cd photography-backend # 2. 初始化 Go 模块 go mod init photography-backend # 3. 创建 API 定义文件 mkdir -p api/desc ``` ### 快速开发流程 ```bash # 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) ```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) ```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) ```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 服务生成**: ```bash # 从主 API 文件生成代码 goctl api go -api api/desc/photography.api -dir api/ ``` 2. **数据模型生成**: ```bash # 从 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 的命名约定 ### 业务逻辑开发规范 ```go // 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 } ``` ### 数据模型开发规范 ```go // 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='用户表'; */ ``` ### 中间件开发规范 ```go // 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)) }) } ``` ### 统一响应格式 ```go // 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, } } ``` ### 错误处理规范 ```go // 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 } ``` ### 配置管理规范 ```yaml # 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 ``` ### 数据库配置结构 ```go // 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"` } ``` ## 📊 统一仓储架构设计 ### 数据库无关设计原则 项目采用统一的仓储接口,通过配置文件切换不同的数据库实现,业务逻辑完全与数据库类型解耦。 ### 仓储接口设计 ```go // 统一仓储接口 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 } ``` ### 配置驱动的数据库切换 ```go // 配置结构 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 } ``` ### 功能差异处理策略 ```go // 高级功能接口 (可选实现) 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 测试工具**: 内置测试支持 ### 测试示例 ```go // 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**: 快速开始指南 ### 文档维护 - 接口变更时同步更新文档 - 提供完整的请求/响应示例 - 包含错误码和处理说明 ## 🚀 部署配置 ### 环境变量 ```bash # 数据库配置 (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 部署 ```bash # 构建镜像 make build-image # 启动服务 make prod-up # 查看日志 make logs ``` ## 📋 常用命令 ### goctl 代码生成命令 ```bash # 生成 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 "代码生成完成!" ``` ### 开发命令 ```bash # 启动开发服务器 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 # 运行测试 ``` ### 数据库命令 (简化) ```bash # 执行 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 命令 ```bash # 构建镜像 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 响应慢**: 检查数据库查询和缓存配置 ### 日志查看 ```bash # 查看应用日志 tail -f logs/app.log # 查看错误日志 tail -f logs/error.log # 查看访问日志 tail -f logs/access.log ``` ## 🎯 模块工作指南 ### 根据工作内容选择模块 #### 🌐 API 接口开发 ```bash cd api/ # 参考 api/CLAUDE.md ``` **适用场景**: - API 定义文件编写 (`api/desc/*.api`) - Handler 处理器开发 (`api/internal/handler/`) - Logic 业务逻辑实现 (`api/internal/logic/`) - 中间件开发 (`api/internal/middleware/`) #### 📊 数据模型开发 ```bash cd api/internal/model/ # 参考 api/internal/model/CLAUDE.md ``` **适用场景**: - SQL 表结构设计 (`api/internal/model/sql/`) - 数据模型生成和自定义方法 - 数据访问层实现 #### 📦 公共包开发 ```bash cd pkg/ # 参考 pkg/CLAUDE.md ``` **适用场景**: - 错误处理 (`pkg/errorx/`) - 响应格式 (`pkg/response/`) - 通用工具 (`pkg/utils/`) - 全局常量 (`pkg/constants/`) #### 🧪 测试开发 ```bash cd tests/ # 参考 tests/CLAUDE.md ``` **适用场景**: API 集成测试、性能基准测试 #### 🐳 部署配置 ```bash cd configs/docker/ # 参考 configs/docker/README.md ``` **适用场景**: Docker 镜像构建、容器化部署、环境配置 ### go-zero 开发流程 (优化版) #### 1. 设计 API 接口 ```bash # 1.1 编辑公共类型 api/desc/common.api # 1.2 编辑具体模块 api/desc/user.api, api/desc/photo.api # 1.3 在主文件中导入 api/desc/photography.api ``` #### 2. 创建数据表结构 ```bash # 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. 生成代码框架 ```bash # 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. 实现业务逻辑 ```bash # 4.1 在 api/internal/logic/ 中实现具体的业务逻辑 # 4.2 在 api/internal/svc/servicecontext.go 中注入依赖 # 4.3 在 pkg/ 中开发通用工具和错误处理 ``` #### 5. 配置和测试 ```bash # 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 社区的最佳实践,为项目的长期维护奠定了良好基础。