@@ -1,42 +1,36 @@
<!--
SYNC IMPACT REPORT - Constitution Amendment
============================================
Version Change: 1 .1.0 → 2.0.0
Date: 2025-11-10
Version Change: 2 .1.0 → 2.1.1
Date: 2025-11-11
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)
- I I. Code Quality Standards → Added "中文注释和输出规范" (Chinese Comments and Output Standards)
AD DED SECTIONS:
- Principle VI: Go Idiomatic Design Principles (Go 味道 vs Java 味道)
- Package organization (flat, not deep hierarchies)
- Interface design (small, focused interfac es)
- 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
EXPAN DED SECTIONS:
- Added "中文注释和输出规范" subsection to Principle II
- Rule: Code comments SHOULD be in Chinese for Chinese-speaking developers
- Rule: Log messages SHOULD be in Chin ese
- 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 - Added Go idiomatic checks
✅ .specify/templates/spec-template.md - Added Go design requirements
✅ .specify/templates/tasks-template.md - Added Go code review tasks
✅ .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:
- None - all placeholders resolved
- Update templates with Chinese comments/logs checks
RATIONALE:
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 hierarc hies, getter/setter methods,
excessive abstraction, and OOP-heavy design s.
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 (C hin ese developers)
while maintaining ecosystem compatibility for exported API s.
-->
# 君鸿卡管系统 Constitution
@@ -107,10 +101,322 @@ excessive abstraction, and OOP-heavy designs.
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 (测试标准)
@@ -689,7 +995,12 @@ Go 语言的设计哲学强调简单性、直接性和实用性。Java 风格的
- [ ] 无 TODO/FIXME 遗留(或已记录 Issue)
- [ ] API 文档已更新(如有 API 变更)
- [ ] 数据库迁移文件已创建(如有 schema 变更)
- [ ] 常量和 Redis key 使用符合规范(无硬编码字符串)
- [ ] ** 常量和 Redis key 使用符合规范** (无硬编码字符串)
- [ ] **无重复硬编码值 ** ( 3 次以上相同字面量已提取为常量)
- [ ] **已定义的常量被正确使用 ** (无重复硬编码已有常量的值)
- [ ] **代码注释优先使用中文 ** (实现注释使用中文,提高团队可读性)
- [ ] **日志消息使用中文 ** ( Info/Warn/Error/Debug 日志使用中文描述)
- [ ] **错误消息支持中文 ** (用户可见错误有中文消息)
- [ ] **无 Java 风格反模式 ** (无 getter/setter、无不必要接口、无过度抽象)
- [ ] **遵循 Go 命名约定 ** (缩写大小写一致、包名简短、无下划线)
- [ ] **使用 Go 惯用并发模式 ** ( goroutines/channels, 无线程池类)
@@ -726,12 +1037,13 @@ Go 语言的设计哲学强调简单性、直接性和实用性。Java 风格的
- 所有 PR 审查 **MUST ** 验证是否符合本宪章
- 违反宪章的代码 **MUST ** 在合并前修正
- 特别关注 Go 惯用法原则,拒绝 Java 风格的代码
- 特别关注常量使用规范,拒绝不必要的硬编码
- 任何复杂性增加(新依赖、新架构层)**MUST** 在设计文档中明确说明必要性
### 运行时开发指导
开发时参考本宪章确保一致性。如有疑问,优先遵守原则,再讨论例外情况。记住:**写 Go 代码,不是用 Go 语法写 Java**。
开发时参考本宪章确保一致性。如有疑问,优先遵守原则,再讨论例外情况。记住:**写 Go 代码,不是用 Go 语法写 Java**。记住:**定义常量是为了使用,不是为了装饰**。
---
**Version ** : 2.0.0 | **Ratified ** : 2025-11-10 | **Last Amended ** : 2025-11-10
**Version ** : 2.1.1 | **Ratified ** : 2025-11-10 | **Last Amended ** : 2025-11-11