feat: 娣诲姞澶氫釜鏂板姛鑳藉拰淇 - 鍖呮嫭鐢ㄦ埛绠$悊銆佹暟鎹簱杩佺Щ銆丟it鎺ㄩ€佸伐鍏风瓑

This commit is contained in:
赵杰 Jie Zhao (雄狮汽车科技)
2025-11-05 10:16:34 +08:00
parent a4261ef06f
commit c9d5c80f42
43 changed files with 4435 additions and 7439 deletions

View File

@@ -8,7 +8,7 @@ Base = declarative_base()
class WorkOrder(Base):
"""工单模型"""
__tablename__ = "work_orders"
id = Column(Integer, primary_key=True)
order_id = Column(String(50), unique=True, nullable=False)
title = Column(String(200), nullable=False)
@@ -20,13 +20,13 @@ class WorkOrder(Base):
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等
@@ -40,14 +40,23 @@ class WorkOrder(Base):
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- 用于区分海外/国内
# 关联对话记录
conversations = relationship("Conversation", back_populates="work_order")
# 关联处理过程记录
process_history = relationship("WorkOrderProcessHistory", back_populates="work_order", order_by="WorkOrderProcessHistory.process_time")
class Conversation(Base):
"""对话记录模型"""
__tablename__ = "conversations"
id = Column(Integer, primary_key=True)
work_order_id = Column(Integer, ForeignKey("work_orders.id"))
user_message = Column(Text, nullable=False)
@@ -56,13 +65,13 @@ class Conversation(Base):
confidence_score = Column(Float)
knowledge_used = Column(Text) # 使用的知识库条目
response_time = Column(Float) # 响应时间(秒)
work_order = relationship("WorkOrder", back_populates="conversations")
class KnowledgeEntry(Base):
"""知识库条目模型"""
__tablename__ = "knowledge_entries"
id = Column(Integer, primary_key=True)
question = Column(Text, nullable=False)
answer = Column(Text, nullable=False)
@@ -80,7 +89,7 @@ class KnowledgeEntry(Base):
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)) # 车架号
@@ -88,7 +97,7 @@ class VehicleData(Base):
data_value = Column(Text, nullable=False) # 数据值JSON格式
timestamp = Column(DateTime, default=datetime.now) # 数据时间戳
is_active = Column(Boolean, default=True) # 是否有效
# 索引
__table_args__ = (
{'extend_existing': True}
@@ -97,7 +106,7 @@ class VehicleData(Base):
class Analytics(Base):
"""分析统计模型"""
__tablename__ = "analytics"
id = Column(Integer, primary_key=True)
date = Column(DateTime, nullable=False)
total_orders = Column(Integer, default=0)
@@ -111,7 +120,7 @@ class Analytics(Base):
class Alert(Base):
"""预警模型"""
__tablename__ = "alerts"
id = Column(Integer, primary_key=True)
rule_name = Column(String(100), nullable=False)
alert_type = Column(String(50), nullable=False)
@@ -126,7 +135,7 @@ class Alert(Base):
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)
@@ -136,3 +145,31 @@ class WorkOrderSuggestion(Base):
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")

View File

@@ -0,0 +1,231 @@
# -*- coding: utf-8 -*-
"""
工单权限管理模块
实现基于角色的访问控制RBAC和工单分发流程
"""
import logging
from typing import List, Dict, Optional, Set
from enum import Enum
logger = logging.getLogger(__name__)
class UserRole(Enum):
"""用户角色枚举"""
# 属地运维(海外/国内)
OVERSEAS_OPS = "overseas_ops" # 海外属地运维
DOMESTIC_OPS = "domestic_ops" # 国内属地运维
# 业务方接口人(各模块负责人)
TBOX_OWNER = "tbox_owner" # TBOX模块负责人
OTA_OWNER = "ota_owner" # OTA模块负责人
DMC_OWNER = "dmc_owner" # DMC模块负责人
MES_OWNER = "mes_owner" # MES模块负责人
APP_OWNER = "app_owner" # APP模块负责人
PKI_OWNER = "pki_owner" # PKI模块负责人
TSP_OWNER = "tsp_owner" # TSP模块负责人
# 系统角色
ADMIN = "admin" # 系统管理员
VIEWER = "viewer" # 只读用户
class WorkOrderModule(Enum):
"""工单模块枚举"""
TBOX = "TBOX"
OTA = "OTA"
DMC = "DMC"
MES = "MES"
APP = "APP"
PKI = "PKI"
TSP = "TSP"
LOCAL_OPS = "local_ops" # 属地运维处理
UNASSIGNED = "unassigned" # 未分配
class WorkOrderStatus:
"""工单状态常量"""
PENDING = "pending" # 待处理
ASSIGNED = "assigned" # 已分配
IN_PROGRESS = "in_progress" # 处理中
RESOLVED = "resolved" # 已解决
CLOSED = "closed" # 已关闭
class WorkOrderPermissionManager:
"""工单权限管理器"""
# 所有模块集合(供属地运维和管理员使用)
ALL_MODULES = {
WorkOrderModule.TBOX, WorkOrderModule.OTA, WorkOrderModule.DMC,
WorkOrderModule.MES, WorkOrderModule.APP, WorkOrderModule.PKI,
WorkOrderModule.TSP, WorkOrderModule.LOCAL_OPS
}
# 角色到模块的映射
ROLE_MODULE_MAP = {
UserRole.TBOX_OWNER: {WorkOrderModule.TBOX},
UserRole.OTA_OWNER: {WorkOrderModule.OTA},
UserRole.DMC_OWNER: {WorkOrderModule.DMC},
UserRole.MES_OWNER: {WorkOrderModule.MES},
UserRole.APP_OWNER: {WorkOrderModule.APP},
UserRole.PKI_OWNER: {WorkOrderModule.PKI},
UserRole.TSP_OWNER: {WorkOrderModule.TSP},
UserRole.OVERSEAS_OPS: ALL_MODULES, # 可访问所有模块
UserRole.DOMESTIC_OPS: ALL_MODULES, # 可访问所有模块
UserRole.ADMIN: ALL_MODULES, # 管理员可访问所有
UserRole.VIEWER: set(), # 只读,由其他逻辑控制
}
@staticmethod
def can_view_all_workorders(role: UserRole) -> bool:
"""判断角色是否可以查看所有工单(属地运维和管理员)"""
return role in [UserRole.OVERSEAS_OPS, UserRole.DOMESTIC_OPS, UserRole.ADMIN]
@staticmethod
def get_accessible_modules(role: UserRole) -> Set[WorkOrderModule]:
"""获取角色可访问的模块列表"""
return WorkOrderPermissionManager.ROLE_MODULE_MAP.get(role, set())
@staticmethod
def can_access_module(role: UserRole, module: WorkOrderModule) -> bool:
"""判断角色是否可以访问指定模块"""
accessible_modules = WorkOrderPermissionManager.get_accessible_modules(role)
# 属地运维和管理员可以访问所有模块
if WorkOrderPermissionManager.can_view_all_workorders(role):
return True
# 业务方只能访问自己的模块
return module in accessible_modules
@staticmethod
def can_dispatch_workorder(role: UserRole) -> bool:
"""判断角色是否可以进行工单分发(属地运维和管理员)"""
return role in [UserRole.OVERSEAS_OPS, UserRole.DOMESTIC_OPS, UserRole.ADMIN]
@staticmethod
def can_update_workorder(role: UserRole, workorder_module: Optional[WorkOrderModule],
assigned_to_module: Optional[WorkOrderModule]) -> bool:
"""判断角色是否可以更新工单"""
# 管理员和属地运维可以更新所有工单
if WorkOrderPermissionManager.can_view_all_workorders(role):
return True
# 业务方只能更新分配给自己的模块的工单
if workorder_module and assigned_to_module:
accessible_modules = WorkOrderPermissionManager.get_accessible_modules(role)
return workorder_module in accessible_modules and workorder_module == assigned_to_module
return False
@staticmethod
def filter_workorders_by_permission(role: UserRole, workorders: List[Dict]) -> List[Dict]:
"""根据权限过滤工单列表"""
if WorkOrderPermissionManager.can_view_all_workorders(role):
# 属地运维和管理员可以看到所有工单
return workorders
# 业务方只能看到自己模块的工单
accessible_modules = WorkOrderPermissionManager.get_accessible_modules(role)
filtered = []
for wo in workorders:
module_str = wo.get("module") or wo.get("assigned_module")
if module_str:
try:
module = WorkOrderModule(module_str)
if module in accessible_modules:
filtered.append(wo)
except ValueError:
# 如果模块值不在枚举中,跳过
continue
else:
# 未分配的工单,业务方看不到
pass
return filtered
class WorkOrderDispatchManager:
"""工单分发管理器"""
# 模块到业务接口人的映射(可以动态配置)
MODULE_OWNER_MAP = {
WorkOrderModule.TBOX: "TBOX业务接口人",
WorkOrderModule.OTA: "OTA业务接口人",
WorkOrderModule.DMC: "DMC业务接口人",
WorkOrderModule.MES: "MES业务接口人",
WorkOrderModule.APP: "APP业务接口人",
WorkOrderModule.PKI: "PKI业务接口人",
WorkOrderModule.TSP: "TSP业务接口人",
}
@staticmethod
def get_module_owner(module: WorkOrderModule) -> str:
"""获取模块的业务接口人"""
return WorkOrderDispatchManager.MODULE_OWNER_MAP.get(module, "未指定")
@staticmethod
def dispatch_workorder(workorder_id: int, target_module: WorkOrderModule,
dispatcher_role: UserRole, dispatcher_name: str) -> Dict:
"""
分发工单到指定模块
Args:
workorder_id: 工单ID
target_module: 目标模块
dispatcher_role: 分发者角色(必须是运维或管理员)
dispatcher_name: 分发者姓名
Returns:
分发结果
"""
# 检查分发权限
if not WorkOrderPermissionManager.can_dispatch_workorder(dispatcher_role):
return {
"success": False,
"error": "无权进行工单分发,只有属地运维和管理员可以分发工单"
}
# 获取模块负责人
module_owner = WorkOrderDispatchManager.get_module_owner(target_module)
# 这里应该更新数据库中的工单信息
# 实际实现时需要调用数据库更新逻辑
return {
"success": True,
"message": f"工单已分发到{target_module.value}模块",
"assigned_module": target_module.value,
"module_owner": module_owner,
"dispatcher": dispatcher_name,
"dispatcher_role": dispatcher_role.value
}
@staticmethod
def suggest_module(description: str, title: str = "") -> Optional[WorkOrderModule]:
"""
根据工单描述建议分配模块可以使用AI分析
Args:
description: 工单描述
title: 工单标题
Returns:
建议的模块
"""
# 简单的关键词匹配实际可以使用AI分析
text = (title + " " + description).lower()
keyword_module_map = {
WorkOrderModule.TBOX: ["tbox", "telematics", "车载", "车联网"],
WorkOrderModule.OTA: ["ota", "over-the-air", "升级", "update"],
WorkOrderModule.DMC: ["dmc", "device management", "设备管理"],
WorkOrderModule.MES: ["mes", "manufacturing", "制造"],
WorkOrderModule.APP: ["app", "application", "应用", "remote control"],
WorkOrderModule.PKI: ["pki", "certificate", "证书"],
WorkOrderModule.TSP: ["tsp", "service", "服务"],
}
for module, keywords in keyword_module_map.items():
for keyword in keywords:
if keyword in text:
return module
return WorkOrderModule.UNASSIGNED