221 lines
8.0 KiB
Python
221 lines
8.0 KiB
Python
import uuid
|
||
|
||
from sqlalchemy import Column, String, Integer, Float, DateTime, Text, JSON, Boolean
|
||
from sqlalchemy.sql import func
|
||
|
||
from database import Base
|
||
|
||
|
||
def _iso(dt):
|
||
return dt.isoformat() if dt else None
|
||
|
||
|
||
class Avatar(Base):
|
||
__tablename__ = "avatars"
|
||
id = Column(String, primary_key=True, default=lambda: uuid.uuid4().hex)
|
||
owner_id = Column(String, default="", index=True) # 归属用户(会会 huihui_user_id);空=未归属/种子数据
|
||
name = Column(String, nullable=False)
|
||
display_name = Column(String, default="")
|
||
description = Column(Text, default="")
|
||
photo_url = Column(String, default="")
|
||
emoji = Column(String, default="🤖")
|
||
status = Column(String, default="active") # active | inactive | training
|
||
token_balance = Column(Integer, default=0)
|
||
config = Column(JSON, default=dict)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
def to_dict(self):
|
||
return {
|
||
"id": self.id,
|
||
"ownerId": self.owner_id,
|
||
"name": self.name,
|
||
"displayName": self.display_name,
|
||
"description": self.description,
|
||
"photoUrl": self.photo_url,
|
||
"emoji": self.emoji,
|
||
"status": self.status,
|
||
"tokenBalance": self.token_balance,
|
||
"config": self.config or {},
|
||
"createdAt": _iso(self.created_at),
|
||
"updatedAt": _iso(self.updated_at),
|
||
}
|
||
|
||
|
||
class Authorization(Base):
|
||
__tablename__ = "authorizations"
|
||
id = Column(String, primary_key=True, default=lambda: uuid.uuid4().hex)
|
||
avatar_id = Column(String, nullable=False, default="")
|
||
target_type = Column(String, default="user") # user | organization | application
|
||
target_id = Column(String, default="")
|
||
target_name = Column(String, default="")
|
||
permissions = Column(JSON, default=list)
|
||
status = Column(String, default="active") # active | inactive
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
|
||
def to_dict(self):
|
||
return {
|
||
"id": self.id,
|
||
"avatarId": self.avatar_id,
|
||
"targetType": self.target_type,
|
||
"targetId": self.target_id,
|
||
"targetName": self.target_name,
|
||
"permissions": self.permissions or [],
|
||
"status": self.status,
|
||
"createdAt": _iso(self.created_at),
|
||
}
|
||
|
||
|
||
class Organization(Base):
|
||
__tablename__ = "organizations"
|
||
id = Column(String, primary_key=True, default=lambda: uuid.uuid4().hex)
|
||
name = Column(String, nullable=False)
|
||
description = Column(Text, default="")
|
||
emoji = Column(String, default="🏢")
|
||
org_type = Column(String, default="team") # team | company | community
|
||
role = Column(String, default="admin") # admin | member | viewer
|
||
member_count = Column(Integer, default=1)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
|
||
def to_dict(self):
|
||
return {
|
||
"id": self.id,
|
||
"name": self.name,
|
||
"description": self.description,
|
||
"emoji": self.emoji,
|
||
"type": self.org_type,
|
||
"role": self.role,
|
||
"memberCount": self.member_count,
|
||
"createdAt": _iso(self.created_at),
|
||
}
|
||
|
||
|
||
class KnowledgeDoc(Base):
|
||
__tablename__ = "knowledge_docs"
|
||
id = Column(String, primary_key=True, default=lambda: uuid.uuid4().hex)
|
||
avatar_id = Column(String, nullable=False, default="")
|
||
filename = Column(String, default="")
|
||
file_type = Column(String, default="") # pdf | doc | docx | xlsx
|
||
file_size = Column(Integer, default=0)
|
||
file_url = Column(String, default="")
|
||
status = Column(String, default="uploaded") # uploaded | parsing | ready
|
||
vectorized = Column(Boolean, default=False) # 是否已向量化
|
||
embedding_model = Column(String, default="") # 向量模型标识
|
||
chunk_count = Column(Integer, default=0) # 切片数量
|
||
vectorized_at = Column(DateTime) # 向量化时间
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
|
||
def to_dict(self):
|
||
return {
|
||
"id": self.id,
|
||
"avatarId": self.avatar_id,
|
||
"filename": self.filename,
|
||
"fileType": self.file_type,
|
||
"fileSize": self.file_size,
|
||
"fileUrl": self.file_url,
|
||
"status": self.status,
|
||
"vectorized": bool(self.vectorized),
|
||
"embeddingModel": self.embedding_model,
|
||
"chunkCount": self.chunk_count,
|
||
"vectorizedAt": _iso(self.vectorized_at),
|
||
"createdAt": _iso(self.created_at),
|
||
}
|
||
|
||
|
||
class QAPair(Base):
|
||
__tablename__ = "qa_pairs"
|
||
id = Column(String, primary_key=True, default=lambda: uuid.uuid4().hex)
|
||
avatar_id = Column(String, nullable=False, default="")
|
||
question = Column(Text, default="")
|
||
answer = Column(Text, default="")
|
||
enabled = Column(Boolean, default=True) # 是否启用(关闭后不参与作答)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
def to_dict(self):
|
||
return {
|
||
"id": self.id,
|
||
"avatarId": self.avatar_id,
|
||
"question": self.question,
|
||
"answer": self.answer,
|
||
"enabled": bool(self.enabled),
|
||
"createdAt": _iso(self.created_at),
|
||
"updatedAt": _iso(self.updated_at),
|
||
}
|
||
|
||
|
||
class KnowledgeChunk(Base):
|
||
__tablename__ = "knowledge_chunks"
|
||
id = Column(String, primary_key=True, default=lambda: uuid.uuid4().hex)
|
||
doc_id = Column(String, default="") # 关联 KnowledgeDoc.id
|
||
avatar_id = Column(String, default="")
|
||
content = Column(Text, default="") # 切片文本
|
||
vector = Column(Text, default="") # JSON 编码的向量
|
||
chunk_index = Column(Integer, default=0)
|
||
embedding_model = Column(String, default="")
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
|
||
def to_dict(self):
|
||
return {
|
||
"id": self.id,
|
||
"docId": self.doc_id,
|
||
"avatarId": self.avatar_id,
|
||
"content": self.content,
|
||
"chunkIndex": self.chunk_index,
|
||
"embeddingModel": self.embedding_model,
|
||
"createdAt": _iso(self.created_at),
|
||
}
|
||
|
||
|
||
class TokenAccount(Base):
|
||
__tablename__ = "token_account"
|
||
id = Column(Integer, primary_key=True)
|
||
balance = Column(Integer, default=1250)
|
||
|
||
|
||
class TokenPlan(Base):
|
||
__tablename__ = "token_plans"
|
||
id = Column(String, primary_key=True)
|
||
name = Column(String, default="")
|
||
amount = Column(Integer, default=0)
|
||
price = Column(Float, default=0)
|
||
badge = Column(String, default="")
|
||
desc = Column(String, default="")
|
||
|
||
def to_dict(self):
|
||
return {
|
||
"id": self.id,
|
||
"name": self.name,
|
||
"amount": self.amount,
|
||
"price": self.price,
|
||
"badge": self.badge,
|
||
"desc": self.desc,
|
||
}
|
||
|
||
|
||
class User(Base):
|
||
"""会会用户 ↔ 本地用户体系映射(短信验证码登录落库)"""
|
||
|
||
__tablename__ = "users"
|
||
id = Column(String, primary_key=True, default=lambda: uuid.uuid4().hex)
|
||
huihui_user_id = Column(String, default="", index=True) # 会会 userId(唯一标识)
|
||
phone = Column(String, default="", index=True)
|
||
nickname = Column(String, default="")
|
||
avatar_url = Column(String, default="")
|
||
huihui_token = Column(String, default="") # 会会 access_token
|
||
app_token = Column(String, default="") # 本系统会话 token
|
||
last_login_at = Column(DateTime)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
def to_dict(self):
|
||
return {
|
||
"id": self.id,
|
||
"huihuiUserId": self.huihui_user_id,
|
||
"phone": self.phone,
|
||
"nickname": self.nickname,
|
||
"avatarUrl": self.avatar_url,
|
||
"createdAt": _iso(self.created_at),
|
||
"lastLoginAt": _iso(self.last_login_at),
|
||
}
|