# Golang 注释规范

本文档定义了 Cadmus 项目中 Go 代码的注释标准，所有开发者应遵循这些规范以确保代码的可读性和可维护性。所有注释使用中文。

## 1. 文件头注释

每个 Go 源文件必须在开头添加文件头注释，说明文件的作用、主要功能和所属模块。

### 格式

```go
// Package xxx 提供了 xxx 功能的实现。
//
// 该文件包含 xxx 相关的核心逻辑，包括：
//   - 功能 A 的实现
//   - 功能 B 的处理
//   - 与 xxx 服务的交互
//
// 主要用途：
//   用于处理 xxx 场景下的 xxx 业务逻辑。
//
// 注意事项：
//   - 使用前需要确保 xxx 条件满足
//   - 并发安全/非并发安全
//
// 作者：xfy
package xxx
```

### 示例

```go
// Package services 提供了 Cadmus 核心业务服务的实现。
//
// 该文件包含媒体服务相关的核心逻辑，包括：
//   - 媒体文件的存储和管理
//   - 媒体元数据的解析和处理
//   - 与存储后端的交互
//
// 主要用途：
//   用于处理用户上传媒体文件的完整生命周期管理。
//
// 注意事项：
//   - 所有公开方法均为并发安全
//   - 使用前需确保存储服务已正确初始化
//
// 作者：xfy
package services
```

## 2. 函数注释

每个函数（包括公开函数和私有函数）都必须添加注释，说明其用途、参数、返回值和使用注意事项。

### 格式

```go
// FunctionName 执行 xxx 操作。
//
// 该函数用于处理 xxx 场景，实现 xxx 功能。
//
// 参数：
//   - param1: 参数1的说明，类型为 xxx，用于 xxx
//   - param2: 参数2的说明，类型为 xxx，用于 xxx
//
// 返回值：
//   - result: 返回结果的说明，类型为 xxx
//   - err: 错误信息，当 xxx 时返回非 nil 值
//
// 使用示例：
//   result, err := FunctionName(param1, param2)
//   if err != nil {
//       // 处理错误
//   }
//
// 注意事项：
//   - 调用前需确保 xxx 条件满足
//   - 该函数不处理 xxx 情况，需要调用方自行处理
func FunctionName(param1 Type1, param2 Type2) (Result, error) {
    // 实现
}
```

### 简单函数格式

对于功能单一、逻辑简单的函数，可使用简化格式：

```go
// ValidateInput 验证用户输入是否有效。
//
// 检查输入字符串是否非空且符合长度要求（1-100字符）。
// 返回 true 表示验证通过，false 表示验证失败。
func ValidateInput(input string) bool {
    return len(input) > 0 && len(input) <= 100
}
```

### 公开函数示例

```go
// SaveMedia 保存媒体文件到存储后端。
//
// 该方法将媒体文件及其元数据持久化到配置的存储系统中。
// 支持多种存储后端，包括本地文件系统、S3 和 MinIO。
//
// 参数：
//   - ctx: 上下文，用于控制超时和取消操作
//   - media: 媒体对象，包含文件数据和元数据
//
// 返回值：
//   - id: 保存成功后生成的唯一标识符
//   - err: 可能的错误包括：
//       - ErrStorageUnavailable: 存储服务不可用
//       - ErrInvalidMedia: 媒体数据无效
//       - ErrQuotaExceeded: 存储配额超限
//
// 使用示例：
//   id, err := service.SaveMedia(ctx, media)
//   if errors.Is(err, services.ErrStorageUnavailable) {
//       // 重试或切换存储后端
//   }
//
// 注意事项：
//   - 该方法是并发安全的，可在多个 goroutine 中同时调用
//   - 大文件上传建议使用 StreamMedia 方法
func (s *MediaService) SaveMedia(ctx context.Context, media *Media) (string, error) {
    // 实现
}
```

### 私有函数示例

```go
// generateUniqueID 生成唯一的媒体标识符。
//
// 使用时间戳和随机数组合生成 UUID 格式的标识符。
// 保证在分布式环境下的唯一性。
//
// 返回值：
//   - 返回格式为 "media-{timestamp}-{random}" 的唯一字符串
func generateUniqueID() string {
    // 实现
}
```

## 3. 代码步骤注释

代码中的关键步骤、复杂逻辑、重要决策点都需要添加注释说明。

### 块注释格式

```go
func ProcessData(data []byte) error {
    // 步骤1: 验证数据格式
    // 检查数据是否符合预期的 JSON 格式，并包含必要字段
    if err := validateFormat(data); err != nil {
        return fmt.Errorf("数据格式验证失败: %w", err)
    }

    // 步骤2: 解析数据内容
    // 将 JSON 字节数组转换为结构体，便于后续处理
    var payload DataPayload
    if err := json.Unmarshal(data, &payload); err != nil {
        return fmt.Errorf("数据解析失败: %w", err)
    }

    // 步骤3: 数据转换
    // 将原始数据转换为业务模型，应用必要的转换规则
    model := transformToModel(payload)

    // 步骤4: 持久化存储
    // 将处理后的数据保存到数据库，使用事务确保数据一致性
    if err := saveWithTransaction(model); err != nil {
        return fmt.Errorf("数据保存失败: %w", err)
    }

    return nil
}
```

### 行内注释格式

对于单行代码的解释，使用行末注释：

```go
func CalculateScore(items []Item) int {
    total := 0
    for _, item := range items {
        // 权重计算：基础分数乘以优先级系数
        weightedScore := item.BaseScore * item.PriorityFactor
        total += weightedScore
    }

    // 应用上限约束，防止分数溢出
    if total > MaxScore {
        total = MaxScore
    }

    return total
}
```

### 复杂逻辑注释示例

```go
func (s *Service) ProcessRequest(ctx context.Context, req *Request) (*Response, error) {
    // === 预处理阶段 ===

    // 验证请求参数完整性
    // 必须字段包括：ID、Type、Timestamp
    if err := s.validateRequest(req); err != nil {
        return nil, err
    }

    // 检查请求是否已处理过
    // 通过缓存查询避免重复处理，提高系统效率
    if cached, exists := s.cache.Get(req.ID); exists {
        // 缓存命中，直接返回缓存结果
        return cached.(*Response), nil
    }

    // === 核心处理阶段 ===

    // 根据请求类型选择处理策略
    // 不同类型有不同的处理逻辑和性能特征
    handler := s.getHandler(req.Type)
    if handler == nil {
        // 未找到处理器，返回不支持错误
        return nil, ErrUnsupportedType
    }

    // 执行处理逻辑
    // 注意：此处可能耗时较长，已通过 ctx 控制超时
    result, err := handler.Handle(ctx, req)
    if err != nil {
        // 处理失败，记录详细错误信息用于排查
        s.logger.Error("处理失败", "request_id", req.ID, "error", err)
        return nil, err
    }

    // === 后处理阶段 ===

    // 构建响应对象
    response := &Response{
        ID:      req.ID,
        Result:  result,
        Status:  StatusSuccess,
        Created: time.Now(),
    }

    // 缓存响应结果，有效期 5 分钟
    // 热点请求的缓存可显著降低系统负载
    s.cache.Set(req.ID, response, 5*time.Minute)

    return response, nil
}
```

### 特殊情况注释

```go
func LoadConfig(path string) (*Config, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        // 文件不存在时返回默认配置，而非错误
        // 设计决策：允许用户不提供配置文件，使用系统默认值
        if os.IsNotExist(err) {
            return DefaultConfig(), nil
        }
        return nil, err
    }

    // 注意：此处故意忽略未知字段
    // 原因：支持配置文件向后兼容，旧版本配置文件可在新版本中使用
    var config Config
    decoder := json.NewDecoder(bytes.NewReader(data))
    decoder.DisallowUnknownFields = false // 允许未知字段
    if err := decoder.Decode(&config); err != nil {
        return nil, err
    }

    return &config, nil
}
```

## 4. 注释最佳实践

### 使用中文注释

所有注释必须使用中文，确保团队成员易于理解：

```go
// 正确 ✓
// 获取用户信息，根据用户 ID 查询数据库返回完整用户对象。

// 错误 ✗
// Get user info by ID from database.
```

### 注释内容要求

注释应说明"为什么"而非仅仅是"是什么"：

```go
// 正确 ✓ - 解释原因
// 使用批处理而非逐条处理，原因是：
// 1. 减少 I/O 操作次数，从 N 次降为 1 次
// 2. 降低事务开销，提升整体性能约 10 倍
func batchInsert(items []Item) error {
    // ...
}

// 错误 ✗ - 仅重复代码含义
// 循环遍历所有元素
for _, item := range items {
    // ...
}
```

### 避免冗余注释

```go
// 正确 ✓ - 提供有价值的信息
// 超时时间设置为 30 秒，因为下游服务 P99 响应时间为 25 秒
const Timeout = 30 * time.Second

// 错误 ✗ - 无意义的注释
// 超时变量
const Timeout = 30 * time.Second
```

### TODO 和 FIXME 注释

使用标准格式标记待办事项：

```go
// TODO(author): 待实现功能的描述
//   - 详细说明需要做什么
//   - 预计完成时间或优先级
func unfinishedFunction() {
    // TODO(xfy): 添加缓存支持，预计 2024-04-01 完成
}

// FIXME(author): 问题描述和修复建议
func problematicFunction() {
    // FIXME(xfy): 当前实现在高并发下存在竞态条件
    // 需要添加互斥锁保护共享资源
}
```

## 5. 结构体和接口注释

### 结构体注释

```go
// Media 表示系统中的媒体文件对象。
//
// 包含媒体文件的基本信息和元数据，用于媒体管理功能。
// 所有字段在创建后不可修改（不可变设计）。
//
// 注意事项：
//   - ID 由系统自动生成，无需手动设置
//   - CreatedAt 使用 UTC 时间
type Media struct {
    // ID 媒体的唯一标识符，格式为 UUID
    ID string `json:"id"`

    // Name 原始文件名，保留用户上传时的文件名
    Name string `json:"name"`

    // Size 文件大小，单位为字节
    Size int64 `json:"size"`

    // Type 媒体类型，如 "image/jpeg"、"video/mp4"
    Type string `json:"type"`

    // CreatedAt 创建时间，使用 UTC 时间戳
    CreatedAt time.Time `json:"created_at"`

    // Metadata 扩展元数据，存储自定义属性
    Metadata map[string]string `json:"metadata,omitempty"`
}
```

### 接口注释

```go
// Storage 定义存储后端的抽象接口。
//
// 该接口抽象了不同存储系统的共性操作，支持：
//   - 本地文件系统
//   - Amazon S3
//   - MinIO
//   - 其他兼容 S3 API 的存储服务
//
// 实现要求：
//   - 所有方法必须是并发安全的
//   - 上传方法应支持大文件流式传输
//   - 错误返回应使用定义的错误类型
type Storage interface {
    // Upload 上传文件到存储后端。
    //
    // 参数：
    //   - ctx: 上下文，用于超时控制
    //   - key: 存储键名，作为文件的唯一标识
    //   - data: 文件内容字节
    //
    // 返回值：
    //   - err: 上传失败时返回错误
    Upload(ctx context.Context, key string, data []byte) error

    // Download 从存储后端下载文件。
    //
    // 参数：
    //   - ctx: 上下文，用于超时控制
    //   - key: 存储键名
    //
    // 返回值：
    //   - data: 文件内容字节
    //   - err: 文件不存在或其他错误
    Download(ctx context.Context, key string) ([]byte, error)

    // Delete 从存储后端删除文件。
    Delete(ctx context.Context, key string) error
}
```

## 6. 常量和变量注释

### 常量注释

```go
// 定义系统配置常量
const (
    // DefaultPort 默认服务端口
    // 当配置文件未指定端口时使用此值
    DefaultPort = 8080

    // MaxUploadSize 最大上传文件大小限制
    // 设为 100MB，平衡存储成本和用户需求
    MaxUploadSize = 100 * 1024 * 1024

    // CacheTTL 缓存默认过期时间
    // 5 分钟可覆盖大多数热点请求场景
    CacheTTL = 5 * time.Minute
)
```

### 变量注释

```go
var (
    // ErrStorageUnavailable 存储服务不可用错误
    // 当无法连接到存储后端时返回
    ErrStorageUnavailable = errors.New("存储服务不可用")

    // ErrInvalidMedia 无效媒体错误
    // 当媒体文件格式或内容不符合要求时返回
    ErrInvalidMedia = errors.New("无效的媒体文件")

    // ErrQuotaExceeded 配额超限错误
    // 当用户存储空间达到上限时返回
    ErrQuotaExceeded = errors.New("存储配额已超限")
)
```

## 7. 包文档注释

每个包应有一个 doc.go 文件或在其主要文件中添加包级注释：

```go
// Package services 提供 Cadmus 的核心业务服务层实现。
//
// 该包包含以下主要服务：
//
//   - MediaService: 媒体文件管理服务
//   - AuthService: 用户认证和授权服务
//   - UserService: 用户信息管理服务
//   - PluginService: 插件系统管理服务
//
// 服务设计原则：
//
//   1. 所有服务通过接口定义，便于测试和替换实现
//   2. 服务间通过依赖注入解耦
//   3. 所有公开方法均为并发安全
//   4. 错误使用语义化错误类型，便于调用方处理
//
// 初始化示例：
//
//   cfg := services.Config{Storage: storageBackend}
//   mediaSvc := services.NewMediaService(cfg)
//   authSvc := services.NewAuthService(cfg)
//
// 注意事项：
//
//   - 使用前需确保依赖服务已正确初始化
//   - 建议通过 services.NewServices() 创建服务集合
//   - 所有服务支持通过配置启用/禁用
package services
```

## 8. 注释检查清单

在提交代码前，请检查以下事项：

- [ ] 文件头部是否有完整的文件注释
- [ ] 所有公开函数是否有详细注释（包括参数、返回值、示例）
- [ ] 所有私有函数是否有功能说明注释
- [ ] 关键逻辑步骤是否有注释说明
- [ ] 复杂算法是否有解释其原理的注释
- [ ] 特殊处理是否有解释其原因的注释
- [ ] 结构体及其字段是否有注释
- [ ] 接口及其方法是否有注释
- [ ] 常量和全局变量是否有注释
- [ ] 所有注释是否使用中文
- [ ] TODO/FIXME 是否标注了负责人和时间

---

_本规范适用于 Cadmus 项目所有 Go 源文件。_
