Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Q
qianjiangb2b
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
AI-甘富林
qianjiangb2b
Commits
6628eb4a
Commit
6628eb4a
authored
Jun 18, 2026
by
AI-甘富林
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
多轮对话支付下单修改
parent
7609888d
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
116 additions
and
1 deletion
+116
-1
ChatPage.tsx
src/pages/ChatPage.tsx
+85
-0
cozeStreamClient.ts
src/utils/cozeStreamClient.ts
+21
-1
difyStreamClient.ts
src/utils/difyStreamClient.ts
+10
-0
No files found.
src/pages/ChatPage.tsx
View file @
6628eb4a
...
@@ -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
;
...
...
src/utils/cozeStreamClient.ts
View file @
6628eb4a
...
@@ -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
;
}
}
...
...
src/utils/difyStreamClient.ts
View file @
6628eb4a
...
@@ -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'
:
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment