Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
chatbot canifa
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
1
Merge Requests
1
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
Vũ Hoàng Anh
chatbot canifa
Commits
90ea36b3
Commit
90ea36b3
authored
Mar 05, 2026
by
Vũ Hoàng Anh
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(api): add dedicated n8n product endpoint with description, update promo tool
parent
37c109a3
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
120 additions
and
8 deletions
+120
-8
promotion_canifa_tool.py
backend/agent/tools/promotion_canifa_tool.py
+31
-8
n8n_api_route.py
backend/api/n8n_api_route.py
+44
-0
server.py
backend/server.py
+2
-0
test_n8n_api.py
backend/tests/test_n8n_api.py
+43
-0
No files found.
backend/agent/tools/promotion_canifa_tool.py
View file @
90ea36b3
...
@@ -33,21 +33,30 @@ async def canifa_get_promotions(check_date: str = None) -> str:
...
@@ -33,21 +33,30 @@ async def canifa_get_promotions(check_date: str = None) -> str:
target_date
=
check_date
target_date
=
check_date
if
not
target_date
:
if
not
target_date
:
target_date
=
datetime
.
now
()
.
strftime
(
"
%
Y-
%
m-
%
d"
)
target_date
=
datetime
.
now
()
.
strftime
(
"
%
Y-
%
m-
%
d"
)
logger
.
info
(
f
"🎁 [Promotion Search] Checking for date: {target_date}"
)
logger
.
info
(
f
"🎁 [Promotion Search] Checking for date: {target_date}"
)
try
:
try
:
sql
=
f
"""
sql
=
f
"""
SELECT
SELECT
name,
name,
description,
description,
description_full,
description_full,
from_date,
from_date,
to_date
to_date,
applied_channel
FROM shared_source.chatbot_rsa_salerule_with_text_embedding
FROM shared_source.chatbot_rsa_salerule_with_text_embedding
WHERE '{target_date}' >= DATE(from_date)
WHERE '{target_date}' >= DATE(from_date)
AND '{target_date}' <= DATE(to_date)
AND '{target_date}' <= DATE(to_date)
ORDER BY to_date ASC
ORDER BY
CASE applied_channel
WHEN 'only_online' THEN 0
WHEN 'both' THEN 1
WHEN 'unknown' THEN 2
WHEN 'only_offline' THEN 3
ELSE 4
END,
to_date ASC
LIMIT 20
LIMIT 20
"""
"""
...
@@ -57,6 +66,14 @@ async def canifa_get_promotions(check_date: str = None) -> str:
...
@@ -57,6 +66,14 @@ async def canifa_get_promotions(check_date: str = None) -> str:
if
not
results
:
if
not
results
:
return
f
"Hiện tại (ngày {target_date}) không có chương trình khuyến mãi nào đang diễn ra trên hệ thống."
return
f
"Hiện tại (ngày {target_date}) không có chương trình khuyến mãi nào đang diễn ra trên hệ thống."
# Map channel values to Vietnamese labels
channel_labels
=
{
"only_online"
:
"Online (Web/App)"
,
"both"
:
"Online + Cửa hàng"
,
"only_offline"
:
"Chỉ tại cửa hàng"
,
"unknown"
:
"Chưa phân loại kênh"
,
}
lines
=
[
f
"Tìm thấy {len(results)} chương trình khuyến mãi đang diễn ra (ngày {target_date}):
\n
"
]
lines
=
[
f
"Tìm thấy {len(results)} chương trình khuyến mãi đang diễn ra (ngày {target_date}):
\n
"
]
for
i
,
res
in
enumerate
(
results
,
1
):
for
i
,
res
in
enumerate
(
results
,
1
):
name
=
res
.
get
(
"name"
,
"CTKM"
)
name
=
res
.
get
(
"name"
,
"CTKM"
)
...
@@ -64,19 +81,25 @@ async def canifa_get_promotions(check_date: str = None) -> str:
...
@@ -64,19 +81,25 @@ async def canifa_get_promotions(check_date: str = None) -> str:
desc_full
=
res
.
get
(
"description_full"
,
""
)
desc_full
=
res
.
get
(
"description_full"
,
""
)
f_date
=
res
.
get
(
"from_date"
,
""
)
f_date
=
res
.
get
(
"from_date"
,
""
)
t_date
=
res
.
get
(
"to_date"
,
""
)
t_date
=
res
.
get
(
"to_date"
,
""
)
channel
=
res
.
get
(
"applied_channel"
,
"unknown"
)
channel_label
=
channel_labels
.
get
(
channel
,
channel
)
# Use description_full if available and longer, else description
# Use description_full if available and longer, else description
content
=
desc_full
if
desc_full
and
len
(
str
(
desc_full
))
>
len
(
str
(
desc
))
else
desc
content
=
desc_full
if
desc_full
and
len
(
str
(
desc_full
))
>
len
(
str
(
desc
))
else
desc
lines
.
append
(
lines
.
append
(
f
"[CTKM {i}]
\n
"
f
"[CTKM {i}]
\n
"
f
"Tên: {name}
\n
"
f
"Tên: {name}
\n
"
f
"Kênh áp dụng: {channel_label}
\n
"
f
"Nội dung: {content}
\n
"
f
"Nội dung: {content}
\n
"
f
"Thời gian: {f_date} đến {t_date}
\n
"
f
"Thời gian: {f_date} đến {t_date}
\n
"
)
)
lines
.
append
(
lines
.
append
(
"LƯU Ý: Trình bày NỘI DUNG ưu đãi chi tiết cho khách. "
"LƯU Ý QUAN TRỌNG VỀ KÊNH ÁP DỤNG:
\n
"
"- Mặc định: ƯU TIÊN trình bày các CTKM 'Online (Web/App)' và 'Online + Cửa hàng' TRƯỚC.
\n
"
"- Chỉ trình bày CTKM 'Chỉ tại cửa hàng' khi khách hỏi CỤ THỂ về ưu đãi tại cửa hàng/offline.
\n
"
"- Trình bày NỘI DUNG ưu đãi chi tiết cho khách. "
"Nếu nội dung chỉ ghi địa điểm áp dụng mà KHÔNG ghi cụ thể giảm bao nhiêu
%
, "
"Nếu nội dung chỉ ghi địa điểm áp dụng mà KHÔNG ghi cụ thể giảm bao nhiêu
%
, "
"hãy trình bày tên chương trình + thời gian + nội dung có sẵn, "
"hãy trình bày tên chương trình + thời gian + nội dung có sẵn, "
"rồi hướng dẫn khách liên hệ hotline 1800 6061 hoặc vào canifa.com để xem chi tiết ưu đãi."
"rồi hướng dẫn khách liên hệ hotline 1800 6061 hoặc vào canifa.com để xem chi tiết ưu đãi."
...
...
backend/api/n8n_api_route.py
0 → 100644
View file @
90ea36b3
import
logging
from
fastapi
import
APIRouter
,
HTTPException
logger
=
logging
.
getLogger
(
__name__
)
router
=
APIRouter
()
@
router
.
get
(
"/api/agent/n8n/products"
,
summary
=
"N8N Specific: Get Sample Products"
)
async
def
n8n_get_sample_products
(
limit
:
int
=
10
):
"""
API DÀNH RIÊNG CHO N8N để lấy danh sách sản phẩm thực tế làm data cho AI sinh câu hỏi.
- Code hoàn toàn tách biệt khỏi hệ thống cũ (không đụng vào logic SearchItem/Embedding)
- Trả về danh sách ngẫu nhiên từ StarRocks
"""
try
:
from
common.starrocks_connection
import
get_db_connection
db
=
get_db_connection
()
# Lấy random các sản phẩm có hiển thị trên web, có giá
query
=
f
"""
SELECT
magento_ref_code,
product_name,
description_text,
original_price,
sale_price,
master_color,
gender_by_product,
product_web_url,
product_image_url_thumbnail
FROM shared_source.magento_product_dimension_with_text_embedding
WHERE sale_price > 0 AND quantity_sold > 0
ORDER BY rand()
LIMIT {limit}
"""
products
=
await
db
.
execute_query_async
(
query
)
return
{
"status"
:
"success"
,
"total"
:
len
(
products
),
"products"
:
products
}
except
Exception
as
e
:
logger
.
error
(
f
"❌ Error in N8N Dedicated Product Fetch API: {e!s}"
,
exc_info
=
True
)
raise
HTTPException
(
status_code
=
500
,
detail
=
f
"N8N API Error: {e!s}"
)
backend/server.py
View file @
90ea36b3
...
@@ -15,6 +15,7 @@ from api.mock_api_route import router as mock_router
...
@@ -15,6 +15,7 @@ from api.mock_api_route import router as mock_router
from
api.prompt_route
import
router
as
prompt_router
from
api.prompt_route
import
router
as
prompt_router
from
api.stock_route
import
router
as
stock_router
from
api.stock_route
import
router
as
stock_router
from
api.tool_prompt_route
import
router
as
tool_prompt_router
from
api.tool_prompt_route
import
router
as
tool_prompt_router
from
api.n8n_api_route
import
router
as
n8n_router
from
common.cache
import
redis_cache
from
common.cache
import
redis_cache
from
common.middleware
import
middleware_manager
from
common.middleware
import
middleware_manager
from
config
import
PORT
from
config
import
PORT
...
@@ -94,6 +95,7 @@ app.include_router(prompt_router)
...
@@ -94,6 +95,7 @@ app.include_router(prompt_router)
app
.
include_router
(
tool_prompt_router
)
# Register new router
app
.
include_router
(
tool_prompt_router
)
# Register new router
app
.
include_router
(
mock_router
)
app
.
include_router
(
mock_router
)
app
.
include_router
(
stock_router
)
app
.
include_router
(
stock_router
)
app
.
include_router
(
n8n_router
)
if
__name__
==
"__main__"
:
if
__name__
==
"__main__"
:
...
...
backend/tests/test_n8n_api.py
0 → 100644
View file @
90ea36b3
import
requests
import
json
import
time
def
test_api
(
limit
):
url
=
f
'http://localhost:5000/api/agent/n8n/products?limit={limit}'
print
(
f
'
\n
--- Testing API with limit={limit} ---'
)
print
(
f
'URL: {url}'
)
start_time
=
time
.
time
()
try
:
response
=
requests
.
get
(
url
,
timeout
=
10
)
elapsed
=
time
.
time
()
-
start_time
print
(
f
'Status Code : {response.status_code}'
)
print
(
f
'Time Taken : {elapsed:.3f} seconds'
)
if
response
.
status_code
==
200
:
data
=
response
.
json
()
print
(
f
'Status Field: {data.get("status")}'
)
print
(
f
'Total Field : {data.get("total")}'
)
products
=
data
.
get
(
"products"
,
[])
print
(
f
'Products returned: {len(products)}'
)
if
len
(
products
)
>
0
:
print
(
'
\n
Sample Product (first item):'
)
print
(
json
.
dumps
(
products
[
0
],
indent
=
2
,
ensure_ascii
=
False
))
else
:
print
(
'WARNING: No products returned. Is the database empty or filter too strict?'
)
else
:
print
(
'ERROR Response:'
)
try
:
print
(
json
.
dumps
(
response
.
json
(),
indent
=
2
,
ensure_ascii
=
False
))
except
:
print
(
response
.
text
)
except
Exception
as
e
:
print
(
f
'Request failed: {e}'
)
if
__name__
==
'__main__'
:
test_api
(
3
)
test_api
(
10
)
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