Commit 6628eb4a authored by AI-甘富林's avatar AI-甘富林

多轮对话支付下单修改

parent 7609888d
...@@ -393,6 +393,16 @@ export default function ChatPage() { ...@@ -393,6 +393,16 @@ export default function ChatPage() {
.replace(/\{\s*"(?:picks|intent)"\s*:[\s\S]*?\}\s*$/g, '') .replace(/\{\s*"(?:picks|intent)"\s*:[\s\S]*?\}\s*$/g, '')
.trim(); .trim();
// 🧹 清洗:移除 LLM 幻觉生成的 URL/链接(包括 Markdown 链接格式)
const hallucinatedUrlPatterns = [
/\[([^\]]*)\]\(https?:\/\/[^\)]+\)/gi,
/https?:\/\/[^\s]+/gi,
];
for (const p of hallucinatedUrlPatterns) {
fullContent = fullContent.replace(p, '');
}
fullContent = fullContent.trim();
if (!fullContent && !(Array.isArray(data.picks) && data.picks.length > 0)) { if (!fullContent && !(Array.isArray(data.picks) && data.picks.length > 0)) {
fullContent = '抱歉,目前店铺暂无符合您需求的商品。我们主要经营手机及配件,您可以试试问"推荐一款手机"~'; fullContent = '抱歉,目前店铺暂无符合您需求的商品。我们主要经营手机及配件,您可以试试问"推荐一款手机"~';
} }
...@@ -441,6 +451,81 @@ export default function ChatPage() { ...@@ -441,6 +451,81 @@ export default function ChatPage() {
} }
} }
// ★ action 分发:LLM 指示前端执行特定操作
if (data.action) {
console.log('🎬 [ACTION] 触发前端操作:', data.action, data.actionPayload);
switch (data.action) {
case 'show_after_sales_form': {
const payload = data.actionPayload || {};
const orderId = payload.orderId || (orders.length > 0 ? orders[0].id : '');
const orderNumber = payload.orderNumber || (orders.length > 0 ? orders[0].order_number : '');
const products = (payload.products && payload.products.length > 0)
? payload.products
: (orders.length > 0 ? orders[0].items || [] : []);
if (orderId && orderNumber) {
setSelectedOrderForAfterSales({ id: orderId, orderNumber, products });
setShowAfterSalesRequest(true);
console.log('✅ [ACTION] 弹出 AfterSalesRequestDialog:', { orderId, orderNumber });
}
break;
}
case 'show_order_detail': {
await fetchOrders();
if (orders.length > 0) {
messageType = 'order';
responseMetadata.orderInfo = orders.map(order => ({
orderId: order.order_number,
orderUuid: order.id,
status: order.status,
items: order.items || [],
total: ${order.total_amount}`,
orderDate: new Date(order.created_at).toLocaleDateString('zh-CN'),
shippingAddress: order.shipping_address
}));
}
break;
}
case 'show_faq':
// FAQ 内容已在 LLM 回复文本中,无需额外 UI 操作
console.log('📚 [ACTION] FAQ 模式,内容已在回复中');
break;
}
}
// ★ components 处理:LLM 返回的 UI 组件数组
if (Array.isArray(data.components) && data.components.length > 0) {
console.log('🧩 [COMPONENTS] LLM 返回组件:', data.components.length);
for (const comp of data.components) {
switch (comp.type) {
case 'order_status_card':
if (!responseMetadata.orderInfo && comp.props) {
responseMetadata.orderInfo = [{
orderId: comp.props.orderNumber || '',
orderUuid: comp.props.orderId || '',
status: comp.props.status || 'unknown',
total: comp.props.total || '',
orderDate: comp.props.createdAt || '',
}];
if (messageType === 'text') messageType = 'order';
}
break;
case 'after_sales_progress':
if (!responseMetadata.afterSalesInfo && comp.props) {
responseMetadata.afterSalesInfo = [{
id: comp.props.afterSalesId || '',
type: comp.props.type || '',
status: comp.props.status || '',
reason: comp.props.reason || '',
createdAt: comp.props.createdAt || '',
refundAmount: comp.props.refundAmount,
}];
if (messageType === 'text') messageType = 'aftersales';
}
break;
}
}
}
// ✅ 保存 conversation_id:先写 ref(同步、零延迟),再持久化 // ✅ 保存 conversation_id:先写 ref(同步、零延迟),再持久化
if (data.conversation_id && activeConversationId) { if (data.conversation_id && activeConversationId) {
difyConvIdMapRef.current[activeConversationId] = data.conversation_id; difyConvIdMapRef.current[activeConversationId] = data.conversation_id;
......
...@@ -28,14 +28,34 @@ interface StreamTextEvent { ...@@ -28,14 +28,34 @@ interface StreamTextEvent {
interface StreamDoneEvent { interface StreamDoneEvent {
type: 'done'; type: 'done';
intent: 'SHOPPING' | 'AFTERSALES' | 'GENERAL' | 'COMPARE'; intent: 'SHOPPING' | 'AFTERSALES' | 'GENERAL' | 'COMPARE' | 'CLARIFY';
picks: Array<{ product_id: string; note: string }>; picks: Array<{ product_id: string; note: string }>;
highlights: string[]; highlights: string[];
comparison?: string;
clarifies?: string[];
next?: string[];
title?: string;
action?: 'show_after_sales_form' | 'show_order_detail' | 'show_faq';
actionPayload?: {
orderId?: string;
orderNumber?: string;
products?: Array<{ id: string; name: string; price: number; image_url?: string }>;
};
components?: Array<{
type: 'order_status_card' | 'after_sales_progress';
props: Record<string, unknown>;
}>;
meta: { meta: {
platform: string; platform: string;
user: string; user: string;
catalog_count: number; catalog_count: number;
probe_id: string; probe_id: string;
source?: string;
hasHistory?: boolean;
historyMessages?: number;
hasOrderData?: boolean;
hasAfterSalesData?: boolean;
hasFAQData?: boolean;
}; };
conversation_id?: string; conversation_id?: string;
} }
......
...@@ -127,6 +127,16 @@ async function processSSELine(line: string, callbacks: CozeStreamCallbacks) { ...@@ -127,6 +127,16 @@ async function processSSELine(line: string, callbacks: CozeStreamCallbacks) {
callbacks.onText(event.content); callbacks.onText(event.content);
break; break;
case 'done': case 'done':
console.log('📦 [DIFY STREAM] done event:', {
intent: event.intent,
picksCount: event.picks?.length || 0,
picksPreview: event.picks?.slice(0, 3).map((p: any) => p.product_id?.substring(0, 12) + '...'),
hasAction: !!event.action,
action: event.action,
hasComponents: !!(event.components?.length),
componentsCount: event.components?.length || 0,
meta: event.meta,
});
await callbacks.onDone(event); await callbacks.onDone(event);
break; break;
case 'error': case 'error':
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment