170 lines
4.7 KiB
Markdown
170 lines
4.7 KiB
Markdown
# 斗地主残局版 - 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. 构建与运行命令
|
||
|
||
```bash
|
||
# 本地开发(需先启动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代码风格规范
|
||
|
||
### 命名规范
|
||
|
||
- **导出**:大驼峰命名,如 `GameManager`、`NewCardLogic`
|
||
- **模块内**:小驼峰命名,如 `userId`、`cardKey`
|
||
- **模块名**:全小写,如 `game`、`handlers`、`ws`
|
||
- **常量**:全大写下划线命名
|
||
|
||
### Import规范
|
||
|
||
```go
|
||
import (
|
||
"encoding/json" // 标准库
|
||
"net/http"
|
||
|
||
"github.com/gorilla/websocket" // 第三方库
|
||
|
||
"doudizhu-server/internal/game" // 本地包
|
||
"doudizhu-server/internal/models"
|
||
)
|
||
```
|
||
|
||
### 错误处理
|
||
|
||
使用哨兵错误模式:
|
||
|
||
```go
|
||
// 定义 (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
|
||
}
|
||
}
|
||
```
|
||
|
||
### 并发安全
|
||
|
||
```go
|
||
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 | 表示动作的执行者或方式,如 `formatByLocale`、`calculateByDate` |
|
||
| from | 表示数据来源、起点或类型转换源,如 `parseFromJson` |
|
||
| to | 指明目标位置、接收方或变换终点,如 `sendToServer`、`uploadToCloud` |
|
||
| with | 说明附加参数或属性,如 `buildWithOptions`、`createWithDefaults` |
|
||
| in | 描述在某个上下文/环境中执行的动作,如 `runInBackground` |
|
||
| on | 与事件相关的处理器或触发点,如 `handleOnClick`、`listenOnChange` |
|
||
| for | 指定目标受众、用途或适用范围,如 `renderForAdmin`、`queryForUser` |
|
||
|
||
## 4. HTTP API响应格式
|
||
|
||
```go
|
||
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命名**,如 `roomId`、`playerId`、`cardCount`
|
||
6. **Go版本**:Go 1.21
|
||
|
||
## 7. 调试
|
||
|
||
```bash
|
||
# 查看日志
|
||
docker compose logs -f app
|
||
|
||
# 前端不生效?强制刷新 (Ctrl+F5)
|
||
```
|