Files
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

374 lines
9.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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*