Files
photography/backend/CLAUDE.md
xujiang 73197d8da8 feat: 完善模块化 CLAUDE.md 文档体系
- 新增 admin/CLAUDE.md - 管理后台开发指导文档
  - 修正技术栈为 React + TypeScript + shadcn/ui
  - 提供完整的管理后台架构设计
  - 包含照片管理、分类管理、日志管理等核心功能
  - 详细的开发环境配置和部署指南

- 新增 backend/CLAUDE.md - 后端开发指导文档
  - 基于 Golang + Gin + GORM 技术栈
  - 完整的 API 接口设计和数据库架构
  - 包含认证、权限、文件存储等核心功能
  - 详细的部署和监控配置

- 新增 ui/CLAUDE.md - UI 备份模块管理文档
  - 支持组件备份和 A/B 测试功能
  - 详细的同步策略和实验环境配置
  - 完整的版本管理和协作流程

- 更新 CLAUDE.md 根目录文档
  - 完善模块选择指南和协调机制
  - 新增模块间通信和依赖关系说明
  - 优化文档维护和使用建议
  - 建立完整的模块化开发规范

通过模块化设计最大限度减少 AI 幻觉,提高开发效率。
2025-07-09 14:23:15 +08:00

26 KiB

后端模块 - CLAUDE.md

此文件为 Claude Code 在后端模块工作时提供指导。

🎯 模块概览

后端模块提供摄影作品集项目的 API 服务,包括照片管理、用户认证、数据存储等核心功能。

功能特性

  • 🔐 用户认证和授权管理
  • 📸 照片和相册 CRUD 操作
  • 📁 文件上传和存储管理
  • 🎨 主题和配置管理
  • 📊 访问统计和数据分析
  • 🔄 数据同步和缓存

技术栈

  • 语言: Go 1.21+
  • 框架: Gin + GORM
  • 数据库: PostgreSQL / MySQL
  • 缓存: Redis
  • 存储: MinIO / AWS S3
  • 认证: JWT Token
  • 部署: Docker + Docker Compose

📁 项目结构

backend/
├── CLAUDE.md                      # 🔍 当前文件 - 后端开发指导
├── cmd/                           # 应用入口
│   └── server/                   # 服务器启动
│       └── main.go
├── internal/                      # 内部模块
│   ├── api/                      # API 层
│   │   ├── handlers/            # 请求处理器
│   │   ├── middleware/          # 中间件
│   │   └── routes/              # 路由定义
│   ├── service/                  # 业务逻辑层
│   │   ├── auth/               # 认证服务
│   │   ├── photo/              # 照片服务
│   │   ├── album/              # 相册服务
│   │   └── user/               # 用户服务
│   ├── repository/              # 数据访问层
│   │   ├── postgres/           # PostgreSQL 实现
│   │   └── redis/              # Redis 实现
│   ├── models/                  # 数据模型
│   ├── config/                  # 配置管理
│   └── utils/                   # 工具函数
├── pkg/                          # 公共包
│   ├── logger/                  # 日志包
│   ├── validator/               # 验证包
│   └── response/                # 响应包
├── migrations/                   # 数据库迁移
├── docs/                        # API 文档
├── scripts/                     # 部署脚本
├── configs/                     # 配置文件
│   ├── config.yaml             # 主配置
│   ├── config.dev.yaml         # 开发环境
│   └── config.prod.yaml        # 生产环境
├── docker-compose.yml           # 容器编排
├── Dockerfile                   # 容器构建
├── Makefile                     # 构建脚本
├── go.mod                       # Go 模块定义
├── go.sum                       # 依赖校验
└── README.md                    # 模块说明

🚀 开发环境配置

环境要求

  • Go: 1.21+
  • Docker: 20.10+
  • Docker Compose: 2.0+
  • PostgreSQL: 14+
  • Redis: 6.2+

快速启动

# 进入后端目录
cd backend/

# 启动开发环境 (Docker)
make dev-up

# 运行数据库迁移
make migrate

# 启动开发服务器
make dev

# 或者直接运行
go run cmd/server/main.go

环境变量配置

# .env
# 数据库配置
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=password
DB_NAME=photography

# Redis 配置
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=

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

# 文件存储配置
STORAGE_TYPE=local  # local, s3, minio
STORAGE_PATH=./uploads
AWS_REGION=us-east-1
AWS_BUCKET=photography-bucket

# 服务器配置
PORT=8080
GIN_MODE=debug

🏗️ 项目架构

分层架构

┌─────────────────┐
│   API Layer     │  ← handlers, middleware, routes
├─────────────────┤
│ Service Layer   │  ← business logic
├─────────────────┤
│Repository Layer │  ← data access
├─────────────────┤
│  Models Layer   │  ← data structures
└─────────────────┘

目录结构详解

🎯 API 层

internal/api/
├── handlers/                   # 请求处理器
│   ├── auth.go                # 认证相关
│   ├── photo.go               # 照片管理
│   ├── album.go               # 相册管理
│   ├── user.go                # 用户管理
│   └── upload.go              # 文件上传
├── middleware/                # 中间件
│   ├── auth.go                # 认证中间件
│   ├── cors.go                # CORS 处理
│   ├── logger.go              # 日志中间件
│   └── rate_limit.go          # 限流中间件
└── routes/                    # 路由定义
    ├── api_v1.go             # API v1 路由
    └── admin.go              # 管理后台路由

💼 业务逻辑层

internal/service/
├── auth/                      # 认证服务
│   ├── auth.go               # 认证逻辑
│   ├── jwt.go                # JWT 处理
│   └── permission.go         # 权限控制
├── photo/                     # 照片服务
│   ├── photo.go              # 照片操作
│   ├── metadata.go           # 元数据处理
│   └── thumbnail.go          # 缩略图生成
├── album/                     # 相册服务
│   ├── album.go              # 相册操作
│   └── organization.go       # 组织管理
└── user/                      # 用户服务
    ├── user.go               # 用户管理
    └── profile.go            # 用户资料

🗄️ 数据访问层

internal/repository/
├── postgres/                  # PostgreSQL 实现
│   ├── photo.go              # 照片数据操作
│   ├── album.go              # 相册数据操作
│   ├── user.go               # 用户数据操作
│   └── migration.go          # 数据迁移
└── redis/                     # Redis 实现
    ├── cache.go              # 缓存操作
    └── session.go            # 会话管理

🗃️ 数据库设计

数据模型定义

// internal/models/photo.go
type Photo struct {
    ID          uint      `gorm:"primaryKey" json:"id"`
    Title       string    `gorm:"not null" json:"title"`
    Description string    `json:"description"`
    URL         string    `gorm:"not null" json:"url"`
    ThumbnailURL string   `json:"thumbnail_url"`
    AlbumID     uint      `json:"album_id"`
    UserID      uint      `json:"user_id"`
    Tags        []Tag     `gorm:"many2many:photo_tags" json:"tags"`
    Metadata    PhotoMetadata `gorm:"type:jsonb" json:"metadata"`
    CreatedAt   time.Time `json:"created_at"`
    UpdatedAt   time.Time `json:"updated_at"`
}

// internal/models/album.go
type Album struct {
    ID          uint      `gorm:"primaryKey" json:"id"`
    Name        string    `gorm:"not null" json:"name"`
    Description string    `json:"description"`
    CoverPhotoID *uint    `json:"cover_photo_id"`
    CoverPhoto  *Photo    `gorm:"foreignKey:CoverPhotoID" json:"cover_photo"`
    Photos      []Photo   `gorm:"foreignKey:AlbumID" json:"photos"`
    IsPublic    bool      `gorm:"default:true" json:"is_public"`
    UserID      uint      `json:"user_id"`
    CreatedAt   time.Time `json:"created_at"`
    UpdatedAt   time.Time `json:"updated_at"`
}

// internal/models/user.go
type User struct {
    ID        uint      `gorm:"primaryKey" json:"id"`
    Username  string    `gorm:"unique;not null" json:"username"`
    Email     string    `gorm:"unique;not null" json:"email"`
    Password  string    `gorm:"not null" json:"-"`
    Role      string    `gorm:"default:user" json:"role"`
    IsActive  bool      `gorm:"default:true" json:"is_active"`
    Profile   UserProfile `gorm:"foreignKey:UserID" json:"profile"`
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
}

数据库迁移

-- migrations/001_create_users.sql
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    username VARCHAR(50) UNIQUE NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    password VARCHAR(255) NOT NULL,
    role VARCHAR(20) DEFAULT 'user',
    is_active BOOLEAN DEFAULT true,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- migrations/002_create_albums.sql
CREATE TABLE albums (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    description TEXT,
    cover_photo_id INTEGER,
    is_public BOOLEAN DEFAULT true,
    user_id INTEGER REFERENCES users(id),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- migrations/003_create_photos.sql
CREATE TABLE photos (
    id SERIAL PRIMARY KEY,
    title VARCHAR(200) NOT NULL,
    description TEXT,
    url VARCHAR(500) NOT NULL,
    thumbnail_url VARCHAR(500),
    album_id INTEGER REFERENCES albums(id),
    user_id INTEGER REFERENCES users(id),
    metadata JSONB,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

🔌 API 接口设计

RESTful API 规范

// internal/api/handlers/photo.go
type PhotoHandler struct {
    photoService *service.PhotoService
}

// GET /api/v1/photos
func (h *PhotoHandler) GetPhotos(c *gin.Context) {
    params := &service.PhotoListParams{}
    if err := c.ShouldBindQuery(params); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    
    photos, total, err := h.photoService.GetPhotos(params)
    if err != nil {
        c.JSON(500, gin.H{"error": err.Error()})
        return
    }
    
    c.JSON(200, gin.H{
        "data": photos,
        "total": total,
        "page": params.Page,
        "per_page": params.PerPage,
    })
}

// POST /api/v1/photos
func (h *PhotoHandler) CreatePhoto(c *gin.Context) {
    var req service.CreatePhotoRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    
    userID := c.GetUint("user_id")
    req.UserID = userID
    
    photo, err := h.photoService.CreatePhoto(&req)
    if err != nil {
        c.JSON(500, gin.H{"error": err.Error()})
        return
    }
    
    c.JSON(201, gin.H{"data": photo})
}

// PUT /api/v1/photos/:id
func (h *PhotoHandler) UpdatePhoto(c *gin.Context) {
    id, _ := strconv.ParseUint(c.Param("id"), 10, 32)
    
    var req service.UpdatePhotoRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    
    photo, err := h.photoService.UpdatePhoto(uint(id), &req)
    if err != nil {
        c.JSON(500, gin.H{"error": err.Error()})
        return
    }
    
    c.JSON(200, gin.H{"data": photo})
}

// DELETE /api/v1/photos/:id
func (h *PhotoHandler) DeletePhoto(c *gin.Context) {
    id, _ := strconv.ParseUint(c.Param("id"), 10, 32)
    
    err := h.photoService.DeletePhoto(uint(id))
    if err != nil {
        c.JSON(500, gin.H{"error": err.Error()})
        return
    }
    
    c.JSON(204, nil)
}

API 路由配置

// internal/api/routes/api_v1.go
func SetupAPIV1Routes(r *gin.Engine, handlers *Handlers) {
    api := r.Group("/api/v1")
    
    // 公开接口
    api.GET("/photos", handlers.Photo.GetPhotos)
    api.GET("/photos/:id", handlers.Photo.GetPhoto)
    api.GET("/albums", handlers.Album.GetAlbums)
    api.GET("/albums/:id", handlers.Album.GetAlbum)
    
    // 认证相关
    auth := api.Group("/auth")
    {
        auth.POST("/login", handlers.Auth.Login)
        auth.POST("/register", handlers.Auth.Register)
        auth.POST("/refresh", handlers.Auth.RefreshToken)
    }
    
    // 需要认证的接口
    protected := api.Group("/", middleware.AuthRequired())
    {
        protected.POST("/photos", handlers.Photo.CreatePhoto)
        protected.PUT("/photos/:id", handlers.Photo.UpdatePhoto)
        protected.DELETE("/photos/:id", handlers.Photo.DeletePhoto)
        protected.POST("/upload", handlers.Upload.UploadFile)
    }
    
    // 管理员接口
    admin := api.Group("/admin", middleware.AdminRequired())
    {
        admin.GET("/users", handlers.User.GetUsers)
        admin.PUT("/users/:id", handlers.User.UpdateUser)
        admin.DELETE("/users/:id", handlers.User.DeleteUser)
    }
}

🔐 认证和授权

JWT 认证实现

// internal/service/auth/jwt.go
type JWTService struct {
    secretKey []byte
    expiresIn time.Duration
}

func NewJWTService(secretKey string, expiresIn time.Duration) *JWTService {
    return &JWTService{
        secretKey: []byte(secretKey),
        expiresIn: expiresIn,
    }
}

func (s *JWTService) GenerateToken(userID uint, role string) (string, error) {
    claims := jwt.MapClaims{
        "user_id": userID,
        "role":    role,
        "exp":     time.Now().Add(s.expiresIn).Unix(),
    }
    
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString(s.secretKey)
}

func (s *JWTService) ValidateToken(tokenString string) (*jwt.Token, error) {
    return jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
        }
        return s.secretKey, nil
    })
}

权限控制中间件

// internal/api/middleware/auth.go
func AuthRequired() gin.HandlerFunc {
    return func(c *gin.Context) {
        tokenString := c.GetHeader("Authorization")
        if tokenString == "" {
            c.JSON(401, gin.H{"error": "Authorization header required"})
            c.Abort()
            return
        }
        
        // 移除 "Bearer " 前缀
        if strings.HasPrefix(tokenString, "Bearer ") {
            tokenString = tokenString[7:]
        }
        
        token, err := jwtService.ValidateToken(tokenString)
        if err != nil || !token.Valid {
            c.JSON(401, gin.H{"error": "Invalid token"})
            c.Abort()
            return
        }
        
        claims, ok := token.Claims.(jwt.MapClaims)
        if !ok {
            c.JSON(401, gin.H{"error": "Invalid token claims"})
            c.Abort()
            return
        }
        
        c.Set("user_id", uint(claims["user_id"].(float64)))
        c.Set("role", claims["role"].(string))
        c.Next()
    }
}

func AdminRequired() gin.HandlerFunc {
    return gin.HandlerFunc(func(c *gin.Context) {
        AuthRequired()(c)
        
        if c.IsAborted() {
            return
        }
        
        role := c.GetString("role")
        if role != "admin" {
            c.JSON(403, gin.H{"error": "Admin access required"})
            c.Abort()
            return
        }
        
        c.Next()
    })
}

📁 文件存储管理

存储接口设计

// internal/service/storage/interface.go
type StorageService interface {
    Upload(file multipart.File, filename string) (string, error)
    Delete(filename string) error
    GetURL(filename string) string
}

// internal/service/storage/local.go
type LocalStorage struct {
    basePath string
    baseURL  string
}

func (s *LocalStorage) Upload(file multipart.File, filename string) (string, error) {
    filepath := path.Join(s.basePath, filename)
    
    out, err := os.Create(filepath)
    if err != nil {
        return "", err
    }
    defer out.Close()
    
    _, err = io.Copy(out, file)
    if err != nil {
        return "", err
    }
    
    return s.GetURL(filename), nil
}

func (s *LocalStorage) GetURL(filename string) string {
    return fmt.Sprintf("%s/%s", s.baseURL, filename)
}

// internal/service/storage/s3.go
type S3Storage struct {
    client *s3.Client
    bucket string
    region string
}

func (s *S3Storage) Upload(file multipart.File, filename string) (string, error) {
    _, err := s.client.PutObject(context.TODO(), &s3.PutObjectInput{
        Bucket: aws.String(s.bucket),
        Key:    aws.String(filename),
        Body:   file,
    })
    
    if err != nil {
        return "", err
    }
    
    return s.GetURL(filename), nil
}

图片处理

// internal/service/photo/thumbnail.go
import (
    "image"
    "image/jpeg"
    "image/png"
    "golang.org/x/image/draw"
)

func GenerateThumbnail(src image.Image, width, height int) (image.Image, error) {
    bounds := src.Bounds()
    srcWidth := bounds.Max.X
    srcHeight := bounds.Max.Y
    
    // 计算缩放比例
    scaleX := float64(width) / float64(srcWidth)
    scaleY := float64(height) / float64(srcHeight)
    scale := math.Min(scaleX, scaleY)
    
    newWidth := int(float64(srcWidth) * scale)
    newHeight := int(float64(srcHeight) * scale)
    
    // 创建新图像
    dst := image.NewRGBA(image.Rect(0, 0, newWidth, newHeight))
    
    // 缩放图像
    draw.BiLinear.Scale(dst, dst.Bounds(), src, src.Bounds(), draw.Over, nil)
    
    return dst, nil
}

🚀 构建和部署

Makefile 配置

# Makefile
.PHONY: build run test clean docker-build docker-run

# 构建
build:
	go build -o bin/server cmd/server/main.go

# 运行
run:
	go run cmd/server/main.go

# 测试
test:
	go test ./...

# 代码检查
lint:
	golangci-lint run

# 清理
clean:
	rm -rf bin/

# 数据库迁移
migrate:
	migrate -path migrations -database "postgres://user:pass@localhost/dbname?sslmode=disable" up

# Docker 构建
docker-build:
	docker build -t photography-backend .

# Docker 运行
docker-run:
	docker-compose up -d

# 开发环境
dev-up:
	docker-compose -f docker-compose.dev.yml up -d

dev-down:
	docker-compose -f docker-compose.dev.yml down

# 生产环境
prod-up:
	docker-compose -f docker-compose.prod.yml up -d

prod-down:
	docker-compose -f docker-compose.prod.yml down

Docker 配置

# Dockerfile
FROM golang:1.21-alpine AS builder

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main cmd/server/main.go

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/

COPY --from=builder /app/main .
COPY --from=builder /app/configs ./configs
COPY --from=builder /app/migrations ./migrations

EXPOSE 8080
CMD ["./main"]

Docker Compose 配置

# docker-compose.yml
version: '3.8'

services:
  backend:
    build: .
    ports:
      - "8080:8080"
    depends_on:
      - postgres
      - redis
    environment:
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_USER=postgres
      - DB_PASSWORD=password
      - DB_NAME=photography
      - REDIS_HOST=redis
      - REDIS_PORT=6379
    volumes:
      - ./uploads:/app/uploads
      - ./configs:/app/configs

  postgres:
    image: postgres:14
    environment:
      - POSTGRES_DB=photography
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  redis:
    image: redis:6.2
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

volumes:
  postgres_data:
  redis_data:

🔧 开发指南

代码规范

  • 使用 Go 标准格式化工具 go fmt
  • 遵循 Go 命名规范
  • 使用 golangci-lint 进行代码检查
  • 编写单元测试,覆盖率不低于 80%

错误处理

// 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,
    }
}

// internal/utils/errors.go
var (
    ErrUserNotFound     = errors.New("user not found")
    ErrPhotoNotFound    = errors.New("photo not found")
    ErrAlbumNotFound    = errors.New("album not found")
    ErrInvalidPassword  = errors.New("invalid password")
    ErrUnauthorized     = errors.New("unauthorized")
    ErrPermissionDenied = errors.New("permission denied")
)

日志管理

// pkg/logger/logger.go
import (
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
)

var Logger *zap.Logger

func init() {
    config := zap.NewProductionConfig()
    config.EncoderConfig.TimeKey = "timestamp"
    config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
    
    Logger, _ = config.Build()
}

func Info(msg string, fields ...zap.Field) {
    Logger.Info(msg, fields...)
}

func Error(msg string, fields ...zap.Field) {
    Logger.Error(msg, fields...)
}

func Debug(msg string, fields ...zap.Field) {
    Logger.Debug(msg, fields...)
}

测试编写

// internal/service/photo/photo_test.go
func TestPhotoService_CreatePhoto(t *testing.T) {
    mockRepo := &MockPhotoRepository{}
    service := NewPhotoService(mockRepo)
    
    req := &CreatePhotoRequest{
        Title:       "Test Photo",
        Description: "Test Description",
        URL:         "https://example.com/photo.jpg",
        AlbumID:     1,
        UserID:      1,
    }
    
    expectedPhoto := &models.Photo{
        ID:          1,
        Title:       req.Title,
        Description: req.Description,
        URL:         req.URL,
        AlbumID:     req.AlbumID,
        UserID:      req.UserID,
    }
    
    mockRepo.On("Create", mock.AnythingOfType("*models.Photo")).Return(expectedPhoto, nil)
    
    photo, err := service.CreatePhoto(req)
    
    assert.NoError(t, err)
    assert.Equal(t, expectedPhoto.Title, photo.Title)
    assert.Equal(t, expectedPhoto.Description, photo.Description)
    mockRepo.AssertExpectations(t)
}

🔄 与其他模块的集成

与前端的集成

  • 提供 RESTful API 供前端调用
  • 支持 CORS 跨域请求
  • 返回标准化的 JSON 响应格式
  • 提供 API 文档 (Swagger)

与管理后台的集成

  • 提供管理员专用的 API 接口
  • 支持批量操作和数据导入导出
  • 提供系统监控和统计信息
  • 支持权限管理和用户管理

与部署模块的集成

  • 提供 Docker 容器化部署
  • 支持负载均衡和水平扩展
  • 集成健康检查和监控
  • 支持优雅关闭和重启

🐛 问题排查

常见问题

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

调试技巧

# 查看日志
docker-compose logs -f backend

# 进入容器
docker-compose exec backend sh

# 数据库查询
docker-compose exec postgres psql -U postgres -d photography

# Redis 查询
docker-compose exec redis redis-cli

📊 性能优化

数据库优化

-- 添加索引
CREATE INDEX idx_photos_album_id ON photos(album_id);
CREATE INDEX idx_photos_user_id ON photos(user_id);
CREATE INDEX idx_photos_created_at ON photos(created_at);

-- 查询优化
EXPLAIN ANALYZE SELECT * FROM photos WHERE album_id = 1;

缓存策略

// internal/service/photo/photo.go
func (s *PhotoService) GetPhoto(id uint) (*models.Photo, error) {
    cacheKey := fmt.Sprintf("photo:%d", id)
    
    // 从缓存获取
    if cached, err := s.cache.Get(cacheKey); err == nil {
        var photo models.Photo
        if err := json.Unmarshal(cached, &photo); err == nil {
            return &photo, nil
        }
    }
    
    // 从数据库获取
    photo, err := s.repo.GetByID(id)
    if err != nil {
        return nil, err
    }
    
    // 存入缓存
    if data, err := json.Marshal(photo); err == nil {
        s.cache.Set(cacheKey, data, 5*time.Minute)
    }
    
    return photo, nil
}

📈 监控和日志

健康检查

// internal/api/handlers/health.go
func (h *HealthHandler) Check(c *gin.Context) {
    status := gin.H{
        "status": "ok",
        "timestamp": time.Now().Unix(),
        "version": "1.0.0",
    }
    
    // 检查数据库连接
    if err := h.db.Ping(); err != nil {
        status["database"] = "error"
        status["status"] = "error"
    } else {
        status["database"] = "ok"
    }
    
    // 检查 Redis 连接
    if err := h.redis.Ping().Err(); err != nil {
        status["redis"] = "error"
        status["status"] = "error"
    } else {
        status["redis"] = "ok"
    }
    
    if status["status"] == "error" {
        c.JSON(500, status)
    } else {
        c.JSON(200, status)
    }
}

指标收集

// internal/middleware/metrics.go
func PrometheusMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        
        c.Next()
        
        duration := time.Since(start)
        status := c.Writer.Status()
        
        // 记录请求指标
        requestDuration.WithLabelValues(c.Request.Method, c.FullPath()).Observe(duration.Seconds())
        requestCount.WithLabelValues(c.Request.Method, c.FullPath(), fmt.Sprintf("%d", status)).Inc()
    }
}

🔮 未来规划

功能扩展

  • 📊 高级搜索和过滤
  • 🔄 数据同步和备份
  • 📱 移动端 API 优化
  • 🤖 AI 图像识别和标记
  • 📈 实时分析和推荐

技术升级

  • 迁移到微服务架构
  • 集成消息队列 (RabbitMQ/Kafka)
  • 支持分布式存储
  • 集成 Elasticsearch 搜索
  • 支持 GraphQL API

📚 参考资料


💡 开发提示: 开始开发前,请确保已经阅读根目录的 CLAUDE.md 文件,了解项目整体架构。开发过程中建议先实现核心功能,再逐步完善其他特性。务必编写单元测试和集成测试,确保代码质量。