Files
junhong_cmp_fiber/.specify/memory/constitution.md

1050 lines
36 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.
<!--
SYNC IMPACT REPORT - Constitution Amendment
============================================
Version Change: 2.1.0 → 2.1.1
Date: 2025-11-11
MODIFIED PRINCIPLES:
- II. Code Quality Standards → Added "中文注释和输出规范" (Chinese Comments and Output Standards)
EXPANDED SECTIONS:
- Added "中文注释和输出规范" subsection to Principle II
- Rule: Code comments SHOULD be in Chinese for Chinese-speaking developers
- Rule: Log messages SHOULD be in Chinese
- Rule: User-facing error messages MUST be in Chinese (with bilingual support)
- Rule: Internal error messages and debug logs SHOULD be in Chinese
- Exceptions: Go doc comments for exported APIs may remain in English for ecosystem compatibility
- Examples of correct Chinese comments and log messages
- Rationale for Chinese-first approach
TEMPLATES REQUIRING UPDATES:
✅ .specify/templates/plan-template.md - Will add Chinese comments check
✅ .specify/templates/spec-template.md - Will add Chinese output requirement
✅ .specify/templates/tasks-template.md - Will add Chinese usage quality gate
FOLLOW-UP ACTIONS:
- Update templates with Chinese comments/logs checks
RATIONALE:
PATCH version bump (2.1.1) - Clarification of existing code quality standards to
prefer Chinese for comments and logs, targeting Chinese-speaking development team.
This is not a breaking change but a refinement of communication standards within
Principle II. The rule acknowledges the project's primary audience (Chinese developers)
while maintaining ecosystem compatibility for exported APIs.
-->
# 君鸿卡管系统 Constitution
## Core Principles
### I. Tech Stack Adherence (技术栈遵守)
**规则 (RULES):**
- 开发时 **MUST** 严格遵守项目定义的技术栈Fiber + GORM + Viper + Zap + Lumberjack.v2 + Validator + sonic JSON + Asynq + PostgreSQL
- **MUST NOT** 使用原生调用或绕过框架的快捷方式(禁止 `database/sql` 直接调用、禁止 `net/http` 替代 Fiber、禁止 `encoding/json` 替代 sonic
- 所有 HTTP 路由和中间件 **MUST** 使用 Fiber 框架
- 所有数据库操作 **MUST** 通过 GORM 进行
- 所有配置管理 **MUST** 使用 Viper
- 所有日志记录 **MUST** 使用 Zap + Lumberjack.v2
- 所有 JSON 序列化 **MUST** 使用 sonic
- 所有异步任务 **MUST** 使用 Asynq
- **MUST** 使用 Go 官方工具链:`go fmt``go vet``golangci-lint`
- **MUST** 使用 Go Modules 进行依赖管理
**理由 (RATIONALE):**
一致的技术栈使用确保代码可维护性、团队协作效率和长期技术债务可控。绕过框架的"快捷方式"会导致代码碎片化、难以调试、性能不一致和安全漏洞。框架选择已经过深思熟虑必须信任并充分利用其生态系统。Go 官方工具链确保代码风格一致性和质量。
---
### II. Code Quality Standards (代码质量标准)
**规则 (RULES):**
- 代码 **MUST** 遵循项目分层架构:`Handler → Service → Store → Model`
- Handler 层 **MUST ONLY** 处理 HTTP 请求/响应,不得包含业务逻辑
- Service 层 **MUST** 包含所有业务逻辑,支持跨模块调用
- Store 层 **MUST** 统一管理所有数据访问,支持事务处理
- Model 层 **MUST** 定义清晰的数据结构和 DTO
- 所有依赖 **MUST** 通过结构体字段进行依赖注入(不使用构造函数模式)
- 所有公共错误 **MUST**`pkg/errors/` 中定义,使用统一错误码
- 所有 API 响应 **MUST** 使用 `pkg/response/` 的统一格式
- 所有常量 **MUST**`pkg/constants/` 中定义和管理
- 所有 Redis key **MUST** 通过 `pkg/constants/` 中的 Key 生成函数统一管理
- **MUST** 为所有导出的函数、类型和常量编写 Go 风格的文档注释(`// FunctionName does something...`
- **MUST** 避免 magic numbers 和 magic strings使用常量定义
**Go 代码风格要求:**
- **MUST** 使用 `gofmt` 格式化所有代码
- **MUST** 遵循 [Effective Go](https://go.dev/doc/effective_go) 和 [Go Code Review Comments](https://go.dev/wiki/CodeReviewComments)
- 变量命名 **MUST** 使用 Go 风格:`userID`(不是 `userId`)、`HTTPServer`(不是 `HttpServer`
- 缩写词 **MUST** 全部大写或全部小写:`URL``ID``HTTP`(导出)或 `url``id``http`(未导出)
- 包名 **MUST** 简短、小写、单数、无下划线:`user``order``pkg`(不是 `users``userService``user_service`
- 接口命名 **SHOULD** 使用 `-er` 后缀:`Reader``Writer``Logger`(不是 `ILogger``LoggerInterface`
**常量管理规范 (Constants Management):**
- 业务常量(状态码、类型枚举等)**MUST** 定义在 `pkg/constants/constants.go` 或按模块分文件
- Redis key **MUST** 使用函数生成,不允许硬编码字符串拼接
- Redis key 生成函数 **MUST** 遵循命名规范:`Redis{Module}{Purpose}Key(params...)`
- Redis key 格式 **MUST** 使用冒号分隔:`{module}:{purpose}:{identifier}`
- 示例:
```go
// 正确:使用常量和生成函数
constants.SIMStatusActive
constants.RedisSIMStatusKey(iccid) // 生成 "sim:status:{iccid}"
// 错误:硬编码和拼接
status := "active"
key := "sim:status:" + iccid
```
**Magic Numbers 和硬编码规则 (Magic Numbers and Hardcoding Rules):**
- **MUST NOT** 在代码中直接使用 magic numbers未定义含义的数字字面量
- **MUST NOT** 在代码中硬编码字符串字面量URL、状态码、配置值、业务规则等
- 当相同的字面量值在 **3 个或以上位置**使用时,**MUST** 提取为常量
- 已定义的常量 **MUST** 被使用,**MUST NOT** 重复硬编码相同的值
- 只允许在以下情况使用字面量:
- 语言特性:`nil`、`true`、`false`
- 数学常量:`0`、`1`、`-1`(用于循环、索引、比较等明确的上下文)
- 一次性使用的临时值(测试数据、日志消息等)
**正确的常量使用:**
```go
// pkg/constants/constants.go
const (
DefaultPageSize = 20
MaxPageSize = 100
MinPageSize = 1
SIMStatusActive = "active"
SIMStatusInactive = "inactive"
SIMStatusSuspended = "suspended"
OrderTypeRecharge = "recharge"
OrderTypeTransfer = "transfer"
)
// internal/handler/user.go
func (h *Handler) ListUsers(c *fiber.Ctx) error {
page := c.QueryInt("page", 1)
pageSize := c.QueryInt("page_size", constants.DefaultPageSize) // ✅ 使用常量
if pageSize > constants.MaxPageSize { // ✅ 使用常量
pageSize = constants.MaxPageSize
}
users, err := h.service.List(page, pageSize)
// ...
}
// internal/service/sim.go
func (s *Service) Activate(iccid string) error {
return s.store.UpdateStatus(iccid, constants.SIMStatusActive) // ✅ 使用常量
}
```
**错误的硬编码模式:**
```go
// ❌ 硬编码 magic number在多处使用
func ListUsers(c *fiber.Ctx) error {
pageSize := c.QueryInt("page_size", 20) // ❌ 硬编码 20
if pageSize > 100 { // ❌ 硬编码 100
pageSize = 100
}
// ...
}
func ListOrders(c *fiber.Ctx) error {
pageSize := c.QueryInt("page_size", 20) // ❌ 重复硬编码 20
// ...
}
// ❌ 硬编码字符串(在多处使用)
func ActivateSIM(iccid string) error {
return UpdateStatus(iccid, "active") // ❌ 硬编码 "active"
}
func IsSIMActive(sim *SIM) bool {
return sim.Status == "active" // ❌ 重复硬编码 "active"
}
// ❌ 已定义常量但不使用
// 常量已定义在 pkg/response/codes.go
func Success(c *fiber.Ctx, data any) error {
return c.JSON(Response{
Code: 0, // ❌ 应该使用 errors.CodeSuccess
Data: data,
})
}
```
**可接受的字面量使用:**
```go
// ✅ 语言特性和明确上下文的数字
if user == nil { // ✅ nil
return errors.New("user not found")
}
for i := 0; i < len(items); i++ { // ✅ 0, 1 用于循环
// ...
}
if count == 1 { // ✅ 1 用于比较
// 特殊处理单个元素
}
// ✅ 测试数据和日志消息
func TestUserCreate(t *testing.T) {
user := &User{
Name: "Test User", // ✅ 测试数据
Email: "test@example.com",
}
// ...
}
logger.Info("processing request", // ✅ 日志消息
zap.String("path", c.Path()),
zap.Int("status", 200))
```
**中文注释和输出规范 (Chinese Comments and Output Standards):**
本项目面向中文开发者,为提高代码可读性和维护效率,**SHOULD** 优先使用中文进行注释和输出。
- 代码注释implementation comments**SHOULD** 使用中文
- 日志消息log messages**SHOULD** 使用中文
- 用户可见的错误消息 **MUST** 使用中文(通过 `pkg/errors/` 的双语消息支持)
- 内部错误消息和调试日志 **SHOULD** 使用中文
- Go 文档注释doc comments for exported APIs**MAY** 使用英文以保持生态兼容性,但中文注释更佳
- 变量名、函数名、类型名 **MUST** 使用英文(遵循 Go 命名规范)
**正确的中文注释使用:**
```go
// GetUserByID 根据用户 ID 获取用户信息
// 如果用户不存在,返回 NotFoundError
func (s *Service) GetUserByID(ctx context.Context, id string) (*User, error) {
// 参数验证
if id == "" {
return nil, errors.New(errors.CodeInvalidParam, "用户 ID 不能为空")
}
// 从存储层获取用户
user, err := s.store.GetByID(ctx, id)
if err != nil {
s.logger.Error("获取用户失败",
zap.String("user_id", id),
zap.Error(err))
return nil, fmt.Errorf("获取用户失败: %w", err)
}
// 检查用户状态
if user.Status != constants.UserStatusActive {
s.logger.Warn("用户状态异常",
zap.String("user_id", id),
zap.String("status", user.Status))
}
return user, nil
}
// CreateOrder 创建新订单
func (s *Service) CreateOrder(ctx context.Context, req *CreateOrderRequest) (*Order, error) {
// 开启事务
tx := s.db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
s.logger.Error("创建订单时发生panic", zap.Any("panic", r))
}
}()
// 验证库存
if err := s.validateStock(ctx, req.ProductID, req.Quantity); err != nil {
tx.Rollback()
return nil, fmt.Errorf("库存验证失败: %w", err)
}
// 创建订单记录
order := &Order{
UserID: req.UserID,
ProductID: req.ProductID,
Quantity: req.Quantity,
Status: constants.OrderStatusPending,
}
if err := tx.Create(order).Error; err != nil {
tx.Rollback()
s.logger.Error("创建订单失败",
zap.String("user_id", req.UserID),
zap.Error(err))
return nil, err
}
tx.Commit()
s.logger.Info("订单创建成功",
zap.String("order_id", order.ID),
zap.String("user_id", req.UserID))
return order, nil
}
```
**正确的中文日志使用:**
```go
// 信息日志
logger.Info("服务启动成功",
zap.String("host", cfg.Server.Host),
zap.Int("port", cfg.Server.Port))
logger.Info("配置热重载成功",
zap.String("config_file", "config.yaml"),
zap.Time("reload_time", time.Now()))
// 警告日志
logger.Warn("Redis 连接延迟较高",
zap.Duration("latency", latency),
zap.String("threshold", "50ms"))
// 错误日志
logger.Error("数据库连接失败",
zap.String("host", cfg.DB.Host),
zap.Int("port", cfg.DB.Port),
zap.Error(err))
logger.Error("令牌验证失败",
zap.String("token", token[:10]+"..."),
zap.String("client_ip", clientIP),
zap.Error(err))
// 调试日志
logger.Debug("处理用户请求",
zap.String("request_id", requestID),
zap.String("method", c.Method()),
zap.String("path", c.Path()),
zap.String("user_id", userID))
```
**用户可见错误消息(双语支持):**
```go
// pkg/errors/codes.go
var errorMessages = map[int]ErrorMessage{
CodeSuccess: {"Success", "成功"},
CodeInternalError: {"Internal server error", "内部服务器错误"},
CodeMissingToken: {"Missing authentication token", "缺失认证令牌"},
CodeInvalidToken: {"Invalid or expired token", "令牌无效或已过期"},
CodeTooManyRequests: {"Too many requests", "请求过于频繁"},
CodeAuthServiceUnavailable: {"Authentication service unavailable", "认证服务不可用"},
}
// 使用时根据语言返回对应消息
message := errors.GetMessage(code, "zh") // 返回中文消息
```
**错误的英文注释(不推荐):**
```go
// ❌ 全英文注释(可读性较差,不便于中文团队维护)
// GetUserByID retrieves user information by user ID
// Returns NotFoundError if user does not exist
func (s *Service) GetUserByID(ctx context.Context, id string) (*User, error) {
// Validate parameters
if id == "" {
return nil, errors.New(errors.CodeInvalidParam, "用户 ID 不能为空")
}
// Fetch user from store
user, err := s.store.GetByID(ctx, id)
if err != nil {
s.logger.Error("Failed to get user", // ❌ 英文日志
zap.String("user_id", id),
zap.Error(err))
return nil, fmt.Errorf("failed to get user: %w", err)
}
return user, nil
}
```
**例外情况(可使用英文):**
```go
// ✅ 导出的包级别文档注释可使用英文(生态兼容性)
// Package user provides user management functionality.
// It includes user CRUD operations, authentication, and authorization.
package user
// ✅ 导出的 API 文档注释可使用英文
// UserService provides user-related business logic operations.
type UserService struct {
store UserStorer
logger *zap.Logger
}
// ✅ 但内部实现注释应使用中文
func (s *UserService) Create(ctx context.Context, req *CreateUserRequest) (*User, error) {
// 验证用户输入
if err := req.Validate(); err != nil {
return nil, err
}
// 检查用户是否已存在
existing, _ := s.store.GetByEmail(ctx, req.Email)
if existing != nil {
return nil, errors.New(errors.CodeUserExists, "用户邮箱已存在")
}
// 创建用户记录
user := &User{
Name: req.Name,
Email: req.Email,
}
return s.store.Create(ctx, user)
}
```
**理由 (RATIONALE):**
清晰的分层架构和代码组织使代码易于理解、测试和维护。统一的错误处理和响应格式提升 API 一致性和客户端集成体验。依赖注入模式便于单元测试和模块替换。集中管理常量和 Redis key 避免拼写错误、重复定义和命名不一致提升代码可维护性和重构安全性。Redis key 统一管理便于监控、调试和缓存策略调整。遵循 Go 官方代码风格确保代码一致性和可读性。
避免硬编码和强制使用常量的规则能够:
1. **提高可维护性**:修改常量值只需改一处,不需要搜索所有硬编码位置
2. **减少错误**:避免手动输入错误(拼写错误、大小写错误)
3. **增强可读性**`constants.MaxPageSize` 比 `100` 更能表达意图
4. **便于重构**IDE 可以追踪常量使用,重命名时不会遗漏
5. **统一业务规则**:确保所有地方使用相同的业务规则值
6. "3 次规则"提供明确的阈值,避免过早优化,同时确保重复值被及时抽取
---
### III. Testing Standards (测试标准)
**规则 (RULES):**
- 所有核心业务逻辑Service 层)**MUST** 有单元测试覆盖
- 所有 API 端点 **MUST** 有集成测试覆盖
- 所有数据库操作 **SHOULD** 有事务回滚测试
- 测试 **MUST** 使用 Go 标准测试框架(`testing` 包)
- 测试文件 **MUST** 与源文件同目录,命名为 `*_test.go`
- 测试函数 **MUST** 使用 `Test` 前缀:`func TestUserCreate(t *testing.T)`
- 基准测试 **MUST** 使用 `Benchmark` 前缀:`func BenchmarkUserCreate(b *testing.B)`
- 测试 **MUST** 可独立运行,不依赖外部服务(使用 mock 或 testcontainers
- 单元测试 **MUST** 在 100ms 内完成
- 集成测试 **SHOULD** 在 1s 内完成
- 测试覆盖率 **SHOULD** 达到 70% 以上(核心业务代码必须 90% 以上)
- 测试 **MUST** 使用 table-driven tests 处理多个测试用例
- 测试 **MUST** 使用 `t.Helper()` 标记辅助函数
**Table-Driven Test 示例:**
```go
func TestUserValidate(t *testing.T) {
tests := []struct {
name string
user User
wantErr bool
}{
{"valid user", User{Name: "John", Email: "john@example.com"}, false},
{"empty name", User{Name: "", Email: "john@example.com"}, true},
{"invalid email", User{Name: "John", Email: "invalid"}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.user.Validate()
if (err != nil) != tt.wantErr {
t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
```
**理由 (RATIONALE):**
高质量的测试是代码质量的基石。单元测试确保业务逻辑正确性,集成测试确保模块间协作正常。快速的测试执行时间保证开发效率。测试独立性避免环境依赖导致的 flaky tests。Table-driven tests 使测试更简洁、易于扩展和维护,这是 Go 社区的最佳实践。
---
### IV. User Experience Consistency (用户体验一致性)
**规则 (RULES):**
- 所有 API 响应 **MUST** 使用统一的 JSON 格式:
```json
{
"code": 0,
"message": "success",
"data": {},
"timestamp": "2025-11-10T15:30:00Z"
}
```
- 所有错误响应 **MUST** 包含明确的错误码和错误消息(中英文双语)
- 所有 API 端点 **MUST** 遵循 RESTful 设计原则
- 所有分页 API **MUST** 使用统一的分页参数:`page`、`page_size`、`total`
- 所有时间字段 **MUST** 使用 ISO 8601 格式RFC3339
- 所有货币金额 **MUST** 使用整数表示(分为单位),避免浮点精度问题
- 所有布尔字段 **MUST** 使用 `true`/`false`,不使用 `0`/`1`
- API 版本 **MUST** 通过 URL 路径管理(如 `/api/v1/...`
**理由 (RATIONALE):**
一致的 API 设计降低客户端开发成本,减少集成错误。统一的数据格式和错误处理提升系统可预测性。清晰的时间和金额表示避免常见的数据处理错误。
---
### V. Performance Requirements (性能要求)
**规则 (RULES):**
- API 响应时间P95**MUST** < 200ms数据库查询 < 50ms
- API 响应时间P99**MUST** < 500ms
- 批量操作 **MUST** 使用批量查询/插入,避免 N+1 查询问题
- 所有数据库查询 **MUST** 有适当的索引支持
- 列表查询 **MUST** 实现分页,默认 `page_size=20`,最大 `page_size=100`
- 异步任务 **MUST** 用于非实时操作(批量同步、分佣计算等)
- 内存使用API 服务)**SHOULD** < 500MB正常负载
- 内存使用Worker 服务)**SHOULD** < 1GB正常负载
- 数据库连接池 **MUST** 配置合理(`MaxOpenConns=25`, `MaxIdleConns=10`, `ConnMaxLifetime=5m`
- Redis 连接池 **MUST** 配置合理(`PoolSize=10`, `MinIdleConns=5`
- 并发操作 **SHOULD** 使用 goroutines 和 channels不是线程池模式
- **MUST** 使用 `context.Context` 进行超时和取消控制
- **MUST** 使用 `sync.Pool` 复用频繁分配的对象(如缓冲区)
**理由 (RATIONALE):**
性能要求确保系统在生产环境下的稳定性和用户体验。批量操作和异步任务避免阻塞主流程。合理的连接池配置平衡性能和资源消耗。明确的性能指标便于监控和优化。使用 Go 的并发原语goroutines、channels而不是传统的线程池模式充分发挥 Go 的并发优势。
---
### VI. Go Idiomatic Design Principles (Go 语言惯用设计原则)
**核心理念:写 Go 味道的代码,不要写 Java 味道的代码**
#### 包组织 (Package Organization)
**MUST 遵循的原则:**
- 包结构 **MUST** 扁平化,避免深层嵌套(最多 2-3 层)
- 包 **MUST** 按功能组织,不是按层次组织
- 包名 **MUST** 描述功能,不是类型(`http` 不是 `httputils`、`handlers`
**正确的 Go 风格:**
```
internal/
├── user/ # user 功能的所有代码
│ ├── handler.go # HTTP handlers
│ ├── service.go # 业务逻辑
│ ├── store.go # 数据访问
│ └── model.go # 数据模型
├── order/
│ ├── handler.go
│ ├── service.go
│ └── store.go
└── sim/
├── handler.go
├── service.go
└── store.go
```
**错误的 Java 风格(禁止):**
```
internal/
├── handlers/
│ ├── user/
│ │ └── UserHandler.go # ❌ 类名式命名
│ └── order/
│ └── OrderHandler.go
├── services/
│ ├── user/
│ │ ├── IUserService.go # ❌ 接口前缀 I
│ │ └── UserServiceImpl.go # ❌ Impl 后缀
│ └── impls/ # ❌ 过度抽象
├── repositories/ # ❌ Repository 模式过度使用
│ └── interfaces/
└── models/
└── entities/
└── dto/ # ❌ 过度分层
```
#### 接口设计 (Interface Design)
**MUST 遵循的原则:**
- 接口 **MUST** 小而专注1-3 个方法),不是大而全
- 接口 **SHOULD** 在使用方定义,不是实现方(依赖倒置)
- 接口命名 **SHOULD** 使用 `-er` 后缀:`Reader`、`Writer`、`Storer`
- **MUST NOT** 使用 `I` 前缀或 `Interface` 后缀
- **MUST NOT** 创建只有一个实现的接口(除非明确需要抽象)
**正确的 Go 风格:**
```go
// 小接口,在使用方定义
type UserStorer interface {
Create(ctx context.Context, user *User) error
GetByID(ctx context.Context, id string) (*User, error)
}
// 具体实现
type PostgresStore struct {
db *gorm.DB
}
func (s *PostgresStore) Create(ctx context.Context, user *User) error {
return s.db.WithContext(ctx).Create(user).Error
}
```
**错误的 Java 风格(禁止):**
```go
// ❌ 大接口,方法过多
type IUserRepository interface {
Create(user *User) error
Update(user *User) error
Delete(id string) error
FindByID(id string) (*User, error)
FindByEmail(email string) (*User, error)
FindAll() ([]*User, error)
FindByStatus(status string) ([]*User, error)
Count() (int, error)
// ... 更多方法
}
// ❌ I 前缀
type IUserService interface {}
// ❌ 不必要的抽象层
type UserRepositoryImpl struct {} // 只有一个实现
```
#### 错误处理 (Error Handling)
**MUST 遵循的原则:**
- 错误 **MUST** 显式返回和检查不使用异常panic/recover
- 错误处理 **MUST** 紧跟错误产生的代码
- **MUST** 使用 `errors.Is()` 和 `errors.As()` 检查错误类型
- **MUST** 使用 `fmt.Errorf()` 包装错误,保留错误链
- 自定义错误 **SHOULD** 实现 `error` 接口
- panic **MUST ONLY** 用于不可恢复的程序错误
**正确的 Go 风格:**
```go
func (s *Service) GetUser(ctx context.Context, id string) (*User, error) {
user, err := s.store.GetByID(ctx, id)
if err != nil {
// 包装错误,添加上下文
return nil, fmt.Errorf("get user by id %s: %w", id, err)
}
return user, nil
}
// 自定义错误类型
type NotFoundError struct {
Resource string
ID string
}
func (e *NotFoundError) Error() string {
return fmt.Sprintf("%s not found: %s", e.Resource, e.ID)
}
```
**错误的 Java 风格(禁止):**
```go
// ❌ 使用 panic 代替错误返回
func GetUser(id string) *User {
user, err := db.Find(id)
if err != nil {
panic(err) // ❌ 不要这样做
}
return user
}
// ❌ try-catch 风格
func DoSomething() {
defer func() {
if r := recover(); r != nil { // ❌ 滥用 recover
log.Println("recovered:", r)
}
}()
// ...
}
// ❌ 异常类层次结构
type Exception interface { // ❌ 不需要
GetMessage() string
GetStackTrace() []string
}
type BusinessException struct {} // ❌ 过度设计
type ValidationException struct {} // ❌ 过度设计
```
#### 结构体和方法 (Structs and Methods)
**MUST 遵循的原则:**
- 结构体 **MUST** 简单直接不是类class的替代品
- **MUST NOT** 为每个字段创建 getter/setter 方法
- **MUST** 直接访问导出的字段(大写开头)
- **MUST** 使用组合composition而不是继承inheritance
- 构造函数 **SHOULD** 命名为 `New` 或 `NewXxx`,返回具体类型
- **MUST NOT** 使用构造器模式Builder Pattern除非真正需要
**正确的 Go 风格:**
```go
// 简单结构体,导出字段直接访问
type User struct {
ID string
Name string
Email string
}
// 简单构造函数
func NewUser(name, email string) *User {
return &User{
ID: generateID(),
Name: name,
Email: email,
}
}
// 使用组合
type Service struct {
store UserStorer
logger *zap.Logger
}
func NewService(store UserStorer, logger *zap.Logger) *Service {
return &Service{
store: store,
logger: logger,
}
}
```
**错误的 Java 风格(禁止):**
```go
// ❌ 私有字段 + getter/setter
type User struct {
id string // ❌ 不需要私有
name string
email string
}
func (u *User) GetID() string { return u.id } // ❌ 不需要
func (u *User) SetID(id string) { u.id = id } // ❌ 不需要
func (u *User) GetName() string { return u.name } // ❌ 不需要
func (u *User) SetName(name string) { u.name = name } // ❌ 不需要
// ❌ 构造器模式(不必要)
type UserBuilder struct {
user *User
}
func NewUserBuilder() *UserBuilder { // ❌ 过度设计
return &UserBuilder{user: &User{}}
}
func (b *UserBuilder) WithName(name string) *UserBuilder {
b.user.name = name
return b
}
func (b *UserBuilder) Build() *User {
return b.user
}
// ❌ 工厂模式(过度抽象)
type UserFactory interface { // ❌ 不需要
CreateUser(name string) *User
}
```
#### 并发模式 (Concurrency Patterns)
**MUST 遵循的原则:**
- **MUST** 使用 goroutines 和 channels不是线程和锁大多数情况
- **MUST** 使用 `context.Context` 传递取消信号
- **MUST** 遵循"通过通信共享内存,不要通过共享内存通信"
- **SHOULD** 使用 `sync.WaitGroup` 等待 goroutines 完成
- **SHOULD** 使用 `sync.Once` 确保只执行一次
- **MUST NOT** 创建线程池类Go 运行时已处理)
**正确的 Go 风格:**
```go
// 使用 goroutines 和 channels
func ProcessBatch(ctx context.Context, items []Item) error {
results := make(chan Result, len(items))
errors := make(chan error, len(items))
for _, item := range items {
go func(item Item) {
result, err := process(ctx, item)
if err != nil {
errors <- err
return
}
results <- result
}(item)
}
// 收集结果
for i := 0; i < len(items); i++ {
select {
case <-ctx.Done():
return ctx.Err()
case err := <-errors:
return err
case <-results:
// 处理结果
}
}
return nil
}
// 使用 sync.WaitGroup
func ProcessConcurrently(items []Item) {
var wg sync.WaitGroup
for _, item := range items {
wg.Add(1)
go func(item Item) {
defer wg.Done()
process(item)
}(item)
}
wg.Wait()
}
```
**错误的 Java 风格(禁止):**
```go
// ❌ 线程池模式
type ThreadPool struct { // ❌ 不需要
workers int
taskQueue chan Task
wg sync.WaitGroup
}
func NewThreadPool(workers int) *ThreadPool { // ❌ Go 运行时已处理
return &ThreadPool{
workers: workers,
taskQueue: make(chan Task, 100),
}
}
// ❌ Future/Promise 模式(不需要单独实现)
type Future struct { // ❌ 直接使用 channel
result chan interface{}
err chan error
}
// ❌ 过度使用 mutex应该用 channel
type SafeMap struct { // ❌ 考虑使用 sync.Map 或 channel
mu sync.Mutex
data map[string]interface{}
}
func (m *SafeMap) Get(key string) interface{} {
m.mu.Lock()
defer m.mu.Unlock()
return m.data[key]
}
```
#### 命名约定 (Naming Conventions)
**MUST 遵循的原则:**
- 变量名 **MUST** 简短且符合上下文:
- 短作用域用短名字:`i`, `j`, `k` 用于循环
- 长作用域用描述性名字:`userRepository`, `configManager`
- 缩写词 **MUST** 保持一致的大小写:`URL`, `HTTP`, `ID`(不是 `Url`, `Http`, `Id`
- **MUST NOT** 使用匈牙利命名法或类型前缀:`strName`, `arrUsers`(禁止)
- **MUST NOT** 使用下划线连接(除了测试和包名):`user_service`(禁止,应该是 `userservice` 或 `UserService`
- 方法接收者名称 **SHOULD** 使用 1-2 个字母的缩写,全文件保持一致
**正确的 Go 风格:**
```go
// 短作用域,短名字
for i, user := range users {
fmt.Println(i, user.Name)
}
// 长作用域,描述性名字
func ProcessUserRegistration(ctx context.Context, req *RegistrationRequest) error {
// ...
}
// 缩写词大小写一致
type UserAPI struct {
BaseURL string
APIKey string
UserID int64
}
// 方法接收者简短一致
type UserService struct {}
func (s *UserService) Create(user *User) error { // s 用于 service
return s.store.Create(user)
}
func (s *UserService) Update(user *User) error { // 保持一致使用 s
return s.store.Update(user)
}
```
**错误的 Java 风格(禁止):**
```go
// ❌ 过长的变量名
func ProcessRegistration() {
userRegistrationRequest := &Request{} // ❌ 太长
userRegistrationValidator := NewValidator() // ❌ 太长
userRegistrationService := NewService() // ❌ 太长
}
// ❌ 匈牙利命名
strUserName := "John" // ❌
intUserAge := 25 // ❌
arrUserList := []User{} // ❌
// ❌ 下划线命名
type User_Service struct {} // ❌ 应该是 UserService
func Get_User_By_Id() {} // ❌ 应该是 GetUserByID
// ❌ 缩写词大小写不一致
type UserApi struct { // ❌ 应该是 UserAPI
BaseUrl string // ❌ 应该是 BaseURL
ApiKey string // ❌ 应该是 APIKey
UserId int64 // ❌ 应该是 UserID
}
// ❌ 方法接收者不一致或过长
func (userService *UserService) Create() {} // ❌ 太长
func (us *UserService) Update() {} // ❌ 与上面不一致
func (this *UserService) Delete() {} // ❌ 不要使用 this/self
```
#### 反模式清单 (Anti-Patterns to Avoid)
**严格禁止的 Java 风格模式:**
1. ❌ **过度抽象**:不需要的接口、工厂、构造器
2. ❌ **Getter/Setter**:直接访问导出字段
3. ❌ **继承层次**:使用组合,不是嵌入
4. ❌ **异常处理**:使用错误返回,不是 panic/recover
5. ❌ **单例模式**:使用包级别变量或 `sync.Once`
6. ❌ **线程池**:直接使用 goroutines
7. ❌ **深层包嵌套**:保持扁平结构
8. ❌ **类型前缀**`IService`, `AbstractBase`, `ServiceImpl`
9. ❌ **Bean 风格**:不需要 POJO/JavaBean 模式
10. ❌ **过度 DI 框架**:简单直接的依赖注入
**理由 (RATIONALE):**
Go 语言的设计哲学强调简单性、直接性和实用性。Java 风格的模式(深层继承、过度抽象、复杂的设计模式)违背了 Go 的核心理念。Go 提供了更简单、更高效的方式来解决相同的问题:接口的隐式实现、结构体组合、显式错误处理、轻量级并发。遵循 Go 惯用法不仅使代码更地道,还能充分发挥 Go 的性能优势和简洁性。
---
## Development Workflow (开发工作流程)
### 分支管理
- **MUST** 从 `main` 分支创建功能分支
- 功能分支命名格式:`feature/###-brief-description` 或 `fix/###-brief-description`
- **MUST** 在合并前保持分支与 `main` 同步rebase 或 merge
- 合并到 `main` **MUST** 通过 Pull Request 并经过代码审查
### 提交规范
- 提交信息 **MUST** 遵循 Conventional Commits 格式:
- `feat: 新功能描述`
- `fix: 修复问题描述`
- `refactor: 重构描述`
- `test: 测试相关`
- `docs: 文档更新`
- `chore: 构建/工具相关`
- 提交信息 **SHOULD** 简洁明了(中文或英文)
### 代码审查
- 所有 PR **MUST** 至少有一人审查通过
- 审查者 **MUST** 验证:
- 代码符合本宪章所有原则(特别是 Go 惯用法原则)
- 无 Java 风格的反模式getter/setter、过度抽象等
- 测试覆盖充分且通过
- 无安全漏洞SQL 注入、XSS、命令注入等
- 性能影响可接受
- 代码通过 `gofmt`、`go vet`、`golangci-lint` 检查
---
## Quality Gates (质量关卡)
### 代码合并前检查
- [ ] 所有测试通过(`go test ./...`
- [ ] 代码格式化(`gofmt -l .` 无输出)
- [ ] 代码静态检查通过(`go vet ./...`
- [ ] 代码质量检查通过(`golangci-lint run`
- [ ] 测试覆盖率符合标准(`go test -cover ./...`
- [ ] 无 TODO/FIXME 遗留(或已记录 Issue
- [ ] API 文档已更新(如有 API 变更)
- [ ] 数据库迁移文件已创建(如有 schema 变更)
- [ ] **常量和 Redis key 使用符合规范**(无硬编码字符串)
- [ ] **无重复硬编码值**3 次以上相同字面量已提取为常量)
- [ ] **已定义的常量被正确使用**(无重复硬编码已有常量的值)
- [ ] **代码注释优先使用中文**(实现注释使用中文,提高团队可读性)
- [ ] **日志消息使用中文**Info/Warn/Error/Debug 日志使用中文描述)
- [ ] **错误消息支持中文**(用户可见错误有中文消息)
- [ ] **无 Java 风格反模式**(无 getter/setter、无不必要接口、无过度抽象
- [ ] **遵循 Go 命名约定**(缩写大小写一致、包名简短、无下划线)
- [ ] **使用 Go 惯用并发模式**goroutines/channels无线程池类
### 上线前检查
- [ ] 所有功能在 staging 环境测试通过
- [ ] 性能测试通过(响应时间、内存使用、并发能力)
- [ ] 数据库迁移在 staging 环境验证通过
- [ ] 监控和告警配置完成
- [ ] 回滚方案已准备
---
## Governance
本宪章是所有开发实践的最高指导原则,优先级高于个人偏好或临时便利。
### 修订流程
- 宪章修订 **MUST** 经过团队讨论并达成共识
- 修订 **MUST** 包含明确的理由和影响分析
- 修订 **MUST** 更新版本号(遵循语义化版本)
- 修订 **MUST** 同步更新相关模板plan-template.md、spec-template.md、tasks-template.md
### 版本策略
- **MAJOR**: 移除或重新定义核心原则(不兼容变更)
- **MINOR**: 新增原则或显著扩展指导内容
- **PATCH**: 澄清说明、措辞优化、错误修正
### 合规审查
- 所有 PR 审查 **MUST** 验证是否符合本宪章
- 违反宪章的代码 **MUST** 在合并前修正
- 特别关注 Go 惯用法原则,拒绝 Java 风格的代码
- 特别关注常量使用规范,拒绝不必要的硬编码
- 任何复杂性增加(新依赖、新架构层)**MUST** 在设计文档中明确说明必要性
### 运行时开发指导
开发时参考本宪章确保一致性。如有疑问,优先遵守原则,再讨论例外情况。记住:**写 Go 代码,不是用 Go 语法写 Java**。记住:**定义常量是为了使用,不是为了装饰**。
---
**Version**: 2.1.1 | **Ratified**: 2025-11-10 | **Last Amended**: 2025-11-11