refactor(database): 阶段二 - 完成核心层重构
- models.py: 迁移25个模型类,使用统一的Mapped类型注解 * 包含: ChatStreams, Messages, PersonInfo, LLMUsage等 * 新增: PermissionNodes, UserPermissions, UserRelationships * 654行纯模型定义代码,无初始化逻辑 - migration.py: 重构数据库迁移逻辑 * check_and_migrate_database: 自动检查和迁移表结构 * create_all_tables: 快速创建所有表 * drop_all_tables: 测试用删除所有表 * 使用新架构的engine和models - __init__.py: 完善导出清单 * 导出所有25个模型类 * 导出迁移函数 * 导出Base和工具函数 - 辅助脚本: * extract_models.py: 自动提取模型定义 * cleanup_models.py: 清理非模型代码 核心层现已完整,下一步进入优化层实现
This commit is contained in:
49
scripts/cleanup_models.py
Normal file
49
scripts/cleanup_models.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""清理 core/models.py,只保留模型定义"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
# 文件路径
|
||||||
|
models_file = os.path.join(
|
||||||
|
os.path.dirname(os.path.dirname(__file__)),
|
||||||
|
"src",
|
||||||
|
"common",
|
||||||
|
"database",
|
||||||
|
"core",
|
||||||
|
"models.py"
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"正在清理文件: {models_file}")
|
||||||
|
|
||||||
|
# 读取文件
|
||||||
|
with open(models_file, "r", encoding="utf-8") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
# 找到最后一个模型类的结束位置(MonthlyPlan的 __table_args__ 结束)
|
||||||
|
# 我们要保留到第593行(包含)
|
||||||
|
keep_lines = []
|
||||||
|
found_end = False
|
||||||
|
|
||||||
|
for i, line in enumerate(lines, 1):
|
||||||
|
keep_lines.append(line)
|
||||||
|
|
||||||
|
# 检查是否到达 MonthlyPlan 的 __table_args__ 结束
|
||||||
|
if i > 580 and line.strip() == ")":
|
||||||
|
# 再检查前一行是否有 Index 相关内容
|
||||||
|
if "idx_monthlyplan" in "".join(lines[max(0, i-5):i]):
|
||||||
|
print(f"找到模型定义结束位置: 第 {i} 行")
|
||||||
|
found_end = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not found_end:
|
||||||
|
print("❌ 未找到模型定义结束标记")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
# 写回文件
|
||||||
|
with open(models_file, "w", encoding="utf-8") as f:
|
||||||
|
f.writelines(keep_lines)
|
||||||
|
|
||||||
|
print(f"✅ 文件清理完成")
|
||||||
|
print(f"保留行数: {len(keep_lines)}")
|
||||||
|
print(f"原始行数: {len(lines)}")
|
||||||
|
print(f"删除行数: {len(lines) - len(keep_lines)}")
|
||||||
66
scripts/extract_models.py
Normal file
66
scripts/extract_models.py
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""提取models.py中的模型定义"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
# 读取原始文件
|
||||||
|
with open('src/common/database/sqlalchemy_models.py', 'r', encoding='utf-8') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
# 找到get_string_field函数的开始和结束
|
||||||
|
get_string_field_start = content.find('# MySQL兼容的字段类型辅助函数')
|
||||||
|
get_string_field_end = content.find('\n\nclass ChatStreams(Base):')
|
||||||
|
get_string_field = content[get_string_field_start:get_string_field_end]
|
||||||
|
|
||||||
|
# 找到第一个class定义开始
|
||||||
|
first_class_pos = content.find('class ChatStreams(Base):')
|
||||||
|
|
||||||
|
# 找到所有class定义,直到遇到非class的def
|
||||||
|
# 简单策略:找到所有以"class "开头且继承Base的类
|
||||||
|
classes_pattern = r'class \w+\(Base\):.*?(?=\nclass \w+\(Base\):|$)'
|
||||||
|
matches = list(re.finditer(classes_pattern, content[first_class_pos:], re.DOTALL))
|
||||||
|
|
||||||
|
if matches:
|
||||||
|
# 取最后一个匹配的结束位置
|
||||||
|
models_content = content[first_class_pos:first_class_pos + matches[-1].end()]
|
||||||
|
else:
|
||||||
|
# 备用方案:从第一个class到文件的85%位置
|
||||||
|
models_end = int(len(content) * 0.85)
|
||||||
|
models_content = content[first_class_pos:models_end]
|
||||||
|
|
||||||
|
# 创建新文件内容
|
||||||
|
header = '''"""SQLAlchemy数据库模型定义
|
||||||
|
|
||||||
|
本文件只包含纯模型定义,使用SQLAlchemy 2.0的Mapped类型注解风格。
|
||||||
|
引擎和会话管理已移至core/engine.py和core/session.py。
|
||||||
|
|
||||||
|
所有模型使用统一的类型注解风格:
|
||||||
|
field_name: Mapped[PyType] = mapped_column(Type, ...)
|
||||||
|
|
||||||
|
这样IDE/Pylance能正确推断实例属性类型。
|
||||||
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
from sqlalchemy import Boolean, DateTime, Float, Index, Integer, String, Text
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.orm import Mapped, mapped_column
|
||||||
|
|
||||||
|
# 创建基类
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
new_content = header + get_string_field + '\n\n' + models_content
|
||||||
|
|
||||||
|
# 写入新文件
|
||||||
|
with open('src/common/database/core/models.py', 'w', encoding='utf-8') as f:
|
||||||
|
f.write(new_content)
|
||||||
|
|
||||||
|
print('✅ Models file rewritten successfully')
|
||||||
|
print(f'File size: {len(new_content)} characters')
|
||||||
|
pattern = r"^class \w+\(Base\):"
|
||||||
|
model_count = len(re.findall(pattern, models_content, re.MULTILINE))
|
||||||
|
print(f'Number of model classes: {model_count}')
|
||||||
@@ -8,14 +8,79 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from .engine import close_engine, get_engine, get_engine_info
|
from .engine import close_engine, get_engine, get_engine_info
|
||||||
|
from .migration import check_and_migrate_database, create_all_tables, drop_all_tables
|
||||||
|
from .models import (
|
||||||
|
ActionRecords,
|
||||||
|
AntiInjectionStats,
|
||||||
|
BanUser,
|
||||||
|
Base,
|
||||||
|
BotPersonalityInterests,
|
||||||
|
CacheEntries,
|
||||||
|
ChatStreams,
|
||||||
|
Emoji,
|
||||||
|
Expression,
|
||||||
|
get_string_field,
|
||||||
|
GraphEdges,
|
||||||
|
GraphNodes,
|
||||||
|
ImageDescriptions,
|
||||||
|
Images,
|
||||||
|
LLMUsage,
|
||||||
|
MaiZoneScheduleStatus,
|
||||||
|
Memory,
|
||||||
|
Messages,
|
||||||
|
MonthlyPlan,
|
||||||
|
OnlineTime,
|
||||||
|
PermissionNodes,
|
||||||
|
PersonInfo,
|
||||||
|
Schedule,
|
||||||
|
ThinkingLog,
|
||||||
|
UserPermissions,
|
||||||
|
UserRelationships,
|
||||||
|
Videos,
|
||||||
|
)
|
||||||
from .session import get_db_session, get_db_session_direct, get_session_factory, reset_session_factory
|
from .session import get_db_session, get_db_session_direct, get_session_factory, reset_session_factory
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
# Engine
|
||||||
"get_engine",
|
"get_engine",
|
||||||
"close_engine",
|
"close_engine",
|
||||||
"get_engine_info",
|
"get_engine_info",
|
||||||
|
# Session
|
||||||
"get_db_session",
|
"get_db_session",
|
||||||
"get_db_session_direct",
|
"get_db_session_direct",
|
||||||
"get_session_factory",
|
"get_session_factory",
|
||||||
"reset_session_factory",
|
"reset_session_factory",
|
||||||
|
# Migration
|
||||||
|
"check_and_migrate_database",
|
||||||
|
"create_all_tables",
|
||||||
|
"drop_all_tables",
|
||||||
|
# Models - Base
|
||||||
|
"Base",
|
||||||
|
"get_string_field",
|
||||||
|
# Models - Tables (按字母顺序)
|
||||||
|
"ActionRecords",
|
||||||
|
"AntiInjectionStats",
|
||||||
|
"BanUser",
|
||||||
|
"BotPersonalityInterests",
|
||||||
|
"CacheEntries",
|
||||||
|
"ChatStreams",
|
||||||
|
"Emoji",
|
||||||
|
"Expression",
|
||||||
|
"GraphEdges",
|
||||||
|
"GraphNodes",
|
||||||
|
"ImageDescriptions",
|
||||||
|
"Images",
|
||||||
|
"LLMUsage",
|
||||||
|
"MaiZoneScheduleStatus",
|
||||||
|
"Memory",
|
||||||
|
"Messages",
|
||||||
|
"MonthlyPlan",
|
||||||
|
"OnlineTime",
|
||||||
|
"PermissionNodes",
|
||||||
|
"PersonInfo",
|
||||||
|
"Schedule",
|
||||||
|
"ThinkingLog",
|
||||||
|
"UserPermissions",
|
||||||
|
"UserRelationships",
|
||||||
|
"Videos",
|
||||||
]
|
]
|
||||||
|
|||||||
230
src/common/database/core/migration.py
Normal file
230
src/common/database/core/migration.py
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
"""数据库迁移模块
|
||||||
|
|
||||||
|
此模块负责数据库结构的自动检查和迁移:
|
||||||
|
- 自动创建不存在的表
|
||||||
|
- 自动为现有表添加缺失的列
|
||||||
|
- 自动为现有表创建缺失的索引
|
||||||
|
|
||||||
|
使用新架构的 engine 和 models
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sqlalchemy import inspect
|
||||||
|
from sqlalchemy.sql import text
|
||||||
|
|
||||||
|
from src.common.database.core.engine import get_engine
|
||||||
|
from src.common.database.core.models import Base
|
||||||
|
from src.common.logger import get_logger
|
||||||
|
|
||||||
|
logger = get_logger("db_migration")
|
||||||
|
|
||||||
|
|
||||||
|
async def check_and_migrate_database(existing_engine=None):
|
||||||
|
"""异步检查数据库结构并自动迁移
|
||||||
|
|
||||||
|
自动执行以下操作:
|
||||||
|
- 创建不存在的表
|
||||||
|
- 为现有表添加缺失的列
|
||||||
|
- 为现有表创建缺失的索引
|
||||||
|
|
||||||
|
Args:
|
||||||
|
existing_engine: 可选的已存在的数据库引擎。如果提供,将使用该引擎;否则获取全局引擎
|
||||||
|
|
||||||
|
Note:
|
||||||
|
此函数是幂等的,可以安全地多次调用
|
||||||
|
"""
|
||||||
|
logger.info("正在检查数据库结构并执行自动迁移...")
|
||||||
|
engine = existing_engine if existing_engine is not None else await get_engine()
|
||||||
|
|
||||||
|
async with engine.connect() as connection:
|
||||||
|
# 在同步上下文中运行inspector操作
|
||||||
|
def get_inspector(sync_conn):
|
||||||
|
return inspect(sync_conn)
|
||||||
|
|
||||||
|
inspector = await connection.run_sync(get_inspector)
|
||||||
|
|
||||||
|
# 获取数据库中已存在的表名
|
||||||
|
db_table_names = await connection.run_sync(
|
||||||
|
lambda conn: set(inspector.get_table_names())
|
||||||
|
)
|
||||||
|
|
||||||
|
# 1. 首先处理表的创建
|
||||||
|
tables_to_create = []
|
||||||
|
for table_name, table in Base.metadata.tables.items():
|
||||||
|
if table_name not in db_table_names:
|
||||||
|
tables_to_create.append(table)
|
||||||
|
|
||||||
|
if tables_to_create:
|
||||||
|
logger.info(f"发现 {len(tables_to_create)} 个不存在的表,正在创建...")
|
||||||
|
try:
|
||||||
|
# 一次性创建所有缺失的表
|
||||||
|
await connection.run_sync(
|
||||||
|
lambda sync_conn: Base.metadata.create_all(
|
||||||
|
sync_conn, tables=tables_to_create
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for table in tables_to_create:
|
||||||
|
logger.info(f"表 '{table.name}' 创建成功。")
|
||||||
|
db_table_names.add(table.name) # 将新创建的表添加到集合中
|
||||||
|
|
||||||
|
# 提交表创建事务
|
||||||
|
await connection.commit()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"创建表时失败: {e}", exc_info=True)
|
||||||
|
await connection.rollback()
|
||||||
|
|
||||||
|
# 2. 然后处理现有表的列和索引的添加
|
||||||
|
for table_name, table in Base.metadata.tables.items():
|
||||||
|
if table_name not in db_table_names:
|
||||||
|
logger.warning(
|
||||||
|
f"跳过检查表 '{table_name}',因为它在创建步骤中可能已失败。"
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
logger.debug(f"正在检查表 '{table_name}' 的列和索引...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 检查并添加缺失的列
|
||||||
|
db_columns = await connection.run_sync(
|
||||||
|
lambda conn: {
|
||||||
|
col["name"] for col in inspector.get_columns(table_name)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
model_columns = {col.name for col in table.c}
|
||||||
|
missing_columns = model_columns - db_columns
|
||||||
|
|
||||||
|
if missing_columns:
|
||||||
|
logger.info(
|
||||||
|
f"在表 '{table_name}' 中发现缺失的列: {', '.join(missing_columns)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def add_columns_sync(conn):
|
||||||
|
dialect = conn.dialect
|
||||||
|
compiler = dialect.ddl_compiler(dialect, None)
|
||||||
|
|
||||||
|
for column_name in missing_columns:
|
||||||
|
column = table.c[column_name]
|
||||||
|
column_type = compiler.get_column_specification(column)
|
||||||
|
sql = f"ALTER TABLE {table.name} ADD COLUMN {column.name} {column_type}"
|
||||||
|
|
||||||
|
if column.default:
|
||||||
|
# 手动处理不同方言的默认值
|
||||||
|
default_arg = column.default.arg
|
||||||
|
if dialect.name == "sqlite" and isinstance(
|
||||||
|
default_arg, bool
|
||||||
|
):
|
||||||
|
# SQLite 将布尔值存储为 0 或 1
|
||||||
|
default_value = "1" if default_arg else "0"
|
||||||
|
elif hasattr(compiler, "render_literal_value"):
|
||||||
|
try:
|
||||||
|
# 尝试使用 render_literal_value
|
||||||
|
default_value = compiler.render_literal_value(
|
||||||
|
default_arg, column.type
|
||||||
|
)
|
||||||
|
except AttributeError:
|
||||||
|
# 如果失败,则回退到简单的字符串转换
|
||||||
|
default_value = (
|
||||||
|
f"'{default_arg}'"
|
||||||
|
if isinstance(default_arg, str)
|
||||||
|
else str(default_arg)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# 对于没有 render_literal_value 的旧版或特定方言
|
||||||
|
default_value = (
|
||||||
|
f"'{default_arg}'"
|
||||||
|
if isinstance(default_arg, str)
|
||||||
|
else str(default_arg)
|
||||||
|
)
|
||||||
|
|
||||||
|
sql += f" DEFAULT {default_value}"
|
||||||
|
|
||||||
|
if not column.nullable:
|
||||||
|
sql += " NOT NULL"
|
||||||
|
|
||||||
|
conn.execute(text(sql))
|
||||||
|
logger.info(f"成功向表 '{table_name}' 添加列 '{column_name}'。")
|
||||||
|
|
||||||
|
await connection.run_sync(add_columns_sync)
|
||||||
|
# 提交列添加事务
|
||||||
|
await connection.commit()
|
||||||
|
else:
|
||||||
|
logger.info(f"表 '{table_name}' 的列结构一致。")
|
||||||
|
|
||||||
|
# 检查并创建缺失的索引
|
||||||
|
db_indexes = await connection.run_sync(
|
||||||
|
lambda conn: {
|
||||||
|
idx["name"] for idx in inspector.get_indexes(table_name)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
model_indexes = {idx.name for idx in table.indexes}
|
||||||
|
missing_indexes = model_indexes - db_indexes
|
||||||
|
|
||||||
|
if missing_indexes:
|
||||||
|
logger.info(
|
||||||
|
f"在表 '{table_name}' 中发现缺失的索引: {', '.join(missing_indexes)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def add_indexes_sync(conn):
|
||||||
|
for index_name in missing_indexes:
|
||||||
|
index_obj = next(
|
||||||
|
(idx for idx in table.indexes if idx.name == index_name),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
if index_obj is not None:
|
||||||
|
index_obj.create(conn)
|
||||||
|
logger.info(
|
||||||
|
f"成功为表 '{table_name}' 创建索引 '{index_name}'。"
|
||||||
|
)
|
||||||
|
|
||||||
|
await connection.run_sync(add_indexes_sync)
|
||||||
|
# 提交索引创建事务
|
||||||
|
await connection.commit()
|
||||||
|
else:
|
||||||
|
logger.debug(f"表 '{table_name}' 的索引一致。")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"在处理表 '{table_name}' 时发生意外错误: {e}", exc_info=True)
|
||||||
|
await connection.rollback()
|
||||||
|
continue
|
||||||
|
|
||||||
|
logger.info("数据库结构检查与自动迁移完成。")
|
||||||
|
|
||||||
|
|
||||||
|
async def create_all_tables(existing_engine=None):
|
||||||
|
"""创建所有表(不进行迁移检查)
|
||||||
|
|
||||||
|
直接创建所有在 Base.metadata 中定义的表。
|
||||||
|
如果表已存在,将被跳过。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
existing_engine: 可选的已存在的数据库引擎
|
||||||
|
|
||||||
|
Note:
|
||||||
|
生产环境建议使用 check_and_migrate_database()
|
||||||
|
"""
|
||||||
|
logger.info("正在创建所有数据库表...")
|
||||||
|
engine = existing_engine if existing_engine is not None else await get_engine()
|
||||||
|
|
||||||
|
async with engine.begin() as connection:
|
||||||
|
await connection.run_sync(Base.metadata.create_all)
|
||||||
|
|
||||||
|
logger.info("数据库表创建完成。")
|
||||||
|
|
||||||
|
|
||||||
|
async def drop_all_tables(existing_engine=None):
|
||||||
|
"""删除所有表(危险操作!)
|
||||||
|
|
||||||
|
删除所有在 Base.metadata 中定义的表。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
existing_engine: 可选的已存在的数据库引擎
|
||||||
|
|
||||||
|
Warning:
|
||||||
|
此操作将删除所有数据,不可恢复!仅用于测试环境!
|
||||||
|
"""
|
||||||
|
logger.warning("⚠️ 正在删除所有数据库表...")
|
||||||
|
engine = existing_engine if existing_engine is not None else await get_engine()
|
||||||
|
|
||||||
|
async with engine.begin() as connection:
|
||||||
|
await connection.run_sync(Base.metadata.drop_all)
|
||||||
|
|
||||||
|
logger.warning("所有数据库表已删除。")
|
||||||
652
src/common/database/core/models.py
Normal file
652
src/common/database/core/models.py
Normal file
@@ -0,0 +1,652 @@
|
|||||||
|
"""SQLAlchemy数据库模型定义
|
||||||
|
|
||||||
|
本文件只包含纯模型定义,使用SQLAlchemy 2.0的Mapped类型注解风格。
|
||||||
|
引擎和会话管理已移至core/engine.py和core/session.py。
|
||||||
|
|
||||||
|
所有模型使用统一的类型注解风格:
|
||||||
|
field_name: Mapped[PyType] = mapped_column(Type, ...)
|
||||||
|
|
||||||
|
这样IDE/Pylance能正确推断实例属性类型。
|
||||||
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
from sqlalchemy import Boolean, DateTime, Float, Index, Integer, String, Text
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.orm import Mapped, mapped_column
|
||||||
|
|
||||||
|
# 创建基类
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
|
||||||
|
# MySQL兼容的字段类型辅助函数
|
||||||
|
def get_string_field(max_length=255, **kwargs):
|
||||||
|
"""
|
||||||
|
根据数据库类型返回合适的字符串字段
|
||||||
|
MySQL需要指定长度的VARCHAR用于索引,SQLite可以使用Text
|
||||||
|
"""
|
||||||
|
from src.config.config import global_config
|
||||||
|
|
||||||
|
if global_config.database.database_type == "mysql":
|
||||||
|
return String(max_length, **kwargs)
|
||||||
|
else:
|
||||||
|
return Text(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class ChatStreams(Base):
|
||||||
|
"""聊天流模型"""
|
||||||
|
|
||||||
|
__tablename__ = "chat_streams"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
stream_id: Mapped[str] = mapped_column(get_string_field(64), nullable=False, unique=True, index=True)
|
||||||
|
create_time: Mapped[float] = mapped_column(Float, nullable=False)
|
||||||
|
group_platform: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
group_id: Mapped[str | None] = mapped_column(get_string_field(100), nullable=True, index=True)
|
||||||
|
group_name: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
last_active_time: Mapped[float] = mapped_column(Float, nullable=False)
|
||||||
|
platform: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
user_platform: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
user_id: Mapped[str] = mapped_column(get_string_field(100), nullable=False, index=True)
|
||||||
|
user_nickname: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
user_cardname: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
energy_value: Mapped[float | None] = mapped_column(Float, nullable=True, default=5.0)
|
||||||
|
sleep_pressure: Mapped[float | None] = mapped_column(Float, nullable=True, default=0.0)
|
||||||
|
focus_energy: Mapped[float | None] = mapped_column(Float, nullable=True, default=0.5)
|
||||||
|
# 动态兴趣度系统字段
|
||||||
|
base_interest_energy: Mapped[float | None] = mapped_column(Float, nullable=True, default=0.5)
|
||||||
|
message_interest_total: Mapped[float | None] = mapped_column(Float, nullable=True, default=0.0)
|
||||||
|
message_count: Mapped[int | None] = mapped_column(Integer, nullable=True, default=0)
|
||||||
|
action_count: Mapped[int | None] = mapped_column(Integer, nullable=True, default=0)
|
||||||
|
reply_count: Mapped[int | None] = mapped_column(Integer, nullable=True, default=0)
|
||||||
|
last_interaction_time: Mapped[float | None] = mapped_column(Float, nullable=True, default=None)
|
||||||
|
consecutive_no_reply: Mapped[int | None] = mapped_column(Integer, nullable=True, default=0)
|
||||||
|
# 消息打断系统字段
|
||||||
|
interruption_count: Mapped[int | None] = mapped_column(Integer, nullable=True, default=0)
|
||||||
|
# 聊天流印象字段
|
||||||
|
stream_impression_text: Mapped[str | None] = mapped_column(Text, nullable=True) # 对聊天流的主观印象描述
|
||||||
|
stream_chat_style: Mapped[str | None] = mapped_column(Text, nullable=True) # 聊天流的总体风格
|
||||||
|
stream_topic_keywords: Mapped[str | None] = mapped_column(Text, nullable=True) # 话题关键词,逗号分隔
|
||||||
|
stream_interest_score: Mapped[float | None] = mapped_column(Float, nullable=True, default=0.5) # 对聊天流的兴趣程度(0-1)
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
Index("idx_chatstreams_stream_id", "stream_id"),
|
||||||
|
Index("idx_chatstreams_user_id", "user_id"),
|
||||||
|
Index("idx_chatstreams_group_id", "group_id"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class LLMUsage(Base):
|
||||||
|
"""LLM使用记录模型"""
|
||||||
|
|
||||||
|
__tablename__ = "llm_usage"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
model_name: Mapped[str] = mapped_column(get_string_field(100), nullable=False, index=True)
|
||||||
|
model_assign_name: Mapped[str] = mapped_column(get_string_field(100), index=True)
|
||||||
|
model_api_provider: Mapped[str] = mapped_column(get_string_field(100), index=True)
|
||||||
|
user_id: Mapped[str] = mapped_column(get_string_field(50), nullable=False, index=True)
|
||||||
|
request_type: Mapped[str] = mapped_column(get_string_field(50), nullable=False, index=True)
|
||||||
|
endpoint: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
prompt_tokens: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||||
|
completion_tokens: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||||
|
time_cost: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||||
|
total_tokens: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||||
|
cost: Mapped[float] = mapped_column(Float, nullable=False)
|
||||||
|
status: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
timestamp: Mapped[datetime.datetime] = mapped_column(DateTime, nullable=False, index=True, default=datetime.datetime.now)
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
Index("idx_llmusage_model_name", "model_name"),
|
||||||
|
Index("idx_llmusage_model_assign_name", "model_assign_name"),
|
||||||
|
Index("idx_llmusage_model_api_provider", "model_api_provider"),
|
||||||
|
Index("idx_llmusage_time_cost", "time_cost"),
|
||||||
|
Index("idx_llmusage_user_id", "user_id"),
|
||||||
|
Index("idx_llmusage_request_type", "request_type"),
|
||||||
|
Index("idx_llmusage_timestamp", "timestamp"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Emoji(Base):
|
||||||
|
"""表情包模型"""
|
||||||
|
|
||||||
|
__tablename__ = "emoji"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
full_path: Mapped[str] = mapped_column(get_string_field(500), nullable=False, unique=True, index=True)
|
||||||
|
format: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
emoji_hash: Mapped[str] = mapped_column(get_string_field(64), nullable=False, index=True)
|
||||||
|
description: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
query_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||||
|
is_registered: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||||
|
is_banned: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||||
|
emotion: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
record_time: Mapped[float] = mapped_column(Float, nullable=False)
|
||||||
|
register_time: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||||
|
usage_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||||
|
last_used_time: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
Index("idx_emoji_full_path", "full_path"),
|
||||||
|
Index("idx_emoji_hash", "emoji_hash"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Messages(Base):
|
||||||
|
"""消息模型"""
|
||||||
|
|
||||||
|
__tablename__ = "messages"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
message_id: Mapped[str] = mapped_column(get_string_field(100), nullable=False, index=True)
|
||||||
|
time: Mapped[float] = mapped_column(Float, nullable=False)
|
||||||
|
chat_id: Mapped[str] = mapped_column(get_string_field(64), nullable=False, index=True)
|
||||||
|
reply_to: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
interest_value: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||||
|
key_words: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
key_words_lite: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
is_mentioned: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
|
||||||
|
|
||||||
|
# 从 chat_info 扁平化而来的字段
|
||||||
|
chat_info_stream_id: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
chat_info_platform: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
chat_info_user_platform: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
chat_info_user_id: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
chat_info_user_nickname: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
chat_info_user_cardname: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
chat_info_group_platform: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
chat_info_group_id: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
chat_info_group_name: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
chat_info_create_time: Mapped[float] = mapped_column(Float, nullable=False)
|
||||||
|
chat_info_last_active_time: Mapped[float] = mapped_column(Float, nullable=False)
|
||||||
|
|
||||||
|
# 从顶层 user_info 扁平化而来的字段
|
||||||
|
user_platform: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
user_id: Mapped[str | None] = mapped_column(get_string_field(100), nullable=True, index=True)
|
||||||
|
user_nickname: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
user_cardname: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
|
||||||
|
processed_plain_text: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
display_message: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
memorized_times: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||||
|
priority_mode: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
priority_info: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
additional_config: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
is_emoji: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||||
|
is_picid: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||||
|
is_command: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||||
|
is_notify: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||||
|
is_public_notice: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||||
|
notice_type: Mapped[str | None] = mapped_column(String(50), nullable=True)
|
||||||
|
|
||||||
|
# 兴趣度系统字段
|
||||||
|
actions: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
should_reply: Mapped[bool | None] = mapped_column(Boolean, nullable=True, default=False)
|
||||||
|
should_act: Mapped[bool | None] = mapped_column(Boolean, nullable=True, default=False)
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
Index("idx_messages_message_id", "message_id"),
|
||||||
|
Index("idx_messages_chat_id", "chat_id"),
|
||||||
|
Index("idx_messages_time", "time"),
|
||||||
|
Index("idx_messages_user_id", "user_id"),
|
||||||
|
Index("idx_messages_should_reply", "should_reply"),
|
||||||
|
Index("idx_messages_should_act", "should_act"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ActionRecords(Base):
|
||||||
|
"""动作记录模型"""
|
||||||
|
|
||||||
|
__tablename__ = "action_records"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
action_id: Mapped[str] = mapped_column(get_string_field(100), nullable=False, index=True)
|
||||||
|
time: Mapped[float] = mapped_column(Float, nullable=False)
|
||||||
|
action_name: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
action_data: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
action_done: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||||
|
action_build_into_prompt: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||||
|
action_prompt_display: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
chat_id: Mapped[str] = mapped_column(get_string_field(64), nullable=False, index=True)
|
||||||
|
chat_info_stream_id: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
chat_info_platform: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
Index("idx_actionrecords_action_id", "action_id"),
|
||||||
|
Index("idx_actionrecords_chat_id", "chat_id"),
|
||||||
|
Index("idx_actionrecords_time", "time"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Images(Base):
|
||||||
|
"""图像信息模型"""
|
||||||
|
|
||||||
|
__tablename__ = "images"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
image_id: Mapped[str] = mapped_column(Text, nullable=False, default="")
|
||||||
|
emoji_hash: Mapped[str] = mapped_column(get_string_field(64), nullable=False, index=True)
|
||||||
|
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
path: Mapped[str] = mapped_column(get_string_field(500), nullable=False, unique=True)
|
||||||
|
count: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
|
||||||
|
timestamp: Mapped[float] = mapped_column(Float, nullable=False)
|
||||||
|
type: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
vlm_processed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
Index("idx_images_emoji_hash", "emoji_hash"),
|
||||||
|
Index("idx_images_path", "path"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ImageDescriptions(Base):
|
||||||
|
"""图像描述信息模型"""
|
||||||
|
|
||||||
|
__tablename__ = "image_descriptions"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
type: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
image_description_hash: Mapped[str] = mapped_column(get_string_field(64), nullable=False, index=True)
|
||||||
|
description: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
timestamp: Mapped[float] = mapped_column(Float, nullable=False)
|
||||||
|
|
||||||
|
__table_args__ = (Index("idx_imagedesc_hash", "image_description_hash"),)
|
||||||
|
|
||||||
|
|
||||||
|
class Videos(Base):
|
||||||
|
"""视频信息模型"""
|
||||||
|
|
||||||
|
__tablename__ = "videos"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
video_id: Mapped[str] = mapped_column(Text, nullable=False, default="")
|
||||||
|
video_hash: Mapped[str] = mapped_column(get_string_field(64), nullable=False, index=True, unique=True)
|
||||||
|
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
count: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
|
||||||
|
timestamp: Mapped[float] = mapped_column(Float, nullable=False)
|
||||||
|
vlm_processed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||||
|
|
||||||
|
# 视频特有属性
|
||||||
|
duration: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||||
|
frame_count: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
||||||
|
fps: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||||
|
resolution: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
file_size: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
Index("idx_videos_video_hash", "video_hash"),
|
||||||
|
Index("idx_videos_timestamp", "timestamp"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OnlineTime(Base):
|
||||||
|
"""在线时长记录模型"""
|
||||||
|
|
||||||
|
__tablename__ = "online_time"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
timestamp: Mapped[str] = mapped_column(Text, nullable=False, default=str(datetime.datetime.now))
|
||||||
|
duration: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||||
|
start_timestamp: Mapped[datetime.datetime] = mapped_column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||||
|
end_timestamp: Mapped[datetime.datetime] = mapped_column(DateTime, nullable=False, index=True)
|
||||||
|
|
||||||
|
__table_args__ = (Index("idx_onlinetime_end_timestamp", "end_timestamp"),)
|
||||||
|
|
||||||
|
|
||||||
|
class PersonInfo(Base):
|
||||||
|
"""人物信息模型"""
|
||||||
|
|
||||||
|
__tablename__ = "person_info"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
person_id: Mapped[str] = mapped_column(get_string_field(100), nullable=False, unique=True, index=True)
|
||||||
|
person_name: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
name_reason: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
platform: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
user_id: Mapped[str] = mapped_column(get_string_field(50), nullable=False, index=True)
|
||||||
|
nickname: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
impression: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
short_impression: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
points: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
forgotten_points: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
info_list: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
know_times: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||||
|
know_since: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||||
|
last_know: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||||
|
attitude: Mapped[int | None] = mapped_column(Integer, nullable=True, default=50)
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
Index("idx_personinfo_person_id", "person_id"),
|
||||||
|
Index("idx_personinfo_user_id", "user_id"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BotPersonalityInterests(Base):
|
||||||
|
"""机器人人格兴趣标签模型"""
|
||||||
|
|
||||||
|
__tablename__ = "bot_personality_interests"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
personality_id: Mapped[str] = mapped_column(get_string_field(100), nullable=False, index=True)
|
||||||
|
personality_description: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
interest_tags: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
embedding_model: Mapped[str] = mapped_column(get_string_field(100), nullable=False, default="text-embedding-ada-002")
|
||||||
|
version: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
|
||||||
|
last_updated: Mapped[datetime.datetime] = mapped_column(DateTime, nullable=False, default=datetime.datetime.now, index=True)
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
Index("idx_botpersonality_personality_id", "personality_id"),
|
||||||
|
Index("idx_botpersonality_version", "version"),
|
||||||
|
Index("idx_botpersonality_last_updated", "last_updated"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Memory(Base):
|
||||||
|
"""记忆模型"""
|
||||||
|
|
||||||
|
__tablename__ = "memory"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
memory_id: Mapped[str] = mapped_column(get_string_field(64), nullable=False, index=True)
|
||||||
|
chat_id: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
memory_text: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
keywords: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
create_time: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||||
|
last_view_time: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||||
|
|
||||||
|
__table_args__ = (Index("idx_memory_memory_id", "memory_id"),)
|
||||||
|
|
||||||
|
|
||||||
|
class Expression(Base):
|
||||||
|
"""表达风格模型"""
|
||||||
|
|
||||||
|
__tablename__ = "expression"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
situation: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
style: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
count: Mapped[float] = mapped_column(Float, nullable=False)
|
||||||
|
last_active_time: Mapped[float] = mapped_column(Float, nullable=False)
|
||||||
|
chat_id: Mapped[str] = mapped_column(get_string_field(64), nullable=False, index=True)
|
||||||
|
type: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
create_date: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||||
|
|
||||||
|
__table_args__ = (Index("idx_expression_chat_id", "chat_id"),)
|
||||||
|
|
||||||
|
|
||||||
|
class ThinkingLog(Base):
|
||||||
|
"""思考日志模型"""
|
||||||
|
|
||||||
|
__tablename__ = "thinking_logs"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
chat_id: Mapped[str] = mapped_column(get_string_field(64), nullable=False, index=True)
|
||||||
|
trigger_text: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
response_text: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
trigger_info_json: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
response_info_json: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
timing_results_json: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
chat_history_json: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
chat_history_in_thinking_json: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
chat_history_after_response_json: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
heartflow_data_json: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
reasoning_data_json: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
created_at: Mapped[datetime.datetime] = mapped_column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||||
|
|
||||||
|
__table_args__ = (Index("idx_thinkinglog_chat_id", "chat_id"),)
|
||||||
|
|
||||||
|
|
||||||
|
class GraphNodes(Base):
|
||||||
|
"""记忆图节点模型"""
|
||||||
|
|
||||||
|
__tablename__ = "graph_nodes"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
concept: Mapped[str] = mapped_column(get_string_field(255), nullable=False, unique=True, index=True)
|
||||||
|
memory_items: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
hash: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
weight: Mapped[float] = mapped_column(Float, nullable=False, default=1.0)
|
||||||
|
created_time: Mapped[float] = mapped_column(Float, nullable=False)
|
||||||
|
last_modified: Mapped[float] = mapped_column(Float, nullable=False)
|
||||||
|
|
||||||
|
__table_args__ = (Index("idx_graphnodes_concept", "concept"),)
|
||||||
|
|
||||||
|
|
||||||
|
class GraphEdges(Base):
|
||||||
|
"""记忆图边模型"""
|
||||||
|
|
||||||
|
__tablename__ = "graph_edges"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
source: Mapped[str] = mapped_column(get_string_field(255), nullable=False, index=True)
|
||||||
|
target: Mapped[str] = mapped_column(get_string_field(255), nullable=False, index=True)
|
||||||
|
strength: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||||
|
hash: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
created_time: Mapped[float] = mapped_column(Float, nullable=False)
|
||||||
|
last_modified: Mapped[float] = mapped_column(Float, nullable=False)
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
Index("idx_graphedges_source", "source"),
|
||||||
|
Index("idx_graphedges_target", "target"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Schedule(Base):
|
||||||
|
"""日程模型"""
|
||||||
|
|
||||||
|
__tablename__ = "schedule"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
date: Mapped[str] = mapped_column(get_string_field(10), nullable=False, unique=True, index=True)
|
||||||
|
schedule_data: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
created_at: Mapped[datetime.datetime] = mapped_column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||||
|
updated_at: Mapped[datetime.datetime] = mapped_column(DateTime, nullable=False, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
||||||
|
|
||||||
|
__table_args__ = (Index("idx_schedule_date", "date"),)
|
||||||
|
|
||||||
|
|
||||||
|
class MaiZoneScheduleStatus(Base):
|
||||||
|
"""麦麦空间日程处理状态模型"""
|
||||||
|
|
||||||
|
__tablename__ = "maizone_schedule_status"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
datetime_hour: Mapped[str] = mapped_column(get_string_field(13), nullable=False, unique=True, index=True)
|
||||||
|
activity: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
is_processed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||||
|
processed_at: Mapped[datetime.datetime | None] = mapped_column(DateTime, nullable=True)
|
||||||
|
story_content: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
send_success: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||||
|
created_at: Mapped[datetime.datetime] = mapped_column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||||
|
updated_at: Mapped[datetime.datetime] = mapped_column(DateTime, nullable=False, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
Index("idx_maizone_datetime_hour", "datetime_hour"),
|
||||||
|
Index("idx_maizone_is_processed", "is_processed"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BanUser(Base):
|
||||||
|
"""被禁用用户模型
|
||||||
|
|
||||||
|
使用 SQLAlchemy 2.0 类型标注写法,方便静态类型检查器识别实际字段类型,
|
||||||
|
避免在业务代码中对属性赋值时报 `Column[...]` 不可赋值的告警。
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = "ban_users"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
platform: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
user_id: Mapped[str] = mapped_column(get_string_field(50), nullable=False, index=True)
|
||||||
|
violation_num: Mapped[int] = mapped_column(Integer, nullable=False, default=0, index=True)
|
||||||
|
reason: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
created_at: Mapped[datetime.datetime] = mapped_column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
Index("idx_violation_num", "violation_num"),
|
||||||
|
Index("idx_banuser_user_id", "user_id"),
|
||||||
|
Index("idx_banuser_platform", "platform"),
|
||||||
|
Index("idx_banuser_platform_user_id", "platform", "user_id"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AntiInjectionStats(Base):
|
||||||
|
"""反注入系统统计模型"""
|
||||||
|
|
||||||
|
__tablename__ = "anti_injection_stats"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
total_messages: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||||
|
"""总处理消息数"""
|
||||||
|
|
||||||
|
detected_injections: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||||
|
"""检测到的注入攻击数"""
|
||||||
|
|
||||||
|
blocked_messages: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||||
|
"""被阻止的消息数"""
|
||||||
|
|
||||||
|
shielded_messages: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||||
|
"""被加盾的消息数"""
|
||||||
|
|
||||||
|
processing_time_total: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
|
||||||
|
"""总处理时间"""
|
||||||
|
|
||||||
|
total_process_time: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
|
||||||
|
"""累计总处理时间"""
|
||||||
|
|
||||||
|
last_process_time: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
|
||||||
|
"""最近一次处理时间"""
|
||||||
|
|
||||||
|
error_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||||
|
"""错误计数"""
|
||||||
|
|
||||||
|
start_time: Mapped[datetime.datetime] = mapped_column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||||
|
"""统计开始时间"""
|
||||||
|
|
||||||
|
created_at: Mapped[datetime.datetime] = mapped_column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||||
|
"""记录创建时间"""
|
||||||
|
|
||||||
|
updated_at: Mapped[datetime.datetime] = mapped_column(DateTime, nullable=False, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
||||||
|
"""记录更新时间"""
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
Index("idx_anti_injection_stats_created_at", "created_at"),
|
||||||
|
Index("idx_anti_injection_stats_updated_at", "updated_at"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CacheEntries(Base):
|
||||||
|
"""工具缓存条目模型"""
|
||||||
|
|
||||||
|
__tablename__ = "cache_entries"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
cache_key: Mapped[str] = mapped_column(get_string_field(500), nullable=False, unique=True, index=True)
|
||||||
|
"""缓存键,包含工具名、参数和代码哈希"""
|
||||||
|
|
||||||
|
cache_value: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
"""缓存的数据,JSON格式"""
|
||||||
|
|
||||||
|
expires_at: Mapped[float] = mapped_column(Float, nullable=False, index=True)
|
||||||
|
"""过期时间戳"""
|
||||||
|
|
||||||
|
tool_name: Mapped[str] = mapped_column(get_string_field(100), nullable=False, index=True)
|
||||||
|
"""工具名称"""
|
||||||
|
|
||||||
|
created_at: Mapped[float] = mapped_column(Float, nullable=False, default=lambda: time.time())
|
||||||
|
"""创建时间戳"""
|
||||||
|
|
||||||
|
last_accessed: Mapped[float] = mapped_column(Float, nullable=False, default=lambda: time.time())
|
||||||
|
"""最后访问时间戳"""
|
||||||
|
|
||||||
|
access_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||||
|
"""访问次数"""
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
Index("idx_cache_entries_key", "cache_key"),
|
||||||
|
Index("idx_cache_entries_expires_at", "expires_at"),
|
||||||
|
Index("idx_cache_entries_tool_name", "tool_name"),
|
||||||
|
Index("idx_cache_entries_created_at", "created_at"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MonthlyPlan(Base):
|
||||||
|
"""月度计划模型"""
|
||||||
|
|
||||||
|
__tablename__ = "monthly_plans"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
plan_text: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
target_month: Mapped[str] = mapped_column(String(7), nullable=False, index=True)
|
||||||
|
status: Mapped[str] = mapped_column(get_string_field(20), nullable=False, default="active", index=True)
|
||||||
|
usage_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||||
|
last_used_date: Mapped[str | None] = mapped_column(String(10), nullable=True, index=True)
|
||||||
|
created_at: Mapped[datetime.datetime] = mapped_column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||||
|
is_deleted: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, index=True)
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
Index("idx_monthlyplan_target_month_status", "target_month", "status"),
|
||||||
|
Index("idx_monthlyplan_last_used_date", "last_used_date"),
|
||||||
|
Index("idx_monthlyplan_usage_count", "usage_count"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PermissionNodes(Base):
|
||||||
|
"""权限节点模型"""
|
||||||
|
|
||||||
|
__tablename__ = "permission_nodes"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
node_name: Mapped[str] = mapped_column(get_string_field(255), nullable=False, unique=True, index=True)
|
||||||
|
description: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
plugin_name: Mapped[str] = mapped_column(get_string_field(100), nullable=False, index=True)
|
||||||
|
default_granted: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||||
|
created_at: Mapped[datetime.datetime] = mapped_column(DateTime, default=datetime.datetime.utcnow, nullable=False)
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
Index("idx_permission_plugin", "plugin_name"),
|
||||||
|
Index("idx_permission_node", "node_name"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class UserPermissions(Base):
|
||||||
|
"""用户权限模型"""
|
||||||
|
|
||||||
|
__tablename__ = "user_permissions"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
platform: Mapped[str] = mapped_column(get_string_field(50), nullable=False, index=True)
|
||||||
|
user_id: Mapped[str] = mapped_column(get_string_field(100), nullable=False, index=True)
|
||||||
|
permission_node: Mapped[str] = mapped_column(get_string_field(255), nullable=False, index=True)
|
||||||
|
granted: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||||
|
granted_at: Mapped[datetime.datetime] = mapped_column(DateTime, default=datetime.datetime.utcnow, nullable=False)
|
||||||
|
granted_by: Mapped[str | None] = mapped_column(get_string_field(100), nullable=True)
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
Index("idx_user_platform_id", "platform", "user_id"),
|
||||||
|
Index("idx_user_permission", "platform", "user_id", "permission_node"),
|
||||||
|
Index("idx_permission_granted", "permission_node", "granted"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class UserRelationships(Base):
|
||||||
|
"""用户关系模型 - 存储用户与bot的关系数据"""
|
||||||
|
|
||||||
|
__tablename__ = "user_relationships"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
user_id: Mapped[str] = mapped_column(get_string_field(100), nullable=False, unique=True, index=True)
|
||||||
|
user_name: Mapped[str | None] = mapped_column(get_string_field(100), nullable=True)
|
||||||
|
user_aliases: Mapped[str | None] = mapped_column(Text, nullable=True) # 用户别名,逗号分隔
|
||||||
|
relationship_text: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||||
|
preference_keywords: Mapped[str | None] = mapped_column(Text, nullable=True) # 用户偏好关键词,逗号分隔
|
||||||
|
relationship_score: Mapped[float] = mapped_column(Float, nullable=False, default=0.3) # 关系分数(0-1)
|
||||||
|
last_updated: Mapped[float] = mapped_column(Float, nullable=False, default=time.time)
|
||||||
|
created_at: Mapped[datetime.datetime] = mapped_column(DateTime, default=datetime.datetime.utcnow, nullable=False)
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
Index("idx_user_relationship_id", "user_id"),
|
||||||
|
Index("idx_relationship_score", "relationship_score"),
|
||||||
|
Index("idx_relationship_updated", "last_updated"),
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user