第一次提交哦啊
This commit is contained in:
parent
6793719648
commit
17093cdc98
@ -11,10 +11,11 @@ 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:
|
||||
"""定时任务服务"""
|
||||
|
||||
|
||||
@staticmethod
|
||||
async def start_scheduler():
|
||||
"""
|
||||
@ -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():
|
||||
@ -183,4 +186,76 @@ class SchedulerService:
|
||||
print(f"锁定过期宝箱时出错: {e}")
|
||||
return 0
|
||||
finally:
|
||||
db.close()
|
||||
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
|
||||
|
||||
@ -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,
|
||||
},
|
||||
]
|
||||
|
||||
# 游戏逻辑配置
|
||||
|
||||
@ -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
|
||||
@ -262,16 +271,19 @@ class UserService:
|
||||
"""
|
||||
today = datetime.now().date()
|
||||
allowance_key = f"daily_allowance:{user.id}:{today.isoformat()}"
|
||||
|
||||
|
||||
# 检查今日是否已领取
|
||||
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()
|
||||
|
||||
|
||||
# 通知用户余额更新
|
||||
import asyncio
|
||||
try:
|
||||
@ -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="每日低保"
|
||||
)
|
||||
@ -324,7 +336,10 @@ class UserService:
|
||||
from datetime import datetime, timedelta
|
||||
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"
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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>
|
||||
)}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user