第二词提交哦啊
This commit is contained in:
parent
30fcbf46ca
commit
5259325b94
@ -21,9 +21,9 @@ class ServerSettings(BaseSettings):
|
|||||||
|
|
||||||
class DatabaseSettings(BaseSettings):
|
class DatabaseSettings(BaseSettings):
|
||||||
"""数据库配置"""
|
"""数据库配置"""
|
||||||
# MySQL连接格式: mysql+pymysql://username:password@host:port/database_name
|
# MySQL连接格式: mysql+pymysql://username:password@host:port/database_name?charset=utf8mb4
|
||||||
DATABASE_URL: str = "mysql+pymysql://root:taiyi1224@localhost:3306/baoxiang"
|
DATABASE_URL: str = "mysql+pymysql://root:taiyi1224@localhost:3306/baoxiang?charset=utf8mb4"
|
||||||
|
|
||||||
# SQLite开发配置 (可选)
|
# SQLite开发配置 (可选)
|
||||||
# SQLite连接格式: sqlite:///./database.db
|
# SQLite连接格式: sqlite:///./database.db
|
||||||
# DATABASE_URL: str = "sqlite:///./treasure_box_game.db"
|
# DATABASE_URL: str = "sqlite:///./treasure_box_game.db"
|
||||||
|
|||||||
@ -29,6 +29,8 @@ else:
|
|||||||
pool_recycle=db_settings.DB_POOL_RECYCLE,
|
pool_recycle=db_settings.DB_POOL_RECYCLE,
|
||||||
echo=False,
|
echo=False,
|
||||||
future=True,
|
future=True,
|
||||||
|
# 设置连接时使用的字符集
|
||||||
|
connect_args={"charset": "utf8mb4", "collation": "utf8mb4_unicode_ci"}
|
||||||
)
|
)
|
||||||
|
|
||||||
# 如果是MySQL,启用严格模式
|
# 如果是MySQL,启用严格模式
|
||||||
@ -37,9 +39,14 @@ if "mysql" in db_settings.DATABASE_URL:
|
|||||||
def set_sqlite_pragma(dbapi_connection, connection_record):
|
def set_sqlite_pragma(dbapi_connection, connection_record):
|
||||||
"""
|
"""
|
||||||
为MySQL连接设置参数(如果使用MySQL)
|
为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)
|
try:
|
||||||
if admin_user and platform_income > 0:
|
admin_user = UserService.get_admin_user(db)
|
||||||
admin_user.balance += platform_income
|
if admin_user and platform_income > 0:
|
||||||
db.commit()
|
# 记录转账前的余额
|
||||||
|
old_balance = admin_user.balance
|
||||||
|
admin_user.balance += platform_income
|
||||||
|
db.commit()
|
||||||
|
|
||||||
UserService.create_transaction(
|
try:
|
||||||
db=db,
|
UserService.create_transaction(
|
||||||
user_id=admin_user.id,
|
db=db,
|
||||||
transaction_type="平台抽水",
|
user_id=admin_user.id,
|
||||||
amount=platform_income,
|
transaction_type="平台抽水",
|
||||||
balance_after=admin_user.balance,
|
amount=platform_income,
|
||||||
related_id=chest.id,
|
balance_after=admin_user.balance,
|
||||||
description=f"宝箱《{chest.title}》平台抽水收益"
|
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
|
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(
|
active_chests = db.query(func.count(Chest.id)).filter(
|
||||||
Chest.streamer_id == streamer.user_id,
|
Chest.streamer_id == streamer.user_id,
|
||||||
Chest.status == "下注中"
|
Chest.status == 0 # BETTING状态
|
||||||
).scalar() or 0
|
).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)
|
total_commission = int(total_winnings * float(streamer.commission_rate) / 100)
|
||||||
|
|
||||||
# 计算平均宝箱价值
|
# 计算平均宝箱价值
|
||||||
average_chest_value = int(total_winnings / total_chests) if total_chests > 0 else 0
|
average_chest_value = int(total_winnings / total_chests) if total_chests > 0 else 0
|
||||||
|
|
||||||
# 计算成功率(这里简化处理)
|
# 计算成功率(已结算宝箱中A选项获胜的比例)
|
||||||
success_rate = 0.75 # 示例值
|
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(
|
return StreamerStatistics(
|
||||||
total_chests=total_chests,
|
total_chests=total_chests,
|
||||||
@ -189,7 +203,7 @@ class StreamerService:
|
|||||||
|
|
||||||
active_chests = db.query(func.count(Chest.id)).filter(
|
active_chests = db.query(func.count(Chest.id)).filter(
|
||||||
Chest.streamer_id == streamer.user_id,
|
Chest.streamer_id == streamer.user_id,
|
||||||
Chest.status == "下注中"
|
Chest.status == 0 # BETTING状态
|
||||||
).scalar() or 0
|
).scalar() or 0
|
||||||
|
|
||||||
max_chests = streamer.max_active_chests
|
max_chests = streamer.max_active_chests
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import Loading from '../components/Loading';
|
|||||||
import type { Transaction } from '../types';
|
import type { Transaction } from '../types';
|
||||||
|
|
||||||
const TransactionHistoryPage = () => {
|
const TransactionHistoryPage = () => {
|
||||||
const { user } = useAuth();
|
const { user: _user } = useAuth(); // 用户信息已在AuthContext中
|
||||||
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
@ -29,30 +29,28 @@ const TransactionHistoryPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getTransactionTypeText = (type: string) => {
|
const getTransactionTypeText = (type: string) => {
|
||||||
switch (type) {
|
// 统一交易类型映射,确保前后端一致性
|
||||||
case 'BET':
|
const typeMap: Record<string, string> = {
|
||||||
return '下注';
|
'BET': '下注',
|
||||||
case 'WIN':
|
'WIN': '获胜奖励',
|
||||||
return '获胜奖励';
|
'下注': '下注',
|
||||||
case '低保':
|
'获胜利': '获胜利',
|
||||||
return '低保';
|
'WINNING': '获胜利',
|
||||||
case '余额清零奖励':
|
'低保': '低保',
|
||||||
return '余额清零奖励';
|
'ALLOWANCE': '低保',
|
||||||
case 'ADMIN_ADJUST':
|
'余额清零奖励': '余额清零奖励',
|
||||||
return '管理员调整';
|
'ADMIN_ADJUST': '管理员调整',
|
||||||
case '管理员调整':
|
'管理员调整': '管理员调整',
|
||||||
return '管理员调整';
|
'ADMIN_OPERATION': '管理员调整',
|
||||||
case 'STREAMER_REVENUE':
|
'STREAMER_REVENUE': '主播分润',
|
||||||
return '主播分润';
|
'PLATFORM_REVENUE': '平台抽水',
|
||||||
case 'PLATFORM_REVENUE':
|
'REGISTER': '新用户注册奖励',
|
||||||
return '平台抽水';
|
'注册奖励': '新用户注册奖励',
|
||||||
case 'REGISTER':
|
'REGISTER_REWARD': '新用户注册奖励',
|
||||||
return '新用户注册奖励';
|
'NEW_USER_REWARD': '新用户注册奖励'
|
||||||
case '注册奖励':
|
};
|
||||||
return '新用户注册奖励';
|
|
||||||
default:
|
return typeMap[type] || type;
|
||||||
return type;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatDate = (dateString: string) => {
|
const formatDate = (dateString: string) => {
|
||||||
@ -91,14 +89,7 @@ const TransactionHistoryPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{loading ? (
|
{transactions.length === 0 ? (
|
||||||
<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 ? (
|
|
||||||
<div className="empty-state">
|
<div className="empty-state">
|
||||||
<div className="empty-state-icon">📊</div>
|
<div className="empty-state-icon">📊</div>
|
||||||
<h3>暂无交易记录</h3>
|
<h3>暂无交易记录</h3>
|
||||||
|
|||||||
@ -55,8 +55,8 @@ const UserProfile = () => {
|
|||||||
setAllowanceInfo(allowanceData);
|
setAllowanceInfo(allowanceData);
|
||||||
|
|
||||||
// 计算用户统计数据
|
// 计算用户统计数据
|
||||||
const bets = txData.filter(tx => tx.type === 'BET');
|
const bets = txData.filter(tx => tx.type === 'BET' || tx.type === '下注');
|
||||||
const wins = txData.filter(tx => tx.type === 'WIN');
|
const wins = txData.filter(tx => tx.type === 'WIN' || tx.type === '获胜利' || tx.type === 'WINNING');
|
||||||
const totalBets = bets.length;
|
const totalBets = bets.length;
|
||||||
const totalWinnings = wins.reduce((sum, tx) => sum + tx.amount, 0);
|
const totalWinnings = wins.reduce((sum, tx) => sum + tx.amount, 0);
|
||||||
const winRate = totalBets > 0 ? (wins.length / totalBets) * 100 : 0;
|
const winRate = totalBets > 0 ? (wins.length / totalBets) * 100 : 0;
|
||||||
@ -92,30 +92,28 @@ const UserProfile = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getTransactionTypeText = (type: string) => {
|
const getTransactionTypeText = (type: string) => {
|
||||||
switch (type) {
|
// 统一交易类型映射,确保前后端一致性
|
||||||
case 'BET':
|
const typeMap: Record<string, string> = {
|
||||||
return '下注';
|
'BET': '下注',
|
||||||
case 'WIN':
|
'WIN': '获胜奖励',
|
||||||
return '获胜奖励';
|
'下注': '下注',
|
||||||
case '低保':
|
'获胜利': '获胜利',
|
||||||
return '低保';
|
'WINNING': '获胜利',
|
||||||
case '余额清零奖励':
|
'低保': '低保',
|
||||||
return '余额清零奖励';
|
'ALLOWANCE': '低保',
|
||||||
case 'ADMIN_ADJUST':
|
'余额清零奖励': '余额清零奖励',
|
||||||
return '管理员调整';
|
'ADMIN_ADJUST': '管理员调整',
|
||||||
case '管理员调整':
|
'管理员调整': '管理员调整',
|
||||||
return '管理员调整';
|
'ADMIN_OPERATION': '管理员调整',
|
||||||
case 'STREAMER_REVENUE':
|
'STREAMER_REVENUE': '主播分润',
|
||||||
return '主播分润';
|
'PLATFORM_REVENUE': '平台抽水',
|
||||||
case 'PLATFORM_REVENUE':
|
'REGISTER': '新用户注册奖励',
|
||||||
return '平台抽水';
|
'注册奖励': '新用户注册奖励',
|
||||||
case 'REGISTER':
|
'REGISTER_REWARD': '新用户注册奖励',
|
||||||
return '新用户注册奖励';
|
'NEW_USER_REWARD': '新用户注册奖励'
|
||||||
case '注册奖励':
|
};
|
||||||
return '新用户注册奖励';
|
|
||||||
default:
|
return typeMap[type] || type;
|
||||||
return type;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getRoleText = (role: string) => {
|
const getRoleText = (role: string) => {
|
||||||
@ -327,14 +325,7 @@ const UserProfile = () => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{loading ? (
|
{transactions.length === 0 ? (
|
||||||
<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 ? (
|
|
||||||
<div className="empty-state">
|
<div className="empty-state">
|
||||||
<div className="empty-state-icon">📊</div>
|
<div className="empty-state-icon">📊</div>
|
||||||
<h4>暂无交易记录</h4>
|
<h4>暂无交易记录</h4>
|
||||||
|
|||||||
@ -172,9 +172,9 @@ export interface StreamerProfile {
|
|||||||
// 主播统计数据
|
// 主播统计数据
|
||||||
export interface StreamerStatistics {
|
export interface StreamerStatistics {
|
||||||
total_chests: number;
|
total_chests: number;
|
||||||
total_bets: number;
|
active_chests: number;
|
||||||
total_pool: number;
|
total_winnings: number;
|
||||||
total_earnings: number;
|
total_commission: number;
|
||||||
avg_participants: number;
|
average_chest_value: number;
|
||||||
success_rate: number;
|
success_rate: number;
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user