- FeishuService 新增 get_user_info() 根据 user_id 获取飞书用户姓名 - feishu_bot.py 处理消息时获取发送者姓名,日志格式改为:发送者=姓名(ID), 群=chat_id, 类型=群聊/私聊, 租户=xxx - feishu_longconn_service.py 同样获取发送者姓名并记录 - Conversation.ip_address 存储 feishu:user_id:sender_name(扩大字段到200字符) - Conversation.invocation_method 存储 feishu_bot(group) / feishu_longconn(p2p) 等详细来源 - ChatSession.source 同步记录详细来源信息
313 lines
14 KiB
Python
313 lines
14 KiB
Python
from sqlalchemy import Column, Integer, String, Text, DateTime, Float, Boolean, ForeignKey, Index
|
||
from sqlalchemy.ext.declarative import declarative_base
|
||
from sqlalchemy.orm import relationship
|
||
from datetime import datetime
|
||
import hashlib
|
||
|
||
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,
|
||
}
|
||
|
||
class WorkOrder(Base):
|
||
"""工单模型"""
|
||
__tablename__ = "work_orders"
|
||
|
||
id = Column(Integer, primary_key=True)
|
||
tenant_id = Column(String(50), nullable=False, default=DEFAULT_TENANT, index=True)
|
||
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) # 资源使用情况
|
||
|
||
# 关联对话记录
|
||
conversations = relationship("Conversation", back_populates="work_order")
|
||
# 关联处理过程记录
|
||
process_history = relationship("WorkOrderProcessHistory", back_populates="work_order", order_by="WorkOrderProcessHistory.process_time")
|
||
|
||
class ChatSession(Base):
|
||
"""对话会话模型 — 将多轮对话组织为一个会话"""
|
||
__tablename__ = "chat_sessions"
|
||
|
||
id = Column(Integer, primary_key=True)
|
||
tenant_id = Column(String(50), nullable=False, default=DEFAULT_TENANT, index=True)
|
||
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地址或来源标识
|
||
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,
|
||
}
|
||
|
||
|
||
class Conversation(Base):
|
||
"""对话记录模型"""
|
||
__tablename__ = "conversations"
|
||
|
||
id = Column(Integer, primary_key=True)
|
||
tenant_id = Column(String(50), nullable=False, default=DEFAULT_TENANT, index=True)
|
||
session_id = Column(String(100), ForeignKey("chat_sessions.session_id"), nullable=True) # 关联会话
|
||
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使用率
|
||
|
||
work_order = relationship("WorkOrder", back_populates="conversations")
|
||
chat_session = relationship("ChatSession", back_populates="messages")
|
||
|
||
class KnowledgeEntry(Base):
|
||
"""知识库条目模型"""
|
||
__tablename__ = "knowledge_entries"
|
||
|
||
id = Column(Integer, primary_key=True)
|
||
tenant_id = Column(String(50), nullable=False, default=DEFAULT_TENANT, index=True)
|
||
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) # 相关性评分
|
||
|
||
class VehicleData(Base):
|
||
"""车辆实时数据模型"""
|
||
__tablename__ = "vehicle_data"
|
||
|
||
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) # 是否有效
|
||
|
||
# 索引
|
||
__table_args__ = (
|
||
{'extend_existing': True}
|
||
)
|
||
|
||
class Analytics(Base):
|
||
"""分析统计模型"""
|
||
__tablename__ = "analytics"
|
||
|
||
id = Column(Integer, primary_key=True)
|
||
tenant_id = Column(String(50), nullable=False, default=DEFAULT_TENANT, index=True)
|
||
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格式)
|
||
|
||
class Alert(Base):
|
||
"""预警模型"""
|
||
__tablename__ = "alerts"
|
||
|
||
id = Column(Integer, primary_key=True)
|
||
tenant_id = Column(String(50), nullable=False, default=DEFAULT_TENANT, index=True)
|
||
rule_name = Column(String(100), nullable=False)
|
||
alert_type = Column(String(50), nullable=False)
|
||
level = Column(String(20), nullable=False) # info, warning, error, critical
|
||
severity = Column(String(20), nullable=False, default="medium") # low, medium, high, critical
|
||
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
|
||
}
|