170 lines
5.7 KiB
Python
170 lines
5.7 KiB
Python
|
|
import json
|
|||
|
|
from dataclasses import dataclass, asdict
|
|||
|
|
from typing import Optional, Dict, Any
|
|||
|
|
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class PlatformMessage:
|
|||
|
|
"""规范消息结构体 - 严格按照WebSocket文档v2格式"""
|
|||
|
|
# 必填字段
|
|||
|
|
type: str # 消息类型:"message" | "staff_list" | "ping" | "pong" | "transfer" | "connect_success" | "error"
|
|||
|
|
|
|||
|
|
# 条件必填字段(根据消息类型)
|
|||
|
|
content: Optional[str] = None # 消息内容
|
|||
|
|
msg_type: Optional[str] = None # 消息实际类型:"text" | "image" | "video" | "product_card" | "order_card"
|
|||
|
|
pin_image: Optional[str] = None # 用户头像URL
|
|||
|
|
sender: Optional[Dict] = None # 发送者信息,必须包含id字段
|
|||
|
|
store_id: Optional[str] = None # 店铺ID
|
|||
|
|
|
|||
|
|
# 可选字段
|
|||
|
|
receiver: Optional[Dict] = None # 接收者信息
|
|||
|
|
data: Optional[Dict] = None # 扩展数据(如客服列表、验证链接等)
|
|||
|
|
uuid: Optional[str] = None # 心跳包UUID
|
|||
|
|
token: Optional[str] = None # 认证令牌(心跳包可能需要)
|
|||
|
|
|
|||
|
|
def __post_init__(self):
|
|||
|
|
"""初始化后处理"""
|
|||
|
|
# 自动设置msg_type(仅对message类型)
|
|||
|
|
if self.type == "message" and self.msg_type is None and self.content:
|
|||
|
|
self.msg_type = self._detect_msg_type()
|
|||
|
|
|
|||
|
|
def _detect_msg_type(self) -> str:
|
|||
|
|
"""自动检测消息类型"""
|
|||
|
|
if not self.content:
|
|||
|
|
return "text"
|
|||
|
|
|
|||
|
|
content = self.content.lower()
|
|||
|
|
|
|||
|
|
# 图片检测
|
|||
|
|
if any(ext in content for ext in ['.jpg', '.jpeg', '.png', '.gif', '.webp']):
|
|||
|
|
return "image"
|
|||
|
|
|
|||
|
|
# 视频检测
|
|||
|
|
if any(ext in content for ext in ['.mp4', '.avi', '.mov', '.wmv', '.flv']):
|
|||
|
|
return "video"
|
|||
|
|
|
|||
|
|
# 订单卡片检测 - 支持多种格式
|
|||
|
|
if ("商品id:" in self.content and "订单号:" in self.content) or \
|
|||
|
|
("商品ID:" in self.content and "订单号:" in self.content) or \
|
|||
|
|
("咨询订单号:" in self.content and "商品ID:" in self.content):
|
|||
|
|
return "order_card"
|
|||
|
|
|
|||
|
|
# 商品卡片检测
|
|||
|
|
if any(keyword in content for keyword in ['goods.html', 'item.html', 'item.jd.com', '商品卡片id:']):
|
|||
|
|
return "product_card"
|
|||
|
|
|
|||
|
|
# 默认文本消息
|
|||
|
|
return "text"
|
|||
|
|
|
|||
|
|
def to_json(self) -> str:
|
|||
|
|
"""序列化为JSON字符串(用于存储)"""
|
|||
|
|
return json.dumps(self.to_dict(), ensure_ascii=False)
|
|||
|
|
|
|||
|
|
def to_dict(self) -> Dict[str, Any]:
|
|||
|
|
"""转换为字典,过滤None值"""
|
|||
|
|
result = {}
|
|||
|
|
for key, value in asdict(self).items():
|
|||
|
|
if value is not None:
|
|||
|
|
result[key] = value
|
|||
|
|
return result
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def from_json(cls, json_str: str):
|
|||
|
|
"""从JSON字符串恢复结构体(用于读取)"""
|
|||
|
|
data = json.loads(json_str)
|
|||
|
|
return cls(**data)
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def from_dict(cls, data: Dict[str, Any]):
|
|||
|
|
"""从字典创建实例"""
|
|||
|
|
return cls(**data)
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def create_text_message(cls, content: str, sender_id: str, store_id: str, pin_image: str = None):
|
|||
|
|
"""创建文本消息"""
|
|||
|
|
return cls(
|
|||
|
|
type="message",
|
|||
|
|
content=content,
|
|||
|
|
msg_type="text",
|
|||
|
|
pin_image=pin_image,
|
|||
|
|
sender={"id": sender_id},
|
|||
|
|
store_id=store_id
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def create_image_message(cls, image_url: str, sender_id: str, store_id: str, pin_image: str = None):
|
|||
|
|
"""创建图片消息"""
|
|||
|
|
return cls(
|
|||
|
|
type="message",
|
|||
|
|
content=image_url,
|
|||
|
|
msg_type="image",
|
|||
|
|
pin_image=pin_image,
|
|||
|
|
sender={"id": sender_id},
|
|||
|
|
store_id=store_id
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def create_video_message(cls, video_url: str, sender_id: str, store_id: str, pin_image: str = None):
|
|||
|
|
"""创建视频消息"""
|
|||
|
|
return cls(
|
|||
|
|
type="message",
|
|||
|
|
content=video_url,
|
|||
|
|
msg_type="video",
|
|||
|
|
pin_image=pin_image,
|
|||
|
|
sender={"id": sender_id},
|
|||
|
|
store_id=store_id
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def create_order_card_message(cls, product_id: str, order_number: str, sender_id: str, store_id: str,
|
|||
|
|
pin_image: str = None):
|
|||
|
|
"""创建订单卡片消息"""
|
|||
|
|
return cls(
|
|||
|
|
type="message",
|
|||
|
|
content=f"商品id:{product_id} 订单号:{order_number}",
|
|||
|
|
msg_type="order_card",
|
|||
|
|
pin_image=pin_image,
|
|||
|
|
sender={"id": sender_id},
|
|||
|
|
store_id=store_id
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def create_product_card_message(cls, product_url: str, sender_id: str, store_id: str, pin_image: str = None):
|
|||
|
|
"""创建商品卡片消息"""
|
|||
|
|
return cls(
|
|||
|
|
type="message",
|
|||
|
|
content=product_url,
|
|||
|
|
msg_type="product_card",
|
|||
|
|
pin_image=pin_image,
|
|||
|
|
sender={"id": sender_id},
|
|||
|
|
store_id=store_id
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def create_staff_list_message(cls, staff_list: list, store_id: str):
|
|||
|
|
"""创建客服列表消息"""
|
|||
|
|
return cls(
|
|||
|
|
type="staff_list",
|
|||
|
|
content="客服列表更新",
|
|||
|
|
data={"staff_list": staff_list},
|
|||
|
|
store_id=store_id
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def create_ping_message(cls, uuid_str: str, token: str = None):
|
|||
|
|
"""创建心跳检测消息"""
|
|||
|
|
message = cls(
|
|||
|
|
type="ping",
|
|||
|
|
uuid=uuid_str
|
|||
|
|
)
|
|||
|
|
if token:
|
|||
|
|
message.token = token
|
|||
|
|
return message
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def create_pong_message(cls, uuid_str: str):
|
|||
|
|
"""创建心跳回复消息"""
|
|||
|
|
return cls(
|
|||
|
|
type="pong",
|
|||
|
|
uuid=uuid_str
|
|||
|
|
)
|