Files
assist/src/core/models.py

313 lines
14 KiB
Python
Raw Normal View History

from sqlalchemy import Column, Integer, String, Text, DateTime, Float, Boolean, ForeignKey, Index
2025-09-06 21:06:18 +08:00
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from datetime import datetime
import hashlib
2025-09-06 21:06:18 +08:00
Base = declarative_base()
# 默认租户ID单租户部署时使用
DEFAULT_TENANT = "default"
class Tenant(Base):
"""租户模型 — 管理多租户(市场)"""
__tablename__ = "tenants"
id = Column(Integer, primary_key=True)
tenant_id = Column(String(50), unique=True, nullable=False) # 唯一标识,如 market_a
name = Column(String(100), nullable=False) # 显示名称,如 "市场A"
description = Column(Text, nullable=True)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
# 租户级配置:飞书 app 凭证、LLM 参数等JSON 格式)
config = Column(Text, nullable=True)
def to_dict(self):
import json
return {
'id': self.id,
'tenant_id': self.tenant_id,
'name': self.name,
'description': self.description,
'is_active': self.is_active,
'config': json.loads(self.config) if self.config else {},
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None,
}
2025-09-06 21:06:18 +08:00
class WorkOrder(Base):
"""工单模型"""
__tablename__ = "work_orders"
2025-09-06 21:06:18 +08:00
id = Column(Integer, primary_key=True)
tenant_id = Column(String(50), nullable=False, default=DEFAULT_TENANT, index=True)
2025-09-06 21:06:18 +08:00
order_id = Column(String(50), unique=True, nullable=False)
title = Column(String(200), nullable=False)
description = Column(Text, nullable=False)
category = Column(String(100), nullable=False)
priority = Column(String(20), nullable=False)
status = Column(String(20), nullable=False)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
resolution = Column(Text)
satisfaction_score = Column(Float)
# 飞书集成字段
feishu_record_id = Column(String(100), unique=True, nullable=True) # 飞书记录ID
assignee = Column(String(100), nullable=True) # 负责人
solution = Column(Text, nullable=True) # 解决方案
ai_suggestion = Column(Text, nullable=True) # AI建议
# 扩展飞书字段
source = Column(String(50), nullable=True) # 来源Mail, Telegram bot等
module = Column(String(100), nullable=True) # 模块local O&M, OTA等
created_by = Column(String(100), nullable=True) # 创建人
wilfulness = Column(String(100), nullable=True) # 责任人
date_of_close = Column(DateTime, nullable=True) # 关闭日期
vehicle_type = Column(String(100), nullable=True) # 车型
vin_sim = Column(String(50), nullable=True) # 车架号/SIM
app_remote_control_version = Column(String(100), nullable=True) # 应用远程控制版本
hmi_sw = Column(String(100), nullable=True) # HMI软件版本
parent_record = Column(String(100), nullable=True) # 父记录
has_updated_same_day = Column(String(50), nullable=True) # 是否同日更新
operating_time = Column(String(100), nullable=True) # 操作时间
# 工单分发和权限管理字段
assigned_module = Column(String(50), nullable=True) # 分配的模块TBOX、OTA等
module_owner = Column(String(100), nullable=True) # 业务接口人/模块负责人
dispatcher = Column(String(100), nullable=True) # 分发人(运维人员)
dispatch_time = Column(DateTime, nullable=True) # 分发时间
region = Column(String(50), nullable=True) # 区域overseas/domestic- 用于区分海外/国内
# 系统优化字段
processing_efficiency = Column(Float) # 处理效率
resource_usage = Column(Text) # 资源使用情况
2025-09-06 21:06:18 +08:00
# 关联对话记录
conversations = relationship("Conversation", back_populates="work_order")
# 关联处理过程记录
process_history = relationship("WorkOrderProcessHistory", back_populates="work_order", order_by="WorkOrderProcessHistory.process_time")
2025-09-06 21:06:18 +08:00
2026-03-20 16:50:26 +08:00
class ChatSession(Base):
"""对话会话模型 — 将多轮对话组织为一个会话"""
__tablename__ = "chat_sessions"
id = Column(Integer, primary_key=True)
tenant_id = Column(String(50), nullable=False, default=DEFAULT_TENANT, index=True)
2026-03-20 16:50:26 +08:00
session_id = Column(String(100), unique=True, nullable=False) # 唯一会话标识
user_id = Column(String(100), nullable=True) # 用户标识
work_order_id = Column(Integer, ForeignKey("work_orders.id"), nullable=True)
title = Column(String(200), nullable=True) # 会话标题(取首条消息摘要)
status = Column(String(20), default="active") # active, ended
message_count = Column(Integer, default=0) # 消息轮数
source = Column(String(100), nullable=True) # 来源websocket, api, feishu_bot(group) 等
ip_address = Column(String(200), nullable=True) # IP地址或来源标识
2026-03-20 16:50:26 +08:00
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
ended_at = Column(DateTime, nullable=True)
# 关联消息
messages = relationship("Conversation", back_populates="chat_session", order_by="Conversation.timestamp")
def to_dict(self):
return {
'id': self.id,
'session_id': self.session_id,
'user_id': self.user_id,
'work_order_id': self.work_order_id,
'title': self.title,
'status': self.status,
'message_count': self.message_count,
'source': self.source,
'ip_address': self.ip_address,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None,
'ended_at': self.ended_at.isoformat() if self.ended_at else None,
}
2025-09-06 21:06:18 +08:00
class Conversation(Base):
"""对话记录模型"""
__tablename__ = "conversations"
2025-09-06 21:06:18 +08:00
id = Column(Integer, primary_key=True)
tenant_id = Column(String(50), nullable=False, default=DEFAULT_TENANT, index=True)
2026-03-20 16:50:26 +08:00
session_id = Column(String(100), ForeignKey("chat_sessions.session_id"), nullable=True) # 关联会话
2025-09-06 21:06:18 +08:00
work_order_id = Column(Integer, ForeignKey("work_orders.id"))
user_message = Column(Text, nullable=False)
assistant_response = Column(Text, nullable=False)
timestamp = Column(DateTime, default=datetime.now)
confidence_score = Column(Float)
knowledge_used = Column(Text) # 使用的知识库条目
response_time = Column(Float) # 响应时间(秒)
ip_address = Column(String(200), nullable=True) # IP地址或来源标识如 feishu:uid:name
invocation_method = Column(String(100), nullable=True) # 调用方式websocket, api, feishu_bot(group) 等)
# 系统优化字段
processing_time = Column(Float) # 处理时间
memory_usage = Column(Float) # 内存使用量
cpu_usage = Column(Float) # CPU使用率
2025-09-06 21:06:18 +08:00
work_order = relationship("WorkOrder", back_populates="conversations")
2026-03-20 16:50:26 +08:00
chat_session = relationship("ChatSession", back_populates="messages")
2025-09-06 21:06:18 +08:00
class KnowledgeEntry(Base):
"""知识库条目模型"""
__tablename__ = "knowledge_entries"
2025-09-06 21:06:18 +08:00
id = Column(Integer, primary_key=True)
tenant_id = Column(String(50), nullable=False, default=DEFAULT_TENANT, index=True)
2025-09-06 21:06:18 +08:00
question = Column(Text, nullable=False)
answer = Column(Text, nullable=False)
category = Column(String(100), nullable=False)
confidence_score = Column(Float, default=0.0)
usage_count = Column(Integer, default=0)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
is_active = Column(Boolean, default=True)
is_verified = Column(Boolean, default=False) # 是否已验证
verified_by = Column(String(100)) # 验证人
verified_at = Column(DateTime) # 验证时间
vector_embedding = Column(Text) # 向量嵌入的JSON字符串
# 系统优化字段
search_frequency = Column(Integer, default=0) # 搜索频率
last_accessed = Column(DateTime) # 最后访问时间
relevance_score = Column(Float) # 相关性评分
2025-09-06 21:06:18 +08:00
class VehicleData(Base):
"""车辆实时数据模型"""
__tablename__ = "vehicle_data"
2025-09-06 21:06:18 +08:00
id = Column(Integer, primary_key=True)
vehicle_id = Column(String(50), nullable=False) # 车辆ID
vehicle_vin = Column(String(17)) # 车架号
data_type = Column(String(50), nullable=False) # 数据类型(位置、状态、故障等)
data_value = Column(Text, nullable=False) # 数据值JSON格式
timestamp = Column(DateTime, default=datetime.now) # 数据时间戳
is_active = Column(Boolean, default=True) # 是否有效
2025-09-06 21:06:18 +08:00
# 索引
__table_args__ = (
{'extend_existing': True}
)
class Analytics(Base):
"""分析统计模型"""
__tablename__ = "analytics"
2025-09-06 21:06:18 +08:00
id = Column(Integer, primary_key=True)
tenant_id = Column(String(50), nullable=False, default=DEFAULT_TENANT, index=True)
2025-09-06 21:06:18 +08:00
date = Column(DateTime, nullable=False)
total_orders = Column(Integer, default=0)
resolved_orders = Column(Integer, default=0)
avg_resolution_time = Column(Float, default=0.0)
satisfaction_avg = Column(Float, default=0.0)
knowledge_hit_rate = Column(Float, default=0.0)
category_distribution = Column(Text) # JSON格式的类别分布
created_at = Column(DateTime, default=datetime.now)
# 分析增强字段
performance_score = Column(Float) # 性能评分
quality_metrics = Column(Text) # 质量指标JSON格式
cost_analysis = Column(Text) # 成本分析JSON格式
optimization_suggestions = Column(Text) # 优化建议JSON格式
2025-09-06 21:06:18 +08:00
class Alert(Base):
"""预警模型"""
__tablename__ = "alerts"
2025-09-06 21:06:18 +08:00
id = Column(Integer, primary_key=True)
tenant_id = Column(String(50), nullable=False, default=DEFAULT_TENANT, index=True)
2025-09-06 21:06:18 +08:00
rule_name = Column(String(100), nullable=False)
alert_type = Column(String(50), nullable=False)
level = Column(String(20), nullable=False) # info, warning, error, critical
2025-09-08 15:27:22 +08:00
severity = Column(String(20), nullable=False, default="medium") # low, medium, high, critical
2025-09-06 21:06:18 +08:00
message = Column(Text, nullable=False)
data = Column(Text) # JSON格式的预警数据
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.now)
resolved_at = Column(DateTime)
class WorkOrderSuggestion(Base):
"""工单AI建议与人工描述表"""
__tablename__ = "work_order_suggestions"
id = Column(Integer, primary_key=True)
work_order_id = Column(Integer, ForeignKey("work_orders.id"), nullable=False)
ai_suggestion = Column(Text)
human_resolution = Column(Text)
ai_similarity = Column(Float)
approved = Column(Boolean, default=False)
use_human_resolution = Column(Boolean, default=False) # 是否使用人工描述入库
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
class WorkOrderProcessHistory(Base):
"""工单处理过程记录表"""
__tablename__ = "work_order_process_history"
id = Column(Integer, primary_key=True)
work_order_id = Column(Integer, ForeignKey("work_orders.id"), nullable=False)
# 处理人员信息
processor_name = Column(String(100), nullable=False) # 处理人员姓名
processor_role = Column(String(50), nullable=True) # 处理人员角色(运维、业务方等)
processor_region = Column(String(50), nullable=True) # 处理人员区域overseas/domestic
# 处理内容
process_content = Column(Text, nullable=False) # 处理内容/操作描述
action_type = Column(String(50), nullable=False) # 操作类型dispatch、process、close、reassign等
# 处理结果
previous_status = Column(String(50), nullable=True) # 处理前的状态
new_status = Column(String(50), nullable=True) # 处理后的状态
assigned_module = Column(String(50), nullable=True) # 分配的模块(如果是分发操作)
# 时间戳
process_time = Column(DateTime, default=datetime.now, nullable=False) # 处理时间
created_at = Column(DateTime, default=datetime.now)
# 关联工单
work_order = relationship("WorkOrder", back_populates="process_history")
class User(Base):
"""用户模型"""
__tablename__ = "users"
id = Column(Integer, primary_key=True)
tenant_id = Column(String(50), nullable=False, default=DEFAULT_TENANT, index=True)
username = Column(String(50), unique=True, nullable=False)
password_hash = Column(String(128), nullable=False)
email = Column(String(120), unique=True, nullable=True)
name = Column(String(100), nullable=True)
role = Column(String(20), default='user') # admin, user, operator
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.now)
last_login = Column(DateTime)
def set_password(self, password):
"""设置密码哈希"""
self.password_hash = hashlib.sha256(password.encode()).hexdigest()
def check_password(self, password):
"""验证密码"""
return self.password_hash == hashlib.sha256(password.encode()).hexdigest()
def to_dict(self):
"""转换为字典格式用于API响应"""
return {
'id': self.id,
'username': self.username,
'email': self.email,
'name': self.name,
'role': self.role,
'is_active': self.is_active,
'created_at': self.created_at.isoformat() if self.created_at else None,
'last_login': self.last_login.isoformat() if self.last_login else None
}