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

1164 lines
34 KiB
Markdown
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.

# 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 社区的最佳实践,为项目的长期维护奠定了良好基础。