第一次提交哦啊

This commit is contained in:
taiyi 2025-12-16 19:03:48 +08:00
parent 6793719648
commit 17093cdc98
6 changed files with 194 additions and 16 deletions

View File

@ -11,6 +11,7 @@ from ..models.game import Chest, ChestStatus
from ..services.user_service import UserService
from ..services.game_service import GameService
from ..core.config import settings
from ..services.balance_monitor_service import BalanceMonitorService
class SchedulerService:
"""定时任务服务"""
@ -24,6 +25,8 @@ class SchedulerService:
asyncio.create_task(SchedulerService._run_daily_allowance_scheduler())
# 创建后台任务扫描过期宝箱
asyncio.create_task(SchedulerService._run_expired_chest_scanner())
# 创建后台任务扫描余额清零用户
asyncio.create_task(SchedulerService._run_zero_balance_scanner())
@staticmethod
async def _run_daily_allowance_scheduler():
@ -184,3 +187,75 @@ class SchedulerService:
return 0
finally:
db.close()
@staticmethod
async def _run_zero_balance_scanner():
"""
运行余额清零用户扫描器
每天零点执行扫描遗漏的余额清零用户并发放奖励
"""
while True:
try:
# 获取当前时间
now = datetime.now()
# 计算下次执行时间(每天零点)
next_run = now.replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(days=1)
# 如果是刚过零点00:00-00:10执行扫描
if now.hour == 0 and now.minute < 10:
print("执行余额清零用户扫描任务...")
SchedulerService._scan_zero_balance_users()
# 计算等待时间(到下一个整点)
sleep_seconds = (next_run - now).total_seconds()
# 等待到下次执行时间
await asyncio.sleep(sleep_seconds)
except Exception as e:
print(f"余额清零扫描任务执行出错: {e}")
# 出错后等待1小时再试
await asyncio.sleep(3600)
@staticmethod
def _scan_zero_balance_users():
"""
扫描余额清零用户并发放奖励
用于定时任务确保没有遗漏
"""
db = SessionLocal()
try:
# 使用BalanceMonitorService扫描
processed_count = BalanceMonitorService.scan_zero_balance_users(db)
print(f"余额清零扫描完成,处理了 {processed_count} 个用户")
except Exception as e:
print(f"扫描余额清零用户时出错: {e}")
finally:
db.close()
@staticmethod
def reset_daily_allowance_status():
"""
重置每日低保领取状态供手动调用
"""
try:
from ..utils.redis import redis_client
# 获取所有低保领取状态的Redis键
keys = redis_client.keys("daily_allowance:*")
if keys:
# 删除所有低保领取状态
deleted_count = redis_client.delete(*keys)
print(f"已清理 {deleted_count} 个低保领取状态标记")
return deleted_count
else:
print("无需清理(暂无低保领取状态标记)")
return 0
except Exception as e:
print(f"重置低保状态时出错: {e}")
return 0

View File

@ -302,6 +302,26 @@ class SystemService:
"is_public": False,
"display_order": 7,
},
{
"config_key": "BALANCE_ZERO_REWARD_AMOUNT",
"config_value": "10000",
"config_type": ConfigType.NUMBER,
"category": ConfigCategory.GAME_ECONOMY,
"description": "余额清零自动发放金额(分)",
"is_editable": True,
"is_public": False,
"display_order": 8,
},
{
"config_key": "ALLOWANCE_RESET_TIME",
"config_value": "00:00",
"config_type": ConfigType.STRING,
"category": ConfigCategory.GAME_ECONOMY,
"description": "低保每日刷新时间(HH:MM格式)",
"is_editable": True,
"is_public": False,
"display_order": 9,
},
]
# 游戏逻辑配置

View File

@ -13,6 +13,8 @@ from ..core.config import settings
game_settings = settings.game
from ..utils.redis import redis_client
from datetime import datetime
from ..services.system_service import SystemService
from ..models.system import ConfigType
class UserService:
@ -159,6 +161,9 @@ class UserService:
"""
调整用户余额使用乐观锁
"""
# 记录变更前的余额
old_balance = user.balance
# 如果指定了版本号,进行版本检查
if expected_version is not None and user.version != expected_version:
raise ValueError("用户数据已更新,请刷新后重试")
@ -200,6 +205,10 @@ class UserService:
db.add(user)
db.commit()
# 余额变更后,触发余额监控服务
from ..services.balance_monitor_service import BalanceMonitorService
BalanceMonitorService.on_balance_changed(db, user, old_balance, user.balance)
return True
@staticmethod
@ -267,8 +276,11 @@ class UserService:
if redis_client.exists(allowance_key):
return False
# 从数据库获取低保金额
daily_allowance = UserService.get_daily_allowance(db)
# 发放低保
user.balance += game_settings.DAILY_ALLOWANCE
user.balance += daily_allowance
user.version += 1
db.commit()
@ -297,7 +309,7 @@ class UserService:
db=db,
user_id=user.id,
transaction_type="低保",
amount=game_settings.DAILY_ALLOWANCE,
amount=daily_allowance,
balance_after=user.balance,
description="每日低保"
)
@ -325,6 +337,9 @@ class UserService:
today = datetime.now().date()
allowance_key = f"daily_allowance:{user.id}:{today.isoformat()}"
# 从数据库获取低保金额
daily_allowance = UserService.get_daily_allowance(db)
# 检查今日是否已领取
if redis_client.exists(allowance_key):
# 如果今天已领取,下次领取时间为明天
@ -333,12 +348,47 @@ class UserService:
return {
"can_claim": False,
"next_claim_time": next_claim_time.isoformat(),
"daily_allowance": game_settings.DAILY_ALLOWANCE
"daily_allowance": daily_allowance
}
else:
# 如果今天未领取,可以立即领取
return {
"can_claim": True,
"next_claim_time": datetime.now().isoformat(),
"daily_allowance": game_settings.DAILY_ALLOWANCE
"daily_allowance": daily_allowance
}
@staticmethod
def get_daily_allowance(db: Session) -> int:
"""
从数据库获取每日低保金额
"""
config = SystemService.get_config(db, "GAME_DAILY_ALLOWANCE")
if config:
typed_value = SystemService.get_typed_value(config)
return int(typed_value)
# 如果数据库中没有配置,返回默认值
return game_settings.DAILY_ALLOWANCE
@staticmethod
def get_balance_zero_reward(db: Session) -> int:
"""
从数据库获取余额清零自动发放金额
"""
config = SystemService.get_config(db, "BALANCE_ZERO_REWARD_AMOUNT")
if config:
typed_value = SystemService.get_typed_value(config)
return int(typed_value)
# 如果数据库中没有配置返回默认值10000分
return 10000
@staticmethod
def get_allowance_reset_time(db: Session) -> str:
"""
从数据库获取低保每日刷新时间
"""
config = SystemService.get_config(db, "ALLOWANCE_RESET_TIME")
if config:
return config.config_value
# 如果数据库中没有配置,返回默认值
return "00:00"

View File

@ -1658,6 +1658,15 @@ body {
font-weight: bold;
}
.allowance-amount {
font-size: 16px;
font-weight: 600;
margin: 12px 0;
padding: 8px;
background: rgba(255, 255, 255, 0.2);
border-radius: 8px;
}
.allowance-countdown {
margin: 16px 0;
}

View File

@ -34,16 +34,22 @@ const TransactionHistoryPage = () => {
return '下注';
case 'WIN':
return '获胜奖励';
case 'ALLOWANCE':
case '低保':
return '低保';
case '余额清零奖励':
return '余额清零奖励';
case 'ADMIN_ADJUST':
return '管理员调整';
case '管理员调整':
return '管理员调整';
case 'STREAMER_REVENUE':
return '主播分润';
case 'PLATFORM_REVENUE':
return '平台抽水';
case 'REGISTER':
return '新用户注册奖励';
case '注册奖励':
return '新用户注册奖励';
default:
return type;
}

View File

@ -92,16 +92,22 @@ const UserProfile = () => {
return '下注';
case 'WIN':
return '获胜奖励';
case 'ALLOWANCE':
case '低保':
return '低保';
case '余额清零奖励':
return '余额清零奖励';
case 'ADMIN_ADJUST':
return '管理员调整';
case '管理员调整':
return '管理员调整';
case 'STREAMER_REVENUE':
return '主播分润';
case 'PLATFORM_REVENUE':
return '平台抽水';
case 'REGISTER':
return '新用户注册奖励';
case '注册奖励':
return '新用户注册奖励';
default:
return type;
}
@ -225,13 +231,16 @@ const UserProfile = () => {
</div>
{/* 低保信息卡片 */}
{user && user.balance < 1000 && (
{allowanceInfo && (
<div className="allowance-card">
<div className="allowance-header">
<span className="allowance-icon">💰</span>
<span className="allowance-title"></span>
</div>
{allowanceInfo?.can_claim ? (
<div className="allowance-amount">
: {allowanceInfo.daily_allowance ? (allowanceInfo.daily_allowance / 100).toFixed(2) : '50.00'}
</div>
{allowanceInfo.can_claim ? (
<button
className="action-btn allowance-claim-btn"
onClick={handleClaimAllowance}
@ -242,11 +251,20 @@ const UserProfile = () => {
) : (
<div className="allowance-countdown">
<div className="countdown-label">:</div>
<div className="countdown-time">{allowanceInfo?.formatted_time_left}</div>
<div className="countdown-time">
{allowanceInfo.next_claim_time
? new Date(allowanceInfo.next_claim_time).toLocaleString('zh-CN', {
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
})
: '明日00:00'}
</div>
</div>
)}
<div className="allowance-note">
</div>
</div>
)}