374 lines
9.3 KiB
Markdown
374 lines
9.3 KiB
Markdown
# Net-Games 数据库设计文档
|
||
|
||
> **版本**: v2.0
|
||
> **更新日期**: 2026-04-09
|
||
> **数据库**: SQLite (开发/生产)
|
||
|
||
---
|
||
|
||
## 1. 技术选型
|
||
|
||
| 环境 | 数据库 | ORM | 说明 |
|
||
|------|--------|-----|------|
|
||
| 开发/生产 | SQLite | SQLAlchemy | 零配置,文件存储,适合当前规模 |
|
||
|
||
**选择理由:**
|
||
- 单机部署,无需外部数据库服务
|
||
- 数据量小(用户<1000,房间实时<100)
|
||
- 备份简单(复制文件即可)
|
||
- 未来可无缝迁移到PostgreSQL
|
||
|
||
---
|
||
|
||
## 2. 数据库文件
|
||
|
||
```
|
||
/home/ubuntu/nanocode/net-games/
|
||
├── net_games_auth.db # 主数据库(生产)
|
||
└── instance/
|
||
└── net_games_auth.db # Flask实例目录(开发)
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 表结构设计
|
||
|
||
### 3.1 users - 用户表
|
||
|
||
存储注册用户信息和游戏积分。
|
||
|
||
```sql
|
||
CREATE TABLE users (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
username VARCHAR(80) UNIQUE NOT NULL, -- 登录用户名(唯一)
|
||
nickname VARCHAR(80), -- 显示昵称(默认同用户名)
|
||
password_hash VARCHAR(200) NOT NULL, -- 密码哈希
|
||
role VARCHAR(20) DEFAULT 'user', -- 角色: admin/user
|
||
game_scores TEXT DEFAULT '{}', -- JSON格式各游戏积分
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
last_login TIMESTAMP
|
||
);
|
||
```
|
||
|
||
**game_scores JSON格式:**
|
||
```json
|
||
{
|
||
"doudizhu": 100,
|
||
"chinese_chess": 50
|
||
}
|
||
```
|
||
|
||
**Python模型** (server.py):
|
||
```python
|
||
class User(db.Model):
|
||
__tablename__ = 'users'
|
||
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
username = db.Column(db.String(80), unique=True, nullable=False)
|
||
nickname = db.Column(db.String(80), nullable=True)
|
||
password_hash = db.Column(db.String(200), nullable=False)
|
||
role = db.Column(db.String(20), default='user')
|
||
game_scores = db.Column(db.Text, default='{}')
|
||
created_at = db.Column(db.DateTime, default=db.func.current_timestamp())
|
||
|
||
def get_game_score(self, game_id):
|
||
scores = json.loads(self.game_scores or '{}')
|
||
return scores.get(game_id, 0)
|
||
|
||
def add_game_score(self, game_id, points):
|
||
scores = json.loads(self.game_scores or '{}')
|
||
scores[game_id] = scores.get(game_id, 0) + points
|
||
self.game_scores = json.dumps(scores)
|
||
return scores[game_id]
|
||
```
|
||
|
||
**默认用户:**
|
||
| 用户名 | 密码 | 昵称 | 角色 |
|
||
|--------|------|------|------|
|
||
| admin | abcd1234 | 管理员 | admin |
|
||
| caixianming | cxm123456 | 蔡叔叔 | user |
|
||
|
||
---
|
||
|
||
### 3.2 内存数据 vs 持久化数据
|
||
|
||
**内存中维护(运行时):**
|
||
| 数据 | 存储位置 | 说明 |
|
||
|------|----------|------|
|
||
| 房间列表 | RoomManager._rooms | Dict[room_id, Room] |
|
||
| 玩家映射 | socket_player_map | Dict[socket_id, player_id] |
|
||
| 用户映射 | user_socket_map | Dict[user_id, socket_id] |
|
||
| 活跃用户 | active_usernames | Dict[username, player_id] |
|
||
| 用户房间 | user_room_map | Dict[username, room_id] |
|
||
|
||
**持久化存储(SQLite):**
|
||
| 数据 | 表 | 说明 |
|
||
|------|-----|------|
|
||
| 用户信息 | users | 账号、密码、积分 |
|
||
|
||
**注意:** 房间和玩家数据仅在内存中维护,游戏结束后房间被清理。如需持久化游戏记录,需要额外设计GameRecord表。
|
||
|
||
|
||
---
|
||
|
||
## 4. 积分系统设计
|
||
|
||
### 4.1 积分规则(斗地主)
|
||
|
||
| 角色 | 胜利 | 失败 |
|
||
|------|------|------|
|
||
| 地主 | +6分 | -4分 |
|
||
| 农民 | +3分 | -2分 |
|
||
|
||
**AI玩家不计积分**,仅人类玩家累计积分。
|
||
|
||
### 4.2 积分更新流程
|
||
|
||
```python
|
||
def calculate_and_update_scores(room, winner_id):
|
||
score_changes = {}
|
||
|
||
for pid, player in game.players.items():
|
||
if player.is_ai:
|
||
score_changes[pid] = {'change': 0, 'is_ai': True}
|
||
continue
|
||
|
||
is_lord = (pid == game.lord_id)
|
||
is_winner = (pid == winner_id)
|
||
|
||
# 计算积分变动
|
||
if is_lord:
|
||
change = 6 if is_winner else -4
|
||
else:
|
||
change = 3 if is_winner else -2
|
||
|
||
# 更新数据库
|
||
db_user = User.query.filter_by(username=player.name).first()
|
||
if db_user:
|
||
new_total = db_user.add_game_score('doudizhu', change)
|
||
db.session.commit()
|
||
score_changes[pid] = {
|
||
'change': change,
|
||
'total': new_total,
|
||
'is_ai': False
|
||
}
|
||
|
||
return score_changes
|
||
```
|
||
|
||
### 4.3 查询积分示例
|
||
|
||
```python
|
||
# 获取用户斗地主积分
|
||
user = User.query.filter_by(username='蔡叔叔').first()
|
||
score = user.get_game_score('doudizhu') # 返回: 100
|
||
|
||
# 获取所有游戏积分
|
||
scores = json.loads(user.game_scores) # 返回: {'doudizhu': 100, ...}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 预留表设计(未来扩展)
|
||
|
||
### 5.1 game_records - 游戏记录表(预留)
|
||
|
||
用于保存游戏历史记录和回放数据。
|
||
|
||
```sql
|
||
CREATE TABLE game_records (
|
||
id VARCHAR(32) PRIMARY KEY,
|
||
room_id VARCHAR(32) NOT NULL,
|
||
game_type VARCHAR(20) NOT NULL,
|
||
|
||
started_at TIMESTAMP NOT NULL,
|
||
ended_at TIMESTAMP,
|
||
duration_seconds INTEGER,
|
||
|
||
-- 玩家信息 (JSON数组)
|
||
players JSON NOT NULL,
|
||
/* 格式:
|
||
[
|
||
{"player_id": "xxx", "name": "玩家A", "role": "landlord",
|
||
"is_ai": false, "score_change": 6},
|
||
{"player_id": "ai_1", "name": "AI-规则", "role": "farmer",
|
||
"is_ai": true, "ai_type": "rule", "score_change": -3}
|
||
]
|
||
*/
|
||
|
||
-- 结果
|
||
winner_id VARCHAR(32),
|
||
result_type VARCHAR(20), -- normal/surrender/timeout
|
||
|
||
-- 回放数据 (压缩存储)
|
||
replay_data BLOB, -- JSON序列化后 gzip 压缩
|
||
|
||
-- 统计
|
||
total_turns INTEGER,
|
||
max_combo INTEGER,
|
||
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
```
|
||
|
||
### 5.2 ai_usage - AI调用记录表(预留)
|
||
|
||
用于监控AI调用成本和性能。
|
||
|
||
```sql
|
||
CREATE TABLE ai_usage (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
ai_type VARCHAR(20) NOT NULL, -- deepseek/rule/minimax
|
||
model_name VARCHAR(50), -- deepseek-chat
|
||
|
||
room_id VARCHAR(32),
|
||
game_type VARCHAR(20),
|
||
|
||
-- Token用量
|
||
input_tokens INTEGER,
|
||
output_tokens INTEGER,
|
||
total_tokens INTEGER,
|
||
|
||
-- 性能
|
||
latency_ms INTEGER,
|
||
|
||
-- 成本估算 (美元)
|
||
estimated_cost DECIMAL(10,6),
|
||
|
||
-- 调试信息
|
||
request_prompt TEXT,
|
||
response_text TEXT,
|
||
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
```
|
||
|
||
### 5.3 rooms - 房间历史表(预留)
|
||
|
||
如需持久化房间信息,可创建此表。
|
||
|
||
```sql
|
||
CREATE TABLE rooms (
|
||
id VARCHAR(32) PRIMARY KEY,
|
||
room_code VARCHAR(10) UNIQUE,
|
||
game_type VARCHAR(20),
|
||
|
||
status VARCHAR(20), -- waiting/playing/ended/closed
|
||
created_at TIMESTAMP,
|
||
started_at TIMESTAMP,
|
||
ended_at TIMESTAMP,
|
||
|
||
max_players INTEGER,
|
||
config JSON,
|
||
|
||
winner_id VARCHAR(32),
|
||
final_state JSON,
|
||
|
||
is_active BOOLEAN DEFAULT TRUE
|
||
);
|
||
```
|
||
|
||
|
||
---
|
||
|
||
## 6. 数据库操作示例
|
||
|
||
### 6.1 初始化数据库
|
||
|
||
```python
|
||
# server.py
|
||
with app.app_context():
|
||
db.create_all() # 创建所有表
|
||
init_default_users() # 创建默认用户
|
||
```
|
||
|
||
### 6.2 常用查询
|
||
|
||
```python
|
||
# 用户认证
|
||
user = User.query.filter_by(username='caixianming').first()
|
||
if user and user.check_password('password'):
|
||
login_user(user)
|
||
|
||
# 获取排行榜
|
||
top_players = User.query.filter(
|
||
User.game_scores != '{}'
|
||
).order_by(
|
||
User.id # 简化排序,实际需解析JSON
|
||
).limit(10).all()
|
||
|
||
# 更新积分
|
||
user = User.query.filter_by(username='蔡叔叔').first()
|
||
new_score = user.add_game_score('doudizhu', 6)
|
||
db.session.commit()
|
||
```
|
||
|
||
### 6.3 数据库备份
|
||
|
||
```bash
|
||
# 备份SQLite数据库
|
||
cp /home/ubuntu/nanocode/net-games/net_games_auth.db \
|
||
/home/ubuntu/nanocode/net-games/backups/net_games_auth_$(date +%Y%m%d).db
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 索引设计
|
||
|
||
```sql
|
||
-- 用户查询优化
|
||
CREATE INDEX idx_users_username ON users(username);
|
||
CREATE INDEX idx_users_role ON users(role);
|
||
|
||
-- 预留:游戏记录查询
|
||
-- CREATE INDEX idx_records_game_type ON game_records(game_type);
|
||
-- CREATE INDEX idx_records_created ON game_records(created_at DESC);
|
||
-- CREATE INDEX idx_records_winner ON game_records(winner_id);
|
||
|
||
-- 预留:AI用量查询
|
||
-- CREATE INDEX idx_ai_usage_time ON ai_usage(created_at);
|
||
-- CREATE INDEX idx_ai_usage_type ON ai_usage(ai_type);
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 数据保留策略
|
||
|
||
| 数据类型 | 当前策略 | 未来规划 |
|
||
|----------|----------|----------|
|
||
| 用户数据 | 永久保留 | 永久保留 |
|
||
| 积分记录 | 实时计算,无历史 | 可考虑添加积分变动日志 |
|
||
| 房间数据 | 内存存储,游戏结束即清理 | 如需持久化,添加rooms表 |
|
||
| 游戏记录 | 不保存 | 如需回放,添加game_records表 |
|
||
| AI调用记录 | 不保存 | 如需成本控制,添加ai_usage表 |
|
||
|
||
---
|
||
|
||
## 9. 更新日志
|
||
|
||
| 日期 | 版本 | 变更内容 |
|
||
|------|------|----------|
|
||
| 2026-04-09 | v2.0 | 更新为实际实现,当前仅使用users表,其他为预留设计 |
|
||
| 2026-04-08 | v1.0 | 初始设计,包含完整ER图和多表设计 |
|
||
|
||
---
|
||
|
||
## 10. 总结
|
||
|
||
**当前实现 (v2.0):**
|
||
- 极简设计,仅使用 `users` 表存储用户信息和积分
|
||
- 房间和玩家数据全部在内存中管理
|
||
- 适合当前小规模部署
|
||
|
||
**未来扩展方向:**
|
||
1. 添加 `game_records` 表保存游戏历史
|
||
2. 添加 `ai_usage` 表监控AI成本
|
||
3. 添加 `rooms` 表持久化房间信息
|
||
4. 如需更复杂功能,迁移到PostgreSQL
|
||
|
||
---
|
||
|
||
*文档版本: v2.0*
|
||
*最后更新: 2026-04-09*
|
||
*数据库文件: net_games_auth.db*
|