feat: add modal shop and lottery management

This commit is contained in:
ngfchl
2026-07-07 13:22:33 +08:00
parent a6923924e7
commit 2ee9fc575c
11 changed files with 933 additions and 55 deletions
+31 -3
View File
@@ -3,7 +3,7 @@ import random
import logging
from datetime import datetime, timedelta
from db.models import GameScore, LotteryEvent, LotteryParticipant, ShopItem
from db.models import GameScore, LotteryEvent, LotteryParticipant, ShopItem, ShopTransaction
logger = logging.getLogger("spam_guard")
@@ -142,13 +142,32 @@ async def upsert_shop_item(chat_id: int, item_key: str, name: str, cost: int, de
return item
async def buy_shop_item(chat_id: int, user_id: int, item_id: str) -> dict:
async def delete_shop_item(chat_id: int, item_key: str) -> bool:
deleted = await ShopItem.filter(chat_id=chat_id, item_key=item_key).delete()
return bool(deleted)
async def list_shop_transactions(chat_id: int, limit: int = 100) -> list[dict]:
rows = await ShopTransaction.filter(chat_id=chat_id).order_by("-created_at").limit(limit).all()
return [{"id": r.id, "chat_id": r.chat_id, "user_id": r.user_id, "username": r.username, "item_key": r.item_key, "item_name": r.item_name, "cost": r.cost, "status": r.status, "note": r.note, "created_at": r.created_at} for r in rows]
async def buy_shop_item(chat_id: int, user_id: int, item_id: str, username: str = "") -> dict:
items = await list_shop_items(chat_id)
item = next((x for x in items if str(x.get("item_key")) == str(item_id) and x.get("enabled", True)), None)
if not item:
return {"ok": False, "reason": "商品不存在"}
if not await deduct_points(chat_id, user_id, int(item["cost"])):
return {"ok": False, "reason": "积分不足", "item": item}
await ShopTransaction.create(
chat_id=chat_id,
user_id=user_id,
username=username,
item_key=str(item.get("item_key")),
item_name=item.get("name") or "",
cost=int(item.get("cost") or 0),
status="success",
)
summary = await get_scores_summary(user_id, chat_id)
return {"ok": True, "item": item, "total": summary["total"]}
@@ -171,12 +190,16 @@ def parse_lottery_condition(raw: str) -> tuple[str, int, str]:
def parse_lottery_end(raw: str) -> tuple[str, int, str]:
text = (raw or "people:3").strip().lower()
if text in {"instant", "即开", "即开即中"}:
return "instant", 1, "即开即中:首位参与者立即中奖"
if text.startswith("people:") or text.startswith("人数:"):
value = max(1, int(text.split(":", 1)[1]))
return "people", value, f"{value} 人自动开奖"
if text.startswith("minutes:") or text.startswith("分钟:"):
value = max(1, int(text.split(":", 1)[1]))
return "minutes", value, f"{value} 分钟后自动开奖"
return "time", value, f"{value} 分钟后自动开奖"
if text.startswith("time:") or text.startswith("时间:"):
return "time_at", 0, f"指定时间开奖:{raw.split(':', 1)[1].strip()}"
if text in {"manual", "手动"}:
return "manual", 0, "手动开奖"
raise ValueError(LOTTERY_END_HELP)
@@ -207,9 +230,14 @@ async def create_lottery_event(chat_id: int, creator_id: int, creator_name: str,
chat_id=chat_id,
creator_id=creator_id,
creator_name=creator_name,
title=spec.get("title") or spec["prize"],
description=spec.get("description") or "",
prize=spec["prize"],
condition_type=spec["condition_type"],
condition_value=spec["condition_value"],
draw_type=spec.get("draw_type") or spec["end_type"],
draw_at=spec.get("draw_at"),
max_participants=spec.get("max_participants") or (spec["end_value"] if spec["end_type"] == "people" else 0),
end_type=spec["end_type"],
end_value=spec["end_value"],
)