2025-12-16 18:06:50 +08:00
|
|
|
|
"""
|
|
|
|
|
|
系统配置服务 - 增强版
|
|
|
|
|
|
"""
|
|
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
|
|
from sqlalchemy import and_, func
|
|
|
|
|
|
from typing import Dict, Any, List, Optional
|
2025-12-16 21:39:32 +08:00
|
|
|
|
from datetime import datetime
|
2025-12-16 18:06:50 +08:00
|
|
|
|
from ..models.system import SystemConfig, ConfigType, ConfigCategory
|
|
|
|
|
|
from ..schemas.system import (
|
2025-12-16 21:39:32 +08:00
|
|
|
|
SystemConfigCreate,
|
|
|
|
|
|
SystemConfigUpdate,
|
2025-12-16 18:06:50 +08:00
|
|
|
|
SystemConfigResponse,
|
|
|
|
|
|
SystemConfigGroup,
|
|
|
|
|
|
SystemConfigSummary
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SystemService:
|
|
|
|
|
|
"""系统配置服务"""
|
|
|
|
|
|
|
|
|
|
|
|
# 配置分类显示名称映射
|
|
|
|
|
|
CATEGORY_NAMES = {
|
|
|
|
|
|
ConfigCategory.GAME_ECONOMY: "游戏经济配置",
|
|
|
|
|
|
ConfigCategory.GAME_LOGIC: "游戏逻辑配置",
|
|
|
|
|
|
ConfigCategory.SYSTEM_OPERATIONS: "系统运维配置",
|
|
|
|
|
|
ConfigCategory.UI_DISPLAY: "界面显示配置",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# 配置分类描述映射
|
|
|
|
|
|
CATEGORY_DESCRIPTIONS = {
|
|
|
|
|
|
ConfigCategory.GAME_ECONOMY: "游戏经济相关配置,如奖励、低保、抽水比例等",
|
|
|
|
|
|
ConfigCategory.GAME_LOGIC: "游戏逻辑相关配置,如倒计时、活跃宝箱数等",
|
|
|
|
|
|
ConfigCategory.SYSTEM_OPERATIONS: "系统运维相关配置,如分页、WebSocket等",
|
|
|
|
|
|
ConfigCategory.UI_DISPLAY: "界面显示相关配置",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_config(db: Session, key: str) -> Optional[SystemConfig]:
|
|
|
|
|
|
"""
|
|
|
|
|
|
根据键获取配置
|
|
|
|
|
|
"""
|
|
|
|
|
|
return db.query(SystemConfig).filter(SystemConfig.config_key == key).first()
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_all_configs(db: Session) -> List[SystemConfig]:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取所有配置
|
|
|
|
|
|
"""
|
|
|
|
|
|
return db.query(SystemConfig).order_by(
|
|
|
|
|
|
SystemConfig.category,
|
|
|
|
|
|
SystemConfig.display_order,
|
|
|
|
|
|
SystemConfig.config_key
|
|
|
|
|
|
).all()
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_configs_by_category(db: Session, category: ConfigCategory) -> List[SystemConfig]:
|
|
|
|
|
|
"""
|
|
|
|
|
|
根据分类获取配置
|
|
|
|
|
|
"""
|
|
|
|
|
|
return db.query(SystemConfig).filter(
|
|
|
|
|
|
SystemConfig.category == category
|
|
|
|
|
|
).order_by(
|
|
|
|
|
|
SystemConfig.display_order,
|
|
|
|
|
|
SystemConfig.config_key
|
|
|
|
|
|
).all()
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_config_groups(db: Session) -> List[SystemConfigGroup]:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取配置分组
|
|
|
|
|
|
"""
|
|
|
|
|
|
configs = SystemService.get_all_configs(db)
|
|
|
|
|
|
groups = {}
|
|
|
|
|
|
|
|
|
|
|
|
for config in configs:
|
|
|
|
|
|
category = config.category
|
|
|
|
|
|
if category not in groups:
|
|
|
|
|
|
groups[category] = []
|
|
|
|
|
|
groups[category].append(config)
|
|
|
|
|
|
|
|
|
|
|
|
result = []
|
|
|
|
|
|
for category, configs in groups.items():
|
|
|
|
|
|
result.append(
|
|
|
|
|
|
SystemConfigGroup(
|
|
|
|
|
|
category=category,
|
|
|
|
|
|
category_name=SystemService.CATEGORY_NAMES.get(category, str(category)),
|
|
|
|
|
|
category_description=SystemService.CATEGORY_DESCRIPTIONS.get(category, ""),
|
|
|
|
|
|
configs=[SystemConfigResponse.model_validate(c) for c in configs]
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def create_config(db: Session, config_data: SystemConfigCreate) -> SystemConfig:
|
|
|
|
|
|
"""
|
|
|
|
|
|
创建配置
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 检查键是否已存在
|
|
|
|
|
|
existing_config = db.query(SystemConfig).filter(
|
|
|
|
|
|
SystemConfig.config_key == config_data.config_key
|
|
|
|
|
|
).first()
|
|
|
|
|
|
if existing_config:
|
|
|
|
|
|
raise ValueError("配置键已存在")
|
|
|
|
|
|
|
|
|
|
|
|
db_config = SystemConfig(
|
|
|
|
|
|
config_key=config_data.config_key,
|
|
|
|
|
|
config_value=config_data.config_value,
|
|
|
|
|
|
config_type=config_data.config_type,
|
|
|
|
|
|
category=config_data.category,
|
|
|
|
|
|
description=config_data.description,
|
|
|
|
|
|
is_editable=config_data.is_editable,
|
|
|
|
|
|
is_public=config_data.is_public,
|
|
|
|
|
|
display_order=config_data.display_order,
|
|
|
|
|
|
)
|
|
|
|
|
|
db.add(db_config)
|
|
|
|
|
|
db.commit()
|
|
|
|
|
|
db.refresh(db_config)
|
2025-12-16 21:39:32 +08:00
|
|
|
|
|
|
|
|
|
|
# 清空配置缓存,确保新配置立即生效
|
|
|
|
|
|
ConfigManager.clear_cache()
|
|
|
|
|
|
|
2025-12-16 18:06:50 +08:00
|
|
|
|
return db_config
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def update_config(db: Session, key: str, config_data: SystemConfigUpdate) -> Optional[SystemConfig]:
|
|
|
|
|
|
"""
|
|
|
|
|
|
更新配置
|
|
|
|
|
|
"""
|
|
|
|
|
|
db_config = SystemService.get_config(db, key)
|
|
|
|
|
|
if not db_config:
|
|
|
|
|
|
return None
|
2025-12-16 21:39:32 +08:00
|
|
|
|
|
2025-12-16 18:06:50 +08:00
|
|
|
|
if not db_config.is_editable:
|
|
|
|
|
|
raise ValueError("该配置项不可编辑")
|
|
|
|
|
|
|
|
|
|
|
|
update_data = config_data.model_dump(exclude_unset=True)
|
|
|
|
|
|
for field, value in update_data.items():
|
|
|
|
|
|
setattr(db_config, field, value)
|
2025-12-16 21:39:32 +08:00
|
|
|
|
|
2025-12-16 18:06:50 +08:00
|
|
|
|
db.commit()
|
|
|
|
|
|
db.refresh(db_config)
|
2025-12-16 21:39:32 +08:00
|
|
|
|
|
|
|
|
|
|
# 清空配置缓存,让配置立即生效
|
|
|
|
|
|
ConfigManager.clear_cache()
|
|
|
|
|
|
|
2025-12-16 18:06:50 +08:00
|
|
|
|
return db_config
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def delete_config(db: Session, key: str) -> bool:
|
|
|
|
|
|
"""
|
|
|
|
|
|
删除配置
|
|
|
|
|
|
"""
|
|
|
|
|
|
db_config = SystemService.get_config(db, key)
|
|
|
|
|
|
if not db_config:
|
|
|
|
|
|
return False
|
2025-12-16 21:39:32 +08:00
|
|
|
|
|
2025-12-16 18:06:50 +08:00
|
|
|
|
if not db_config.is_editable:
|
|
|
|
|
|
raise ValueError("该配置项不可删除")
|
2025-12-16 21:39:32 +08:00
|
|
|
|
|
2025-12-16 18:06:50 +08:00
|
|
|
|
db.delete(db_config)
|
|
|
|
|
|
db.commit()
|
2025-12-16 21:39:32 +08:00
|
|
|
|
|
|
|
|
|
|
# 清空配置缓存,让配置立即生效
|
|
|
|
|
|
ConfigManager.clear_cache()
|
|
|
|
|
|
|
2025-12-16 18:06:50 +08:00
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def batch_update_configs(db: Session, configs: Dict[str, str]) -> List[SystemConfig]:
|
|
|
|
|
|
"""
|
|
|
|
|
|
批量更新配置
|
|
|
|
|
|
"""
|
|
|
|
|
|
updated_configs = []
|
|
|
|
|
|
for key, value in configs.items():
|
|
|
|
|
|
config = SystemService.update_config(db, key, SystemConfigUpdate(config_value=value))
|
|
|
|
|
|
if config:
|
|
|
|
|
|
updated_configs.append(config)
|
2025-12-16 21:39:32 +08:00
|
|
|
|
|
2025-12-16 18:06:50 +08:00
|
|
|
|
return updated_configs
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_config_summary(db: Session) -> SystemConfigSummary:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取配置概览
|
|
|
|
|
|
"""
|
|
|
|
|
|
total_configs = db.query(SystemConfig).count()
|
|
|
|
|
|
editable_configs = db.query(SystemConfig).filter(SystemConfig.is_editable == True).count()
|
|
|
|
|
|
public_configs = db.query(SystemConfig).filter(SystemConfig.is_public == True).count()
|
|
|
|
|
|
|
|
|
|
|
|
# 统计各分类的配置数量
|
|
|
|
|
|
category_counts = {}
|
|
|
|
|
|
for category in ConfigCategory:
|
|
|
|
|
|
count = db.query(SystemConfig).filter(SystemConfig.category == category).count()
|
|
|
|
|
|
category_counts[category.value] = count
|
|
|
|
|
|
|
|
|
|
|
|
return SystemConfigSummary(
|
|
|
|
|
|
total_configs=total_configs,
|
|
|
|
|
|
editable_configs=editable_configs,
|
|
|
|
|
|
public_configs=public_configs,
|
|
|
|
|
|
category_counts=category_counts
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_public_configs(db: Session) -> Dict[str, str]:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取公开配置(供前端使用)
|
|
|
|
|
|
"""
|
|
|
|
|
|
configs = db.query(SystemConfig).filter(SystemConfig.is_public == True).all()
|
|
|
|
|
|
return {c.config_key: c.config_value for c in configs}
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_typed_value(config: SystemConfig) -> Any:
|
|
|
|
|
|
"""
|
|
|
|
|
|
根据配置类型获取正确类型的值
|
|
|
|
|
|
"""
|
|
|
|
|
|
if config.config_type == ConfigType.NUMBER:
|
|
|
|
|
|
try:
|
|
|
|
|
|
return float(config.config_value) if '.' in config.config_value else int(config.config_value)
|
|
|
|
|
|
except ValueError:
|
|
|
|
|
|
return config.config_value
|
|
|
|
|
|
elif config.config_type == ConfigType.BOOLEAN:
|
|
|
|
|
|
return config.config_value.lower() in ('true', '1', 'yes', 'on')
|
|
|
|
|
|
elif config.config_type == ConfigType.JSON:
|
|
|
|
|
|
import json
|
|
|
|
|
|
try:
|
|
|
|
|
|
return json.loads(config.config_value)
|
|
|
|
|
|
except json.JSONDecodeError:
|
|
|
|
|
|
return config.config_value
|
|
|
|
|
|
else:
|
|
|
|
|
|
return config.config_value
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def initialize_default_configs(db: Session):
|
|
|
|
|
|
"""
|
|
|
|
|
|
初始化默认系统配置
|
|
|
|
|
|
"""
|
|
|
|
|
|
from ..models.system import ConfigType, ConfigCategory
|
|
|
|
|
|
|
|
|
|
|
|
# 检查是否已有配置
|
|
|
|
|
|
existing_count = db.query(SystemConfig).count()
|
|
|
|
|
|
if existing_count > 0:
|
|
|
|
|
|
print(f"系统已有 {existing_count} 个配置项,跳过初始化")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 游戏经济配置
|
|
|
|
|
|
game_economy_configs = [
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "GAME_NEW_USER_REWARD",
|
|
|
|
|
|
"config_value": "100000",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.GAME_ECONOMY,
|
|
|
|
|
|
"description": "新用户注册奖励(分)",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "GAME_DAILY_ALLOWANCE",
|
|
|
|
|
|
"config_value": "5000",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.GAME_ECONOMY,
|
|
|
|
|
|
"description": "每日低保(分)",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 2,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "GAME_ALLOWANCE_THRESHOLD",
|
|
|
|
|
|
"config_value": "1000",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.GAME_ECONOMY,
|
|
|
|
|
|
"description": "低保门槛(分)",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 3,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "GAME_HOUSE_EDGE",
|
|
|
|
|
|
"config_value": "0.10",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.GAME_ECONOMY,
|
|
|
|
|
|
"description": "总抽水比例",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 4,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "GAME_STREAMER_SHARE",
|
|
|
|
|
|
"config_value": "0.05",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.GAME_ECONOMY,
|
|
|
|
|
|
"description": "主播分润比例",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 5,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "GAME_PLATFORM_SHARE",
|
|
|
|
|
|
"config_value": "0.05",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.GAME_ECONOMY,
|
|
|
|
|
|
"description": "平台分润比例",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 6,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "GAME_DAILY_CHECKIN_REWARD",
|
|
|
|
|
|
"config_value": "1000",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.GAME_ECONOMY,
|
|
|
|
|
|
"description": "每日签到奖励(分)",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 7,
|
|
|
|
|
|
},
|
2025-12-16 19:03:48 +08:00
|
|
|
|
{
|
|
|
|
|
|
"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,
|
|
|
|
|
|
},
|
2025-12-16 18:06:50 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
# 游戏逻辑配置
|
|
|
|
|
|
game_logic_configs = [
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "GAME_DEFAULT_COUNTDOWN",
|
|
|
|
|
|
"config_value": "300",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.GAME_LOGIC,
|
|
|
|
|
|
"description": "默认倒计时(秒)",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "GAME_MIN_COUNTDOWN",
|
|
|
|
|
|
"config_value": "10",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.GAME_LOGIC,
|
|
|
|
|
|
"description": "最小倒计时(秒)",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 2,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "GAME_MAX_COUNTDOWN",
|
|
|
|
|
|
"config_value": "3600",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.GAME_LOGIC,
|
|
|
|
|
|
"description": "最大倒计时(秒)",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 3,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "GAME_DEFAULT_MAX_ACTIVE_CHESTS",
|
|
|
|
|
|
"config_value": "10",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.GAME_LOGIC,
|
|
|
|
|
|
"description": "默认最大活跃宝箱数",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 4,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "GAME_MIN_COMMISSION_RATE",
|
|
|
|
|
|
"config_value": "0.0",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.GAME_LOGIC,
|
|
|
|
|
|
"description": "最小主播抽成(%)",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 5,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "GAME_MAX_COMMISSION_RATE",
|
|
|
|
|
|
"config_value": "100.0",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.GAME_LOGIC,
|
|
|
|
|
|
"description": "最大主播抽成(%)",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 6,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "GAME_DEFAULT_COMMISSION_RATE",
|
|
|
|
|
|
"config_value": "5.0",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.GAME_LOGIC,
|
|
|
|
|
|
"description": "默认主播抽成(%)",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 7,
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
# 系统运维配置
|
|
|
|
|
|
system_operations_configs = [
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "PAGINATION_DEFAULT_PAGE_SIZE",
|
|
|
|
|
|
"config_value": "20",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.SYSTEM_OPERATIONS,
|
|
|
|
|
|
"description": "默认分页大小",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "PAGINATION_ADMIN_PAGE_SIZE",
|
|
|
|
|
|
"config_value": "20",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.SYSTEM_OPERATIONS,
|
|
|
|
|
|
"description": "管理员页面分页大小",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 2,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "PAGINATION_ANNOUNCEMENT_PAGE_SIZE",
|
|
|
|
|
|
"config_value": "10",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.SYSTEM_OPERATIONS,
|
|
|
|
|
|
"description": "公告页面分页大小",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 3,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "WS_BROADCAST_INTERVAL",
|
|
|
|
|
|
"config_value": "200",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.SYSTEM_OPERATIONS,
|
|
|
|
|
|
"description": "WebSocket广播间隔(毫秒)",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 4,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "WS_HEARTBEAT_INTERVAL",
|
|
|
|
|
|
"config_value": "30",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.SYSTEM_OPERATIONS,
|
|
|
|
|
|
"description": "WebSocket心跳间隔(秒)",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 5,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "WS_CONNECTION_TIMEOUT",
|
|
|
|
|
|
"config_value": "60",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.SYSTEM_OPERATIONS,
|
|
|
|
|
|
"description": "WebSocket连接超时(秒)",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 6,
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
# 界面显示配置
|
|
|
|
|
|
ui_display_configs = [
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "ANNOUNCEMENT_DEFAULT_PRIORITY",
|
|
|
|
|
|
"config_value": "0",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.UI_DISPLAY,
|
|
|
|
|
|
"description": "公告默认优先级",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"config_key": "ANNOUNCEMENT_MAX_PRIORITY",
|
|
|
|
|
|
"config_value": "100",
|
|
|
|
|
|
"config_type": ConfigType.NUMBER,
|
|
|
|
|
|
"category": ConfigCategory.UI_DISPLAY,
|
|
|
|
|
|
"description": "公告最大优先级",
|
|
|
|
|
|
"is_editable": True,
|
|
|
|
|
|
"is_public": False,
|
|
|
|
|
|
"display_order": 2,
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
# 合并所有配置
|
|
|
|
|
|
all_configs = (
|
|
|
|
|
|
game_economy_configs +
|
|
|
|
|
|
game_logic_configs +
|
|
|
|
|
|
system_operations_configs +
|
|
|
|
|
|
ui_display_configs
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# 创建配置项
|
|
|
|
|
|
created_count = 0
|
|
|
|
|
|
for config_data in all_configs:
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 检查配置是否已存在
|
|
|
|
|
|
existing = db.query(SystemConfig).filter(
|
|
|
|
|
|
SystemConfig.config_key == config_data["config_key"]
|
|
|
|
|
|
).first()
|
|
|
|
|
|
|
|
|
|
|
|
if not existing:
|
|
|
|
|
|
db_config = SystemConfig(**config_data)
|
|
|
|
|
|
db.add(db_config)
|
|
|
|
|
|
created_count += 1
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"创建配置 {config_data['config_key']} 失败: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
db.commit()
|
|
|
|
|
|
print(f"成功初始化 {created_count} 个默认配置项")
|
2025-12-16 21:39:32 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ConfigManager:
|
|
|
|
|
|
"""
|
|
|
|
|
|
配置管理器 - 统一配置访问接口
|
|
|
|
|
|
提供从数据库动态读取配置的能力,支持缓存
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
# 配置缓存:key=配置键, value=(配置值, 缓存时间)
|
|
|
|
|
|
_config_cache = {}
|
|
|
|
|
|
_cache_expire_seconds = 300 # 5分钟缓存
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_game_config(db: Session, config_key: str, default_value: Any = None) -> Any:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取游戏配置 - 从数据库读取,支持缓存
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
db: 数据库会话
|
|
|
|
|
|
config_key: 配置键
|
|
|
|
|
|
default_value: 默认值(当数据库无此配置时使用)
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
配置值(已转换为正确类型)
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 检查缓存
|
|
|
|
|
|
now = datetime.now()
|
|
|
|
|
|
if config_key in ConfigManager._config_cache:
|
|
|
|
|
|
value, cached_time = ConfigManager._config_cache[config_key]
|
|
|
|
|
|
if (now - cached_time).total_seconds() < ConfigManager._cache_expire_seconds:
|
|
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
|
|
|
# 从数据库读取
|
|
|
|
|
|
config = SystemService.get_config(db, config_key)
|
|
|
|
|
|
if config:
|
|
|
|
|
|
value = SystemService.get_typed_value(config)
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 如果数据库中没有,使用默认值
|
|
|
|
|
|
value = default_value
|
|
|
|
|
|
if value is None:
|
|
|
|
|
|
print(f"警告:配置项 {config_key} 不存在于数据库中,也未提供默认值")
|
|
|
|
|
|
|
|
|
|
|
|
# 更新缓存
|
|
|
|
|
|
ConfigManager._config_cache[config_key] = (value, now)
|
|
|
|
|
|
|
|
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def clear_cache():
|
|
|
|
|
|
"""
|
|
|
|
|
|
清空配置缓存
|
|
|
|
|
|
在配置更新后调用
|
|
|
|
|
|
"""
|
|
|
|
|
|
ConfigManager._config_cache.clear()
|
|
|
|
|
|
print("配置缓存已清空")
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def refresh_config(db: Session, config_key: str):
|
|
|
|
|
|
"""
|
|
|
|
|
|
刷新单个配置的缓存
|
|
|
|
|
|
在配置更新后调用
|
|
|
|
|
|
"""
|
|
|
|
|
|
if config_key in ConfigManager._config_cache:
|
|
|
|
|
|
del ConfigManager._config_cache[config_key]
|
|
|
|
|
|
print(f"配置 {config_key} 缓存已刷新")
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_streamer_share(db: Session) -> float:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取主播分润比例
|
|
|
|
|
|
"""
|
|
|
|
|
|
return ConfigManager.get_game_config(db, "GAME_STREAMER_SHARE", 0.05)
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_platform_share(db: Session) -> float:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取平台分润比例
|
|
|
|
|
|
"""
|
|
|
|
|
|
return ConfigManager.get_game_config(db, "GAME_PLATFORM_SHARE", 0.05)
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_new_user_reward(db: Session) -> int:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取新用户注册奖励
|
|
|
|
|
|
"""
|
|
|
|
|
|
return ConfigManager.get_game_config(db, "GAME_NEW_USER_REWARD", 100000)
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_daily_allowance(db: Session) -> int:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取每日低保金额
|
|
|
|
|
|
"""
|
|
|
|
|
|
return ConfigManager.get_game_config(db, "GAME_DAILY_ALLOWANCE", 5000)
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_allowance_threshold(db: Session) -> int:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取低保门槛
|
|
|
|
|
|
"""
|
|
|
|
|
|
return ConfigManager.get_game_config(db, "GAME_ALLOWANCE_THRESHOLD", 1000)
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_house_edge(db: Session) -> float:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取总抽水比例
|
|
|
|
|
|
"""
|
|
|
|
|
|
return ConfigManager.get_game_config(db, "GAME_HOUSE_EDGE", 0.10)
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_default_countdown(db: Session) -> int:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取默认倒计时
|
|
|
|
|
|
"""
|
|
|
|
|
|
return ConfigManager.get_game_config(db, "GAME_DEFAULT_COUNTDOWN", 300)
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_min_countdown(db: Session) -> int:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取最小倒计时
|
|
|
|
|
|
"""
|
|
|
|
|
|
return ConfigManager.get_game_config(db, "GAME_MIN_COUNTDOWN", 10)
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_max_countdown(db: Session) -> int:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取最大倒计时
|
|
|
|
|
|
"""
|
|
|
|
|
|
return ConfigManager.get_game_config(db, "GAME_MAX_COUNTDOWN", 3600)
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_default_max_active_chests(db: Session) -> int:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取默认最大活跃宝箱数
|
|
|
|
|
|
"""
|
|
|
|
|
|
return ConfigManager.get_game_config(db, "GAME_DEFAULT_MAX_ACTIVE_CHESTS", 10)
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_min_commission_rate(db: Session) -> float:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取最小主播抽成
|
|
|
|
|
|
"""
|
|
|
|
|
|
return ConfigManager.get_game_config(db, "GAME_MIN_COMMISSION_RATE", 0.0)
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_max_commission_rate(db: Session) -> float:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取最大主播抽成
|
|
|
|
|
|
"""
|
|
|
|
|
|
return ConfigManager.get_game_config(db, "GAME_MAX_COMMISSION_RATE", 100.0)
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_default_commission_rate(db: Session) -> float:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取默认主播抽成
|
|
|
|
|
|
"""
|
|
|
|
|
|
return ConfigManager.get_game_config(db, "GAME_DEFAULT_COMMISSION_RATE", 5.0)
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_daily_checkin_reward(db: Session) -> int:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取每日签到奖励
|
|
|
|
|
|
"""
|
|
|
|
|
|
return ConfigManager.get_game_config(db, "GAME_DAILY_CHECKIN_REWARD", 1000)
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_balance_zero_reward(db: Session) -> int:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取余额清零自动发放金额
|
|
|
|
|
|
"""
|
|
|
|
|
|
return ConfigManager.get_game_config(db, "BALANCE_ZERO_REWARD_AMOUNT", 10000)
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_allowance_reset_time(db: Session) -> str:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取低保每日刷新时间
|
|
|
|
|
|
"""
|
|
|
|
|
|
return ConfigManager.get_game_config(db, "ALLOWANCE_RESET_TIME", "00:00")
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_ws_broadcast_interval(db: Session) -> int:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取WebSocket广播间隔(毫秒)
|
|
|
|
|
|
"""
|
|
|
|
|
|
return ConfigManager.get_game_config(db, "WS_BROADCAST_INTERVAL", 200)
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_ws_heartbeat_interval(db: Session) -> int:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取WebSocket心跳间隔(秒)
|
|
|
|
|
|
"""
|
|
|
|
|
|
return ConfigManager.get_game_config(db, "WS_HEARTBEAT_INTERVAL", 30)
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def get_ws_connection_timeout(db: Session) -> int:
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取WebSocket连接超时(秒)
|
|
|
|
|
|
"""
|
|
|
|
|
|
return ConfigManager.get_game_config(db, "WS_CONNECTION_TIMEOUT", 60)
|