Contributing - zhoudm1743/go-util GitHub Wiki

欢迎为 Go-Util 项目贡献力量!本指南将帮助您了解如何参与项目开发,提交代码,报告问题,以及改进文档。

🌟 参与方式

1. 代码贡献

  • 🔧 新功能开发 - 实现新的工具类和方法
  • 🐛 Bug 修复 - 修复已知问题和缺陷
  • 性能优化 - 提升现有功能的性能
  • 🧪 测试完善 - 增加单元测试和集成测试

2. 文档贡献

  • 📝 API 文档 - 完善函数和方法的文档说明
  • 📚 使用指南 - 编写教程和最佳实践
  • 🌍 多语言支持 - 翻译文档到其他语言
  • 💡 示例代码 - 提供实用的代码示例

3. 社区参与

  • 💬 问题解答 - 帮助其他用户解决问题
  • 🔍 问题报告 - 发现并报告 bug
  • 💡 功能建议 - 提出新功能和改进建议
  • 📢 推广分享 - 在社区分享使用经验

🚀 快速开始

1. 环境准备

# 1. Fork 项目到您的 GitHub 账户
# 2. 克隆项目到本地
git clone https://github.com/your-username/go-util.git
cd go-util

# 3. 添加上游仓库
git remote add upstream https://github.com/zhoudm1743/go-util.git

# 4. 安装 Go(版本 >= 1.19)
go version

# 5. 安装依赖
go mod download

# 6. 运行测试确保环境正常
go test ./...

2. 开发流程

# 1. 创建功能分支
git checkout -b feature/your-feature-name

# 2. 进行开发
# ... 编写代码 ...

# 3. 运行测试
go test ./...

# 4. 运行代码检查
go vet ./...
go fmt ./...

# 5. 提交更改
git add .
git commit -m "feat: 添加新功能说明"

# 6. 推送到您的仓库
git push origin feature/your-feature-name

# 7. 创建 Pull Request

📋 代码规范

1. 命名约定

// ✅ 推荐:使用语义化的函数名
func ProcessUserData(users []User) []ProcessedUser {
    return util.ArraysFromSlice(users).
        Filter(isValidUser).
        Map(transformUser).
        ToSlice()
}

// ✅ 推荐:接口名使用 -er 后缀
type DataProcessor interface {
    Process(data []byte) ([]byte, error)
}

// ✅ 推荐:包名简短且有意义
package stringutils // 而不是 strutils 或 string_utilities

// ✅ 推荐:常量使用大写
const (
    DefaultTimeout = 30 * time.Second
    MaxRetries     = 3
)

2. 代码结构

// ✅ 推荐:文件组织结构
// types_str.go - 字符串工具
// types_array.go - 数组工具
// types_map.go - 映射工具
// types_time.go - 时间工具

// ✅ 推荐:包内导入顺序
import (
    // 标准库
    "context"
    "fmt"
    "time"
    
    // 第三方库
    "github.com/example/package"
    
    // 本地导入
    "github.com/zhoudm1743/go-util/internal"
)

// ✅ 推荐:结构体字段排序
type User struct {
    // 导出字段在前
    ID    string
    Name  string
    Email string
    
    // 未导出字段在后
    password string
    lastLogin time.Time
}

3. 注释规范

// Package util 提供了一套强大的 Go 语言工具库,
// 包含字符串、数组、映射、时间等常用操作的链式API。
//
// 使用示例:
//   str := util.Str("hello world").Upper().ReplaceRegex(`\s+`, "_").String()
//   // 结果: "HELLO_WORLD"
package util

// XStr 表示一个字符串包装器,提供链式操作方法。
//
// XStr 支持各种字符串操作,包括大小写转换、格式验证、
// 内容替换、编码解码等功能。
//
// 使用示例:
//   email := util.Str(userInput).Trim().Lower().String()
//   if util.Str(email).IsEmail() {
//       // 处理有效邮箱
//   }
type XStr struct {
    value string
}

// Upper 将字符串转换为大写。
//
// 返回一个新的 XStr 实例,原实例保持不变。
//
// 使用示例:
//   result := util.Str("hello").Upper().String() // "HELLO"
func (s *XStr) Upper() *XStr {
    return &XStr{value: strings.ToUpper(s.value)}
}

// IsEmail 检查字符串是否为有效的邮箱地址。
//
// 使用正则表达式验证邮箱格式的有效性。
//
// 返回值:
//   - true: 字符串是有效的邮箱地址
//   - false: 字符串不是有效的邮箱地址
//
// 使用示例:
//   valid := util.Str("[email protected]").IsEmail() // true
//   invalid := util.Str("not-an-email").IsEmail()   // false
func (s *XStr) IsEmail() bool {
    pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
    matched, _ := regexp.MatchString(pattern, s.value)
    return matched
}

4. 错误处理

// ✅ 推荐:定义自定义错误类型
type ValidationError struct {
    Field   string
    Value   interface{}
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("字段 %s 验证失败: %s (值: %v)", e.Field, e.Message, e.Value)
}

// ✅ 推荐:使用 errors.Is 和 errors.As
func ProcessData(data string) error {
    if err := validateData(data); err != nil {
        var validationErr *ValidationError
        if errors.As(err, &validationErr) {
            // 处理验证错误
            return fmt.Errorf("数据处理失败: %w", err)
        }
        return err
    }
    return nil
}

// ✅ 推荐:链式操作中的错误处理
func (s *XStr) SafeToInt() (int, error) {
    if s.IsBlank() {
        return 0, errors.New("不能将空字符串转换为整数")
    }
    
    value, err := strconv.Atoi(s.value)
    if err != nil {
        return 0, fmt.Errorf("字符串转整数失败: %w", err)
    }
    
    return value, nil
}

🧪 测试指南

1. 单元测试

// ✅ 推荐:测试文件命名 *_test.go
// types_str_test.go

func TestXStr_Upper(t *testing.T) {
    tests := []struct {
        name     string
        input    string
        expected string
    }{
        {
            name:     "基础大写转换",
            input:    "hello",
            expected: "HELLO",
        },
        {
            name:     "混合字符",
            input:    "Hello World",
            expected: "HELLO WORLD",
        },
        {
            name:     "空字符串",
            input:    "",
            expected: "",
        },
        {
            name:     "已经是大写",
            input:    "HELLO",
            expected: "HELLO",
        },
        {
            name:     "包含数字和特殊字符",
            input:    "hello123!@#",
            expected: "HELLO123!@#",
        },
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result := util.Str(tt.input).Upper().String()
            if result != tt.expected {
                t.Errorf("Upper() = %v, want %v", result, tt.expected)
            }
        })
    }
}

// ✅ 推荐:边界条件测试
func TestXStr_IsEmail_EdgeCases(t *testing.T) {
    testCases := []struct {
        email    string
        expected bool
        reason   string
    }{
        {"", false, "空字符串"},
        {"@", false, "只有@符号"},
        {"user@", false, "缺少域名"},
        {"@domain.com", false, "缺少用户名"},
        {"user@domain", false, "缺少顶级域名"},
        {"[email protected]", true, "用户名包含点"},
        {"[email protected]", true, "用户名包含加号"},
        {"[email protected]", true, "长邮箱地址"},
    }
    
    for _, tc := range testCases {
        t.Run(tc.reason, func(t *testing.T) {
            result := util.Str(tc.email).IsEmail()
            if result != tc.expected {
                t.Errorf("IsEmail(%q) = %v, want %v (%s)", 
                    tc.email, result, tc.expected, tc.reason)
            }
        })
    }
}

2. 基准测试

// ✅ 推荐:性能基准测试
func BenchmarkXStr_Upper(b *testing.B) {
    testStr := "hello world this is a benchmark test"
    
    b.ResetTimer()
    b.ReportAllocs()
    
    for i := 0; i < b.N; i++ {
        _ = util.Str(testStr).Upper().String()
    }
}

func BenchmarkXStr_ChainedOperations(b *testing.B) {
    testStr := "  hello WORLD [email protected]  "
    
    b.ResetTimer()
    b.ReportAllocs()
    
    for i := 0; i < b.N; i++ {
        result := util.Str(testStr).
            Trim().
            Lower().
            ReplaceRegex(`\s+`, "_").
            String()
        _ = result
    }
}

// ✅ 推荐:内存分配测试
func TestXStr_MemoryAllocation(t *testing.T) {
    const iterations = 1000
    
    // 测试链式操作的内存分配
    var allocsBefore runtime.MemStats
    runtime.ReadMemStats(&allocsBefore)
    
    for i := 0; i < iterations; i++ {
        _ = util.Str("test").Upper().Lower().Trim().String()
    }
    
    var allocsAfter runtime.MemStats
    runtime.ReadMemStats(&allocsAfter)
    
    allocsPerOp := (allocsAfter.Mallocs - allocsBefore.Mallocs) / iterations
    
    // 确保每次操作的内存分配在合理范围内
    if allocsPerOp > 10 {
        t.Errorf("过多的内存分配: %d allocs/op", allocsPerOp)
    }
}

3. 集成测试

// ✅ 推荐:功能集成测试
func TestUserDataProcessingIntegration(t *testing.T) {
    // 模拟真实的用户数据处理场景
    rawUsers := []map[string]interface{}{
        {"name": "  Alice  ", "email": "[email protected]", "age": "25"},
        {"name": "Bob", "email": "[email protected]", "age": "30"},
        {"name": "", "email": "invalid-email", "age": "abc"},
    }
    
    var processedUsers []User
    var errors []error
    
    for i, rawUser := range rawUsers {
        // 使用 Go-Util 处理用户数据
        name := util.Str(fmt.Sprintf("%v", rawUser["name"])).Trim().String()
        email := util.Str(fmt.Sprintf("%v", rawUser["email"])).Lower().Trim().String()
        ageStr := util.Str(fmt.Sprintf("%v", rawUser["age"])).Trim().String()
        
        // 验证数据
        if util.Str(name).IsBlank() {
            errors = append(errors, fmt.Errorf("用户 %d: 姓名不能为空", i))
            continue
        }
        
        if !util.Str(email).IsEmail() {
            errors = append(errors, fmt.Errorf("用户 %d: 邮箱格式无效", i))
            continue
        }
        
        if !util.Str(ageStr).IsNumeric() {
            errors = append(errors, fmt.Errorf("用户 %d: 年龄必须为数字", i))
            continue
        }
        
        age := util.Str(ageStr).Int()
        
        processedUsers = append(processedUsers, User{
            Name:  name,
            Email: email,
            Age:   age,
        })
    }
    
    // 验证结果
    if len(processedUsers) != 2 {
        t.Errorf("期望处理2个用户,实际处理了%d个", len(processedUsers))
    }
    
    if len(errors) != 1 {
        t.Errorf("期望1个错误,实际有%d个错误", len(errors))
    }
    
    // 验证处理后的数据
    alice := processedUsers[0]
    if alice.Name != "Alice" || alice.Email != "[email protected]" || alice.Age != 25 {
        t.Errorf("Alice 数据处理错误: %+v", alice)
    }
}

📝 提交规范

1. 提交信息格式

<type>(<scope>): <description>

[optional body]

[optional footer(s)]

Type 类型:

  • feat: 新功能
  • fix: Bug 修复
  • docs: 文档更新
  • style: 代码格式化(不影响功能)
  • refactor: 代码重构
  • perf: 性能优化
  • test: 测试相关
  • chore: 构建过程或辅助工具的变动

示例:

# 新功能
git commit -m "feat(str): 添加 IsPhoneNumber 方法用于验证手机号"

# Bug 修复
git commit -m "fix(array): 修复 ParallelMap 在空数组时的 panic 问题"

# 文档更新
git commit -m "docs(readme): 更新安装说明和使用示例"

# 性能优化
git commit -m "perf(map): 优化 MergeWith 方法的内存分配"

# 重构
git commit -m "refactor(time): 重构时区处理逻辑提高可读性"

2. Pull Request 要求

PR 标题格式:

[Type] Brief description

PR 描述模板:

## 📋 变更类型
- [ ] 新功能 (feat)
- [ ] Bug 修复 (fix)
- [ ] 文档更新 (docs)
- [ ] 性能优化 (perf)
- [ ] 代码重构 (refactor)
- [ ] 测试相关 (test)

## 📝 变更描述
简要描述本次变更的内容和目的。

## 🔧 具体变更
- 添加了 XStr.IsPhoneNumber() 方法
- 支持中国大陆手机号格式验证
- 添加了相应的单元测试

## 🧪 测试
- [ ] 添加了单元测试
- [ ] 添加了基准测试
- [ ] 现有测试全部通过
- [ ] 测试覆盖率 >= 80%

## 📚 文档
- [ ] 更新了 API 文档
- [ ] 更新了使用示例
- [ ] 更新了 README

## ✅ 检查清单
- [ ] 代码符合项目规范
- [ ] 没有引入 breaking changes
- [ ] 性能没有明显下降
- [ ] 代码经过了 `go fmt` 格式化
- [ ] 代码通过了 `go vet` 检查

## 🔗 相关链接
- 关闭 Issue: #123
- 相关 PR: #456

🔍 代码审查

1. 审查要点

功能性检查:

  • ✅ 功能是否按预期工作
  • ✅ 边界条件是否正确处理
  • ✅ 错误处理是否完善
  • ✅ 性能是否可接受

代码质量检查:

  • ✅ 代码结构是否清晰
  • ✅ 命名是否语义化
  • ✅ 注释是否充分
  • ✅ 是否遵循项目规范

测试检查:

  • ✅ 测试覆盖率是否充足
  • ✅ 测试用例是否全面
  • ✅ 基准测试是否必要
  • ✅ 集成测试是否通过

2. 审查反馈

给出建设性反馈:

✅ 好的反馈示例:
建议在第 45 行添加空值检查,避免潜在的 panic:
```go
if input == nil {
    return "", errors.New("input cannot be nil")
}

❌ 不好的反馈示例: 这里有问题。


#### 认可好的实现:
```markdown
👍 这个实现很优雅,使用链式调用提高了代码可读性。
💡 很好的性能优化,减少了内存分配。

🏆 贡献者等级

1. 新手贡献者

  • 🌱 入门任务: 文档更新、简单 bug 修复
  • 📝 学习资源: 提供详细的开发指南
  • 🤝 导师支持: 配对有经验的贡献者

2. 常规贡献者

  • 🔧 中等任务: 新功能开发、性能优化
  • 🧪 测试责任: 确保代码质量
  • 📋 问题分类: 帮助分类和优先级排序

3. 核心贡献者

  • 🔑 关键功能: 架构设计、核心模块开发
  • 👥 代码审查: 审查其他贡献者的代码
  • 📊 项目规划: 参与项目路线图制定

4. 维护者

  • 🎯 项目方向: 决定项目发展方向
  • 🔄 发布管理: 负责版本发布
  • 🌐 社区建设: 建设和维护社区

🎯 项目路线图

当前版本 (v1.4.x)

  • ✅ 核心工具类完善
  • ✅ JSONx 高性能 JSON 操作
  • ✅ JWT 零依赖实现
  • ✅ XEnum 类型安全枚举

下一版本 (v1.5.x)

  • 🔄 计划中: 数据库 ORM 集成工具
  • 🔄 计划中: HTTP 客户端增强
  • 🔄 计划中: 缓存抽象层
  • 🔄 计划中: 配置管理工具

长期规划 (v2.0.x)

  • 🔮 构想中: 微服务工具套件
  • 🔮 构想中: 分布式锁实现
  • 🔮 构想中: 消息队列抽象
  • 🔮 构想中: 监控和指标收集

🏅 贡献者认可

贡献统计

我们将在以下地方认可贡献者:

  • 📊 GitHub 贡献图: 自动显示代码贡献
  • 📝 CONTRIBUTORS.md: 列出所有贡献者
  • 🎉 发布说明: 感谢版本贡献者
  • 🌟 项目主页: 突出核心贡献者

特殊贡献奖

  • 🥇 月度贡献者: 每月最活跃贡献者
  • 🏆 年度贡献者: 年度杰出贡献者
  • 💎 特殊贡献: 重大功能或修复贡献者
  • 🎨 文档大师: 文档质量突出贡献者

📞 获取帮助

开发问题

社区支持

  • 💬 开发者群组: 加入开发者交流群
  • 📺 在线会议: 每月贡献者会议
  • 📚 知识库: 查看常见问题解答

📄 许可证

通过向本项目贡献代码,您同意您的贡献将在 MIT 许可证下进行许可。


🤝 感谢您对 Go-Util 项目的贡献!每一个贡献都让这个项目变得更好!

⚠️ **GitHub.com Fallback** ⚠️