Files
doucan/AGENTS.md
2026-02-20 23:39:49 +08:00

4.7 KiB
Raw Permalink Blame History

斗地主残局版 - AGENTS.md

1. 项目结构

doudizhu-server/
├── app/                    # Go后端
│   ├── cmd/main.go         # 程序入口
│   ├── internal/
│   │   ├── models/         # 数据模型Card, Room, Player等
│   │   ├── game/           # 游戏逻辑(牌型判断、对局管理)
│   │   ├── handlers/       # HTTP API处理器
│   │   └── ws/             # WebSocket处理
│   ├── Dockerfile
│   └── go.mod
├── nginx/nginx.conf        # Nginx反向代理配置
├── nginx/html/             # 静态文件HTML/CSS/JS
├── compose.yaml
├── README.md
└── API.md

2. 构建与运行命令

# 本地开发需先启动Redis
docker run -d --name redis -p 6379:6379 redis:7-alpine
cd doudizhu-server/app && go mod tidy && go run ./cmd

# Docker部署
cd doudizhu-server && docker compose up -d --build

# 代码格式化自动格式化所有Go文件
gofmt -w ./app

# 静态检查
go vet ./app/...

# 测试(当前项目暂无测试文件,以下为参考命令)
# go test ./app/...
# go test ./app/internal/game/...
# go test ./app/internal/game/... -run TestCardType
# go test ./app/... -cover

3. Go代码风格规范

命名规范

  • 导出:大驼峰命名,如 GameManagerNewCardLogic
  • 模块内:小驼峰命名,如 userIdcardKey
  • 模块名:全小写,如 gamehandlersws
  • 常量:全大写下划线命名

Import规范

import (
    "encoding/json"    // 标准库
    "net/http"

    "github.com/gorilla/websocket"  // 第三方库

    "doudizhu-server/internal/game"  // 本地包
    "doudizhu-server/internal/models"
)

错误处理

使用哨兵错误模式:

// 定义 (internal/game/errors.go)
var (
    ErrRoomNotFound = errors.New("room not found")
    ErrNotYourTurn  = errors.New("not your turn")
)

// 使用:早返回
func (h *Handler) CreateRoom(w http.ResponseWriter, r *http.Request) {
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        h.writeError(w, http.StatusBadRequest, "invalid request")
        return
    }
}

并发安全

type GameManager struct {
    rooms map[string]*models.Room
    mu    sync.RWMutex
}

// 读操作用RLock
func (gm *GameManager) GetRoom(id string) (*models.Room, error) {
    gm.mu.RLock()
    defer gm.mu.RUnlock()
    // ...
}

// 写操作用Lock
func (gm *GameManager) CreateRoom(...) {
    gm.mu.Lock()
    defer gm.mu.Unlock()
    // ...
}

函数长度

  • 禁止超过150行不宜超过50行
  • 保持函数单一职责,提高可读性和可维护性

代码优雅性

  1. 空行规范:不同逻辑块之间空一行
  2. 运算符空格:双目运算符(+, =, == 等)两端必须加空格
  3. 禁止多行合并:禁止使用分号将多行代码合并到一行

介词命名规范(用于函数名)

介词 用法
by 表示动作的执行者或方式,如 formatByLocalecalculateByDate
from 表示数据来源、起点或类型转换源,如 parseFromJson
to 指明目标位置、接收方或变换终点,如 sendToServeruploadToCloud
with 说明附加参数或属性,如 buildWithOptionscreateWithDefaults
in 描述在某个上下文/环境中执行的动作,如 runInBackground
on 与事件相关的处理器或触发点,如 handleOnClicklistenOnChange
for 指定目标受众、用途或适用范围,如 renderForAdminqueryForUser

4. HTTP API响应格式

type ApiResponse struct {
    Status  int         `json:"status"`  // HTTP状态码
    Code    int         `json:"code"`    // 0=成功, 1=失败
    Message string      `json:"message"`
    Data    interface{} `json:"data"`
}

5. 游戏规则要点

  • 超人强第55张牌最大单牌可管一切
  • 简化牌型:三张可成顺子/炸弹,两对可成连对
  • 发牌每人初始5张一轮后摸1张
  • 无地主:上一轮获胜者先出

6. 开发注意事项

  1. 代码变动后同步更新文档README.md 和 API.md
  2. 后端API路由必须在 /api
  3. WebSocket路径/api/ws?roomId=xxx&playerId=xxx
  4. 前端不使用npm/react采用HTML+CSS+JS
  5. JSON字段用camelCase命名,如 roomIdplayerIdcardCount
  6. Go版本Go 1.21

7. 调试

# 查看日志
docker compose logs -f app

# 前端不生效?强制刷新 (Ctrl+F5)