做一次小小的备份,等会又删掉了
This commit is contained in:
@@ -1,31 +1,42 @@
|
||||
<!--
|
||||
SYNC IMPACT REPORT - Constitution Amendment
|
||||
============================================
|
||||
Version Change: INITIAL → 1.0.0
|
||||
Version Change: 1.1.0 → 2.0.0
|
||||
Date: 2025-11-10
|
||||
|
||||
NEW PRINCIPLES ESTABLISHED:
|
||||
- I. Tech Stack Adherence (Fiber 生态系统)
|
||||
- II. Code Quality Standards (代码质量标准)
|
||||
- III. Testing Standards (测试标准)
|
||||
- IV. User Experience Consistency (用户体验一致性)
|
||||
- V. Performance Requirements (性能要求)
|
||||
MODIFIED PRINCIPLES:
|
||||
- I. Tech Stack Adherence → Added explicit Go tooling requirements
|
||||
- II. Code Quality Standards → Completely rewritten to emphasize Go idioms and eliminate Java patterns
|
||||
- NEW: Go Idiomatic Design Principles (Principle VI)
|
||||
|
||||
SECTIONS ADDED:
|
||||
- Development Workflow (开发工作流程)
|
||||
- Quality Gates (质量关卡)
|
||||
ADDED SECTIONS:
|
||||
- Principle VI: Go Idiomatic Design Principles (Go 味道 vs Java 味道)
|
||||
- Package organization (flat, not deep hierarchies)
|
||||
- Interface design (small, focused interfaces)
|
||||
- Error handling (explicit, not exceptions)
|
||||
- Concurrency patterns (goroutines and channels)
|
||||
- Naming conventions (Go-style, not Java camelCase)
|
||||
- Anti-patterns to avoid (getters/setters, builder patterns, etc.)
|
||||
|
||||
SECTIONS EXPANDED:
|
||||
- Code Quality Standards now explicitly forbid Java-style patterns
|
||||
- Added Go project structure requirements
|
||||
- Added explicit guidance against OOP over-engineering
|
||||
|
||||
TEMPLATES REQUIRING UPDATES:
|
||||
✅ .specify/templates/plan-template.md - Constitution Check section aligned
|
||||
✅ .specify/templates/spec-template.md - Requirements section aligned with principles
|
||||
✅ .specify/templates/tasks-template.md - Task organization reflects quality gates
|
||||
✅ .specify/templates/plan-template.md - Added Go idiomatic checks
|
||||
✅ .specify/templates/spec-template.md - Added Go design requirements
|
||||
✅ .specify/templates/tasks-template.md - Added Go code review tasks
|
||||
|
||||
FOLLOW-UP ACTIONS:
|
||||
- None - all placeholders resolved
|
||||
|
||||
RATIONALE:
|
||||
Initial constitution establishment for 君鸿卡管系统 project. Major version 1.0.0
|
||||
as this is the first formal governance document defining core development principles.
|
||||
MAJOR version bump (2.0.0) - Fundamental shift in design philosophy from Java-style to
|
||||
Go-idiomatic patterns. This is a breaking change in coding standards that affects all
|
||||
existing and future code. Added new principle (VI) defining mandatory Go idioms and
|
||||
explicitly forbidding Java patterns like deep class hierarchies, getter/setter methods,
|
||||
excessive abstraction, and OOP-heavy designs.
|
||||
-->
|
||||
|
||||
# 君鸿卡管系统 Constitution
|
||||
@@ -44,10 +55,12 @@ as this is the first formal governance document defining core development princi
|
||||
- 所有日志记录 **MUST** 使用 Zap + Lumberjack.v2
|
||||
- 所有 JSON 序列化 **MUST** 使用 sonic
|
||||
- 所有异步任务 **MUST** 使用 Asynq
|
||||
- **MUST** 使用 Go 官方工具链:`go fmt`、`go vet`、`golangci-lint`
|
||||
- **MUST** 使用 Go Modules 进行依赖管理
|
||||
|
||||
**理由 (RATIONALE):**
|
||||
|
||||
一致的技术栈使用确保代码可维护性、团队协作效率和长期技术债务可控。绕过框架的"快捷方式"会导致代码碎片化、难以调试、性能不一致和安全漏洞。框架选择已经过深思熟虑,必须信任并充分利用其生态系统。
|
||||
一致的技术栈使用确保代码可维护性、团队协作效率和长期技术债务可控。绕过框架的"快捷方式"会导致代码碎片化、难以调试、性能不一致和安全漏洞。框架选择已经过深思熟虑,必须信任并充分利用其生态系统。Go 官方工具链确保代码风格一致性和质量。
|
||||
|
||||
---
|
||||
|
||||
@@ -60,15 +73,43 @@ as this is the first formal governance document defining core development princi
|
||||
- Service 层 **MUST** 包含所有业务逻辑,支持跨模块调用
|
||||
- Store 层 **MUST** 统一管理所有数据访问,支持事务处理
|
||||
- Model 层 **MUST** 定义清晰的数据结构和 DTO
|
||||
- 所有依赖 **MUST** 通过 `Service` 和 `Store` 结构体进行依赖注入
|
||||
- 所有依赖 **MUST** 通过结构体字段进行依赖注入(不使用构造函数模式)
|
||||
- 所有公共错误 **MUST** 在 `pkg/errors/` 中定义,使用统一错误码
|
||||
- 所有 API 响应 **MUST** 使用 `pkg/response/` 的统一格式
|
||||
- **MUST** 为所有公共函数编写清晰的注释(英文或中文)
|
||||
- 所有常量 **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
|
||||
```
|
||||
|
||||
**理由 (RATIONALE):**
|
||||
|
||||
清晰的分层架构和代码组织使代码易于理解、测试和维护。统一的错误处理和响应格式提升 API 一致性和客户端集成体验。依赖注入模式便于单元测试和模块替换。
|
||||
清晰的分层架构和代码组织使代码易于理解、测试和维护。统一的错误处理和响应格式提升 API 一致性和客户端集成体验。依赖注入模式便于单元测试和模块替换。集中管理常量和 Redis key 避免拼写错误、重复定义和命名不一致,提升代码可维护性和重构安全性。Redis key 统一管理便于监控、调试和缓存策略调整。遵循 Go 官方代码风格确保代码一致性和可读性。
|
||||
|
||||
---
|
||||
|
||||
@@ -81,14 +122,43 @@ as this is the first formal governance document defining core development princi
|
||||
- 所有数据库操作 **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。
|
||||
高质量的测试是代码质量的基石。单元测试确保业务逻辑正确性,集成测试确保模块间协作正常。快速的测试执行时间保证开发效率。测试独立性避免环境依赖导致的 flaky tests。Table-driven tests 使测试更简洁、易于扩展和维护,这是 Go 社区的最佳实践。
|
||||
|
||||
---
|
||||
|
||||
@@ -133,10 +203,444 @@ as this is the first formal governance document defining core development princi
|
||||
- 内存使用(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 的性能优势和简洁性。
|
||||
|
||||
---
|
||||
|
||||
@@ -164,10 +668,12 @@ as this is the first formal governance document defining core development princi
|
||||
|
||||
- 所有 PR **MUST** 至少有一人审查通过
|
||||
- 审查者 **MUST** 验证:
|
||||
- 代码符合本宪章所有原则
|
||||
- 代码符合本宪章所有原则(特别是 Go 惯用法原则)
|
||||
- 无 Java 风格的反模式(getter/setter、过度抽象等)
|
||||
- 测试覆盖充分且通过
|
||||
- 无安全漏洞(SQL 注入、XSS、命令注入等)
|
||||
- 性能影响可接受
|
||||
- 代码通过 `gofmt`、`go vet`、`golangci-lint` 检查
|
||||
|
||||
---
|
||||
|
||||
@@ -176,12 +682,17 @@ as this is the first formal governance document defining core development princi
|
||||
### 代码合并前检查
|
||||
|
||||
- [ ] 所有测试通过(`go test ./...`)
|
||||
- [ ] 代码格式化(`gofmt` 或 `goimports`)
|
||||
- [ ] 代码静态检查通过(`go vet` 和 `golangci-lint`)
|
||||
- [ ] 测试覆盖率符合标准
|
||||
- [ ] 代码格式化(`gofmt -l .` 无输出)
|
||||
- [ ] 代码静态检查通过(`go vet ./...`)
|
||||
- [ ] 代码质量检查通过(`golangci-lint run`)
|
||||
- [ ] 测试覆盖率符合标准(`go test -cover ./...`)
|
||||
- [ ] 无 TODO/FIXME 遗留(或已记录 Issue)
|
||||
- [ ] API 文档已更新(如有 API 变更)
|
||||
- [ ] 数据库迁移文件已创建(如有 schema 变更)
|
||||
- [ ] 常量和 Redis key 使用符合规范(无硬编码字符串)
|
||||
- [ ] **无 Java 风格反模式**(无 getter/setter、无不必要接口、无过度抽象)
|
||||
- [ ] **遵循 Go 命名约定**(缩写大小写一致、包名简短、无下划线)
|
||||
- [ ] **使用 Go 惯用并发模式**(goroutines/channels,无线程池类)
|
||||
|
||||
### 上线前检查
|
||||
|
||||
@@ -214,12 +725,13 @@ as this is the first formal governance document defining core development princi
|
||||
|
||||
- 所有 PR 审查 **MUST** 验证是否符合本宪章
|
||||
- 违反宪章的代码 **MUST** 在合并前修正
|
||||
- 特别关注 Go 惯用法原则,拒绝 Java 风格的代码
|
||||
- 任何复杂性增加(新依赖、新架构层)**MUST** 在设计文档中明确说明必要性
|
||||
|
||||
### 运行时开发指导
|
||||
|
||||
开发时参考本宪章确保一致性。如有疑问,优先遵守原则,再讨论例外情况。
|
||||
开发时参考本宪章确保一致性。如有疑问,优先遵守原则,再讨论例外情况。记住:**写 Go 代码,不是用 Go 语法写 Java**。
|
||||
|
||||
---
|
||||
|
||||
**Version**: 1.0.0 | **Ratified**: 2025-11-10 | **Last Amended**: 2025-11-10
|
||||
**Version**: 2.0.0 | **Ratified**: 2025-11-10 | **Last Amended**: 2025-11-10
|
||||
|
||||
@@ -37,20 +37,43 @@
|
||||
- [ ] All HTTP operations use Fiber framework
|
||||
- [ ] All database operations use GORM
|
||||
- [ ] All async tasks use Asynq
|
||||
- [ ] Uses Go official toolchain: `go fmt`, `go vet`, `golangci-lint`
|
||||
- [ ] Uses Go Modules for dependency management
|
||||
|
||||
**Code Quality Standards**:
|
||||
- [ ] Follows Handler → Service → Store → Model architecture
|
||||
- [ ] Handler layer only handles HTTP, no business logic
|
||||
- [ ] Service layer contains business logic with cross-module support
|
||||
- [ ] Store layer manages all data access with transaction support
|
||||
- [ ] Uses dependency injection via Service/Store structs
|
||||
- [ ] Uses dependency injection via struct fields (not constructor patterns)
|
||||
- [ ] Unified error codes in `pkg/errors/`
|
||||
- [ ] Unified API responses via `pkg/response/`
|
||||
- [ ] All constants defined in `pkg/constants/`
|
||||
- [ ] All Redis keys managed via key generation functions (no hardcoded strings)
|
||||
- [ ] All exported functions/types have Go-style doc comments
|
||||
- [ ] Code formatted with `gofmt`
|
||||
- [ ] Follows Effective Go and Go Code Review Comments
|
||||
|
||||
**Go Idiomatic Design**:
|
||||
- [ ] Package structure is flat (max 2-3 levels), organized by feature
|
||||
- [ ] Interfaces are small (1-3 methods), defined at use site
|
||||
- [ ] No Java-style patterns: no I-prefix, no Impl-suffix, no getters/setters
|
||||
- [ ] Error handling is explicit (return errors, no panic/recover abuse)
|
||||
- [ ] Uses composition over inheritance
|
||||
- [ ] Uses goroutines and channels (not thread pools)
|
||||
- [ ] Uses `context.Context` for cancellation and timeouts
|
||||
- [ ] Naming follows Go conventions: short receivers, consistent abbreviations (URL, ID, HTTP)
|
||||
- [ ] No Hungarian notation or type prefixes
|
||||
- [ ] Simple constructors (New/NewXxx), no Builder pattern unless necessary
|
||||
|
||||
**Testing Standards**:
|
||||
- [ ] Unit tests for all core business logic (Service layer)
|
||||
- [ ] Integration tests for all API endpoints
|
||||
- [ ] Tests use Go standard testing framework
|
||||
- [ ] Test files named `*_test.go` in same directory
|
||||
- [ ] Test functions use `Test` prefix, benchmarks use `Benchmark` prefix
|
||||
- [ ] Table-driven tests for multiple test cases
|
||||
- [ ] Test helpers marked with `t.Helper()`
|
||||
- [ ] Tests are independent (no external service dependencies)
|
||||
- [ ] Target coverage: 70%+ overall, 90%+ for core business
|
||||
|
||||
@@ -69,6 +92,9 @@
|
||||
- [ ] List queries implement pagination (default 20, max 100)
|
||||
- [ ] Non-realtime operations use async tasks
|
||||
- [ ] Database and Redis connection pools properly configured
|
||||
- [ ] Uses goroutines/channels for concurrency (not thread pools)
|
||||
- [ ] Uses `context.Context` for timeout control
|
||||
- [ ] Uses `sync.Pool` for frequently allocated objects
|
||||
|
||||
## Project Structure
|
||||
|
||||
|
||||
@@ -104,12 +104,26 @@
|
||||
- [ ] All async tasks use Asynq
|
||||
- [ ] All logging uses Zap + Lumberjack.v2
|
||||
- [ ] All configuration uses Viper
|
||||
- [ ] Uses Go official toolchain: `go fmt`, `go vet`, `golangci-lint`
|
||||
|
||||
**Architecture Requirements**:
|
||||
- [ ] Implementation follows Handler → Service → Store → Model layers
|
||||
- [ ] Dependencies injected via Service/Store structs
|
||||
- [ ] Dependencies injected via struct fields (not constructor patterns)
|
||||
- [ ] Unified error codes defined in `pkg/errors/`
|
||||
- [ ] Unified API responses via `pkg/response/`
|
||||
- [ ] All constants defined in `pkg/constants/` (no magic numbers/strings)
|
||||
- [ ] All Redis keys managed via `pkg/constants/` key generation functions
|
||||
- [ ] Package structure is flat, organized by feature (not by layer)
|
||||
|
||||
**Go Idiomatic Design Requirements**:
|
||||
- [ ] No Java-style patterns: no getter/setter methods, no I-prefix interfaces, no Impl-suffix
|
||||
- [ ] Interfaces are small (1-3 methods), defined where used
|
||||
- [ ] Error handling is explicit (return errors, not panic)
|
||||
- [ ] Uses composition (struct embedding) not inheritance
|
||||
- [ ] Uses goroutines and channels for concurrency
|
||||
- [ ] Naming follows Go conventions: `UserID` not `userId`, `HTTPServer` not `HttpServer`
|
||||
- [ ] No Hungarian notation or type prefixes
|
||||
- [ ] Simple and direct code structure
|
||||
|
||||
**API Design Requirements**:
|
||||
- [ ] All APIs follow RESTful principles
|
||||
@@ -125,10 +139,13 @@
|
||||
- [ ] Batch operations use bulk queries
|
||||
- [ ] List queries implement pagination (default 20, max 100)
|
||||
- [ ] Non-realtime operations delegated to async tasks
|
||||
- [ ] Uses `context.Context` for timeouts and cancellation
|
||||
|
||||
**Testing Requirements**:
|
||||
- [ ] Unit tests for all Service layer business logic
|
||||
- [ ] Integration tests for all API endpoints
|
||||
- [ ] Tests use Go standard testing framework with `*_test.go` files
|
||||
- [ ] Table-driven tests for multiple test cases
|
||||
- [ ] Tests are independent and use mocks/testcontainers
|
||||
- [ ] Target coverage: 70%+ overall, 90%+ for core business logic
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ description: "Task list template for feature implementation"
|
||||
- [ ] T003 [P] Configure linting (golangci-lint) and formatting tools (gofmt/goimports)
|
||||
- [ ] T004 [P] Setup unified error codes in pkg/errors/
|
||||
- [ ] T005 [P] Setup unified API response in pkg/response/
|
||||
- [ ] T006 [P] Setup constants management in pkg/constants/ (business constants and Redis key functions)
|
||||
|
||||
---
|
||||
|
||||
@@ -64,18 +65,18 @@ description: "Task list template for feature implementation"
|
||||
|
||||
Foundational tasks for 君鸿卡管系统 tech stack:
|
||||
|
||||
- [ ] T006 Setup PostgreSQL database connection via GORM with connection pool (MaxOpenConns=25, MaxIdleConns=10)
|
||||
- [ ] T007 Setup Redis connection with connection pool (PoolSize=10, MinIdleConns=5)
|
||||
- [ ] T008 [P] Setup database migrations framework (golang-migrate or GORM AutoMigrate)
|
||||
- [ ] T009 [P] Implement Fiber routing structure in internal/router/
|
||||
- [ ] T010 [P] Implement Fiber middleware (authentication, logging, recovery, validation) in internal/handler/middleware/
|
||||
- [ ] T011 [P] Setup Zap logger with Lumberjack rotation in pkg/logger/
|
||||
- [ ] T012 [P] Setup Viper configuration management in pkg/config/
|
||||
- [ ] T013 [P] Setup Asynq task queue client and server in pkg/queue/
|
||||
- [ ] T014 [P] Setup Validator integration in pkg/validator/
|
||||
- [ ] T015 Create base Store structure with transaction support in internal/store/
|
||||
- [ ] T016 Create base Service structure with dependency injection in internal/service/
|
||||
- [ ] T017 Setup sonic JSON as default serializer for Fiber
|
||||
- [ ] T007 Setup PostgreSQL database connection via GORM with connection pool (MaxOpenConns=25, MaxIdleConns=10)
|
||||
- [ ] T008 Setup Redis connection with connection pool (PoolSize=10, MinIdleConns=5)
|
||||
- [ ] T009 [P] Setup database migrations framework (golang-migrate or GORM AutoMigrate)
|
||||
- [ ] T010 [P] Implement Fiber routing structure in internal/router/
|
||||
- [ ] T011 [P] Implement Fiber middleware (authentication, logging, recovery, validation) in internal/handler/middleware/
|
||||
- [ ] T012 [P] Setup Zap logger with Lumberjack rotation in pkg/logger/
|
||||
- [ ] T013 [P] Setup Viper configuration management in pkg/config/
|
||||
- [ ] T014 [P] Setup Asynq task queue client and server in pkg/queue/
|
||||
- [ ] T015 [P] Setup Validator integration in pkg/validator/
|
||||
- [ ] T016 Create base Store structure with transaction support in internal/store/
|
||||
- [ ] T017 Create base Service structure with dependency injection in internal/service/
|
||||
- [ ] T018 Setup sonic JSON as default serializer for Fiber
|
||||
|
||||
**Checkpoint**: Foundation ready - user story implementation can now begin in parallel
|
||||
|
||||
@@ -179,6 +180,11 @@ Foundational tasks for 君鸿卡管系统 tech stack:
|
||||
- [ ] TXXX Quality Gate: Check no TODO/FIXME remains (or documented in issues)
|
||||
- [ ] TXXX Quality Gate: Verify database migrations work correctly
|
||||
- [ ] TXXX Quality Gate: Verify API documentation updated (if API changes)
|
||||
- [ ] TXXX Quality Gate: Verify no hardcoded constants or Redis keys (all use pkg/constants/)
|
||||
- [ ] TXXX Quality Gate: Verify no Java-style anti-patterns (no getter/setter, no I-prefix, no Impl-suffix)
|
||||
- [ ] TXXX Quality Gate: Verify Go naming conventions (UserID not userId, HTTPServer not HttpServer)
|
||||
- [ ] TXXX Quality Gate: Verify error handling is explicit (no panic/recover abuse)
|
||||
- [ ] TXXX Quality Gate: Verify uses goroutines/channels (not thread pool patterns)
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user