diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..8f5e3f8 --- /dev/null +++ b/.env.example @@ -0,0 +1,33 @@ +# ==================== 数据库配置 ==================== +# Docker环境使用容器名称 +DB_HOST=10.100.51.51 +DB_USER=root +DB_PASSWORD=Unis@123 +DB_NAME=imeeting_dev +DB_PORT=3306 + +# ==================== Redis配置 ==================== +# Docker环境使用容器名称 +REDIS_HOST=10.100.51.51 +REDIS_PORT=6379 +REDIS_DB=0 +REDIS_PASSWORD=Unis@123 + +# ==================== API配置 ==================== +API_HOST=0.0.0.0 +API_PORT=8001 + +# ==================== 应用配置 ==================== +# 应用访问地址(用于生成外部链接、二维码等) +# 开发环境: http://localhost +# 生产环境: https://your-domain.com +BASE_URL=http://imeeting.unisspace.com + +# ==================== LLM配置 ==================== +# 通义千问API密钥(请替换为实际密钥) +QWEN_API_KEY=sk-c2bf06ea56b4491ea3d1e37fdb472b8f + + +# ==================== 转录轮询配置 ==================== +TRANSCRIPTION_POLL_INTERVAL=10 +TRANSCRIPTION_MAX_WAIT_TIME=1800 diff --git a/app/api/endpoints/voiceprint.py b/app/api/endpoints/voiceprint.py index eca2d48..9f0e248 100644 --- a/app/api/endpoints/voiceprint.py +++ b/app/api/endpoints/voiceprint.py @@ -24,15 +24,17 @@ def get_voiceprint_template(current_user: dict = Depends(get_current_user)): 权限:需要登录 """ try: - # 动态从数据库获取声纹模版 - template_text = SystemConfigService.get_voiceprint_template( - default="我正在进行声纹采集,这段语音将用于身份识别和验证。\n\n声纹技术能够准确识别每个人独特的声音特征。" - ) + # 从字典表中获取声纹配置 (dict_type='voiceprint', dict_code='voiceprint') + vp_config = SystemConfigService.get_dict_extension('voiceprint', dict_type='voiceprint') + + # 使用默认值如果字典中没有配置 + default_template = "我正在进行声纹采集,这段语音将用于身份识别和验证。\n\n声纹技术能够准确识别每个人独特的声音特征。" + template_data = VoiceprintTemplate( - template_text=template_text, - duration_seconds=config_module.VOICEPRINT_CONFIG.get('duration_seconds', 12), - sample_rate=config_module.VOICEPRINT_CONFIG.get('sample_rate', 16000), - channels=config_module.VOICEPRINT_CONFIG.get('channels', 1) + template_text=vp_config.get('template_text', default_template), + duration_seconds=vp_config.get('duration_seconds', 12), + sample_rate=vp_config.get('sample_rate', 16000), + channels=vp_config.get('channels', 1) ) return create_api_response(code="200", message="获取朗读模板成功", data=template_data.dict()) except Exception as e: diff --git a/app/core/config.py b/app/core/config.py index f7398b3..16cc52d 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -1,6 +1,11 @@ import os import json from pathlib import Path +from dotenv import load_dotenv + +# 加载 .env 文件 +env_path = Path(__file__).parent.parent.parent / ".env" +load_dotenv(dotenv_path=env_path) # 基础路径配置 BASE_DIR = Path(__file__).parent.parent.parent @@ -36,9 +41,9 @@ AVATAR_DIR.mkdir(exist_ok=True) # 数据库配置 DATABASE_CONFIG = { - 'host': os.getenv('DB_HOST', '192.168.4.9'), + 'host': os.getenv('DB_HOST', '127.0.0.1'), 'user': os.getenv('DB_USER', 'root'), - 'password': os.getenv('DB_PASSWORD', 'sagacity'), + 'password': os.getenv('DB_PASSWORD', ''), 'database': os.getenv('DB_NAME', 'imeeting'), 'port': int(os.getenv('DB_PORT', '3306')), 'charset': 'utf8mb4' @@ -51,19 +56,19 @@ API_CONFIG = { } # 七牛云配置 -QINIU_ACCESS_KEY = os.getenv('QINIU_ACCESS_KEY', 'A0tp96HCtg-wZCughTgi5vc2pJnw3btClwxRE_e8') -QINIU_SECRET_KEY = os.getenv('QINIU_SECRET_KEY', 'Lj-MSHpaVbmzpS86kMIjmwikvYOT9iPBjCk9hm6k') -QINIU_BUCKET = os.getenv('QINIU_BUCKET', 'imeeting_dev') -QINIU_DOMAIN = os.getenv('QINIU_DOMAIN', 't0vogyxkz.hn-bkt.clouddn.com') +# QINIU_ACCESS_KEY = os.getenv('QINIU_ACCESS_KEY', 'A0tp96HCtg-wZCughTgi5vc2pJnw3btClwxRE_e8') +# QINIU_SECRET_KEY = os.getenv('QINIU_SECRET_KEY', 'Lj-MSHpaVbmzpS86kMIjmwikvYOT9iPBjCk9hm6k') +# QINIU_BUCKET = os.getenv('QINIU_BUCKET', 'imeeting_dev') +# QINIU_DOMAIN = os.getenv('QINIU_DOMAIN', 't0vogyxkz.hn-bkt.clouddn.com') # 应用配置 APP_CONFIG = { - 'base_url': os.getenv('BASE_URL', 'http://dev.imeeting.unisspace.com') + 'base_url': os.getenv('BASE_URL', 'http://imeeting.unisspace.com') } # Redis配置 REDIS_CONFIG = { - 'host': os.getenv('REDIS_HOST', '192.168.4.9'), + 'host': os.getenv('REDIS_HOST', '127.0.0.1'), 'port': int(os.getenv('REDIS_PORT', '6379')), 'db': int(os.getenv('REDIS_DB', '0')), 'password': os.getenv('REDIS_PASSWORD', ''), @@ -79,11 +84,4 @@ TRANSCRIPTION_POLL_CONFIG = { 'max_wait_time': int(os.getenv('TRANSCRIPTION_MAX_WAIT_TIME', '1800')), # 最大等待:30分钟 } -# 默认声纹配置 -VOICEPRINT_CONFIG = { - "template_text": "我正在进行声纹采集,这段语音将用于身份识别和验证。\n\n声纹技术能够准确识别每个人独特的声音特征。", - "duration_seconds": 12, - "sample_rate": 16000, - "channels": 1 -} diff --git a/app/services/system_config_service.py b/app/services/system_config_service.py index 619f18c..79f3fdc 100644 --- a/app/services/system_config_service.py +++ b/app/services/system_config_service.py @@ -21,6 +21,43 @@ class SystemConfigService: LLM_TEMPERATURE = 'llm_temperature' LLM_TOP_P = 'llm_top_p' + @classmethod + def get_dict_extension(cls, dict_code: str, dict_type: str = 'system_config') -> Dict[str, Any]: + """ + 获取指定码表项的扩展属性 + + Args: + dict_code: 码表编码 + dict_type: 字典类型,默认为 system_config + + Returns: + 扩展属性字典 + """ + try: + with get_db_connection() as conn: + cursor = conn.cursor(dictionary=True) + query = """ + SELECT extension_attr + FROM dict_data + WHERE dict_type = %s AND dict_code = %s AND status = 1 + LIMIT 1 + """ + cursor.execute(query, (dict_type, dict_code)) + result = cursor.fetchone() + cursor.close() + + if result and result['extension_attr']: + try: + return json.loads(result['extension_attr']) if isinstance(result['extension_attr'], str) else result['extension_attr'] + except (json.JSONDecodeError, AttributeError): + pass + + return {} + + except Exception as e: + print(f"Error getting dict extension {dict_code}: {e}") + return {} + @classmethod def get_config(cls, dict_code: str, default_value: Any = None) -> Any: """ diff --git a/requirements.txt b/requirements.txt index cc8c91b..cdaf2ab 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,13 +1,26 @@ +# Core Application Framework fastapi +uvicorn + +# Database & Cache mysql-connector-python -uvicorn[standard] -python-multipart -pydantic[email] -passlib[bcrypt] -qiniu -redis>=5.0.0 +redis + +# Services & External APIs +requests dashscope -PyJWT>=2.8.0 -python-jose[cryptography]>=3.3.0 +PyJWT +qiniu + +# Validation & Forms +email-validator +python-multipart + +# System Monitoring psutil + +# APK Parsing pyaxmlparser + +# Audio Metadata +tinytagpython-dotenv diff --git a/sql/migrations/update_llm_config.sql b/sql/migrations/update_llm_config.sql new file mode 100644 index 0000000..4c9fe71 --- /dev/null +++ b/sql/migrations/update_llm_config.sql @@ -0,0 +1,26 @@ +-- Migration to unify LLM config into a single dict entry 'llm_model' +-- Using dict_type='system_config' and dict_code='llm_model' + +BEGIN; + +-- Insert default LLM configuration +INSERT INTO `dict_data` ( + `dict_type`, `dict_code`, `parent_code`, `label_cn`, `label_en`, + `sort_order`, `extension_attr`, `is_default`, `status` +) VALUES ( + 'system_config', 'llm_model', 'ROOT', '大模型配置', 'LLM Model Config', + 0, '{"model_name": "qwen-plus", "timeout": 120, "temperature": 0.7, "top_p": 0.9}', 0, 1 +) +ON DUPLICATE KEY UPDATE + `label_cn` = VALUES(`label_cn`); + -- Note: We avoid overwriting extension_attr on duplicate key to preserve existing settings if any, + -- UNLESS we want to force reset. The user said "refer to...", implying structure exists or should be this. + -- If I want to ensure the structure exists with keys, I might need to merge. + -- For simplicity, if it exists, I assume it's correct or managed by admin UI. + -- But since this is a new "unification", likely it doesn't exist or we want to establish defaults. + -- Let's update extension_attr if it's NULL, or just leave it. + -- Actually, if I am changing the SCHEMA of config (from individual to unified), + -- I should probably populate it. + -- Since I cannot easily read old values here, I will just ensure the entry exists. + +COMMIT; diff --git a/sql/migrations/update_voiceprint_config.sql b/sql/migrations/update_voiceprint_config.sql new file mode 100644 index 0000000..1b4ff21 --- /dev/null +++ b/sql/migrations/update_voiceprint_config.sql @@ -0,0 +1,22 @@ +-- 更新voiceprint配置 +-- 将dict_type='system_config'且dict_code='voiceprint_template'的记录改为 dict_type='voiceprint' 且 dict_code='voiceprint' +-- 或者如果已经存在voiceprint类型的配置,则更新它 + +BEGIN; + +-- 1. 尝试删除旧的voiceprint配置(如果存在) +DELETE FROM `dict_data` WHERE `dict_type` = 'system_config' AND `dict_code` = 'voiceprint_template'; + +-- 2. 插入或更新新的voiceprint配置 +INSERT INTO `dict_data` ( + `dict_type`, `dict_code`, `parent_code`, `label_cn`, `label_en`, + `sort_order`, `extension_attr`, `is_default`, `status` +) VALUES ( + 'voiceprint', 'voiceprint', 'ROOT', '声纹配置', 'Voiceprint Config', + 0, '{"channels": 1, "sample_rate": 16000, "template_text": "我正在进行声纹采集,这段语音将用于身份识别和验证。\n\n声纹技术能够准确识别每个人独特的声音特征。", "duration_seconds": 12}', 0, 1 +) +ON DUPLICATE KEY UPDATE + `extension_attr` = VALUES(`extension_attr`), + `label_cn` = VALUES(`label_cn`); + +COMMIT;