第二词提交哦啊
This commit is contained in:
parent
30fcbf46ca
commit
5259325b94
@ -21,9 +21,9 @@ class ServerSettings(BaseSettings):
|
||||
|
||||
class DatabaseSettings(BaseSettings):
|
||||
"""数据库配置"""
|
||||
# MySQL连接格式: mysql+pymysql://username:password@host:port/database_name
|
||||
DATABASE_URL: str = "mysql+pymysql://root:taiyi1224@localhost:3306/baoxiang"
|
||||
|
||||
# MySQL连接格式: mysql+pymysql://username:password@host:port/database_name?charset=utf8mb4
|
||||
DATABASE_URL: str = "mysql+pymysql://root:taiyi1224@localhost:3306/baoxiang?charset=utf8mb4"
|
||||
|
||||
# SQLite开发配置 (可选)
|
||||
# SQLite连接格式: sqlite:///./database.db
|
||||
# DATABASE_URL: str = "sqlite:///./treasure_box_game.db"
|
||||
|
||||
@ -29,6 +29,8 @@ else:
|
||||
pool_recycle=db_settings.DB_POOL_RECYCLE,
|
||||
echo=False,
|
||||
future=True,
|
||||
# 设置连接时使用的字符集
|
||||
connect_args={"charset": "utf8mb4", "collation": "utf8mb4_unicode_ci"}
|
||||
)
|
||||
|
||||
# 如果是MySQL,启用严格模式
|
||||
@ -37,9 +39,14 @@ if "mysql" in db_settings.DATABASE_URL:
|
||||
def set_sqlite_pragma(dbapi_connection, connection_record):
|
||||
"""
|
||||
为MySQL连接设置参数(如果使用MySQL)
|
||||
注意:不再设置时区,使用服务器本地时间以保持一致性
|
||||
"""
|
||||
pass
|
||||
# 设置字符集为utf8mb4
|
||||
try:
|
||||
cursor = dbapi_connection.cursor()
|
||||
cursor.execute("SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci")
|
||||
cursor.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# 创建会话
|
||||
|
||||
@ -374,20 +374,55 @@ class GameService:
|
||||
)
|
||||
|
||||
# 平台抽水 - 直接到管理员账户
|
||||
admin_user = UserService.get_admin_user(db)
|
||||
if admin_user and platform_income > 0:
|
||||
admin_user.balance += platform_income
|
||||
db.commit()
|
||||
try:
|
||||
admin_user = UserService.get_admin_user(db)
|
||||
if admin_user and platform_income > 0:
|
||||
# 记录转账前的余额
|
||||
old_balance = admin_user.balance
|
||||
admin_user.balance += platform_income
|
||||
db.commit()
|
||||
|
||||
UserService.create_transaction(
|
||||
db=db,
|
||||
user_id=admin_user.id,
|
||||
transaction_type="平台抽水",
|
||||
amount=platform_income,
|
||||
balance_after=admin_user.balance,
|
||||
related_id=chest.id,
|
||||
description=f"宝箱《{chest.title}》平台抽水收益"
|
||||
)
|
||||
try:
|
||||
UserService.create_transaction(
|
||||
db=db,
|
||||
user_id=admin_user.id,
|
||||
transaction_type="平台抽水",
|
||||
amount=platform_income,
|
||||
balance_after=admin_user.balance,
|
||||
related_id=chest.id,
|
||||
description=f"宝箱《{chest.title}》平台抽水收益"
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"创建平台抽水交易记录失败: {str(e)}")
|
||||
# 即使交易记录创建失败,也不应回滚余额变更
|
||||
# 可以在这里添加告警通知或其他补偿措施
|
||||
|
||||
# 通知管理员余额更新
|
||||
import asyncio
|
||||
try:
|
||||
# 尝试获取当前事件循环
|
||||
loop = asyncio.get_running_loop()
|
||||
# 局部导入避免循环导入
|
||||
from ..routers.websocket import notify_user_balance_update
|
||||
loop.create_task(notify_user_balance_update(admin_user.id, admin_user.balance))
|
||||
except RuntimeError:
|
||||
# 如果没有运行中的事件循环,则在新线程中处理
|
||||
import threading
|
||||
def run_async():
|
||||
async def _run():
|
||||
# 局部导入避免循环导入
|
||||
from ..routers.websocket import notify_user_balance_update
|
||||
await notify_user_balance_update(admin_user.id, admin_user.balance)
|
||||
asyncio.run(_run())
|
||||
thread = threading.Thread(target=run_async, daemon=True)
|
||||
thread.start()
|
||||
|
||||
print(f"平台抽水转账成功: {platform_income} 分至管理员账户 {admin_user.id}")
|
||||
except Exception as e:
|
||||
# 如果转账失败,记录错误并回滚
|
||||
db.rollback()
|
||||
print(f"平台抽水转账失败: {str(e)}")
|
||||
# 可以在这里添加更详细的错误处理逻辑,比如发送告警通知
|
||||
|
||||
# 计算赔率
|
||||
winner_pool = chest.pool_a if winner == "A" else chest.pool_b
|
||||
|
||||
@ -103,18 +103,32 @@ class StreamerService:
|
||||
|
||||
active_chests = db.query(func.count(Chest.id)).filter(
|
||||
Chest.streamer_id == streamer.user_id,
|
||||
Chest.status == "下注中"
|
||||
Chest.status == 0 # BETTING状态
|
||||
).scalar() or 0
|
||||
|
||||
# 计算总获奖(需要根据实际业务逻辑)
|
||||
total_winnings = 0
|
||||
# 查询已结算的宝箱以计算总奖金
|
||||
settled_chests = db.query(Chest).filter(
|
||||
Chest.streamer_id == streamer.user_id,
|
||||
Chest.status == 3 # FINISHED状态
|
||||
).all()
|
||||
|
||||
# 计算总获奖金额(所有已结算宝箱的奖金池总和)
|
||||
total_winnings = sum(int(chest.pool_a + chest.pool_b) for chest in settled_chests)
|
||||
|
||||
# 计算抽成收益
|
||||
total_commission = int(total_winnings * float(streamer.commission_rate) / 100)
|
||||
|
||||
# 计算平均宝箱价值
|
||||
average_chest_value = int(total_winnings / total_chests) if total_chests > 0 else 0
|
||||
|
||||
# 计算成功率(这里简化处理)
|
||||
success_rate = 0.75 # 示例值
|
||||
# 计算成功率(已结算宝箱中A选项获胜的比例)
|
||||
settled_count = len(settled_chests)
|
||||
if settled_count > 0:
|
||||
a_win_count = sum(1 for chest in settled_chests if chest.result == "A")
|
||||
success_rate = a_win_count / settled_count
|
||||
else:
|
||||
# 如果没有已结算的宝箱,则使用默认值
|
||||
success_rate = 0.0
|
||||
|
||||
return StreamerStatistics(
|
||||
total_chests=total_chests,
|
||||
@ -189,7 +203,7 @@ class StreamerService:
|
||||
|
||||
active_chests = db.query(func.count(Chest.id)).filter(
|
||||
Chest.streamer_id == streamer.user_id,
|
||||
Chest.status == "下注中"
|
||||
Chest.status == 0 # BETTING状态
|
||||
).scalar() or 0
|
||||
|
||||
max_chests = streamer.max_active_chests
|
||||
|
||||
@ -5,7 +5,7 @@ import Loading from '../components/Loading';
|
||||
import type { Transaction } from '../types';
|
||||
|
||||
const TransactionHistoryPage = () => {
|
||||
const { user } = useAuth();
|
||||
const { user: _user } = useAuth(); // 用户信息已在AuthContext中
|
||||
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@ -29,30 +29,28 @@ const TransactionHistoryPage = () => {
|
||||
};
|
||||
|
||||
const getTransactionTypeText = (type: string) => {
|
||||
switch (type) {
|
||||
case 'BET':
|
||||
return '下注';
|
||||
case 'WIN':
|
||||
return '获胜奖励';
|
||||
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;
|
||||
}
|
||||
// 统一交易类型映射,确保前后端一致性
|
||||
const typeMap: Record<string, string> = {
|
||||
'BET': '下注',
|
||||
'WIN': '获胜奖励',
|
||||
'下注': '下注',
|
||||
'获胜利': '获胜利',
|
||||
'WINNING': '获胜利',
|
||||
'低保': '低保',
|
||||
'ALLOWANCE': '低保',
|
||||
'余额清零奖励': '余额清零奖励',
|
||||
'ADMIN_ADJUST': '管理员调整',
|
||||
'管理员调整': '管理员调整',
|
||||
'ADMIN_OPERATION': '管理员调整',
|
||||
'STREAMER_REVENUE': '主播分润',
|
||||
'PLATFORM_REVENUE': '平台抽水',
|
||||
'REGISTER': '新用户注册奖励',
|
||||
'注册奖励': '新用户注册奖励',
|
||||
'REGISTER_REWARD': '新用户注册奖励',
|
||||
'NEW_USER_REWARD': '新用户注册奖励'
|
||||
};
|
||||
|
||||
return typeMap[type] || type;
|
||||
};
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
@ -91,14 +89,7 @@ const TransactionHistoryPage = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{loading ? (
|
||||
<div className="text-center text-secondary" style={{ padding: '40px' }}>
|
||||
<div className="spinner-border" role="status">
|
||||
<span className="sr-only">加载中...</span>
|
||||
</div>
|
||||
<p style={{ marginTop: '10px' }}>正在加载交易记录...</p>
|
||||
</div>
|
||||
) : transactions.length === 0 ? (
|
||||
{transactions.length === 0 ? (
|
||||
<div className="empty-state">
|
||||
<div className="empty-state-icon">📊</div>
|
||||
<h3>暂无交易记录</h3>
|
||||
|
||||
@ -55,8 +55,8 @@ const UserProfile = () => {
|
||||
setAllowanceInfo(allowanceData);
|
||||
|
||||
// 计算用户统计数据
|
||||
const bets = txData.filter(tx => tx.type === 'BET');
|
||||
const wins = txData.filter(tx => tx.type === 'WIN');
|
||||
const bets = txData.filter(tx => tx.type === 'BET' || tx.type === '下注');
|
||||
const wins = txData.filter(tx => tx.type === 'WIN' || tx.type === '获胜利' || tx.type === 'WINNING');
|
||||
const totalBets = bets.length;
|
||||
const totalWinnings = wins.reduce((sum, tx) => sum + tx.amount, 0);
|
||||
const winRate = totalBets > 0 ? (wins.length / totalBets) * 100 : 0;
|
||||
@ -92,30 +92,28 @@ const UserProfile = () => {
|
||||
};
|
||||
|
||||
const getTransactionTypeText = (type: string) => {
|
||||
switch (type) {
|
||||
case 'BET':
|
||||
return '下注';
|
||||
case 'WIN':
|
||||
return '获胜奖励';
|
||||
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;
|
||||
}
|
||||
// 统一交易类型映射,确保前后端一致性
|
||||
const typeMap: Record<string, string> = {
|
||||
'BET': '下注',
|
||||
'WIN': '获胜奖励',
|
||||
'下注': '下注',
|
||||
'获胜利': '获胜利',
|
||||
'WINNING': '获胜利',
|
||||
'低保': '低保',
|
||||
'ALLOWANCE': '低保',
|
||||
'余额清零奖励': '余额清零奖励',
|
||||
'ADMIN_ADJUST': '管理员调整',
|
||||
'管理员调整': '管理员调整',
|
||||
'ADMIN_OPERATION': '管理员调整',
|
||||
'STREAMER_REVENUE': '主播分润',
|
||||
'PLATFORM_REVENUE': '平台抽水',
|
||||
'REGISTER': '新用户注册奖励',
|
||||
'注册奖励': '新用户注册奖励',
|
||||
'REGISTER_REWARD': '新用户注册奖励',
|
||||
'NEW_USER_REWARD': '新用户注册奖励'
|
||||
};
|
||||
|
||||
return typeMap[type] || type;
|
||||
};
|
||||
|
||||
const getRoleText = (role: string) => {
|
||||
@ -327,14 +325,7 @@ const UserProfile = () => {
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{loading ? (
|
||||
<div className="text-center text-secondary" style={{ padding: '20px' }}>
|
||||
<div className="spinner-border" role="status">
|
||||
<span className="sr-only">加载中...</span>
|
||||
</div>
|
||||
<p style={{ marginTop: '10px' }}>正在加载交易记录...</p>
|
||||
</div>
|
||||
) : transactions.length === 0 ? (
|
||||
{transactions.length === 0 ? (
|
||||
<div className="empty-state">
|
||||
<div className="empty-state-icon">📊</div>
|
||||
<h4>暂无交易记录</h4>
|
||||
|
||||
@ -172,9 +172,9 @@ export interface StreamerProfile {
|
||||
// 主播统计数据
|
||||
export interface StreamerStatistics {
|
||||
total_chests: number;
|
||||
total_bets: number;
|
||||
total_pool: number;
|
||||
total_earnings: number;
|
||||
avg_participants: number;
|
||||
active_chests: number;
|
||||
total_winnings: number;
|
||||
total_commission: number;
|
||||
average_chest_value: number;
|
||||
success_rate: number;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user