Private
Public Access
1
0
Files
u-desk/docs/03-模块文档/文件系统/filesystem-code-style-guide.md

7.4 KiB
Raw Blame History

文件管理模块代码风格规范

概述

本文档定义了文件管理模块的代码风格规范,确保代码一致性、可读性和可维护性。


1. 注释规范

1.1 包注释

每个包应该有一个简短的包注释,说明包的用途。

// Package filesystem 提供文件系统操作功能
//
// 核心功能:
// - 文件读写、删除、列表
// - 路径验证和安全检查
// - ZIP文件操作
// - 审计日志和回收站
package filesystem

1.2 函数注释

使用标准Go文档注释风格

// DeletePath 删除文件或目录
//
// 参数:
//   path - 文件或目录路径
//
// 返回:
//   error - 错误信息nil表示成功
//
// 示例:
//   err := fs.DeletePath("/path/to/file")
func (s *FileSystemService) DeletePath(path string) error {
    // 实现...
}

1.3 禁止的注释风格

// 禁止使用emoji
// 🔒 安全检查
// ✅ 优化
// ⚠️ 警告

// 应使用纯文本
// 安全检查
// 性能优化
// 警告

2. 错误处理规范

2.1 错误包装

使用 WrapError 添加上下文:

// 推荐做法
data, err := os.ReadFile(path)
if err != nil {
    return "", WrapError("读取文件", path, err)
}

// 避免裸错误
return "", err // ❌ 不推荐
return "", fmt.Errorf("失败: %w", err) // ✅ 推荐

2.2 错误消息

使用中文描述(面向中文用户):

// 推荐
return fmt.Errorf("文件不存在: %s", path)

// 避免使用英文
return fmt.Errorf("file not found: %s", path) // ❌

2.3 错误忽略

必须注释说明原因:

// 推荐:注释说明原因
if err := logger.Close(); err != nil {
    // 日志关闭失败,程序即将退出,忽略错误
}

// 禁止:无注释忽略
_ = logger.Close() // ❌

3. 命名规范

3.1 常量命名

使用大驼峰命名法:

const (
    MaxZipSize        = 100 * 1024 * 1024
    DefaultDirPermissions = 0755
    AuditFlushInterval  = 5 * time.Second
)

3.2 变量命名

使用小驼峰命名法:

var (
    globalService     *FileSystemService
    defaultConfig      *Config
    defaultPermissions os.FileMode = 0644
)

3.3 接口命名

接口名应该是动作或能力的描述,通常以 -er 结尾:

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Validator interface {
    Validate(path string) error
}

4. 函数设计规范

4.1 函数长度

推荐单个函数不超过50行。如果超过考虑拆分子函数

// 推荐:拆分子函数
func DeletePath(path string) error {
    if err := validatePath(path); err != nil {
        return err
    }

    if err := checkPermissions(path); err != nil {
        return err
    }

    return performDelete(path)
}

// 避免:长函数
func DeletePath(path string) error {
    // 100行代码...
}

4.2 参数数量

函数参数不超过5个。如果超过使用结构体

// 推荐:使用结构体
type DeleteOptions struct {
    Path          string
    Force         bool
    SkipRecycle   bool
    IgnoreLock    bool
    Reason        string
}

func DeleteWithOptions(opts DeleteOptions) error {
    // 实现...
}

// 避免:过多参数
func DeleteWithOptions(path string, force bool, skipRecycle bool, ignoreLock bool, reason string, timeout int) error {
    // 参数过多
}

4.3 返回值

函数返回值遵循以下顺序:

  1. 结果
  2. 错误
// 推荐
func ReadFile(path string) ([]byte, error)

// 避免多个返回值
func ReadFile(path string) ([]byte, bool, error, int)

5. 代码组织

5.1 文件组织

每个文件应该有单一的职责:

filesystem/
├── fs.go              # 核心文件操作
├── service.go         # 文件系统服务
├── path_validator.go  # 路径验证
├── filetype_manager.go # 文件类型管理
├── zip.go             # ZIP操作
├── errors.go          # 错误定义
├── logger.go          # 日志记录
└── constants.go       # 常量定义

5.2 导入顺序

标准库 → 第三方库 → 项目内部:

import (
    // 标准库
    "context"
    "fmt"
    "os"

    // 第三方库
    "github.com/google/uuid"

    // 项目内部
    "go-desk/internal/common"
)

6. 性能规范

6.1 避免重复计算

使用缓存或预计算:

// 推荐:缓存结果
type statsCache struct {
    mu    sync.RWMutex
    cache map[string]*DirectoryStats
}

func (c *statsCache) Get(path string) (*DirectoryStats, error) {
    c.mu.RLock()
    if stats, ok := c.cache[path]; ok {
        c.mu.RUnlock()
        return stats, nil
    }
    c.mu.RUnlock()

    // 计算并缓存
    stats, err := GetDirectoryStats(path)
    if err != nil {
        return nil, err
    }

    c.mu.Lock()
    c.cache[path] = stats
    c.mu.Unlock()

    return stats, nil
}

// 避免:重复计算
func processData(path string) {
    stats1, _ := GetDirectoryStats(path)
    stats2, _ := GetDirectoryStats(path) // 重复计算
}

6.2 资源释放

使用 defer 确保资源释放:

// 推荐
func ReadFile(path string) ([]byte, error) {
    file, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    defer file.Close() // 确保关闭

    return io.ReadAll(file)
}

7. 并发安全

7.1 共享状态

使用互斥锁保护共享状态:

type SafeCounter struct {
    mu    sync.RWMutex
    count int
}

func (c *SafeCounter) Increment() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.count++
}

func (c *SafeCounter) Get() int {
    c.mu.RLock()
    defer c.mu.RUnlock()
    return c.count
}

7.2 避免数据竞争

不要在goroutine中直接共享变量

// 推荐:传递参数
for i := 0; i < 10; i++ {
    go func(n int) {
        fmt.Println(n)
    }(i)
}

// 避免:闭包捕获
for i := 0; i < 10; i++ {
    go func() {
        fmt.Println(i) // 数据竞争
    }()
}

8. 测试规范

8.1 测试文件命名

测试文件命名为 xxx_test.go

// fs_test.go
package filesystem

import "testing"

func TestDeletePath(t *testing.T) {
    // 测试代码
}

8.2 表格驱动测试

使用表格驱动测试多种场景:

func TestValidatePath(t *testing.T) {
    tests := []struct {
        name    string
        path    string
        wantErr bool
    }{
        {"正常路径", "/tmp/test.txt", false},
        {"路径遍历", "/tmp/../etc/passwd", true},
        {"空路径", "", true},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            err := ValidatePath(tt.path)
            if (err != nil) != tt.wantErr {
                t.Errorf("ValidatePath() error = %v, wantErr %v", err, tt.wantErr)
            }
        })
    }
}

9. 文档规范

9.1 README

每个模块应该有README说明

# 文件系统模块

## 功能
- 文件读写
- 路径验证
- ZIP操作

## 使用示例
...

## 配置
...

9.2 API文档

导出的函数和类型必须有文档注释。


10. 代码审查清单

提交代码前,确保:

  • 移除所有emoji注释
  • 函数有文档注释
  • 错误处理完善(无忽略错误)
  • 命名符合规范
  • 无魔法数字(使用常量)
  • 无重复代码遵循DRY
  • 导入顺序正确
  • 资源正确释放defer

版本: 1.0 最后更新: 2026-01-27