fix(race): 代码质量审查修复 + commands包拆分 + 赛马取消命令
- P1: bet.py赔率计算移入锁内防竞态 - P1: config.py TESTERS解析失败添加warning日志 - P2: 新增赛马取消命令(积分退还/任务取消/状态重置) - P3: bet.py清理未使用的_send_to_scope导入 - 将commands.py拆分为commands/包(access/bet/help/race/register) - OpenSpec变更提案: fix-race-conditions-and-logs
This commit is contained in:
187
danding_bot/plugins/group_horse_racing/commands/bet.py
Normal file
187
danding_bot/plugins/group_horse_racing/commands/bet.py
Normal file
@@ -0,0 +1,187 @@
|
||||
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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user