from nonebot import on_command from nonebot.adapters.onebot.v11 import Bot, Event from . import ( room_store, points_service, config, logger, get_scope, check_access, get_event_id, _resolve_horse_selector, _format_horse_label, calculate_odds, ) @cancel_cmd.handle() async def handle_cancel(bot: Bot, event: Event): """Handle cancel registration.""" if not await check_access(bot, event): await cancel_cmd.finish("无权限访问此功能") return scope = get_scope(event) user_id = get_event_id(event) lock = room_store.get_lock(scope) async with lock: room = room_store.get_room(scope) if not room: await cancel_cmd.finish("房间不存在") return if room.state != RoomState.WAITING: await cancel_cmd.finish("比赛正在进行中,无法取消报名") return user_horse = _find_user_horse(room, user_id) if not user_horse: await cancel_cmd.finish("你还没有报名") return bets_to_refund = [b for b in room.bets if b.horse_name == user_horse.name] for bet in bets_to_refund: await points_service.refund_bet_points(bet.user_id, bet.amount, "取消报名退还下注") room.bets = [b for b in room.bets if b.horse_name != user_horse.name] del room.horses[user_horse.name] await cancel_cmd.finish(f"已取消报名,{_format_horse_label(user_horse)} 已退出") bet_cmd = on_command("赛马下注", priority=5) cancel_bet_cmd = on_command("赛马取消下注", priority=5) @cancel_bet_cmd.handle() async def handle_cancel_bet(bot: Bot, event: Event): """Handle cancel bet - refund all bets placed by the user in current room.""" if not await check_access(bot, event): await cancel_bet_cmd.finish("无权限访问此功能") return scope = get_scope(event) user_id = get_event_id(event) lock = room_store.get_lock(scope) async with lock: room = room_store.get_room(scope) if not room: await cancel_bet_cmd.finish("房间不存在") return if room.state != RoomState.WAITING: await cancel_bet_cmd.finish("比赛已开始,无法取消下注") return user_bets = [b for b in room.bets if b.user_id == user_id] if not user_bets: await cancel_bet_cmd.finish("你还没有下注") return total_refund = 0 refund_errors = [] for bet in user_bets: try: await points_service.refund_bet_points(bet.user_id, bet.amount, "取消下注退还") total_refund += bet.amount except Exception as e: logger.error(f"退还下注失败 user={bet.user_id} amount={bet.amount}: {e}") refund_errors.append(bet) # 只移除已成功退还的下注 if refund_errors: failed_amount = sum(b.amount for b in refund_errors) room.bets = [b for b in room.bets if b.user_id != user_id or b in refund_errors] await cancel_bet_cmd.finish(f"退还部分失败:成功退还 {total_refund} 积分,{len(refund_errors)} 笔退还失败({failed_amount} 积分),请联系管理员") return else: room.bets = [b for b in room.bets if b.user_id != user_id] await cancel_bet_cmd.finish(f"已取消 {len(user_bets)} 笔下注,退还 {total_refund} 积分") @bet_cmd.handle() async def handle_bet(bot: Bot, event: Event): """Handle bet placement.""" if not await check_access(bot, event): await bet_cmd.finish("无权限访问此功能") return msg = str(event.get_message()).strip() parts = msg.split() if len(parts) < 3: await bet_cmd.finish("请使用:/赛马下注 <序号|马匹名> <金额>") return horse_selector = parts[1] try: amount = int(parts[2]) except ValueError: await bet_cmd.finish("金额必须是正整数") return if amount < config.MIN_BET: await bet_cmd.finish(f"最低下注金额为 {config.MIN_BET}") return scope = get_scope(event) user_id = get_event_id(event) lock = room_store.get_lock(scope) async with lock: room = room_store.get_room(scope) if not room: await bet_cmd.finish("房间不存在,请先报名") return if room.state != RoomState.WAITING: await bet_cmd.finish("比赛正在进行中,无法下注") return horse = _resolve_horse_selector(room, horse_selector) if not horse: await bet_cmd.finish(f"马匹序号/名称 \"{horse_selector}\" 不存在") return success, balance = await points_service.spend_bet_points(user_id, amount, f"下注 {_format_horse_label(horse)}") if not success: await bet_cmd.finish(f"积分不足(当前余额:{balance})") return room.bets.append(Bet(user_id=user_id, horse_name=horse.name, amount=amount)) odds = calculate_odds(room) await bet_cmd.finish(f"下注成功!{_format_horse_label(horse)} {amount}积分(当前赔率:{odds.get(horse.name, config.MIN_ODDS):.2f})") odds_cmd = on_command("赛马赔率", priority=5) @odds_cmd.handle() async def handle_odds(bot: Bot, event: Event): """Handle odds display.""" if not await check_access(bot, event): await odds_cmd.finish("无权限访问此功能") return scope = get_scope(event) room = room_store.get_room(scope) if not room: await odds_cmd.finish("房间不存在,请先报名") return if not room.horses: await odds_cmd.finish("还没有马匹报名") return odds = calculate_odds(room) lines = ["当前赔率:"] total_bet = sum(b.amount for b in room.bets) for horse in _get_horses_in_order(room): odd = odds.get(horse.name, config.MIN_ODDS) horse_bet = sum(b.amount for b in room.bets if b.horse_name == horse.name) lines.append(f" {_format_horse_label(horse)} - {odd:.2f}倍 (总下注: {horse_bet})") lines.append(f"总下注池: {total_bet}") await odds_cmd.finish("\n".join(lines)) race_list_cmd = on_command("赛马列表", priority=5)