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

9.3 KiB
Raw Permalink Blame History

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 - 用户表

存储注册用户信息和游戏积分。

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格式:

{
  "doudizhu": 100,
  "chinese_chess": 50
}

Python模型 (server.py):

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 积分更新流程

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 查询积分示例

# 获取用户斗地主积分
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 - 游戏记录表(预留)

用于保存游戏历史记录和回放数据。

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调用成本和性能。

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 - 房间历史表(预留)

如需持久化房间信息,可创建此表。

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 初始化数据库

# server.py
with app.app_context():
    db.create_all()  # 创建所有表
    init_default_users()  # 创建默认用户

6.2 常用查询

# 用户认证
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 数据库备份

# 备份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. 索引设计

-- 用户查询优化
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