## Technical Design ### 1. SQLite → aiosqlite \\\python # room_store.py import aiosqlite class RoomStore: async def _get_conn(self) -> aiosqlite.Connection: db_path = Path(self.config.DB_PATH) db_path.parent.mkdir(parents=True, exist_ok=True) conn = await aiosqlite.connect(str(db_path)) conn.row_factory = aiosqlite.Row await conn.execute('''CREATE TABLE IF NOT EXISTS race_history (...)''') await conn.commit() return conn async def get_last_horse_name(self, user_id: str) -> Optional[str]: conn = await self._get_conn() try: cursor = await conn.execute("SELECT horse_name FROM ...", (user_id,)) row = await cursor.fetchone() return row[0] if row else None finally: await conn.close() \\\ > 每次操作独立连接和关闭保持与原逻辑一致,仅将同步调用替换为 async。 ### 2. 积分重试加延时 + 区分失败原因 \\\python # points_service.py async def spend_bet_points(self, user_id: str, amount: int, reason: str = "赛马下注"): success, balance = await points_api.spend_points(user_id, amount, "horse_race", reason) if success: return True, balance # 余额不足不再重试 if balance < amount: return False, balance # 网络/其他失败,短暂等待后重试一次 await asyncio.sleep(1.0) success, balance = await points_api.spend_points(user_id, amount, "horse_race", reason) return success, balance \\\ ### 3. 消息发送加日志 \\\python # commands.py import logging logger = logging("group_horse_racing") async def _send_to_scope(bot: Bot, scope: str, message: ...): try: if scope.startswith("group_"): await bot.send_group_msg(...) elif scope.startswith("test_"): await bot.send_private_msg(...) except Exception: logger.warning(f"发送消息到 {scope} 失败", exc_info=True) \\\ ### 4. 赔率快照(结算时锁内固定) 在比赛结束结算时,将赔率在 Room Lock 内计算后传入结算函数,保证赔率与下注分布一致: \\\python # 在 start_race 的 lock 内计算赔率快照 odds_snapshot = {} for horse_name, horse in room.horses.items(): odds_snapshot[horse_name] = room.calculate_odds(horse_name) # 锁内计算 # 传入结算函数使用快照赔率 await _settle_race(bot, event, room, config, odds_snapshot) \\\ ### 5. 补存积分变化 \\\python # room_store.py - save_race_result await conn.execute( "INSERT INTO race_history (...point_changes, point_change_summaries) VALUES (..., ?, ?)", (..., json.dumps(result.point_changes), json.dumps(result.point_change_summaries)) ) \\\ 需在 CREATE TABLE 中新增两个 TEXT 字段,使用 \IF NOT EXISTS\ 保证兼容。 ### 6. 测试数据隔离 \\\python # test_commands.py from .commands import get_scope, check_access, race_engine, _rooms as prod_rooms from .room_store import RoomStore as _RealRoomStore # 测试命令用独立 store _test_store = _RealRoomStore(config) # 或用内存 store \\\ > 不再共享生产 room_store 实例,测试操作独立数据。 ### 7. 马名去重统一 将 dict key 统一用 normalized name(casefold),或在注册时统一用原始名但比较用 casefold。