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:
Windpicker-owo
2025-11-01 13:27:33 +08:00
parent c0db312df5
commit 64bdd0df12
10 changed files with 2563 additions and 5 deletions

View 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