Files
qihuaiflow/backend/python/app/models.py
T
QihuAI Design 1578d68f95 fix: 修复前台4项问题(MCP列举/微信技能上限/Word资产栏/技能MCP反馈) + 同步容器live状态
- #549 平台MCP列举: 注入全量已启用MCP清单, AI不再凭空编造
- #550 微信Bot技能自动发现上限 15->300
- #551 知识库Word文档同步资产后Word栏可见 + 双向删除同步(assets/knowledge级联软删)
- #552 对话反馈实际使用的技能/MCP(used_mcp字段), 含历史消息序列化
- 同步容器内其他未提交live改动(im_bot_platform/TokenDashboard/WechatBot/ChatPage)
2026-07-18 08:06:37 +08:00

1127 lines
50 KiB
Python

"""
AIFlow 数据库模型定义 - SQLAlchemy ORM
所有用户数据查询必须带 user_id 过滤实现物理隔离
"""
import uuid
from datetime import datetime, date
from sqlalchemy import (
Column, String, Integer, BigInteger, Float, Boolean,
DateTime, Date, Text, Enum, ForeignKey, Index, JSON,
DECIMAL, UniqueConstraint
)
from sqlalchemy.orm import relationship
from .database import Base
def gen_uuid() -> str:
return str(uuid.uuid4())
# ============================================================
# 用户与权限
# ============================================================
class User(Base):
__tablename__ = "users"
id = Column(String(36), primary_key=True, default=gen_uuid)
username = Column(String(50), unique=True, nullable=False)
email = Column(String(100), default=None)
phone = Column(String(20), default=None)
password_hash = Column(String(255), nullable=False)
avatar = Column(String(500), default=None)
role = Column(String(10), nullable=False, default="user") # user / admin
status = Column(String(10), nullable=False, default="active") # active / banned
real_name_status = Column(String(20), nullable=False, default="unverified")
real_name = Column(String(50), default=None)
id_card = Column(String(20), default=None)
membership_plan_id = Column(String(36), default=None)
membership_level = Column(Integer, default=0)
membership_expire_at = Column(DateTime, default=None)
auto_renew = Column(Boolean, default=False)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class LoginLog(Base):
__tablename__ = "login_logs"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
ip = Column(String(45), default=None)
user_agent = Column(String(500), default=None)
login_at = Column(DateTime, default=datetime.now)
class ApiKey(Base):
__tablename__ = "api_keys"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
api_key = Column(String(64), unique=True, nullable=False)
secret = Column(String(128), nullable=False)
name = Column(String(100), default=None)
status = Column(String(10), nullable=False, default="active") # active / revoked
last_used_at = Column(DateTime, default=None)
created_at = Column(DateTime, default=datetime.now)
expired_at = Column(DateTime, default=None)
description = Column(String(500), default=None, comment="密钥描述")
rate_limit = Column(Integer, default=60, comment="速率限制(请求/分钟)")
total_calls = Column(BigInteger, default=0, comment="总调用次数")
# ============================================================
# AI聊天与频道
# ============================================================
class Conversation(Base):
__tablename__ = "conversations"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
title = Column(String(200), nullable=False, default="新对话")
model_id = Column(String(36), default=None)
status = Column(String(10), nullable=False, default="active") # active / archived
pinned = Column(Boolean, default=False)
knowledge_graph_count = Column(Integer, default=0)
knowledge_graph_base_id = Column(String(64), default=None, comment="已生成的知识图谱知识库ID")
source = Column(String(32), default=None, comment="对话来源,如 wechat_bot")
ext_agent_conversation_id = Column(String(128), default=None, comment="外部Agent(Dify/Coze)会话ID,用于维持多轮上下文")
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class Message(Base):
__tablename__ = "messages"
id = Column(String(36), primary_key=True, default=gen_uuid)
conversation_id = Column(String(36), ForeignKey("conversations.id"), nullable=False)
role = Column(String(10), nullable=False) # user / assistant / system
content = Column(Text, nullable=False)
token_used = Column(Integer, default=0)
model_id = Column(String(36), default=None)
knowledge_ids = Column(JSON, default=None)
agent_ids = Column(JSON, default=None)
skill_ids = Column(JSON, default=None)
mcp_ids = Column(JSON, default=None, comment="该轮对话选择的MCP应用ID列表")
attachments = Column(JSON, default=None, comment="该轮对话附带的文件附件列表")
used_skills = Column(JSON, default=None, comment="该轮对话调用的进化技能名列表")
used_mcp = Column(JSON, default=None, comment="该轮对话调用的MCP应用名列表")
created_at = Column(DateTime, default=datetime.now)
class Channel(Base):
__tablename__ = "channels"
id = Column(String(36), primary_key=True, default=gen_uuid)
owner_id = Column(String(36), ForeignKey("users.id"), nullable=False)
name = Column(String(100), nullable=False)
description = Column(Text, default=None)
platform = Column(String(20), nullable=False)
webhook_url = Column(String(500), default=None)
credential = Column(JSON, default=None)
oauth_state = Column(String(500), default=None)
settings = Column(JSON, default=None)
account_name = Column(String(100), default=None)
default_agent_id = Column(String(36), default=None)
default_skill_id = Column(String(36), default=None)
knowledge_base_id = Column(String(36), default=None)
bound_model_id = Column(String(36), default=None)
features = Column(JSON, default=None)
status = Column(String(20), nullable=False, default="pending")
is_active = Column(Boolean, default=True)
last_active = Column(DateTime, default=None)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class ChannelMember(Base):
__tablename__ = "channel_members"
__table_args__ = (UniqueConstraint("channel_id", "user_id"),)
id = Column(String(36), primary_key=True, default=gen_uuid)
channel_id = Column(String(36), ForeignKey("channels.id"), nullable=False)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
role = Column(String(10), nullable=False, default="member") # owner / member
joined_at = Column(DateTime, default=datetime.now)
class ChannelMessage(Base):
__tablename__ = "channel_messages"
id = Column(String(36), primary_key=True, default=gen_uuid)
channel_id = Column(String(36), ForeignKey("channels.id"), nullable=False)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
role = Column(String(10), nullable=False)
content = Column(Text, nullable=False)
token_used = Column(Integer, default=0)
created_at = Column(DateTime, default=datetime.now)
# ============================================================
# 知识库
# ============================================================
class KnowledgeBase(Base):
__tablename__ = "knowledge_bases"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
name = Column(String(200), nullable=False)
description = Column(Text, default=None)
directory_path = Column(String(500), default="/")
document_count = Column(Integer, default=0)
knowledge_graph_count = Column(Integer, default=0)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
is_official = Column(Boolean, default=False)
status = Column(String(20), default="active")
external_service_id = Column(String(36), default=None)
external_id = Column(String(100), default=None)
external_sync_status = Column(String(20), default="none")
external_config = Column(JSON, default=None)
class KnowledgeDirectory(Base):
__tablename__ = "knowledge_directories"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
knowledge_base_id = Column(String(36), ForeignKey("knowledge_bases.id"), default=None)
parent_id = Column(String(36), default=None)
name = Column(String(200), nullable=False)
created_at = Column(DateTime, default=datetime.now)
class KnowledgeDocument(Base):
__tablename__ = "knowledge_documents"
id = Column(String(36), primary_key=True, default=gen_uuid)
knowledge_base_id = Column(String(36), ForeignKey("knowledge_bases.id"), nullable=False)
directory_id = Column(String(36), ForeignKey("knowledge_directories.id"), default=None)
filename = Column(String(255), nullable=False)
file_type = Column(String(20), nullable=False)
file_size = Column(BigInteger, default=0)
file_path = Column(String(500), default=None)
status = Column(String(20), nullable=False, default="uploading")
chunk_count = Column(Integer, default=0)
error_message = Column(Text, default=None)
content = Column(Text, default=None)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class KnowledgeDocumentChunk(Base):
"""知识库文档分段表 — Stage C.1"""
__tablename__ = "knowledge_document_chunks"
id = Column(String(36), primary_key=True, default=gen_uuid)
document_id = Column(String(36), ForeignKey("knowledge_documents.id"), nullable=False)
knowledge_base_id = Column(String(36), ForeignKey("knowledge_bases.id"), nullable=False)
chunk_index = Column(Integer, nullable=False, default=0, comment="分段序号")
content = Column(Text, nullable=False, comment="分段文本内容")
start_char = Column(Integer, default=0, comment="起始字符位置")
end_char = Column(Integer, default=0, comment="结束字符位置")
chunk_metadata = Column(JSON, default=None, comment="分段元数据(标题、关键词等)")
token_count = Column(Integer, default=0, comment="分段token数估算")
created_at = Column(DateTime, default=datetime.now)
__table_args__ = (
Index("idx_kdc_doc_id", "document_id"),
Index("idx_kdc_kb_id", "knowledge_base_id"),
)
class KnowledgeGraphNode(Base):
__tablename__ = "knowledge_graph_nodes"
id = Column(String(36), primary_key=True, default=gen_uuid)
knowledge_base_id = Column(String(36), ForeignKey("knowledge_bases.id"), nullable=False)
source_doc_id = Column(String(36), ForeignKey("knowledge_documents.id"), default=None)
label = Column(String(500), nullable=False)
type = Column(String(50), nullable=False, comment="entity/attribute/concept/event")
properties = Column(JSON, default=None)
description = Column(Text, default=None)
created_at = Column(DateTime, default=datetime.now)
class KnowledgeGraphEdge(Base):
__tablename__ = "knowledge_graph_edges"
id = Column(String(36), primary_key=True, default=gen_uuid)
knowledge_base_id = Column(String(36), ForeignKey("knowledge_bases.id"), nullable=False)
source_node_id = Column(String(36), ForeignKey("knowledge_graph_nodes.id"), nullable=False)
target_node_id = Column(String(36), ForeignKey("knowledge_graph_nodes.id"), nullable=False)
relation = Column(String(200), nullable=False)
weight = Column(Float, default=1.0)
description = Column(Text, default=None)
created_at = Column(DateTime, default=datetime.now)
class KnowledgeGraphBuildLog(Base):
__tablename__ = "knowledge_graph_build_logs"
id = Column(String(36), primary_key=True, default=gen_uuid)
knowledge_base_id = Column(String(36), ForeignKey("knowledge_bases.id"), nullable=False)
document_id = Column(String(36), ForeignKey("knowledge_documents.id"), nullable=False)
node_count = Column(Integer, default=0)
edge_count = Column(Integer, default=0)
status = Column(String(20), default="completed")
created_at = Column(DateTime, default=datetime.now)
# ============================================================
# 应用市场
# ============================================================
class MarketApp(Base):
__tablename__ = "market_apps"
id = Column(String(36), primary_key=True, default=gen_uuid)
name = Column(String(200), nullable=False)
type = Column(String(10), nullable=False) # skill / agent / mcp
source = Column(String(10), nullable=False, default="user") # official / user
publisher_id = Column(String(36), ForeignKey("users.id"), default=None)
publisher_name = Column(String(100), default=None)
description = Column(Text, default=None)
icon = Column(String(500), default=None)
tutorial = Column(Text, default=None)
price = Column(DECIMAL(10, 2), nullable=False, default=0.00)
is_member_free = Column(Boolean, default=False)
join_promotion = Column(Boolean, default=False)
status = Column(String(10), nullable=False, default="pending") # pending / published / removed
rating_avg = Column(DECIMAL(3, 2), default=0.00)
review_count = Column(Integer, default=0)
install_count = Column(Integer, default=0)
skill_content = Column(Text, default=None)
mcp_transport = Column(String(20), default=None)
platform = Column(String(20), default=None, comment="Agent 接入平台:dify/n8n/coze")
capability = Column(String(20), default=None, comment="能力分类:text/thinking/vision/tool/image/audio/asr/ocr/embedding/rerank/video")
agent_endpoint = Column(String(500), default=None)
agent_api_key = Column(String(500), default=None, comment="单个Agent接入的API Key")
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class AppPurchase(Base):
__tablename__ = "app_purchases"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
app_id = Column(String(36), ForeignKey("market_apps.id"), nullable=False)
price = Column(DECIMAL(10, 2), nullable=False, default=0.00)
order_id = Column(String(36), default=None)
status = Column(String(20), nullable=False, default="purchased") # purchased / installed
installed_at = Column(DateTime, default=None)
purchased_at = Column(DateTime, default=None)
created_at = Column(DateTime, default=datetime.now)
class AppReview(Base):
__tablename__ = "app_reviews"
__table_args__ = (UniqueConstraint("user_id", "app_id"),)
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
app_id = Column(String(36), ForeignKey("market_apps.id"), nullable=False)
rating = Column(Integer, nullable=False)
content = Column(Text, default=None)
created_at = Column(DateTime, default=datetime.now)
class AppInstallation(Base):
__tablename__ = "app_installations"
__table_args__ = (UniqueConstraint("user_id", "app_id"),)
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
app_id = Column(String(36), ForeignKey("market_apps.id"), nullable=False)
installed_at = Column(DateTime, default=datetime.now)
# ============================================================
# 付费与订单
# ============================================================
class MembershipPlan(Base):
__tablename__ = "membership_plans"
id = Column(String(36), primary_key=True, default=gen_uuid)
name = Column(String(100), nullable=False)
level = Column(Integer, nullable=False, default=0)
duration_type = Column(String(10), nullable=False) # month / quarter / year
price = Column(DECIMAL(10, 2), nullable=False)
daily_free_tokens = Column(Integer, default=0)
features = Column(JSON, default=None)
free_app_ids = Column(JSON, default=None)
free_model_ids = Column(JSON, default=None)
# 配额字段
max_agents = Column(Integer, default=5)
max_workflows = Column(Integer, default=3)
max_knowledge_bases = Column(Integer, default=2)
max_storage_mb = Column(Integer, default=1024)
api_calls_per_day = Column(Integer, default=100)
api_calls_per_min = Column(Integer, default=10)
# 权限字段
can_publish_apps = Column(Boolean, default=False)
can_create_api_keys = Column(Boolean, default=False)
can_use_workflow = Column(Boolean, default=False)
can_use_mcp = Column(Boolean, default=False)
can_use_code_exec = Column(Boolean, default=False)
can_use_privacy_mode = Column(Boolean, default=False)
can_use_dag = Column(Boolean, default=False)
allowed_model_tiers = Column(JSON, default=None)
permissions = Column(JSON, default=None)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class MembershipSubscription(Base):
__tablename__ = "membership_subscriptions"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
plan_id = Column(String(36), ForeignKey("membership_plans.id"), nullable=False)
order_id = Column(String(36), default=None)
start_at = Column(DateTime, nullable=False)
end_at = Column(DateTime, nullable=False)
auto_renew = Column(Boolean, default=False)
status = Column(String(15), nullable=False, default="active") # active / expired / downgraded
created_at = Column(DateTime, default=datetime.now)
class Order(Base):
__tablename__ = "orders"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
order_type = Column(String(15), nullable=False) # membership / service / app
item_id = Column(String(36), default=None)
item_name = Column(String(200), default=None)
amount = Column(DECIMAL(10, 2), nullable=False)
coupon_id = Column(String(36), default=None)
discount_amount = Column(DECIMAL(10, 2), default=0.00)
payment_method = Column(String(10), default=None) # wechat / alipay
payment_id = Column(String(200), default=None)
status = Column(String(15), nullable=False, default="pending") # pending / paid / refunded / cancelled
paid_at = Column(DateTime, default=None)
receipt_number = Column(String(100), default=None)
receipt_type = Column(String(20), default=None)
receipt_title = Column(String(200), default=None)
receipt_tax_number = Column(String(50), default=None)
receipt_issued_at = Column(DateTime, default=None)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class Coupon(Base):
__tablename__ = "coupons"
id = Column(String(36), primary_key=True, default=gen_uuid)
code = Column(String(50), unique=True, nullable=False)
type = Column(String(10), nullable=False) # fixed / percent
value = Column(DECIMAL(10, 2), nullable=False)
min_amount = Column(DECIMAL(10, 2), default=0.00)
valid_from = Column(DateTime, nullable=False)
valid_to = Column(DateTime, nullable=False)
total_count = Column(Integer, default=None)
used_count = Column(Integer, default=0)
status = Column(String(10), nullable=False, default="active") # active / expired / disabled
created_at = Column(DateTime, default=datetime.now)
class UserCoupon(Base):
__tablename__ = "user_coupons"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
coupon_id = Column(String(36), ForeignKey("coupons.id"), nullable=False)
status = Column(String(10), nullable=False, default="unused") # unused / used / expired
used_at = Column(DateTime, default=None)
created_at = Column(DateTime, default=datetime.now)
class ValueAddedService(Base):
__tablename__ = "value_added_services"
id = Column(String(36), primary_key=True, default=gen_uuid)
name = Column(String(200), nullable=False)
description = Column(Text, default=None)
price = Column(DECIMAL(10, 2), nullable=False)
type = Column(String(20), nullable=False) # app_custom / platform_proxy / data_custom / private_project
features = Column(JSON, default=None) # 服务特性
status = Column(String(10), nullable=False, default="active") # active / inactive / deleted
assigned_agent_id = Column(String(36), default=None) # 应用定制服务绑定的Agent(市场应用ID)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class CustomServiceOrder(Base):
__tablename__ = "custom_service_orders"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
service_id = Column(String(36), default=None)
service_type = Column(String(10), nullable=False)
description = Column(Text, default=None)
status = Column(String(15), nullable=False, default="pending")
price = Column(DECIMAL(10, 2), default=0.00)
order_id = Column(String(36), default=None)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class CustomServiceRequest(Base):
"""增值服务工单表 - 用于数据定制服务和私有化项目服务"""
__tablename__ = "custom_service_requests"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
service_id = Column(String(36), ForeignKey("value_added_services.id"), nullable=False)
request_type = Column(String(20), nullable=False) # data_custom / private_project
title = Column(String(200), nullable=False)
description = Column(Text, default=None)
requirements = Column(JSON, default=None) # 需求详情JSON
status = Column(String(20), nullable=False, default="pending") # pending / processing / completed / rejected
assigned_agent_id = Column(String(36), default=None)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
# ============================================================
# Token与计费
# ============================================================
class ModelProvider(Base):
__tablename__ = "model_providers"
id = Column(String(36), primary_key=True, default=gen_uuid)
name = Column(String(100), nullable=False)
protocol = Column(String(30), nullable=False, default="openai_compatible")
api_key = Column(String(255), default=None)
api_base_url = Column(String(500), default=None)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class AIModel(Base):
__tablename__ = "models"
id = Column(String(36), primary_key=True, default=gen_uuid)
provider_id = Column(String(36), ForeignKey("model_providers.id"), nullable=False)
name = Column(String(100), nullable=False)
display_name = Column(String(200), default=None, comment="display name")
api_key = Column(String(255), default=None)
endpoint = Column(String(500), default=None)
capabilities = Column(JSON, default=None)
timeout = Column(Integer, default=60)
status = Column(String(10), nullable=False, default="paid") # free / paid
price_per_million_tokens = Column(DECIMAL(10, 4), nullable=False, default=0.0000)
is_enabled = Column(Boolean, default=True)
is_default = Column(Boolean, default=False) # 全局默认模型
total_calls = Column(BigInteger, default=0)
total_tokens = Column(BigInteger, default=0)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class TokenUsageLog(Base):
__tablename__ = "token_usage_logs"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
model_id = Column(String(36), default=None)
model_name = Column(String(100), default=None)
app_id = Column(String(36), default=None)
app_name = Column(String(200), default=None)
conversation_id = Column(String(36), default=None)
prompt_tokens = Column(Integer, default=0)
completion_tokens = Column(Integer, default=0)
total_tokens = Column(Integer, default=0)
cost = Column(DECIMAL(10, 6), default=0.000000)
created_at = Column(DateTime, default=datetime.now)
class TokenBalance(Base):
__tablename__ = "token_balances"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), unique=True, nullable=False)
total_consumed = Column(BigInteger, default=0)
balance = Column(BigInteger, default=0)
daily_free_used = Column(BigInteger, default=0)
daily_free_date = Column(Date, default=None)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
# ============================================================
# 通知与日志
# ============================================================
class Notification(Base):
__tablename__ = "notifications"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
type = Column(String(15), nullable=False)
title = Column(String(200), nullable=False)
content = Column(Text, default=None)
is_read = Column(Boolean, default=False)
created_at = Column(DateTime, default=datetime.now)
class OperationLog(Base):
__tablename__ = "operation_logs"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), nullable=False)
username = Column(String(50), default=None)
action = Column(String(100), nullable=False)
target = Column(String(100), default=None)
target_id = Column(String(36), default=None)
detail = Column(Text, default=None)
ip = Column(String(45), default=None)
created_at = Column(DateTime, default=datetime.now)
class AuditLog(Base):
__tablename__ = "audit_logs"
id = Column(String(36), primary_key=True, default=gen_uuid)
admin_id = Column(String(36), nullable=False)
admin_name = Column(String(50), default=None)
action = Column(String(100), nullable=False)
target_type = Column(String(50), default=None)
target_id = Column(String(36), default=None)
detail = Column(Text, default=None)
created_at = Column(DateTime, default=datetime.now)
class RiskRule(Base):
__tablename__ = "risk_rules"
id = Column(String(36), primary_key=True, default=gen_uuid)
name = Column(String(100), nullable=False)
type = Column(String(15), nullable=False)
threshold = Column(Integer, default=0)
duration = Column(Integer, default=3600)
is_enabled = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
# ============================================================
# 系统配置
# ============================================================
class SiteSetting(Base):
__tablename__ = "site_settings"
id = Column(String(36), primary_key=True, default=gen_uuid)
key = Column(String(100), unique=True, nullable=False)
value = Column(Text, default=None)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class EmailConfig(Base):
__tablename__ = "email_configs"
id = Column(String(36), primary_key=True, default=gen_uuid)
smtp_host = Column(String(200), nullable=False)
smtp_port = Column(Integer, nullable=False, default=587)
username = Column(String(200), nullable=False)
password = Column(String(200), nullable=False)
from_address = Column(String(200), nullable=False)
use_tls = Column(Boolean, default=True)
use_ssl = Column(Boolean, default=False)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class SmsConfig(Base):
__tablename__ = "sms_configs"
id = Column(String(36), primary_key=True, default=gen_uuid)
provider = Column(String(50), nullable=False)
api_key = Column(String(200), nullable=False)
api_secret = Column(String(200), nullable=False)
sign_name = Column(String(100), default=None)
template_code = Column(String(100), default=None)
# 短信宝 (SMSBao) 字段
username = Column(String(100), default=None) # 短信宝用户名 u
password = Column(String(200), default=None) # 短信宝密码(明文存储,发送时 MD5 32位)
goods_id = Column(String(100), default=None) # 短信宝专属通道产品 ID g
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class PaymentConfig(Base):
__tablename__ = "payment_configs"
id = Column(String(36), primary_key=True, default=gen_uuid)
type = Column(String(10), nullable=False) # wechat / alipay
app_id = Column(String(100), nullable=False)
merchant_id = Column(String(100), default=None) # 微信: 商户号
api_key = Column(String(255), nullable=False) # 微信: APIv2密钥 / 支付宝: 应用私钥
api_v3_key = Column(String(255), default=None) # 微信: APIv3密钥
cert_path = Column(String(500), default=None) # 微信: 证书路径
cert_serial_no = Column(String(200), default=None) # 微信: 证书序列号
alipay_public_key = Column(Text, default=None) # 支付宝: 支付宝公钥
app_private_key = Column(Text, default=None) # 支付宝: 应用私钥
gateway_url = Column(String(500), default=None) # 支付宝: 网关地址
notify_url = Column(String(500), default=None) # 支付回调通知地址
return_url = Column(String(500), default=None) # 支付宝: 同步跳转地址
status = Column(String(10), nullable=False, default="inactive") # active / inactive
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class RegisterSetting(Base):
__tablename__ = "register_settings"
id = Column(String(36), primary_key=True, default=gen_uuid)
phone_required = Column(Boolean, default=True)
phone_sms_required = Column(Boolean, default=True)
email_required = Column(Boolean, default=True)
email_verify_required = Column(Boolean, default=True)
real_name_required = Column(Boolean, default=False)
aliyun_access_key = Column(String(200), default=None)
aliyun_secret_key = Column(String(200), default=None)
aliyun_endpoint = Column(String(200), default=None)
sms_sign_name = Column(String(100), default=None)
sms_template_code = Column(String(50), default=None)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
# ============================================================
# 广告位
# ============================================================
class AdBanner(Base):
__tablename__ = "ad_banners"
id = Column(String(36), primary_key=True, default=gen_uuid)
title = Column(String(200), nullable=False)
image_url = Column(String(500), default=None)
link_app_id = Column(String(36), default=None)
link_app_name = Column(String(200), default=None)
sort_order = Column(Integer, default=0)
click_count = Column(Integer, default=0)
status = Column(String(10), nullable=False, default="active") # active / inactive
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
# ============================================================
# 资产管理
# ============================================================
class AssetCategory(Base):
__tablename__ = "asset_categories"
id = Column(String(36), primary_key=True, default=gen_uuid)
name = Column(String(100), nullable=False)
type = Column(String(10), nullable=False)
icon = Column(String(200), default=None)
color = Column(String(20), default=None)
is_default = Column(Boolean, default=False)
user_id = Column(String(36), default=None)
created_at = Column(DateTime, default=datetime.now)
class Asset(Base):
__tablename__ = "assets"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
name = Column(String(200), nullable=False)
category = Column(String(10), nullable=False)
sub_category = Column(String(50), default=None)
file_type = Column(String(50), default=None)
size = Column(BigInteger, default=0)
description = Column(Text, default=None)
cover_url = Column(String(500), default=None)
url = Column(String(500), default=None)
content = Column(Text, default=None)
novel_meta = Column(JSON, default=None)
status = Column(String(10), nullable=False, default="active") # active / deleted
knowledge_doc_id = Column(String(36), default=None, comment="关联的知识库文档ID(由知识库同步资产时写入)")
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class NovelChapter(Base):
__tablename__ = "novel_chapters"
id = Column(String(36), primary_key=True, default=gen_uuid)
asset_id = Column(String(36), ForeignKey("assets.id"), nullable=False)
title = Column(String(200), nullable=False)
word_count = Column(Integer, default=0)
sort_order = Column(Integer, default=0)
content = Column(Text(length=4294967295), default=None)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
# ============================================================
# 预留扩展
# ============================================================
class Team(Base):
__tablename__ = "teams"
id = Column(String(36), primary_key=True, default=gen_uuid)
owner_id = Column(String(36), ForeignKey("users.id"), nullable=False)
name = Column(String(100), nullable=False)
description = Column(Text, default=None)
status = Column(String(10), nullable=False, default="active")
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class Referral(Base):
__tablename__ = "referrals"
id = Column(String(36), primary_key=True, default=gen_uuid)
referrer_id = Column(String(36), ForeignKey("users.id"), nullable=False)
referee_id = Column(String(36), ForeignKey("users.id"), nullable=False)
referral_code = Column(String(50), default=None)
status = Column(String(10), nullable=False, default="pending")
commission_rate = Column(DECIMAL(5, 2), default=0.00)
created_at = Column(DateTime, default=datetime.now)
class OpenApiClient(Base):
__tablename__ = "open_api_clients"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
name = Column(String(100), nullable=False)
client_id = Column(String(64), unique=True, nullable=False)
client_secret = Column(String(128), nullable=False)
allowed_models = Column(JSON, default=None)
rate_limit = Column(Integer, default=60)
status = Column(String(10), nullable=False, default="active")
created_at = Column(DateTime, default=datetime.now)
total_calls = Column(BigInteger, default=0, comment="总调用次数")
last_used_at = Column(DateTime, default=None, comment="最后使用时间")
# ============================================================
# API调用统计
# ============================================================
class ApiCallStat(Base):
"""API调用统计表 - 按端点聚合"""
__tablename__ = "api_call_stats"
id = Column(String(36), primary_key=True, default=gen_uuid)
endpoint = Column(String(200), nullable=False)
method = Column(String(10), nullable=False)
call_count = Column(BigInteger, default=0)
total_response_time = Column(Float, default=0.0)
error_count = Column(BigInteger, default=0)
last_called_at = Column(DateTime, default=None)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
__table_args__ = (
UniqueConstraint("endpoint", "method", name="uk_endpoint_method"),
)
class SlowRequestLog(Base):
"""慢请求记录表"""
__tablename__ = "slow_request_logs"
id = Column(String(36), primary_key=True, default=gen_uuid)
request_id = Column(String(64), default=None)
method = Column(String(10), nullable=False)
endpoint = Column(String(500), nullable=False)
status_code = Column(Integer, default=0)
response_time = Column(Float, default=0.0)
threshold = Column(Float, default=3.0)
created_at = Column(DateTime, default=datetime.now)
__table_args__ = (
Index("idx_srl_created", "created_at"),
)
# ============================================================
# AI自进化引擎 - 用户进化配置
# ============================================================
class UserEvolutionConfig(Base):
__tablename__ = "user_evolution_config"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), unique=True, nullable=False, index=True)
skill_dir_path = Column(String(500), nullable=True, comment="用户Skill仓库根目录")
trajectory_dir_path = Column(String(500), nullable=True, comment="用户对话轨迹目录")
watcher_status = Column(String(20), default="stopped", comment="运行中/已停止/错误")
watcher_error = Column(String(500), nullable=True, comment="watcher异常信息")
pipeline_status = Column(String(20), default="idle", comment="流水线状态: running/idle/error")
pipeline_processed = Column(Integer, default=0, comment="已处理轨迹数")
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class EvolutionSkill(Base):
__tablename__ = "evolution_skills"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False, index=True)
skill_name = Column(String(200), nullable=False, comment="技能名称")
description = Column(Text, nullable=True)
tags = Column(JSON, nullable=True, comment="标签列表")
category = Column(String(100), nullable=True, comment="技能分类")
state = Column(String(20), default="baby", comment="baby/main/staging/frozen")
use_count = Column(Integer, default=0, comment="使用次数")
version = Column(Integer, default=1)
skill_md_path = Column(String(500), nullable=True, comment="SKILL.md文件路径")
git_commit_sha = Column(String(40), nullable=True)
last_used_at = Column(DateTime, nullable=True)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class EvolutionEvent(Base):
__tablename__ = "evolution_events"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False, index=True)
skill_name = Column(String(200), nullable=False)
event_type = Column(String(50), nullable=False, comment="created/promoted/rejected/frozen/unfrozen/edited/deleted/milestone")
description = Column(Text, nullable=True)
version_before = Column(Integer, nullable=True)
version_after = Column(Integer, nullable=True)
created_at = Column(DateTime, default=datetime.now)
class EvolutionTrajectory(Base):
__tablename__ = "evolution_trajectories"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False, index=True)
conversation_id = Column(String(36), nullable=True, comment="关联的对话ID")
file_path = Column(String(500), nullable=True, comment="轨迹文件路径")
status = Column(String(20), default="discovered", comment="discovered/splitting/split_done/indexed/clustering/done/error/filtered")
atom_count = Column(Integer, default=0)
token_used = Column(Integer, default=0)
error_msg = Column(String(500), nullable=True, comment="轨迹处理失败的错误信息")
retry_count = Column(Integer, default=0, comment="失败重试次数")
tasks_extracted = Column(Integer, default=0, comment="拆分的AtomTask数量")
created_at = Column(DateTime, default=datetime.now)
processed_at = Column(DateTime, nullable=True)
class SkillTrajectoryLink(Base):
"""技能-轨迹关联表:记录技能与对话轨迹的关联关系"""
__tablename__ = "skill_trajectory_links"
id = Column(String(36), primary_key=True, default=gen_uuid)
skill_name = Column(String(200), nullable=False, index=True, comment="关联的技能名称")
user_id = Column(String(36), ForeignKey("users.id"), nullable=False, index=True)
trajectory_id = Column(String(36), nullable=True, comment="关联的轨迹ID")
atom_id = Column(String(36), nullable=True, comment="原子任务ID")
weightscore = Column(Integer, default=0, comment="关联权重分")
created_at = Column(DateTime, default=datetime.now)
# ============================================================
# AI自进化v2 - 新增表
# ============================================================
class SkillVersion(Base):
"""技能版本历史表:存储每个版本的SKILL.md完整内容,支持回滚"""
__tablename__ = "skill_versions"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
skill_name = Column(String(200), nullable=False)
version = Column(Integer, nullable=False, default=1)
skill_md_content = Column(Text(4294967295), nullable=True, comment="该版本的SKILL.md完整内容")
git_commit_sha = Column(String(40), nullable=True)
description = Column(Text, nullable=True, comment="版本变更说明")
created_at = Column(DateTime, default=datetime.now)
__table_args__ = (
Index("idx_sv_user_skill", "user_id", "skill_name"),
Index("idx_sv_version", "version"),
)
class EvolutionReport(Base):
"""进化报告表:存储周报/月报"""
__tablename__ = "evolution_reports"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
report_type = Column(String(20), nullable=False, comment="weekly / monthly")
period_start = Column(Date, nullable=False)
period_end = Column(Date, nullable=False)
report_data = Column(JSON, nullable=True, comment="报告详细数据(JSON)")
created_at = Column(DateTime, default=datetime.now)
__table_args__ = (
Index("idx_er_user_type", "user_id", "report_type"),
Index("idx_er_period", "period_start", "period_end"),
)
class SkillPublishRecord(Base):
"""技能市场发布记录表"""
__tablename__ = "skill_publish_records"
id = Column(String(36), primary_key=True, default=gen_uuid)
skill_id = Column(String(36), nullable=False, comment="关联evolution_skills.id")
skill_name = Column(String(200), nullable=False)
user_id = Column(String(36), ForeignKey("users.id"), nullable=False)
market_app_id = Column(String(36), nullable=True, comment="关联market_apps.id")
price = Column(DECIMAL(10, 2), default=0.00)
market_description = Column(Text, nullable=True)
status = Column(String(20), nullable=False, default="published", comment="published/unpublished")
published_at = Column(DateTime, default=datetime.now)
created_at = Column(DateTime, default=datetime.now)
__table_args__ = (
Index("idx_spr_skill_id", "skill_id"),
Index("idx_spr_user_id", "user_id"),
Index("idx_spr_status", "status"),
)
class TeamSharedSkill(Base):
"""团队共享技能表"""
__tablename__ = "team_shared_skills"
id = Column(String(36), primary_key=True, default=gen_uuid)
team_id = Column(String(36), ForeignKey("teams.id"), nullable=False)
skill_id = Column(String(36), nullable=False, comment="关联evolution_skills.id")
skill_name = Column(String(200), nullable=False)
shared_by = Column(String(36), ForeignKey("users.id"), nullable=False)
shared_by_name = Column(String(100), nullable=True, comment="分享者昵称")
is_public = Column(Boolean, default=True, comment="团队内是否可见")
created_at = Column(DateTime, default=datetime.now)
__table_args__ = (
UniqueConstraint("team_id", "skill_id", name="uk_tss_team_skill"),
)
class ExternalKBService(Base):
"""外部知识库服务配置"""
__tablename__ = "external_kb_services"
id = Column(String(36), primary_key=True, default=gen_uuid)
name = Column(String(200), nullable=False)
service_type = Column(String(20), nullable=False)
api_url = Column(String(500), nullable=False)
api_key = Column(String(255), default="")
config = Column(JSON, default=None)
is_enabled = Column(Boolean, default=True)
status = Column(String(20), default="disconnected")
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class OSSStorageConfig(Base):
__tablename__ = "oss_storage_configs"
id = Column(String(36), primary_key=True, default=gen_uuid)
name = Column(String(200), nullable=False)
provider = Column(String(20), nullable=False)
endpoint = Column(String(500), nullable=False)
access_key = Column(String(255), nullable=False)
secret_key = Column(String(255), nullable=False)
bucket = Column(String(200), nullable=False)
region = Column(String(100), default="")
base_path = Column(String(300), default="")
is_default = Column(Boolean, default=False)
is_enabled = Column(Boolean, default=True)
status = Column(String(20), default="disconnected")
config = Column(JSON, default=None)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class UserStorageQuota(Base):
__tablename__ = "user_storage_quotas"
id = Column(String(36), primary_key=True, default=gen_uuid)
user_id = Column(String(36), nullable=False, unique=True)
quota_bytes = Column(BigInteger, nullable=False, default=1073741824)
used_bytes = Column(BigInteger, nullable=False, default=0)
quota_type = Column(String(20), nullable=False, default="membership")
membership_level = Column(Integer, default=0)
plan_id = Column(String(36), default=None)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class AutomatedTask(Base):
__tablename__ = "automated_tasks"
id = Column(String(36), primary_key=True, default=gen_uuid)
name = Column(String(200), nullable=False)
description = Column(Text, default="")
trigger_type = Column(String(20), nullable=False)
cron_expression = Column(String(100), default=None)
event_condition = Column(JSON, default=None)
action_type = Column(String(30), nullable=False)
action_config = Column(JSON, default=None)
is_enabled = Column(Boolean, default=True)
status = Column(String(20), default="idle")
max_retries = Column(Integer, default=3)
timeout_seconds = Column(Integer, default=300)
last_run_at = Column(DateTime, default=None)
next_run_at = Column(DateTime, default=None)
total_runs = Column(Integer, default=0)
success_runs = Column(Integer, default=0)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class TaskExecutionLog(Base):
__tablename__ = "task_execution_logs"
id = Column(String(36), primary_key=True, default=gen_uuid)
task_id = Column(String(36), nullable=False)
task_name = Column(String(200), default="")
status = Column(String(20), default="running")
trigger_type = Column(String(20), default="manual")
started_at = Column(DateTime, default=None)
finished_at = Column(DateTime, default=None)
duration_ms = Column(Integer, default=0)
result_summary = Column(Text, default="")
error_message = Column(Text, default="")
created_at = Column(DateTime, default=datetime.now)
class MCPServer(Base):
__tablename__ = "mcp_servers"
id = Column(String(36), primary_key=True, default=gen_uuid)
name = Column(String(100), nullable=False)
description = Column(Text, default="")
transport_type = Column(String(20), default="stdio")
command = Column(String(500), default="")
args = Column(Text, default="")
env_vars = Column(Text, default="")
url = Column(String(500), default="")
headers = Column(Text, default="")
is_enabled = Column(Boolean, default=True)
status = Column(String(20), default="disconnected")
last_connected_at = Column(DateTime, default=None)
tools_count = Column(Integer, default=0)
resources_count = Column(Integer, default=0)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class SmsVerificationCode(Base):
__tablename__ = "sms_verification_codes"
id = Column(String(36), primary_key=True, default=gen_uuid)
phone = Column(String(20), nullable=False, index=True)
code = Column(String(10), nullable=False)
purpose = Column(String(30), nullable=False) # register / reset_password
expires_at = Column(DateTime, nullable=False)
used = Column(Boolean, default=False)
created_at = Column(DateTime, default=datetime.now)