Skip to content

Commit 8b25c35

Browse files
xjanovaclaude
andcommitted
fix: แก้ 4 บัครอบ 2 ระบบตลาดสด - Deep Code Review
1. Missing order_select:text handler - ผู้ใช้พิมพ์ข้อความขณะเลือกสินค้า จากผลค้นหา ตกไป default handler แทนที่จะค้นหาใหม่หรือเลือกสินค้า 2. Rider postback ปุ่มไม่ทำงาน - Quick Reply ปุ่มเลือกประเภทไรเดอร์ (rider_type_delivery/service/both) และหมวดหมู่ (rider_cat_fresh/food/ doc/all) ส่ง postback ไป handleMenuPostback แต่ไม่มี case รองรับ ทำให้กดปุ่มแล้วกลับหน้าเมนูหลักแทนที่จะดำเนินการต่อ 3. Flood protection race condition - cache()->increment() บน key ที่ไม่มี อาจคืน false ใน driver บางตัว แล้ว put() เขียนทับกลับเป็น 1 แก้ไขโดยใช้ cache()->add() สร้าง key อย่าง atomic ก่อน increment 4. listing_complete/rider_complete state ไม่มี handler - ผู้ใช้ส่งข้อความ ระหว่างอยู่ state เหล่านี้ตก default handler เพิ่ม auto-reset กลับ idle Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d60f1a2 commit 8b25c35

File tree

2 files changed

+112
-4
lines changed

2 files changed

+112
-4
lines changed

app/Http/Controllers/FreshMarketWebhookController.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,12 @@ protected function handleMessageEvent(array $event): void
124124
return;
125125
}
126126

127-
// Flood protection (ใช้ increment เพื่อป้องกัน race condition)
127+
// Flood protection
128+
// ใช้ cache add + increment เพื่อป้องกัน race condition
129+
// add() จะสร้าง key เฉพาะเมื่อยังไม่มี (atomic) แล้ว increment ตาม
128130
$floodKey = "fm_flood:{$userId}";
131+
cache()->add($floodKey, 0, now()->addSeconds(10));
129132
$floodCount = (int) cache()->increment($floodKey);
130-
if ($floodCount === 1) {
131-
cache()->put($floodKey, 1, now()->addSeconds(10));
132-
}
133133

134134
if ($floodCount >= 5) {
135135
Log::warning('FreshMarketWebhook: Flood detected', ['user_id' => $userId]);

app/Services/FreshMarketChannelManager.php

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,18 @@ protected function handleMenuPostback(string $lineUserId, array $params, FreshMa
514514
'chat_ai' => $this->enterAIChatMode($conversation),
515515
'help' => $this->handleCommand('help', $lineUserId, $conversation, []),
516516
'back_to_menu' => $this->buildGreetingWithButtons($conversation),
517+
518+
// ===== Rider Registration: ประเภทไรเดอร์ =====
519+
'rider_type_delivery' => $this->handleRiderRegister_Text($conversation, '1', []),
520+
'rider_type_service' => $this->handleRiderRegister_Text($conversation, '2', []),
521+
'rider_type_both' => $this->handleRiderRegister_Text($conversation, '3', []),
522+
523+
// ===== Rider Registration: หมวดหมู่งาน =====
524+
'rider_cat_fresh' => $this->handleRiderCategory_Text($conversation, '1', []),
525+
'rider_cat_food' => $this->handleRiderCategory_Text($conversation, '2', []),
526+
'rider_cat_doc' => $this->handleRiderCategory_Text($conversation, '3', []),
527+
'rider_cat_all' => $this->handleRiderCategory_Text($conversation, '4', []),
528+
517529
default => $this->buildGreetingWithButtons($conversation),
518530
};
519531
}
@@ -583,6 +595,9 @@ protected function dispatchByState(FreshMarketConversation $conversation, string
583595
'search_browsing:location' => $this->handleSearchLocation_Location($conversation, $inputData['lat'], $inputData['lng'], $extra),
584596

585597
// === ORDER FLOW ===
598+
'order_select:text' => $this->handleOrderSelect_Text($conversation, $inputData, $extra),
599+
'order_select:image' => $this->handleWrongInputType($conversation, 'image'),
600+
'order_select:location' => $this->handleOrderQuantity_Location($conversation, $inputData['lat'] ?? 0, $inputData['lng'] ?? 0, $extra),
586601
'order_quantity:text' => $this->handleOrderQuantity_Text($conversation, $inputData, $extra),
587602
'order_quantity:location' => $this->handleOrderQuantity_Location($conversation, $inputData['lat'], $inputData['lng'], $extra),
588603
'order_quantity:image' => $this->handleWrongInputType($conversation, 'image'),
@@ -597,6 +612,11 @@ protected function dispatchByState(FreshMarketConversation $conversation, string
597612
'search_location:image' => $this->handleWrongInputType($conversation, 'image'),
598613
'search_browsing:image' => $this->handleWrongInputType($conversation, 'image'),
599614

615+
// === LISTING COMPLETE (ลงขายเสร็จ → กลับ idle อัตโนมัติ) ===
616+
'listing_complete:text' => $this->handleCompletedState_Text($conversation, $inputData, $extra),
617+
'listing_complete:image' => $this->handleCompletedState_Text($conversation, '', $extra),
618+
'listing_complete:location' => $this->handleCompletedState_Text($conversation, '', $extra),
619+
600620
// === RIDER REGISTRATION FLOW ===
601621
'rider_register:text' => $this->handleRiderRegister_Text($conversation, $inputData, $extra),
602622
'rider_register:image' => $this->handleWrongInputType($conversation, 'image'),
@@ -605,6 +625,11 @@ protected function dispatchByState(FreshMarketConversation $conversation, string
605625
'rider_category:image' => $this->handleWrongInputType($conversation, 'image'),
606626
'rider_category:location' => $this->handleWrongInputType($conversation, 'location'),
607627

628+
// === RIDER COMPLETE (สมัครเสร็จ → กลับ idle อัตโนมัติ) ===
629+
'rider_complete:text' => $this->handleCompletedState_Text($conversation, $inputData, $extra),
630+
'rider_complete:image' => $this->handleCompletedState_Text($conversation, '', $extra),
631+
'rider_complete:location' => $this->handleCompletedState_Text($conversation, '', $extra),
632+
608633
// === DEFAULT ===
609634
default => $this->handleWrongInputType($conversation, $inputType),
610635
};
@@ -1270,6 +1295,63 @@ protected function handleSearchBrowsing_Text(FreshMarketConversation $conversati
12701295
// ║ BUYER ORDER FLOW HANDLERS ║
12711296
// ╚══════════════════════════════════════════╝
12721297

1298+
/**
1299+
* order_select + text → ผู้ใช้พิมพ์ข้อความขณะเลือกสินค้า
1300+
*
1301+
* ถ้าเป็นตัวเลข → พยายาม match กับ listing จากผลค้นหาล่าสุด
1302+
* ถ้าเป็นข้อความ → ค้นหาใหม่ด้วย query
1303+
*/
1304+
protected function handleOrderSelect_Text(FreshMarketConversation $conversation, string $message, array $extra): array
1305+
{
1306+
$searchCtx = $conversation->getFlowContext('search');
1307+
$listingIds = $searchCtx['result_listing_ids'] ?? [];
1308+
1309+
// ถ้าพิมพ์ตัวเลข → ลอง match กับ listing
1310+
$msg = trim($message);
1311+
if (is_numeric($msg)) {
1312+
$index = (int) $msg - 1;
1313+
if (isset($listingIds[$index])) {
1314+
$listing = FreshMarketListing::find($listingIds[$index]);
1315+
if ($listing && $listing->isAvailableForPurchase()) {
1316+
// เริ่ม order flow เหมือน postback
1317+
$conversation->transitionTo(FreshMarketConversation::STATE_ORDER_QUANTITY, [
1318+
'order' => [
1319+
'listing_id' => $listing->id,
1320+
'listing_title' => $listing->title,
1321+
'listing_price' => $listing->price,
1322+
'listing_unit' => $listing->unit,
1323+
'seller_shop_name' => $listing->seller?->shop_name ?? 'ร้านค้า',
1324+
'started_at' => now()->toIso8601String(),
1325+
],
1326+
]);
1327+
1328+
$progress = $conversation->getProgressText();
1329+
1330+
return [
1331+
'text' => "{$progress}\n\n🛒 {$listing->title}\n💰 ฿" . number_format($listing->price, 0) . " / {$listing->unit}\n\nจะสั่งกี่ {$listing->unit} คะ?\nนัดรับเองหรือให้ส่ง?",
1332+
];
1333+
}
1334+
}
1335+
}
1336+
1337+
// ถ้ามีพิกัดเดิม → ค้นหาด้วย query ใหม่
1338+
if ($conversation->last_search_latitude) {
1339+
return $this->searchNearbyAndRespond(
1340+
$conversation,
1341+
(float) $conversation->last_search_latitude,
1342+
(float) $conversation->last_search_longitude,
1343+
['query' => $message]
1344+
);
1345+
}
1346+
1347+
// ไม่มีพิกัด → แนะนำส่งตำแหน่ง
1348+
$conversation->transitionTo(FreshMarketConversation::STATE_SEARCH_LOCATION);
1349+
1350+
return [
1351+
'text' => "📍 ส่งตำแหน่งมาเพื่อค้นหา \"{$message}\" ใกล้บ้านค่ะ",
1352+
];
1353+
}
1354+
12731355
/**
12741356
* order_quantity + text → AI parse จำนวนและวิธีรับ
12751357
*/
@@ -1908,6 +1990,32 @@ protected function handleRiderRejectJobPostback(string $lineUserId, array $param
19081990
];
19091991
}
19101992

1993+
// ╔══════════════════════════════════════════╗
1994+
// ║ COMPLETED STATE HANDLER ║
1995+
// ╚══════════════════════════════════════════╝
1996+
1997+
/**
1998+
* listing_complete / rider_complete + any input
1999+
*
2000+
* สถานะ "เสร็จ" เหล่านี้ควรถูก reset ไป idle แล้ว
2001+
* แต่ถ้าเกิด race condition หรือผู้ใช้ส่งข้อความเร็วมาก
2002+
* ก็ reset ให้อัตโนมัติแล้วเริ่มต้นใหม่
2003+
*/
2004+
protected function handleCompletedState_Text(FreshMarketConversation $conversation, mixed $input, array $extra): array
2005+
{
2006+
$conversation->resetToIdle();
2007+
2008+
// ถ้ามีข้อความ → ลอง dispatch ใหม่ใน idle
2009+
if (is_string($input) && ! empty(trim($input))) {
2010+
$command = $this->detectCommand($input);
2011+
if ($command) {
2012+
return $this->handleCommand($command, $conversation->line_user_id, $conversation, $extra);
2013+
}
2014+
}
2015+
2016+
return $this->buildGreetingWithButtons($conversation);
2017+
}
2018+
19112019
// ╔══════════════════════════════════════════╗
19122020
// ║ HELPER: LISTING CREATION ║
19132021
// ╚══════════════════════════════════════════╝

0 commit comments

Comments
 (0)