Files
recommend/core/base.py

621 lines
20 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
核心基座架构 - Diet Recommendation App
统一的基座设计,支持所有功能模块的扩展
"""
from abc import ABC, abstractmethod
from typing import Dict, List, Optional, Any, Union, Callable
from dataclasses import dataclass, field
import json
import sqlite3
import logging
from datetime import datetime, date
from pathlib import Path
import asyncio
from enum import Enum
import threading
from queue import Queue
import os
try:
from dotenv import load_dotenv
load_dotenv()
except Exception:
# 如果没有.env文件或加载失败使用默认配置
pass
# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class ModuleType(Enum):
"""模块类型枚举"""
DATA_COLLECTION = "data_collection"
USER_ANALYSIS = "user_analysis"
RECOMMENDATION = "recommendation"
GUI_INTERFACE = "gui_interface"
NOTIFICATION = "notification"
@dataclass
class BaseConfig:
"""基础配置类"""
app_name: str = "个性化饮食推荐助手"
version: str = "1.0.0"
debug: bool = True
database_path: str = "data/app.db"
model_path: str = "models/"
log_level: str = "INFO"
# API配置
qwen_api_key: Optional[str] = None
qwen_base_url: str = "https://dashscope.aliyuncs.com/compatible-mode/v1"
qwen_model: str = "qwen-plus-latest"
# 用户配置
max_recommendations: int = 5
min_training_samples: int = 10
model_update_threshold: int = 50
@dataclass
class UserData:
"""统一用户数据结构"""
user_id: str
profile: Dict[str, Any] = field(default_factory=dict)
meals: List[Dict[str, Any]] = field(default_factory=list)
feedback: List[Dict[str, Any]] = field(default_factory=list)
preferences: Dict[str, Any] = field(default_factory=dict)
created_at: str = field(default_factory=lambda: datetime.now().isoformat())
updated_at: str = field(default_factory=lambda: datetime.now().isoformat())
@dataclass
class AnalysisResult:
"""统一分析结果结构"""
module_type: ModuleType
user_id: str
input_data: Any
result: Dict[str, Any]
confidence: float
timestamp: str = field(default_factory=lambda: datetime.now().isoformat())
metadata: Dict[str, Any] = field(default_factory=dict)
class BaseModule(ABC):
"""基础模块抽象类"""
def __init__(self, config: BaseConfig, module_type: ModuleType):
self.config = config
self.module_type = module_type
self.is_initialized = False
self.logger = logging.getLogger(f"{self.__class__.__name__}")
@abstractmethod
def initialize(self) -> bool:
"""初始化模块"""
pass
@abstractmethod
def process(self, input_data: Any, user_data: UserData) -> AnalysisResult:
"""处理数据"""
pass
@abstractmethod
def cleanup(self) -> bool:
"""清理资源"""
pass
def is_ready(self) -> bool:
"""检查模块是否就绪"""
return self.is_initialized
class DataManager:
"""数据管理基座"""
def __init__(self, config: BaseConfig):
self.config = config
self.db_path = Path(config.database_path)
self.db_path.parent.mkdir(parents=True, exist_ok=True)
self._init_database()
def _init_database(self):
"""初始化数据库"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
# 用户表
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
user_id TEXT PRIMARY KEY,
data TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# 分析结果表
cursor.execute('''
CREATE TABLE IF NOT EXISTS analysis_results (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT,
module_type TEXT,
input_data TEXT,
result TEXT,
confidence REAL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
metadata TEXT,
FOREIGN KEY (user_id) REFERENCES users (user_id)
)
''')
# 系统配置表
cursor.execute('''
CREATE TABLE IF NOT EXISTS system_config (
key TEXT PRIMARY KEY,
value TEXT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
conn.commit()
conn.close()
def save_user_data(self, user_data: UserData) -> bool:
"""保存用户数据"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
INSERT OR REPLACE INTO users (user_id, data, updated_at)
VALUES (?, ?, CURRENT_TIMESTAMP)
''', (user_data.user_id, json.dumps(user_data.__dict__)))
conn.commit()
conn.close()
return True
except Exception as e:
logger.error(f"保存用户数据失败: {e}")
return False
def get_user_data(self, user_id: str) -> Optional[UserData]:
"""获取用户数据"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
# 获取用户基本信息
cursor.execute('SELECT data FROM users WHERE user_id = ?', (user_id,))
result = cursor.fetchone()
if not result:
conn.close()
return None
# 解析用户基本信息
data_dict = json.loads(result[0])
# 获取餐食记录
cursor.execute('''
SELECT date, meal_type, foods, quantities, calories, satisfaction_score, food_items
FROM meal_records
WHERE user_id = ?
ORDER BY date DESC
''', (user_id,))
meal_rows = cursor.fetchall()
meals = []
for row in meal_rows:
meal = {
'date': row[0],
'meal_type': row[1],
'foods': json.loads(row[2]) if row[2] else [],
'quantities': json.loads(row[3]) if row[3] else [],
'calories': row[4],
'satisfaction_score': row[5],
'food_items': json.loads(row[6]) if row[6] else []
}
meals.append(meal)
# 获取反馈记录
cursor.execute('''
SELECT date, recommended_foods, user_choice, feedback_type
FROM feedback_records
WHERE user_id = ?
ORDER BY date DESC
''', (user_id,))
feedback_rows = cursor.fetchall()
feedback = []
for row in feedback_rows:
fb = {
'date': row[0],
'recommended_foods': json.loads(row[1]) if row[1] else [],
'user_choice': row[2],
'feedback_type': row[3]
}
feedback.append(fb)
# 获取问卷数据
cursor.execute('''
SELECT questionnaire_type, answers
FROM questionnaire_records
WHERE user_id = ?
''', (user_id,))
questionnaire_rows = cursor.fetchall()
preferences = {}
for row in questionnaire_rows:
preferences[row[0]] = json.loads(row[1]) if row[1] else {}
conn.close()
# 构建完整的用户数据
user_data = UserData(
user_id=data_dict['user_id'],
profile=data_dict.get('profile', {}),
meals=meals,
feedback=feedback,
preferences=preferences,
created_at=data_dict.get('created_at', ''),
updated_at=data_dict.get('updated_at', '')
)
return user_data
except Exception as e:
logger.error(f"获取用户数据失败: {e}")
return None
def save_analysis_result(self, result: AnalysisResult) -> bool:
"""保存分析结果"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
INSERT INTO analysis_results
(user_id, module_type, input_data, result, confidence, metadata)
VALUES (?, ?, ?, ?, ?, ?)
''', (
result.user_id,
result.module_type.value,
json.dumps(result.input_data),
json.dumps(result.result),
result.confidence,
json.dumps(result.metadata)
))
conn.commit()
conn.close()
return True
except Exception as e:
logger.error(f"保存分析结果失败: {e}")
return False
def get_analysis_history(self, user_id: str, module_type: Optional[ModuleType] = None,
limit: int = 10) -> List[AnalysisResult]:
"""获取分析历史"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
if module_type:
cursor.execute('''
SELECT module_type, input_data, result, confidence, timestamp, metadata
FROM analysis_results
WHERE user_id = ? AND module_type = ?
ORDER BY timestamp DESC
LIMIT ?
''', (user_id, module_type.value, limit))
else:
cursor.execute('''
SELECT module_type, input_data, result, confidence, timestamp, metadata
FROM analysis_results
WHERE user_id = ?
ORDER BY timestamp DESC
LIMIT ?
''', (user_id, limit))
results = cursor.fetchall()
conn.close()
analysis_results = []
for row in results:
result = AnalysisResult(
module_type=ModuleType(row[0]),
user_id=user_id,
input_data=json.loads(row[1]),
result=json.loads(row[2]),
confidence=row[3],
timestamp=row[4],
metadata=json.loads(row[5])
)
analysis_results.append(result)
return analysis_results
except Exception as e:
logger.error(f"获取分析历史失败: {e}")
return []
class EventBus:
"""事件总线基座"""
def __init__(self):
self.subscribers: Dict[str, List[Callable]] = {}
self.event_queue = Queue()
self.is_running = False
self.worker_thread = None
def subscribe(self, event_type: str, callback: Callable):
"""订阅事件"""
if event_type not in self.subscribers:
self.subscribers[event_type] = []
self.subscribers[event_type].append(callback)
def unsubscribe(self, event_type: str, callback: Callable):
"""取消订阅"""
if event_type in self.subscribers:
self.subscribers[event_type].remove(callback)
def publish(self, event_type: str, data: Any):
"""发布事件"""
self.event_queue.put((event_type, data))
def start(self):
"""启动事件总线"""
self.is_running = True
self.worker_thread = threading.Thread(target=self._process_events)
self.worker_thread.start()
def stop(self):
"""停止事件总线"""
self.is_running = False
if self.worker_thread:
self.worker_thread.join()
def _process_events(self):
"""处理事件"""
while self.is_running:
try:
event_type, data = self.event_queue.get(timeout=1)
if event_type in self.subscribers:
for callback in self.subscribers[event_type]:
try:
callback(data)
except Exception as e:
logger.error(f"事件处理失败: {e}")
self.event_queue.task_done()
except:
continue
class ModuleManager:
"""模块管理器基座"""
def __init__(self, config: BaseConfig):
self.config = config
self.modules: Dict[ModuleType, BaseModule] = {}
self.data_manager = DataManager(config)
self.event_bus = EventBus()
self.is_initialized = False
def register_module(self, module: BaseModule) -> bool:
"""注册模块"""
try:
# 检查模块是否已经注册
if module.module_type in self.modules:
logger.warning(f"模块 {module.module_type.value} 已经注册,跳过重复注册")
return True
if module.initialize():
self.modules[module.module_type] = module
logger.info(f"模块 {module.module_type.value} 注册成功")
return True
else:
logger.error(f"模块 {module.module_type.value} 初始化失败")
return False
except Exception as e:
logger.error(f"注册模块失败: {e}")
return False
def process_request(self, module_type: ModuleType, input_data: Any,
user_id: str) -> Optional[AnalysisResult]:
"""处理请求"""
if module_type not in self.modules:
logger.error(f"模块 {module_type.value} 未注册")
return None
module = self.modules[module_type]
if not module.is_ready():
logger.error(f"模块 {module_type.value} 未就绪")
return None
# 获取用户数据
user_data = self.data_manager.get_user_data(user_id)
if not user_data:
logger.error(f"用户 {user_id} 数据不存在")
return None
try:
# 处理请求
result = module.process(input_data, user_data)
# 保存结果
self.data_manager.save_analysis_result(result)
# 发布事件
self.event_bus.publish(f"{module_type.value}_completed", result)
return result
except Exception as e:
logger.error(f"处理请求失败: {e}")
return None
def get_module_status(self) -> Dict[str, bool]:
"""获取模块状态"""
return {module_type.value: module.is_ready()
for module_type, module in self.modules.items()}
def initialize_all(self) -> bool:
"""初始化所有模块"""
try:
self.event_bus.start()
self.is_initialized = True
logger.info("模块管理器初始化完成")
return True
except Exception as e:
logger.error(f"初始化失败: {e}")
return False
def cleanup_all(self) -> bool:
"""清理所有模块"""
try:
self.event_bus.stop()
for module in self.modules.values():
module.cleanup()
self.is_initialized = False
logger.info("模块管理器清理完成")
return True
except Exception as e:
logger.error(f"清理失败: {e}")
return False
class AppCore:
"""应用核心基座"""
def __init__(self, config: BaseConfig):
self.config = config
self.module_manager = ModuleManager(config)
self.data_manager = DataManager(config)
self.is_running = False
def start(self) -> bool:
"""启动应用"""
try:
if self.module_manager.initialize_all():
self.is_running = True
logger.info("应用启动成功")
return True
else:
logger.error("应用启动失败")
return False
except Exception as e:
logger.error(f"启动应用失败: {e}")
return False
def stop(self) -> bool:
"""停止应用"""
try:
if self.module_manager.cleanup_all():
self.is_running = False
logger.info("应用停止成功")
return True
else:
logger.error("应用停止失败")
return False
except Exception as e:
logger.error(f"停止应用失败: {e}")
return False
def create_user(self, user_id: str, initial_data: Dict[str, Any]) -> bool:
"""创建用户"""
user_data = UserData(
user_id=user_id,
profile=initial_data.get('profile', {}),
preferences=initial_data.get('preferences', {})
)
return self.module_manager.data_manager.save_user_data(user_data)
def process_user_request(self, module_type: ModuleType, input_data: Any,
user_id: str) -> Optional[AnalysisResult]:
"""处理用户请求"""
return self.module_manager.process_request(module_type, input_data, user_id)
def get_user_data(self, user_id: str) -> Optional[UserData]:
"""获取用户数据"""
return self.module_manager.data_manager.get_user_data(user_id)
def get_analysis_history(self, user_id: str, module_type: Optional[ModuleType] = None) -> List[AnalysisResult]:
"""获取分析历史"""
return self.module_manager.data_manager.get_analysis_history(user_id, module_type)
# 全局应用实例
app_core: Optional[AppCore] = None
def get_app_core() -> AppCore:
"""获取应用核心实例"""
global app_core
if app_core is None:
config = BaseConfig()
app_core = AppCore(config)
return app_core
def initialize_app(config: Optional[BaseConfig] = None) -> bool:
"""初始化应用"""
global app_core
if config is None:
config = BaseConfig()
app_core = AppCore(config)
return app_core.start()
def cleanup_app() -> bool:
"""清理应用"""
global app_core
if app_core:
return app_core.stop()
return True
if __name__ == "__main__":
# 测试基座架构
print("测试核心基座架构...")
# 初始化应用
if initialize_app():
print("✅ 应用初始化成功")
# 创建测试用户
test_user_id = "test_user_001"
initial_data = {
"profile": {
"name": "测试用户",
"age": 25,
"gender": ""
},
"preferences": {
"taste": "sweet",
"diet": "balanced"
}
}
app = get_app_core()
if app.create_user(test_user_id, initial_data):
print("✅ 用户创建成功")
# 获取用户数据
user_data = app.get_user_data(test_user_id)
if user_data:
print(f"✅ 用户数据获取成功: {user_data.user_id}")
# 清理应用
cleanup_app()
print("✅ 应用清理完成")
else:
print("❌ 应用初始化失败")
print("基座架构测试完成!")