feat(database): 完成API层、Utils层和兼容层重构 (Stage 4-6)
Stage 4: API层重构
=================
新增文件:
- api/crud.py (430行): CRUDBase泛型类,提供12个CRUD方法
* get, get_by, get_multi, create, update, delete
* count, exists, get_or_create, bulk_create, bulk_update
* 集成缓存: 自动缓存读操作,写操作清除缓存
* 集成批处理: 可选use_batch参数透明使用AdaptiveBatchScheduler
- api/query.py (461行): 高级查询构建器
* QueryBuilder: 链式调用,MongoDB风格操作符
- 操作符: __gt, __lt, __gte, __lte, __ne, __in, __nin, __like, __isnull
- 方法: filter, filter_or, order_by, limit, offset, no_cache
- 执行: all, first, count, exists, paginate
* AggregateQuery: 聚合查询
- sum, avg, max, min, group_by_count
- api/specialized.py (461行): 业务特定API
* ActionRecords: store_action_info, get_recent_actions
* Messages: get_chat_history, get_message_count, save_message
* PersonInfo: get_or_create_person, update_person_affinity
* ChatStreams: get_or_create_chat_stream, get_active_streams
* LLMUsage: record_llm_usage, get_usage_statistics
* UserRelationships: get_user_relationship, update_relationship_affinity
- 更新api/__init__.py: 导出所有API接口
Stage 5: Utils层实现
===================
新增文件:
- utils/decorators.py (320行): 数据库操作装饰器
* @retry: 自动重试失败操作,指数退避
* @timeout: 超时控制
* @cached: 自动缓存函数结果
* @measure_time: 性能测量,慢查询日志
* @transactional: 事务管理,自动提交/回滚
* @db_operation: 组合装饰器
- utils/monitoring.py (330行): 性能监控系统
* DatabaseMonitor: 单例监控器
* OperationMetrics: 操作指标 (次数、时间、错误)
* DatabaseMetrics: 全局指标
- 连接池统计
- 缓存命中率
- 批处理统计
- 预加载统计
* 便捷函数: get_monitor, record_operation, print_stats
- 更新utils/__init__.py: 导出装饰器和监控函数
Stage 6: 兼容层实现
==================
新增目录: compatibility/
- adapter.py (370行): 向后兼容适配器
* 完全兼容旧API签名: db_query, db_save, db_get, store_action_info
* 支持MongoDB风格操作符 (\, \, \)
* 内部使用新架构 (QueryBuilder + CRUDBase)
* 保持返回dict格式不变
* MODEL_MAPPING: 25个模型映射
- __init__.py: 导出兼容API
更新database/__init__.py:
- 导出核心层 (engine, session, models, migration)
- 导出优化层 (cache, preloader, batch_scheduler)
- 导出API层 (CRUD, Query, 业务API)
- 导出Utils层 (装饰器, 监控)
- 导出兼容层 (db_query, db_save等)
核心特性
========
类型安全: Generic[T]提供完整类型推断
缓存透明: 自动缓存,用户无需关心
批处理透明: 可选批处理,自动优化高频写入
链式查询: 流畅的API设计
业务封装: 常用操作封装成便捷函数
向后兼容: 兼容层保证现有代码无缝迁移
性能监控: 完整的指标收集和报告
统计数据
========
- 新增文件: 7个
- 代码行数: ~2050行
- API函数: 14个业务API + 6个装饰器
- 兼容函数: 5个 (db_query, db_save, db_get等)
下一步
======
- 更新28个文件的import语句 (从sqlalchemy_database_api迁移)
- 移动旧文件到old/目录
- 编写Stage 4-6的测试
- 集成测试验证兼容性
This commit is contained in:
450
src/common/database/api/specialized.py
Normal file
450
src/common/database/api/specialized.py
Normal file
@@ -0,0 +1,450 @@
|
||||
"""业务特定API
|
||||
|
||||
提供特定业务场景的数据库操作函数
|
||||
"""
|
||||
|
||||
import time
|
||||
from typing import Any, Optional
|
||||
|
||||
import orjson
|
||||
|
||||
from src.common.database.api.crud import CRUDBase
|
||||
from src.common.database.api.query import QueryBuilder
|
||||
from src.common.database.core.models import (
|
||||
ActionRecords,
|
||||
ChatStreams,
|
||||
LLMUsage,
|
||||
Messages,
|
||||
PersonInfo,
|
||||
UserRelationships,
|
||||
)
|
||||
from src.common.database.core.session import get_db_session
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("database.specialized")
|
||||
|
||||
|
||||
# CRUD实例
|
||||
_action_records_crud = CRUDBase(ActionRecords)
|
||||
_chat_streams_crud = CRUDBase(ChatStreams)
|
||||
_llm_usage_crud = CRUDBase(LLMUsage)
|
||||
_messages_crud = CRUDBase(Messages)
|
||||
_person_info_crud = CRUDBase(PersonInfo)
|
||||
_user_relationships_crud = CRUDBase(UserRelationships)
|
||||
|
||||
|
||||
# ===== ActionRecords 业务API =====
|
||||
async def store_action_info(
|
||||
chat_stream=None,
|
||||
action_build_into_prompt: bool = False,
|
||||
action_prompt_display: str = "",
|
||||
action_done: bool = True,
|
||||
thinking_id: str = "",
|
||||
action_data: Optional[dict] = None,
|
||||
action_name: str = "",
|
||||
) -> Optional[dict[str, Any]]:
|
||||
"""存储动作信息到数据库
|
||||
|
||||
Args:
|
||||
chat_stream: 聊天流对象
|
||||
action_build_into_prompt: 是否将此动作构建到提示中
|
||||
action_prompt_display: 动作的提示显示文本
|
||||
action_done: 动作是否完成
|
||||
thinking_id: 关联的思考ID
|
||||
action_data: 动作数据字典
|
||||
action_name: 动作名称
|
||||
|
||||
Returns:
|
||||
保存的记录数据或None
|
||||
"""
|
||||
try:
|
||||
# 构建动作记录数据
|
||||
action_id = thinking_id or str(int(time.time() * 1000000))
|
||||
record_data = {
|
||||
"action_id": action_id,
|
||||
"time": time.time(),
|
||||
"action_name": action_name,
|
||||
"action_data": orjson.dumps(action_data or {}).decode("utf-8"),
|
||||
"action_done": action_done,
|
||||
"action_build_into_prompt": action_build_into_prompt,
|
||||
"action_prompt_display": action_prompt_display,
|
||||
}
|
||||
|
||||
# 从chat_stream获取聊天信息
|
||||
if chat_stream:
|
||||
record_data.update(
|
||||
{
|
||||
"chat_id": getattr(chat_stream, "stream_id", ""),
|
||||
"chat_info_stream_id": getattr(chat_stream, "stream_id", ""),
|
||||
"chat_info_platform": getattr(chat_stream, "platform", ""),
|
||||
}
|
||||
)
|
||||
else:
|
||||
record_data.update(
|
||||
{
|
||||
"chat_id": "",
|
||||
"chat_info_stream_id": "",
|
||||
"chat_info_platform": "",
|
||||
}
|
||||
)
|
||||
|
||||
# 使用get_or_create保存记录
|
||||
saved_record = await _action_records_crud.get_or_create(
|
||||
defaults=record_data,
|
||||
action_id=action_id,
|
||||
)
|
||||
|
||||
if saved_record:
|
||||
logger.debug(f"成功存储动作信息: {action_name} (ID: {action_id})")
|
||||
return {col.name: getattr(saved_record, col.name) for col in saved_record.__table__.columns}
|
||||
else:
|
||||
logger.error(f"存储动作信息失败: {action_name}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"存储动作信息时发生错误: {e}", exc_info=True)
|
||||
return None
|
||||
|
||||
|
||||
async def get_recent_actions(
|
||||
chat_id: str,
|
||||
limit: int = 10,
|
||||
) -> list[ActionRecords]:
|
||||
"""获取最近的动作记录
|
||||
|
||||
Args:
|
||||
chat_id: 聊天ID
|
||||
limit: 限制数量
|
||||
|
||||
Returns:
|
||||
动作记录列表
|
||||
"""
|
||||
query = QueryBuilder(ActionRecords)
|
||||
return await query.filter(chat_id=chat_id).order_by("-time").limit(limit).all()
|
||||
|
||||
|
||||
# ===== Messages 业务API =====
|
||||
async def get_chat_history(
|
||||
stream_id: str,
|
||||
limit: int = 50,
|
||||
offset: int = 0,
|
||||
) -> list[Messages]:
|
||||
"""获取聊天历史
|
||||
|
||||
Args:
|
||||
stream_id: 流ID
|
||||
limit: 限制数量
|
||||
offset: 偏移量
|
||||
|
||||
Returns:
|
||||
消息列表
|
||||
"""
|
||||
query = QueryBuilder(Messages)
|
||||
return await (
|
||||
query.filter(chat_info_stream_id=stream_id)
|
||||
.order_by("-time")
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
.all()
|
||||
)
|
||||
|
||||
|
||||
async def get_message_count(stream_id: str) -> int:
|
||||
"""获取消息数量
|
||||
|
||||
Args:
|
||||
stream_id: 流ID
|
||||
|
||||
Returns:
|
||||
消息数量
|
||||
"""
|
||||
query = QueryBuilder(Messages)
|
||||
return await query.filter(chat_info_stream_id=stream_id).count()
|
||||
|
||||
|
||||
async def save_message(
|
||||
message_data: dict[str, Any],
|
||||
use_batch: bool = True,
|
||||
) -> Optional[Messages]:
|
||||
"""保存消息
|
||||
|
||||
Args:
|
||||
message_data: 消息数据
|
||||
use_batch: 是否使用批处理
|
||||
|
||||
Returns:
|
||||
保存的消息实例
|
||||
"""
|
||||
return await _messages_crud.create(message_data, use_batch=use_batch)
|
||||
|
||||
|
||||
# ===== PersonInfo 业务API =====
|
||||
async def get_or_create_person(
|
||||
platform: str,
|
||||
person_id: str,
|
||||
defaults: Optional[dict[str, Any]] = None,
|
||||
) -> Optional[PersonInfo]:
|
||||
"""获取或创建人员信息
|
||||
|
||||
Args:
|
||||
platform: 平台
|
||||
person_id: 人员ID
|
||||
defaults: 默认值
|
||||
|
||||
Returns:
|
||||
人员信息实例
|
||||
"""
|
||||
return await _person_info_crud.get_or_create(
|
||||
defaults=defaults or {},
|
||||
platform=platform,
|
||||
person_id=person_id,
|
||||
)
|
||||
|
||||
|
||||
async def update_person_affinity(
|
||||
platform: str,
|
||||
person_id: str,
|
||||
affinity_delta: float,
|
||||
) -> bool:
|
||||
"""更新人员好感度
|
||||
|
||||
Args:
|
||||
platform: 平台
|
||||
person_id: 人员ID
|
||||
affinity_delta: 好感度变化值
|
||||
|
||||
Returns:
|
||||
是否成功
|
||||
"""
|
||||
try:
|
||||
# 获取现有人员
|
||||
person = await _person_info_crud.get_by(
|
||||
platform=platform,
|
||||
person_id=person_id,
|
||||
)
|
||||
|
||||
if not person:
|
||||
logger.warning(f"人员不存在: {platform}/{person_id}")
|
||||
return False
|
||||
|
||||
# 更新好感度
|
||||
new_affinity = (person.affinity or 0.0) + affinity_delta
|
||||
await _person_info_crud.update(
|
||||
person.id,
|
||||
{"affinity": new_affinity},
|
||||
)
|
||||
|
||||
logger.debug(f"更新好感度: {platform}/{person_id} {affinity_delta:+.2f} -> {new_affinity:.2f}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"更新好感度失败: {e}", exc_info=True)
|
||||
return False
|
||||
|
||||
|
||||
# ===== ChatStreams 业务API =====
|
||||
async def get_or_create_chat_stream(
|
||||
stream_id: str,
|
||||
platform: str,
|
||||
defaults: Optional[dict[str, Any]] = None,
|
||||
) -> Optional[ChatStreams]:
|
||||
"""获取或创建聊天流
|
||||
|
||||
Args:
|
||||
stream_id: 流ID
|
||||
platform: 平台
|
||||
defaults: 默认值
|
||||
|
||||
Returns:
|
||||
聊天流实例
|
||||
"""
|
||||
return await _chat_streams_crud.get_or_create(
|
||||
defaults=defaults or {},
|
||||
stream_id=stream_id,
|
||||
platform=platform,
|
||||
)
|
||||
|
||||
|
||||
async def get_active_streams(
|
||||
platform: Optional[str] = None,
|
||||
limit: int = 100,
|
||||
) -> list[ChatStreams]:
|
||||
"""获取活跃的聊天流
|
||||
|
||||
Args:
|
||||
platform: 平台(可选)
|
||||
limit: 限制数量
|
||||
|
||||
Returns:
|
||||
聊天流列表
|
||||
"""
|
||||
query = QueryBuilder(ChatStreams)
|
||||
|
||||
if platform:
|
||||
query = query.filter(platform=platform)
|
||||
|
||||
return await query.order_by("-last_message_time").limit(limit).all()
|
||||
|
||||
|
||||
# ===== LLMUsage 业务API =====
|
||||
async def record_llm_usage(
|
||||
model_name: str,
|
||||
input_tokens: int,
|
||||
output_tokens: int,
|
||||
stream_id: Optional[str] = None,
|
||||
platform: Optional[str] = None,
|
||||
use_batch: bool = True,
|
||||
) -> Optional[LLMUsage]:
|
||||
"""记录LLM使用情况
|
||||
|
||||
Args:
|
||||
model_name: 模型名称
|
||||
input_tokens: 输入token数
|
||||
output_tokens: 输出token数
|
||||
stream_id: 流ID
|
||||
platform: 平台
|
||||
use_batch: 是否使用批处理
|
||||
|
||||
Returns:
|
||||
LLM使用记录实例
|
||||
"""
|
||||
usage_data = {
|
||||
"model_name": model_name,
|
||||
"input_tokens": input_tokens,
|
||||
"output_tokens": output_tokens,
|
||||
"total_tokens": input_tokens + output_tokens,
|
||||
"timestamp": time.time(),
|
||||
}
|
||||
|
||||
if stream_id:
|
||||
usage_data["stream_id"] = stream_id
|
||||
if platform:
|
||||
usage_data["platform"] = platform
|
||||
|
||||
return await _llm_usage_crud.create(usage_data, use_batch=use_batch)
|
||||
|
||||
|
||||
async def get_usage_statistics(
|
||||
start_time: Optional[float] = None,
|
||||
end_time: Optional[float] = None,
|
||||
model_name: Optional[str] = None,
|
||||
) -> dict[str, Any]:
|
||||
"""获取使用统计
|
||||
|
||||
Args:
|
||||
start_time: 开始时间戳
|
||||
end_time: 结束时间戳
|
||||
model_name: 模型名称
|
||||
|
||||
Returns:
|
||||
统计数据字典
|
||||
"""
|
||||
from src.common.database.api.query import AggregateQuery
|
||||
|
||||
query = AggregateQuery(LLMUsage)
|
||||
|
||||
# 添加时间过滤
|
||||
if start_time:
|
||||
async with get_db_session() as session:
|
||||
from sqlalchemy import and_
|
||||
|
||||
conditions = []
|
||||
if start_time:
|
||||
conditions.append(LLMUsage.timestamp >= start_time)
|
||||
if end_time:
|
||||
conditions.append(LLMUsage.timestamp <= end_time)
|
||||
if model_name:
|
||||
conditions.append(LLMUsage.model_name == model_name)
|
||||
|
||||
if conditions:
|
||||
query._conditions = conditions
|
||||
|
||||
# 聚合统计
|
||||
total_input = await query.sum("input_tokens")
|
||||
total_output = await query.sum("output_tokens")
|
||||
total_count = await query.filter().count() if hasattr(query, "count") else 0
|
||||
|
||||
return {
|
||||
"total_input_tokens": int(total_input),
|
||||
"total_output_tokens": int(total_output),
|
||||
"total_tokens": int(total_input + total_output),
|
||||
"request_count": total_count,
|
||||
}
|
||||
|
||||
|
||||
# ===== UserRelationships 业务API =====
|
||||
async def get_user_relationship(
|
||||
platform: str,
|
||||
user_id: str,
|
||||
target_id: str,
|
||||
) -> Optional[UserRelationships]:
|
||||
"""获取用户关系
|
||||
|
||||
Args:
|
||||
platform: 平台
|
||||
user_id: 用户ID
|
||||
target_id: 目标用户ID
|
||||
|
||||
Returns:
|
||||
用户关系实例
|
||||
"""
|
||||
return await _user_relationships_crud.get_by(
|
||||
platform=platform,
|
||||
user_id=user_id,
|
||||
target_id=target_id,
|
||||
)
|
||||
|
||||
|
||||
async def update_relationship_affinity(
|
||||
platform: str,
|
||||
user_id: str,
|
||||
target_id: str,
|
||||
affinity_delta: float,
|
||||
) -> bool:
|
||||
"""更新关系好感度
|
||||
|
||||
Args:
|
||||
platform: 平台
|
||||
user_id: 用户ID
|
||||
target_id: 目标用户ID
|
||||
affinity_delta: 好感度变化值
|
||||
|
||||
Returns:
|
||||
是否成功
|
||||
"""
|
||||
try:
|
||||
# 获取或创建关系
|
||||
relationship = await _user_relationships_crud.get_or_create(
|
||||
defaults={"affinity": 0.0, "interaction_count": 0},
|
||||
platform=platform,
|
||||
user_id=user_id,
|
||||
target_id=target_id,
|
||||
)
|
||||
|
||||
if not relationship:
|
||||
logger.error(f"无法创建关系: {platform}/{user_id}->{target_id}")
|
||||
return False
|
||||
|
||||
# 更新好感度和互动次数
|
||||
new_affinity = (relationship.affinity or 0.0) + affinity_delta
|
||||
new_count = (relationship.interaction_count or 0) + 1
|
||||
|
||||
await _user_relationships_crud.update(
|
||||
relationship.id,
|
||||
{
|
||||
"affinity": new_affinity,
|
||||
"interaction_count": new_count,
|
||||
"last_interaction_time": time.time(),
|
||||
},
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
f"更新关系: {platform}/{user_id}->{target_id} "
|
||||
f"好感度{affinity_delta:+.2f}->{new_affinity:.2f} "
|
||||
f"互动{new_count}次"
|
||||
)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"更新关系好感度失败: {e}", exc_info=True)
|
||||
return False
|
||||
Reference in New Issue
Block a user