mickeywu520 commited on
Commit
68c8519
·
1 Parent(s): 89879a0

修正訂單查詢問題

Browse files
backend/services/database_service.py CHANGED
@@ -567,40 +567,85 @@ class DatabaseService:
567
  if db:
568
  close_database_session(db)
569
 
570
- def search_orders(self, user_id: str = None, status: str = None, limit: int = 10) -> DatabaseResult:
571
- """訂單查詢"""
 
 
572
  db = None
573
  try:
574
  db = get_database_session()
575
-
576
- # 使用 SalesOrder 作為主要的訂單查詢
577
- query = db.query(SalesOrder)
578
-
 
 
 
 
 
 
 
 
 
 
579
  # 用戶篩選 (銷售人員)
580
  if user_id:
581
  query = query.filter(SalesOrder.salesperson_id == user_id)
582
-
583
- # 狀態篩選
 
 
 
 
584
  if status:
585
- query = query.filter(SalesOrder.status.ilike(f"%{status}%"))
586
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
587
  orders = query.limit(limit).all()
588
-
589
  # 轉換為字典格式
590
  order_data = []
591
  for order in orders:
 
 
 
592
  order_info = {
593
  "order_id": order.so_number,
594
  "sales_date": order.sales_date.isoformat() if order.sales_date else None,
595
- "customer_name": order.customer.customerName if order.customer else None,
596
- "salesperson_name": order.salesperson.name if order.salesperson else None,
597
- "status": order.status.value if order.status else None,
 
 
 
 
 
 
 
598
  "total_amount": float(order.total_amount) if order.total_amount else 0.0,
 
599
  "created_at": order.created_at.isoformat() if order.created_at else None,
600
  "updated_at": order.updated_at.isoformat() if order.updated_at else None
601
  }
602
  order_data.append(order_info)
603
-
604
  return DatabaseResult(
605
  success=True,
606
  data=order_data,
@@ -617,6 +662,23 @@ class DatabaseService:
617
  if db:
618
  close_database_session(db)
619
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
620
  def get_low_stock_products(self, threshold: int = 10) -> DatabaseResult:
621
  """低庫存商品查詢"""
622
  db = None
@@ -719,8 +781,12 @@ class DatabaseService:
719
  return self.search_products(query_text=product_name)
720
 
721
  # 訂單查詢
722
- elif any(keyword in message_lower for keyword in ['訂單', '訂購', '購買']):
723
- return self.search_orders(user_id=user_id)
 
 
 
 
724
 
725
  # 低庫存查詢
726
  elif any(keyword in message_lower for keyword in ['低庫存', '缺貨', '不足']):
@@ -746,9 +812,63 @@ class DatabaseService:
746
  # 簡單的商品名稱提取邏輯
747
  # 移除常見的查詢關鍵字
748
  keywords_to_remove = ['查詢', '搜尋', '找', '商品', '產品', '庫存', '有沒有', '請問']
749
-
750
  cleaned_message = message
751
  for keyword in keywords_to_remove:
752
  cleaned_message = cleaned_message.replace(keyword, '')
753
-
754
- return cleaned_message.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
567
  if db:
568
  close_database_session(db)
569
 
570
+ def search_orders(self, user_id: str = None, status: str = None, order_number: str = None,
571
+ customer_id: str = None, date_from: str = None, date_to: str = None,
572
+ limit: int = 10) -> DatabaseResult:
573
+ """增強的訂單查詢功能"""
574
  db = None
575
  try:
576
  db = get_database_session()
577
+
578
+ # 使用 SalesOrder 作為主要的訂單查詢,預載入關聯資料
579
+ query = db.query(SalesOrder).options(
580
+ joinedload(SalesOrder.customer),
581
+ joinedload(SalesOrder.salesperson)
582
+ )
583
+
584
+ # 訂單編號查詢 (支援 SO- 格式)
585
+ if order_number:
586
+ if not order_number.startswith('SO-'):
587
+ # 如果用戶只輸入部分編號,自動補全
588
+ order_number = f"SO-{order_number}"
589
+ query = query.filter(SalesOrder.so_number.ilike(f"%{order_number}%"))
590
+
591
  # 用戶篩選 (銷售人員)
592
  if user_id:
593
  query = query.filter(SalesOrder.salesperson_id == user_id)
594
+
595
+ # 客戶篩選
596
+ if customer_id:
597
+ query = query.filter(SalesOrder.customer_id == customer_id)
598
+
599
+ # 狀態篩選 (支援中文和英文)
600
  if status:
601
+ status_mapping = {
602
+ '已交付': 'DELIVERED',
603
+ '已出貨': 'SHIPPED',
604
+ '處理中': 'PROCESSING',
605
+ '已取消': 'CANCELLED',
606
+ '待處理': 'PENDING'
607
+ }
608
+ # 檢查是否為中文狀態,轉換為英文
609
+ english_status = status_mapping.get(status, status)
610
+ query = query.filter(SalesOrder.status.ilike(f"%{english_status}%"))
611
+
612
+ # 日期範圍篩選
613
+ if date_from:
614
+ query = query.filter(SalesOrder.sales_date >= date_from)
615
+ if date_to:
616
+ query = query.filter(SalesOrder.sales_date <= date_to)
617
+
618
+ # 按日期降序排列,最新的在前面
619
+ query = query.order_by(SalesOrder.sales_date.desc())
620
+
621
  orders = query.limit(limit).all()
622
+
623
  # 轉換為字典格式
624
  order_data = []
625
  for order in orders:
626
+ # 狀態中文化
627
+ status_display = self._get_order_status_display(order.status)
628
+
629
  order_info = {
630
  "order_id": order.so_number,
631
  "sales_date": order.sales_date.isoformat() if order.sales_date else None,
632
+ "customer_id": order.customer_id,
633
+ "customer_name": order.customer.customerName if order.customer else "未知客戶",
634
+ "salesperson_id": order.salesperson_id,
635
+ "salesperson_name": order.salesperson.name if order.salesperson else "未指定",
636
+ "status": order.status,
637
+ "status_display": status_display,
638
+ "payment_term": order.payment_term,
639
+ "subtotal": float(order.subtotal) if order.subtotal else 0.0,
640
+ "tax_amount": float(order.tax_amount) if order.tax_amount else 0.0,
641
+ "discount_amount": float(order.discount_amount) if order.discount_amount else 0.0,
642
  "total_amount": float(order.total_amount) if order.total_amount else 0.0,
643
+ "notes": order.notes or "",
644
  "created_at": order.created_at.isoformat() if order.created_at else None,
645
  "updated_at": order.updated_at.isoformat() if order.updated_at else None
646
  }
647
  order_data.append(order_info)
648
+
649
  return DatabaseResult(
650
  success=True,
651
  data=order_data,
 
662
  if db:
663
  close_database_session(db)
664
 
665
+ def _get_order_status_display(self, status) -> str:
666
+ """將訂單狀態轉換為中文顯示"""
667
+ status_mapping = {
668
+ 'DELIVERED': '已交付',
669
+ 'SHIPPED': '已出貨',
670
+ 'PROCESSING': '處理中',
671
+ 'CANCELLED': '已取消',
672
+ 'PENDING': '待處理',
673
+ 'CONFIRMED': '已確認',
674
+ 'COMPLETED': '已完成'
675
+ }
676
+
677
+ if hasattr(status, 'value'):
678
+ return status_mapping.get(status.value, str(status.value))
679
+ else:
680
+ return status_mapping.get(str(status), str(status))
681
+
682
  def get_low_stock_products(self, threshold: int = 10) -> DatabaseResult:
683
  """低庫存商品查詢"""
684
  db = None
 
781
  return self.search_products(query_text=product_name)
782
 
783
  # 訂單查詢
784
+ elif any(keyword in message_lower for keyword in ['訂單', '訂購', '購買', 'so-']):
785
+ # 提取訂單編號
786
+ order_number = self._extract_order_number(user_message)
787
+ # 提取狀態
788
+ status = self._extract_order_status(user_message)
789
+ return self.search_orders(user_id=user_id, order_number=order_number, status=status)
790
 
791
  # 低庫存查詢
792
  elif any(keyword in message_lower for keyword in ['低庫存', '缺貨', '不足']):
 
812
  # 簡單的商品名稱提取邏輯
813
  # 移除常見的查詢關鍵字
814
  keywords_to_remove = ['查詢', '搜尋', '找', '商品', '產品', '庫存', '有沒有', '請問']
815
+
816
  cleaned_message = message
817
  for keyword in keywords_to_remove:
818
  cleaned_message = cleaned_message.replace(keyword, '')
819
+
820
+ return cleaned_message.strip()
821
+
822
+ def _extract_order_number(self, message: str) -> str:
823
+ """從訊息中提取訂單編號"""
824
+ import re
825
+
826
+ # 尋找 SO- 格式的訂單編號
827
+ so_pattern = r'SO-\d{8}-\d{3}'
828
+ match = re.search(so_pattern, message.upper())
829
+ if match:
830
+ return match.group()
831
+
832
+ # 尋找日期格式的編號 (20250706-001)
833
+ date_pattern = r'\d{8}-\d{3}'
834
+ match = re.search(date_pattern, message)
835
+ if match:
836
+ return f"SO-{match.group()}"
837
+
838
+ # 尋找純數字編號
839
+ number_pattern = r'\d{6,}'
840
+ match = re.search(number_pattern, message)
841
+ if match:
842
+ return match.group()
843
+
844
+ return None
845
+
846
+ def _extract_order_status(self, message: str) -> str:
847
+ """從訊息中提取訂單狀態"""
848
+ message_lower = message.lower()
849
+
850
+ # 中文狀態關鍵字
851
+ status_keywords = {
852
+ '已交付': 'DELIVERED',
853
+ '已出貨': 'SHIPPED',
854
+ '出貨': 'SHIPPED',
855
+ '交付': 'DELIVERED',
856
+ '處理中': 'PROCESSING',
857
+ '已取消': 'CANCELLED',
858
+ '取消': 'CANCELLED',
859
+ '待處理': 'PENDING',
860
+ '已確認': 'CONFIRMED',
861
+ '已完成': 'COMPLETED'
862
+ }
863
+
864
+ for chinese, english in status_keywords.items():
865
+ if chinese in message_lower:
866
+ return english
867
+
868
+ # 英文狀態關鍵字
869
+ english_keywords = ['delivered', 'shipped', 'processing', 'cancelled', 'pending', 'confirmed', 'completed']
870
+ for keyword in english_keywords:
871
+ if keyword in message_lower:
872
+ return keyword.upper()
873
+
874
+ return None
backend/services/nlp_service.py CHANGED
@@ -612,19 +612,38 @@ class NLPService:
612
  return response
613
 
614
  def _format_order_response(self, data: List[Dict[str, Any]]) -> str:
615
- """格式化訂單查詢回應"""
 
 
 
616
  if len(data) == 1:
617
  order = data[0]
618
- return f"訂單資訊:\n" \
 
619
  f"訂單編號:{order.get('order_id', 'N/A')}\n" \
620
- f"狀態:{order.get('status', 'N/A')}\n" \
 
 
 
 
621
  f"總金額:${order.get('total_amount', 0)}\n" \
622
- f"建立時間:{order.get('created_at', 'N/A')}"
623
  else:
624
- response = f"找到 {len(data)} 筆訂單:\n"
625
  for i, order in enumerate(data[:5], 1):
626
- response += f"{i}. {order.get('order_id', 'N/A')} - {order.get('status', 'N/A')} - ${order.get('total_amount', 0)}\n"
627
- return response
 
 
 
 
 
 
 
 
 
 
 
628
 
629
  def _format_low_stock_response(self, data: List[Dict[str, Any]]) -> str:
630
  """格式化低庫存警告回應"""
 
612
  return response
613
 
614
  def _format_order_response(self, data: List[Dict[str, Any]]) -> str:
615
+ """格式化訂單查詢回應 - 適配銷售訂單資料結構"""
616
+ if not data:
617
+ return "沒有找到符合條件的訂單。"
618
+
619
  if len(data) == 1:
620
  order = data[0]
621
+ status_display = order.get('status_display', order.get('status', 'N/A'))
622
+ return f"📋 訂單詳細資訊:\n" \
623
  f"訂單編號:{order.get('order_id', 'N/A')}\n" \
624
+ f"銷售日期:{order.get('sales_date', 'N/A')}\n" \
625
+ f"狀態:{status_display}\n" \
626
+ f"客戶:{order.get('customer_name', 'N/A')}\n" \
627
+ f"銷售人員:{order.get('salesperson_name', 'N/A')}\n" \
628
+ f"付款條件:{order.get('payment_term', 'N/A')}\n" \
629
  f"總金額:${order.get('total_amount', 0)}\n" \
630
+ f"更新時間:{order.get('updated_at', 'N/A')[:10] if order.get('updated_at') else 'N/A'}"
631
  else:
632
+ response = f"📋 找到 {len(data)} 筆訂單:\n\n"
633
  for i, order in enumerate(data[:5], 1):
634
+ status_display = order.get('status_display', order.get('status', 'N/A'))
635
+ sales_date = order.get('sales_date', 'N/A')
636
+ if isinstance(sales_date, str) and len(sales_date) > 10:
637
+ sales_date = sales_date[:10] # 只顯示日期部分
638
+
639
+ response += f"{i}. {order.get('order_id', 'N/A')}\n"
640
+ response += f" 狀態:{status_display} | 日期:{sales_date}\n"
641
+ response += f" 客戶:{order.get('customer_name', 'N/A')} | 金額:${order.get('total_amount', 0)}\n\n"
642
+
643
+ if len(data) > 5:
644
+ response += f"... 還有 {len(data) - 5} 筆訂單"
645
+
646
+ return response.strip()
647
 
648
  def _format_low_stock_response(self, data: List[Dict[str, Any]]) -> str:
649
  """格式化低庫存警告回應"""
test_order_queries.py ADDED
@@ -0,0 +1,272 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 測試訂單查詢功能 - 基於實際銷售訂單資料
3
+ """
4
+
5
+ def test_order_number_extraction():
6
+ """測試訂單編號提取功能"""
7
+
8
+ def extract_order_number_simple(message: str) -> str:
9
+ """簡化版訂單編號提取"""
10
+ import re
11
+
12
+ # 尋找 SO- 格式的訂單編號
13
+ so_pattern = r'SO-\d{8}-\d{3}'
14
+ match = re.search(so_pattern, message.upper())
15
+ if match:
16
+ return match.group()
17
+
18
+ # 尋找日期格式的編號 (20250706-001)
19
+ date_pattern = r'\d{8}-\d{3}'
20
+ match = re.search(date_pattern, message)
21
+ if match:
22
+ return f"SO-{match.group()}"
23
+
24
+ # 尋找純數字編號
25
+ number_pattern = r'\d{6,}'
26
+ match = re.search(number_pattern, message)
27
+ if match:
28
+ return match.group()
29
+
30
+ return None
31
+
32
+ print("🔍 測試訂單編號提取")
33
+ print("=" * 50)
34
+
35
+ test_queries = [
36
+ "查詢訂單 SO-20250706-001",
37
+ "我的訂單 SO-20250708-001 狀態如何?",
38
+ "20250706-001 這個訂單",
39
+ "訂單編號 20250708-001",
40
+ "SO20250706001", # 沒有連字符
41
+ "查詢我的訂單", # 沒有編號
42
+ ]
43
+
44
+ for query in test_queries:
45
+ order_number = extract_order_number_simple(query)
46
+ print(f"'{query}' → 訂單編號: {order_number}")
47
+
48
+ def test_order_status_extraction():
49
+ """測試訂單狀態提取功能"""
50
+
51
+ def extract_order_status_simple(message: str) -> str:
52
+ """簡化版訂單狀態提取"""
53
+ message_lower = message.lower()
54
+
55
+ # 中文狀態關鍵字
56
+ status_keywords = {
57
+ '已交付': 'DELIVERED',
58
+ '已出貨': 'SHIPPED',
59
+ '出貨': 'SHIPPED',
60
+ '交付': 'DELIVERED',
61
+ '處理中': 'PROCESSING',
62
+ '已取消': 'CANCELLED',
63
+ '取消': 'CANCELLED',
64
+ '待處理': 'PENDING',
65
+ '已確認': 'CONFIRMED',
66
+ '已完成': 'COMPLETED'
67
+ }
68
+
69
+ for chinese, english in status_keywords.items():
70
+ if chinese in message_lower:
71
+ return english
72
+
73
+ # 英文狀態關鍵字
74
+ english_keywords = ['delivered', 'shipped', 'processing', 'cancelled', 'pending', 'confirmed', 'completed']
75
+ for keyword in english_keywords:
76
+ if keyword in message_lower:
77
+ return keyword.upper()
78
+
79
+ return None
80
+
81
+ print("\n📊 測試訂單狀態提取")
82
+ print("=" * 50)
83
+
84
+ test_queries = [
85
+ "查詢已交付的訂單",
86
+ "已出貨訂單有哪些?",
87
+ "處理中的訂單",
88
+ "DELIVERED 狀態的訂單",
89
+ "shipped orders",
90
+ "我的訂單狀態", # 沒有狀態
91
+ ]
92
+
93
+ for query in test_queries:
94
+ status = extract_order_status_simple(query)
95
+ print(f"'{query}' → 狀態: {status}")
96
+
97
+ def test_order_query_simulation():
98
+ """模擬訂單查詢功能"""
99
+
100
+ # 模擬的銷售訂單資料(來自您的 Supabase)
101
+ orders = [
102
+ {
103
+ "id": 1,
104
+ "so_number": "SO-20250706-001",
105
+ "sales_date": "2025-07-06",
106
+ "customer_id": 1,
107
+ "salesperson_id": 1,
108
+ "payment_term": "MONTHLY",
109
+ "status": "DELIVERED",
110
+ "subtotal": 0,
111
+ "tax_amount": 0,
112
+ "discount_amount": 0,
113
+ "total_amount": 0,
114
+ "created_at": "2025-07-06 14:53:23",
115
+ "updated_at": "2025-07-06 14:55:18"
116
+ },
117
+ {
118
+ "id": 2,
119
+ "so_number": "SO-20250708-001",
120
+ "sales_date": "2025-07-08",
121
+ "customer_id": 1,
122
+ "salesperson_id": 1,
123
+ "payment_term": "CASH",
124
+ "status": "SHIPPED",
125
+ "subtotal": 0,
126
+ "tax_amount": 0,
127
+ "discount_amount": 0,
128
+ "total_amount": 0,
129
+ "created_at": "2025-07-08 06:24:34",
130
+ "updated_at": "2025-07-10 07:08:12"
131
+ }
132
+ ]
133
+
134
+ def search_orders_simulation(order_number=None, status=None):
135
+ """模擬訂單搜尋"""
136
+ results = []
137
+
138
+ for order in orders:
139
+ match = True
140
+
141
+ # 訂單編號篩選
142
+ if order_number:
143
+ if order_number not in order["so_number"]:
144
+ match = False
145
+
146
+ # 狀態篩選
147
+ if status:
148
+ if status.upper() != order["status"]:
149
+ match = False
150
+
151
+ if match:
152
+ results.append(order)
153
+
154
+ return results
155
+
156
+ def get_status_display(status):
157
+ """狀態中文顯示"""
158
+ status_mapping = {
159
+ 'DELIVERED': '已交付',
160
+ 'SHIPPED': '已出貨',
161
+ 'PROCESSING': '處理中',
162
+ 'CANCELLED': '已取消',
163
+ 'PENDING': '待處理'
164
+ }
165
+ return status_mapping.get(status, status)
166
+
167
+ print("\n🛍️ 測試訂單查詢模擬")
168
+ print("=" * 50)
169
+
170
+ test_scenarios = [
171
+ {"query": "查詢訂單 SO-20250706-001", "order_number": "SO-20250706-001", "status": None},
172
+ {"query": "已交付的訂單", "order_number": None, "status": "DELIVERED"},
173
+ {"query": "已出貨訂單", "order_number": None, "status": "SHIPPED"},
174
+ {"query": "我的所有訂單", "order_number": None, "status": None},
175
+ {"query": "SO-20250708-001 狀態", "order_number": "SO-20250708-001", "status": None},
176
+ ]
177
+
178
+ for scenario in test_scenarios:
179
+ print(f"\n查詢: '{scenario['query']}'")
180
+ results = search_orders_simulation(
181
+ order_number=scenario["order_number"],
182
+ status=scenario["status"]
183
+ )
184
+
185
+ print(f"找到 {len(results)} 個訂單:")
186
+ for order in results:
187
+ status_display = get_status_display(order["status"])
188
+ print(f" ✅ {order['so_number']} - {status_display} - {order['sales_date']} - 付款: {order['payment_term']}")
189
+
190
+ def test_natural_language_processing():
191
+ """測試自然語言處理"""
192
+
193
+ print("\n🤖 測試自然語言訂單查詢")
194
+ print("=" * 50)
195
+
196
+ def extract_order_number_simple(message: str) -> str:
197
+ import re
198
+ so_pattern = r'SO-\d{8}-\d{3}'
199
+ match = re.search(so_pattern, message.upper())
200
+ if match:
201
+ return match.group()
202
+
203
+ date_pattern = r'\d{8}-\d{3}'
204
+ match = re.search(date_pattern, message)
205
+ if match:
206
+ return f"SO-{match.group()}"
207
+
208
+ return None
209
+
210
+ def extract_order_status_simple(message: str) -> str:
211
+ message_lower = message.lower()
212
+ status_keywords = {
213
+ '已交付': 'DELIVERED',
214
+ '已出貨': 'SHIPPED',
215
+ '出貨': 'SHIPPED',
216
+ '交付': 'DELIVERED',
217
+ '處理中': 'PROCESSING'
218
+ }
219
+
220
+ for chinese, english in status_keywords.items():
221
+ if chinese in message_lower:
222
+ return english
223
+
224
+ return None
225
+
226
+ natural_queries = [
227
+ "請問我的訂單 SO-20250706-001 現在是什麼狀態?",
228
+ "查詢已交付的所有訂單",
229
+ "20250708-001 這個訂單出貨了嗎?",
230
+ "我有哪些已出貨的訂單?",
231
+ "訂單狀態查詢"
232
+ ]
233
+
234
+ for query in natural_queries:
235
+ print(f"\n自然語言查詢: '{query}'")
236
+ order_number = extract_order_number_simple(query)
237
+ status = extract_order_status_simple(query)
238
+
239
+ print(f" 提取的訂單編號: {order_number}")
240
+ print(f" 提取的狀態: {status}")
241
+
242
+ # 判斷查詢類型
243
+ if order_number and status:
244
+ query_type = "特定訂單狀態查詢"
245
+ elif order_number:
246
+ query_type = "特定訂單查詢"
247
+ elif status:
248
+ query_type = "狀態篩選查詢"
249
+ else:
250
+ query_type = "一般訂單查詢"
251
+
252
+ print(f" 查詢類型: {query_type}")
253
+
254
+ def main():
255
+ """主函數"""
256
+ print("🚀 開始訂單查詢功能測試\n")
257
+
258
+ test_order_number_extraction()
259
+ test_order_status_extraction()
260
+ test_order_query_simulation()
261
+ test_natural_language_processing()
262
+
263
+ print("\n" + "=" * 50)
264
+ print("✅ 測試完成!")
265
+ print("\n💡 分析結果:")
266
+ print("1. 訂單編號提取邏輯能正確識別 SO- 格式")
267
+ print("2. 狀態提取支援中文和英文")
268
+ print("3. 查詢模擬能正確篩選訂單")
269
+ print("4. 自然語言處理能提取關鍵資訊")
270
+
271
+ if __name__ == "__main__":
272
+ main()
test_search_orders.py ADDED
@@ -0,0 +1,255 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 測試 /search 訂單查詢功能 - 基於實際銷售訂單資料
3
+ """
4
+
5
+ import requests
6
+ import json
7
+
8
+ def test_search_api_orders():
9
+ """測試 /search API 的訂單查詢功能"""
10
+ base_url = "http://localhost:7860"
11
+
12
+ print("🔍 測試 /search API 訂單查詢功能")
13
+ print("=" * 60)
14
+
15
+ # 測試案例基於您的實際銷售訂單資料
16
+ test_cases = [
17
+ {
18
+ "name": "查詢特定訂單編號",
19
+ "message": "查詢訂單 SO-20250706-001",
20
+ "expected": "應該找到 2025-07-06 的已交付訂單"
21
+ },
22
+ {
23
+ "name": "查詢另一個訂單編號",
24
+ "message": "SO-20250708-001 狀態如何?",
25
+ "expected": "應該找到 2025-07-08 的已出貨訂單"
26
+ },
27
+ {
28
+ "name": "查詢已交付訂單",
29
+ "message": "查詢已交付的訂單",
30
+ "expected": "應該找到狀態為 DELIVERED 的訂單"
31
+ },
32
+ {
33
+ "name": "查詢已出貨訂單",
34
+ "message": "已出貨訂單有哪些?",
35
+ "expected": "應該找到狀態為 SHIPPED 的訂單"
36
+ },
37
+ {
38
+ "name": "一般訂單查詢",
39
+ "message": "我的訂單",
40
+ "expected": "應該顯示所有訂單"
41
+ },
42
+ {
43
+ "name": "訂單狀態查詢",
44
+ "message": "訂單狀態查詢",
45
+ "expected": "應該顯示訂單列表"
46
+ },
47
+ {
48
+ "name": "使用日期格式編號",
49
+ "message": "20250706-001 這個訂單",
50
+ "expected": "應該找到對應的 SO- 訂單"
51
+ }
52
+ ]
53
+
54
+ for i, test_case in enumerate(test_cases, 1):
55
+ print(f"\n{i}. {test_case['name']}")
56
+ print(f" 查詢: '{test_case['message']}'")
57
+ print(f" 預期: {test_case['expected']}")
58
+
59
+ try:
60
+ payload = {
61
+ "message": test_case["message"],
62
+ "user_id": "test_user_search"
63
+ }
64
+
65
+ response = requests.post(
66
+ f"{base_url}/search",
67
+ json=payload,
68
+ timeout=30,
69
+ headers={"Content-Type": "application/json"}
70
+ )
71
+
72
+ print(f" 狀態碼: {response.status_code}")
73
+
74
+ if response.status_code == 200:
75
+ result = response.json()
76
+ print(f" 成功: {result.get('success', False)}")
77
+
78
+ # 顯示回應內容
79
+ response_text = result.get('text', 'No response')
80
+ if len(response_text) > 200:
81
+ print(f" 回應: {response_text[:200]}...")
82
+ else:
83
+ print(f" 回應: {response_text}")
84
+
85
+ # 檢查是否包含訂單相關資訊
86
+ if any(keyword in response_text for keyword in ['SO-', '訂單', '狀態', '已交付', '已出貨']):
87
+ print(f" ✅ 包含訂單相關資訊")
88
+ else:
89
+ print(f" ❌ 未包含預期的訂單資訊")
90
+
91
+ else:
92
+ print(f" ❌ API 錯誤: {response.text}")
93
+
94
+ except Exception as e:
95
+ print(f" ❌ 請求失敗: {str(e)}")
96
+
97
+ print("-" * 60)
98
+
99
+ def test_route_api_orders():
100
+ """測試 /route API 對訂單查詢的路由"""
101
+ base_url = "http://localhost:7860"
102
+
103
+ print(f"\n🔄 測試 /route API 訂單查詢路由")
104
+ print("=" * 60)
105
+
106
+ # 測試訂單查詢是否被正確路由
107
+ order_queries = [
108
+ "查詢訂單 SO-20250706-001",
109
+ "我的訂單狀態",
110
+ "已交付的訂單",
111
+ "訂單編號 SO-20250708-001"
112
+ ]
113
+
114
+ for i, query in enumerate(order_queries, 1):
115
+ print(f"\n{i}. 路由測試: '{query}'")
116
+
117
+ try:
118
+ payload = {
119
+ "message": query,
120
+ "user_id": "test_user_route"
121
+ }
122
+
123
+ response = requests.post(
124
+ f"{base_url}/route",
125
+ json=payload,
126
+ timeout=30,
127
+ headers={"Content-Type": "application/json"}
128
+ )
129
+
130
+ print(f" 狀態碼: {response.status_code}")
131
+
132
+ if response.status_code == 200:
133
+ result = response.json()
134
+ mode = result.get('mode', 'unknown')
135
+ success = result.get('success', False)
136
+
137
+ print(f" 路由模式: {mode}")
138
+ print(f" 成功: {success}")
139
+
140
+ # 檢查是否路由到正確的模式
141
+ if mode == "search":
142
+ print(f" ✅ 正確路由到搜尋模式")
143
+ elif mode == "product_query":
144
+ print(f" ⚠️ 路由到商品查詢模式(可能需要調整)")
145
+ else:
146
+ print(f" ❌ 路由到非預期模式: {mode}")
147
+
148
+ # 顯示部分回應
149
+ response_text = result.get('text', 'No response')
150
+ if len(response_text) > 100:
151
+ print(f" 回應: {response_text[:100]}...")
152
+ else:
153
+ print(f" 回應: {response_text}")
154
+
155
+ else:
156
+ print(f" ❌ API 錯誤: {response.text}")
157
+
158
+ except Exception as e:
159
+ print(f" ❌ 請求失敗: {str(e)}")
160
+
161
+ def test_order_query_patterns():
162
+ """測試訂單查詢模式識別"""
163
+
164
+ print(f"\n🤖 測試訂單查詢模式識別")
165
+ print("=" * 60)
166
+
167
+ def analyze_order_query_intent(message: str):
168
+ """簡化版訂單查詢意圖分析"""
169
+ message_lower = message.lower()
170
+
171
+ # 訂單查詢關鍵字
172
+ order_keywords = ['訂單', '訂購', '購買', 'so-', 'order']
173
+
174
+ # 狀態關鍵字
175
+ status_keywords = ['狀態', '已交付', '已出貨', '處理中', '取消', 'delivered', 'shipped']
176
+
177
+ # 查詢動作關鍵字
178
+ action_keywords = ['查詢', '搜尋', '找', '看', '檢查']
179
+
180
+ has_order_keyword = any(keyword in message_lower for keyword in order_keywords)
181
+ has_status_keyword = any(keyword in message_lower for keyword in status_keywords)
182
+ has_action_keyword = any(keyword in message_lower for keyword in action_keywords)
183
+
184
+ # 計算信心度
185
+ confidence = 0.0
186
+ if has_order_keyword:
187
+ confidence += 0.5
188
+ if has_status_keyword:
189
+ confidence += 0.3
190
+ if has_action_keyword:
191
+ confidence += 0.2
192
+
193
+ return {
194
+ "is_order_query": has_order_keyword,
195
+ "has_status": has_status_keyword,
196
+ "has_action": has_action_keyword,
197
+ "confidence": min(confidence, 1.0),
198
+ "should_route_to_search": has_order_keyword and confidence > 0.5
199
+ }
200
+
201
+ test_messages = [
202
+ "查詢訂單 SO-20250706-001",
203
+ "我的訂單狀態如何?",
204
+ "已交付的訂單有哪些?",
205
+ "SO-20250708-001 出貨了嗎?",
206
+ "訂單編號查詢",
207
+ "購買記錄",
208
+ "今天天氣如何?", # 非訂單查詢
209
+ "是否有推薦貓砂?" # 商品查詢
210
+ ]
211
+
212
+ for message in test_messages:
213
+ print(f"\n訊息: '{message}'")
214
+ analysis = analyze_order_query_intent(message)
215
+
216
+ print(f" 訂單查詢: {analysis['is_order_query']}")
217
+ print(f" 包含狀態: {analysis['has_status']}")
218
+ print(f" 包含動作: {analysis['has_action']}")
219
+ print(f" 信心度: {analysis['confidence']:.2f}")
220
+ print(f" 建議路由: {'搜尋模式' if analysis['should_route_to_search'] else '其他模式'}")
221
+
222
+ def main():
223
+ """主函數"""
224
+ print("🚀 開始 /search 訂單查詢功能測試\n")
225
+
226
+ # 檢查 API 服務是否運行
227
+ try:
228
+ response = requests.get("http://localhost:7860/health", timeout=5)
229
+ if response.status_code == 200:
230
+ print("✅ API 服務運行正常\n")
231
+ else:
232
+ print("❌ API 服務狀態異常\n")
233
+ return
234
+ except Exception as e:
235
+ print(f"❌ 無法連接到 API 服務: {str(e)}")
236
+ print("請確保 LineBot 服務正在運行在 localhost:7860\n")
237
+ # 仍然執行模式識別測試
238
+ test_order_query_patterns()
239
+ return
240
+
241
+ # 執行 API 測試
242
+ test_search_api_orders()
243
+ test_route_api_orders()
244
+ test_order_query_patterns()
245
+
246
+ print("\n" + "=" * 60)
247
+ print("✅ 測試完成!")
248
+ print("\n💡 測試總結:")
249
+ print("1. /search API 應該能正確處理訂單查詢")
250
+ print("2. 支援 SO- 格式訂單編號查詢")
251
+ print("3. 支援狀態篩選(已交付、已出貨等)")
252
+ print("4. 路由系統應該將訂單查詢導向搜尋模式")
253
+
254
+ if __name__ == "__main__":
255
+ main()