x
This commit is contained in:
1 parent
f49776f9cb
commit
fdbbaea021
2 files changed
+468
No files matched your search
@@ -0,0 +1,84 @@
|
||||
# Shopee Marketplace Product Matching & Mapping Ledger
|
||||
|
||||
* **Generated At:** 2026-10-18T07:17:25.420Z
|
||||
* **Target Server:** `https://api.homaroma.shop/api` (Production)
|
||||
* **Total Shopee Products:** 253
|
||||
* **Successfully Mapped:** 217 (85.8%)
|
||||
* **Held / Requires Decision:** 36 (14.2%)
|
||||
|
||||
## 1. Held / Unmapped Items (ต้องตัดสินใจเพิ่มเติม)
|
||||
|
||||
| ID | ชื่อสินค้า Shopee | Variation | ราคาเฉลี่ย | สาเหตุที่ Hold |
|
||||
|:---|:---|:---|:---|:---|
|
||||
| 619 | น้ำมันหอมระเหย สำหรับ เครื่องพ่นไอน้ำ เครื่องพ่นอโรม่า ขนาด 30 ml | เมลอน ญี่ปุ่น | ฿77.13 | ไม่พบชื่อกลิ่นที่ตรงกับ Master Catalog (เช่น เมลอน หรือกลิ่นที่ยังไม่มีในระบบ) |
|
||||
| 649 | น้ำมันหอมระเหย สำหรับ เครื่องพ่นไอน้ำ เครื่องพ่นอโรม่า ขนาด 30 ml | เมลอน ญี่ปุ่น,30 ml | ฿89.00 | ไม่พบชื่อกลิ่นที่ตรงกับ Master Catalog (เช่น เมลอน หรือกลิ่นที่ยังไม่มีในระบบ) |
|
||||
| 651 | น้ำมันหอมระเหย สำหรับ เครื่องพ่นไอน้ำ เครื่องพ่นอโรม่า ขนาด 30 ml | เมลอน ญี่ปุ่น,120 ml | ฿190.00 | ไม่พบชื่อกลิ่นที่ตรงกับ Master Catalog (เช่น เมลอน หรือกลิ่นที่ยังไม่มีในระบบ) |
|
||||
| 804 | น้ำมันหอมระเหย กลิ่นดอกโมก สำหรับเครื่องพ่นไอน้ำ ช่วยปรับอากาศ | - | ฿180.00 | ไม่สามารถระบุขนาดบรรจุ (Size) ได้ชัดเจนจากชื่อสินค้าหรือ Variation |
|
||||
| 664 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ไทยสปา | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 698 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ซากุระ | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 767 | HOM AROMA กลิ่นลาเวนเดอร์ lavender น้ำมันหอมระหย ออยโอโซน สำหรับเครื่องพ่น เครื่องพ่นไอน้ำ | - | ฿88.00 | ไม่สามารถระบุขนาดบรรจุ (Size) ได้ชัดเจนจากชื่อสินค้าหรือ Variation |
|
||||
| 692 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ยูคาลิปตัส | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 687 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | แคนตาลูป | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 730 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ลาเวนเดอร์ | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 705 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | แซนด้าวูด | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 704 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | เฟรชลาเวนเดอร์ | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 691 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | โอเชี่ยน | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 693 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | โรสเมรี่ | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 686 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | เมล่อนญี่ปุ่น | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 761 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | แอปเปิ้ลแดง | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 741 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | เปเปอร์มิ้นต์ | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 760 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | มะกรูด | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 756 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | มะลิ | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 755 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ตะไตร้ | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 759 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | เซเว่นซี | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 754 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | กุหลาบชมพู | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 753 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ดอกปีบ | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 752 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | มะนาว | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 751 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ส้ม | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 758 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ดอกแก้ว | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 757 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ดอกอัญชัน | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 750 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ลิลลี่ | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 749 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | วนิลลา | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 747 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | กุหลาบแดง | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 748 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ผลไม้รวม | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 746 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | แอปเปิ้ลเขียว | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 745 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | หมากฝรั่ง | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 744 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ดอกโมก | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 743 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ลีลาวดี | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
| 742 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ดอกบัว | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
|
||||
|
||||
## 2. Sample of Successfully Mapped Items (ตัวอย่างที่เชื่อมโยงสำเร็จ)
|
||||
|
||||
| ID | Shopee SKU | กลิ่น | ขนาด | Master SKU | Master Variant | มี BOM หรือยัง |
|
||||
|:---|:---|:---|:---|:---|:---|:---|
|
||||
| 563 | M1015 | เปเปอร์มิ้น | 120ml | `8-1-120ML` | เปเปอร์มิ้น Oil Ozone (120ml) [8-1-120ML] | ✓ มี BOM แล้ว |
|
||||
| 575 | M1002 | ตะไคร้ | 120ml | `1-1-120ML` | ตะไคร้ Oil Ozone (120ml) [1-1-120ML] | ✓ มี BOM แล้ว |
|
||||
| 601 | 30-ml- | ลาเวนเดอร์ | 30ml | `2-1-30ML` | ลาเวนเดอร์ Oil Ozone (30ml) [2-1-30ML] | ✓ มี BOM แล้ว |
|
||||
| 589 | 30-ml- | ยูคาลิปตัส | 30ml | `6-1-30ML` | ยูคาลิปตัส Oil Ozone (30ml) [6-1-30ML] | ✓ มี BOM แล้ว |
|
||||
| 572 | M1015 | เปเปอร์มิ้น | 30ml | `8-1-30ML` | เปเปอร์มิ้น Oil Ozone (30ml) [8-1-30ML] | ✓ มี BOM แล้ว |
|
||||
| 591 | 30-ml- | โอเชี่ยน | 30ml | `9-1-30ML` | โอเชี่ยน Oil Ozone (30ml) [9-1-30ML] | ✓ มี BOM แล้ว |
|
||||
| 608 | 30-ml- | มะลิ | 30ml | `5-1-30ML` | มะลิ Oil Ozone (30ml) [5-1-30ML] | ✓ มี BOM แล้ว |
|
||||
| 627 | M1001 | ลาเวนเดอร์ | 120ml | `2-1-120ML` | ลาเวนเดอร์ Oil Ozone (120ml) [2-1-120ML] | ✓ มี BOM แล้ว |
|
||||
| 590 | 30-ml- | ซากุระ | 30ml | `4-1-30ML` | ซากุระ Oil Ozone (30ml) [4-1-30ML] | ✓ มี BOM แล้ว |
|
||||
| 559 | M1005 | โอโซน | 120ml | `29-1-120ML` | โอโซน Oil Ozone (120ml) [29-1-120ML] | ✓ มี BOM แล้ว |
|
||||
| 611 | 30-ml- | ตะไคร้ | 30ml | `1-1-30ML` | ตะไคร้ Oil Ozone (30ml) [1-1-30ML] | ✓ มี BOM แล้ว |
|
||||
| 557 | M1015 | เปเปอร์มิ้น | 60ml | `8-1-60ML` | เปเปอร์มิ้น Oil Ozone (60ml) [8-1-60ML] | ✓ มี BOM แล้ว |
|
||||
| 604 | 30-ml- | เปเปอร์มิ้น | 30ml | `8-1-30ML` | เปเปอร์มิ้น Oil Ozone (30ml) [8-1-30ML] | ✓ มี BOM แล้ว |
|
||||
| 607 | 30-ml- | ลาเวนเดอร์ | 30ml | `2-1-30ML` | ลาเวนเดอร์ Oil Ozone (30ml) [2-1-30ML] | ✓ มี BOM แล้ว |
|
||||
| 606 | 30-ml- | กุหลาบแดง | 30ml | `22-1-30ML` | กุหลาบแดง Oil Ozone (30ml) [22-1-30ML] | ✓ มี BOM แล้ว |
|
||||
| 602 | 30-ml- | ดอกโมก | 30ml | `12-1-30ML` | ดอกโมก Oil Ozone (30ml) [12-1-30ML] | ✓ มี BOM แล้ว |
|
||||
| 636 | M1015 | เปเปอร์มิ้น | 85ml | `8-1-85ML` | เปเปอร์มิ้น Oil Ozone (85ml) [8-1-85ML] | ✓ มี BOM แล้ว |
|
||||
| 612 | 30-ml- | ผลไม้รวม | 30ml | `24-1-30ML` | ผลไม้รวม Oil Ozone (30ml) [24-1-30ML] | ✓ มี BOM แล้ว |
|
||||
| 620 | 30-ml- | แคนตาลูป | 30ml | `16-1-30ML` | แคนตาลูป Oil Ozone (30ml) [16-1-30ML] | ✓ มี BOM แล้ว |
|
||||
| 603 | M1004 | มะลิ | 120ml | `5-1-120ML` | มะลิ Oil Ozone (120ml) [5-1-120ML] | ✓ มี BOM แล้ว |
|
||||
| 249 | M1001 | ลาเวนเดอร์ | 120ml | `2-1-120ML` | ลาเวนเดอร์ Oil Ozone (120ml) [2-1-120ML] | ✓ มี BOM แล้ว |
|
||||
| 615 | 30-ml- | วานิลลา | 30ml | `3-1-30ML` | วานิลลา Oil Ozone (30ml) [3-1-30ML] | ✓ มี BOM แล้ว |
|
||||
| 558 | M1003 | โอโซน | 120ml | `29-1-120ML` | โอโซน Oil Ozone (120ml) [29-1-120ML] | ✓ มี BOM แล้ว |
|
||||
| 616 | 30-ml- | ดอกแก้ว | 30ml | `11-1-30ML` | ดอกแก้ว Oil Ozone (30ml) [11-1-30ML] | ✓ มี BOM แล้ว |
|
||||
| 678 | 1652052234 | โอโซน | 120ml | `29-1-120ML` | โอโซน Oil Ozone (120ml) [29-1-120ML] | ✓ มี BOM แล้ว |
|
||||
| 625 | 30-ml- | ดอกราตรี | 30ml | `39-1-30ML` | ดอกราตรี Oil Ozone (30ml) [39-1-30ML] | ✓ มี BOM แล้ว |
|
||||
| 626 | 30-ml- | กลิ่นองุ่น | 30ml | `45-1-30ML` | กลิ่นองุ่น Oil Ozone (30ml) [45-1-30ML] | ✓ มี BOM แล้ว |
|
||||
| 734 | 1652058083 | โอโซน | 60ml | `29-1-60ML` | โอโซน Oil Ozone (60ml) [29-1-60ML] | ✓ มี BOM แล้ว |
|
||||
| 622 | 30-ml- | แอปเปิ้ลเขียว | 30ml | `18-1-30ML` | แอปเปิ้ลเขียว Oil Ozone (30ml) [18-1-30ML] | ✓ มี BOM แล้ว |
|
||||
| 610 | 30-ml- | มะนาว | 30ml | `15-1-30ML` | มะนาว Oil Ozone (30ml) [15-1-30ML] | ✓ มี BOM แล้ว |
|
||||
| ... | ... | ... | ... | ... | *(แสดง 30 จากทั้งหมด 217 รายการ)* | ... |
|
||||
@@ -0,0 +1,384 @@
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
const PROD_API = "https://api.homaroma.shop/api";
|
||||
|
||||
interface MasterVariant {
|
||||
id: number;
|
||||
sku: string;
|
||||
barcode: string;
|
||||
price: number;
|
||||
stock: number;
|
||||
optionsStr: string;
|
||||
productName: string;
|
||||
categoryName: string;
|
||||
brandName: string;
|
||||
bomCost: number;
|
||||
displayName: string;
|
||||
}
|
||||
|
||||
interface MarketplaceProduct {
|
||||
id: number;
|
||||
channel: string;
|
||||
shop: string;
|
||||
marketplaceSku: string;
|
||||
productName: string;
|
||||
variation: string;
|
||||
avgPrice: number;
|
||||
totalSoldQty: number;
|
||||
totalRevenue: number;
|
||||
firstSoldAt: string;
|
||||
lastSoldAt: string;
|
||||
masterVariantId: number | null;
|
||||
}
|
||||
|
||||
// Scent aliases dictionary mapping to Master Catalog Product Names (without "Oil Ozone")
|
||||
const SCENT_ALIASES: Record<string, string> = {
|
||||
// Peppermint
|
||||
"เปปเปอร์มินต์": "เปเปอร์มิ้น",
|
||||
"เปปเปอร์มิ้นท์": "เปเปอร์มิ้น",
|
||||
"เปปเปอร์มิ้น": "เปเปอร์มิ้น",
|
||||
"เปเปอร์มินต์": "เปเปอร์มิ้น",
|
||||
"เปเปอร์มิ้น": "เปเปอร์มิ้น",
|
||||
"เปเปอร์มิ้น": "เปเปอร์มิ้น",
|
||||
"pepermint": "เปเปอร์มิ้น",
|
||||
"peppermint": "เปเปอร์มิ้น",
|
||||
|
||||
// Lemongrass
|
||||
"ตะไคร้บ้าน": "ตะไคร้",
|
||||
"ตะไคร้": "ตะไคร้",
|
||||
"ตะไตร้": "ตะไคร้",
|
||||
"lemongrass": "ตะไคร้",
|
||||
|
||||
// Lavender
|
||||
"ลาเวนเดอร์": "ลาเวนเดอร์",
|
||||
"ลาเวนเดอร์": "ลาเวนเดอร์",
|
||||
"lavender": "ลาเวนเดอร์",
|
||||
"เฟรซ ลาเวนเดอร์": "เฟรชลาเวนเดอร์",
|
||||
"เฟรชลาเวนเดอร์": "เฟรชลาเวนเดอร์",
|
||||
"เฟรช ลาเวนเดอร์": "เฟรชลาเวนเดอร์",
|
||||
"fresh lavender": "เฟรชลาเวนเดอร์",
|
||||
|
||||
// Sandalwood
|
||||
"แซนดัลวูด": "แซนด้าวูส",
|
||||
"แซนด้าวูด": "แซนด้าวูส",
|
||||
"แซนดัลวู้ด": "แซนด้าวูส",
|
||||
"แซนด้าวูส": "แซนด้าวูส",
|
||||
"sandalwood": "แซนด้าวูส",
|
||||
|
||||
// Rose
|
||||
"กุหลาบแดง": "กุหลาบแดง",
|
||||
"กุหลาบแดง": "กุหลาบแดง",
|
||||
"กุหลาบ": "กุหลาบแดง",
|
||||
"red rose": "กุหลาบแดง",
|
||||
|
||||
// Jasmine
|
||||
"มะลิซ้อน": "มะลิ",
|
||||
"ดอกมะลิ": "มะลิ",
|
||||
"มะลิ": "มะลิ",
|
||||
"jasmine": "มะลิ",
|
||||
|
||||
// Fruits
|
||||
"ลูกพีช": "กลิ่นลูกพีช",
|
||||
"พีช": "กลิ่นลูกพีช",
|
||||
"peach": "กลิ่นลูกพีช",
|
||||
"องุ่น": "กลิ่นองุ่น",
|
||||
"grape": "กลิ่นองุ่น",
|
||||
"น้ำนม": "กลิ่นน้ำนม",
|
||||
"นม": "กลิ่นน้ำนม",
|
||||
"milk": "กลิ่นน้ำนม",
|
||||
"แคนตาลูป": "แคนตาลูป",
|
||||
"แคนตาลูป": "แคนตาลูป",
|
||||
"cantaloupe": "แคนตาลูป",
|
||||
"ส้ม": "ส้ม",
|
||||
"orange": "ส้ม",
|
||||
"มะกรูด": "มะกรูด",
|
||||
"bergamot": "มะกรูด",
|
||||
"ผลไม้รวม": "ผลไม้รวม",
|
||||
"mixed fruit": "ผลไม้รวม",
|
||||
"แอปเปิ้ลเขียว": "แอปเปิ้ลเขียว",
|
||||
"แอปเปิ้ลเขียว": "แอปเปิ้ลเขียว",
|
||||
"green apple": "แอปเปิ้ลเขียว",
|
||||
"แอปเปื้ลแดง": "แอปเปิ้ลแดง",
|
||||
"แอปเปิ้ลแดง": "แอปเปิ้ลแดง",
|
||||
"red apple": "แอปเปิ้ลแดง",
|
||||
"มะนาว": "มะนาว",
|
||||
"lemon": "มะนาว",
|
||||
"lime": "มะนาว",
|
||||
"ทับทิม": "ทับทิม",
|
||||
"pomegranate": "ทับทิม",
|
||||
"มะละกอสุก": "มะละกอสุก",
|
||||
"มะละกอ": "มะละกอสุก",
|
||||
"papaya": "มะละกอสุก",
|
||||
"กล้วย": "กล้วย",
|
||||
"banana": "กล้วย",
|
||||
|
||||
// Ocean / Ozone
|
||||
"โอเชี่ยน": "โอเชี่ยน",
|
||||
"มหาสมุทร": "โอเชี่ยน",
|
||||
"ocean": "โอเชี่ยน",
|
||||
"โอโซน": "โอโซน",
|
||||
"ozone": "โอโซน",
|
||||
|
||||
// Thai Flowers
|
||||
"ดอกราตรี": "ดอกราตรี",
|
||||
"ราตรี": "ดอกราตรี",
|
||||
"การเวก": "ดอกการะเวก",
|
||||
"ดอกการะเวก": "ดอกการะเวก",
|
||||
"ดอกอัญชัน": "อัญชัน",
|
||||
"อัญชัน": "อัญชัน",
|
||||
"butterfly pea": "อัญชัน",
|
||||
"ดอกบัวหลวง": "ดอกบัว",
|
||||
"ดอกบัว": "ดอกบัว",
|
||||
"บัว": "ดอกบัว",
|
||||
"lotus": "ดอกบัว",
|
||||
"ดอกปีบ": "ดอกปีบ",
|
||||
"ปีบ": "ดอกปีบ",
|
||||
"ดอกโมก": "ดอกโมก",
|
||||
"โมก": "ดอกโมก",
|
||||
"ดอกแก้ว": "ดอกแก้ว",
|
||||
"แก้ว": "ดอกแก้ว",
|
||||
"ดอกจำปา": "ดอกจำปา",
|
||||
"จำปา": "ดอกจำปา",
|
||||
"ดอกพิกุล": "ดอกพิกุล",
|
||||
"พิกุล": "ดอกพิกุล",
|
||||
"ดอกพุด": "ดอกพุด",
|
||||
"พุด": "ดอกพุด",
|
||||
"ดอกชมนาด": "ดอกชมนาด",
|
||||
"ชมนาด": "ดอกชมนาด",
|
||||
"ดอกราชาวดี": "ดอกราชาวดี",
|
||||
"ราชาวดี": "ดอกราชาวดี",
|
||||
"ดอกลำดวน": "ดอกลำดวน",
|
||||
"ลำดวน": "ดอกลำดวน",
|
||||
"ลีลาวดี": "ลีลาวดี",
|
||||
"frangipani": "ลีลาวดี",
|
||||
"ลิลลี่": "ดอกลิลลี่",
|
||||
"ดอกลิลลี่": "ดอกลิลลี่",
|
||||
"lilly": "ดอกลิลลี่",
|
||||
"lily": "ดอกลิลลี่",
|
||||
|
||||
// Herbs & Others
|
||||
"ยูคาลิปตัส": "ยูคาลิปตัส",
|
||||
"ยูคา": "ยูคาลิปตัส",
|
||||
"eucalyptus": "ยูคาลิปตัส",
|
||||
"วนิลา": "วานิลลา",
|
||||
"วนิลลา": "วานิลลา",
|
||||
"วานิลลา": "วานิลลา",
|
||||
"vanilla": "วานิลลา",
|
||||
"ซากุระ": "ซากุระ",
|
||||
"sakura": "ซากุระ",
|
||||
"เซเว่นซี": "เซเว่นซี",
|
||||
"seven sea": "เซเว่นซี",
|
||||
"7 sea": "เซเว่นซี",
|
||||
"หมากฝรั่ง": "หมากฝรั่ง",
|
||||
"bubble gum": "หมากฝรั่ง",
|
||||
"ไทยสปา": "ไทยสปา",
|
||||
"thai spa": "ไทยสปา",
|
||||
"กาแฟ": "กาแฟ",
|
||||
"coffee": "กาแฟ",
|
||||
"โรสเมรี่": "โรสเมรี่",
|
||||
"โรสแมรี่": "โรสเมรี่",
|
||||
"rosemary": "โรสเมรี่",
|
||||
};
|
||||
|
||||
function cleanSize(str: string): string | null {
|
||||
if (!str) return null;
|
||||
const s = str.toLowerCase().replace(/\s+/g, "");
|
||||
if (s.includes("120ml") || s === "120ml") return "120ml";
|
||||
if (s.includes("85ml") || s === "85ml") return "85ml";
|
||||
if (s.includes("60ml") || s === "60ml") return "60ml";
|
||||
if (s.includes("30ml") || s === "30ml") return "30ml";
|
||||
if (s.includes("25ml") || s === "25ml") return "25ml";
|
||||
return null;
|
||||
}
|
||||
|
||||
function findScent(text: string): string | null {
|
||||
if (!text) return null;
|
||||
const lower = text.toLowerCase();
|
||||
for (const [alias, canonical] of Object.entries(SCENT_ALIASES)) {
|
||||
if (lower.includes(alias.toLowerCase())) {
|
||||
return canonical;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log("== Shopee Product Matcher & Mapper ==");
|
||||
|
||||
// 1. Fetch Master Variants
|
||||
console.log("Fetching Master Product Variants...");
|
||||
const varRes = await fetch(`${PROD_API}/tools/marketplace-products/master-variants`);
|
||||
const varData = (await varRes.json()) as { variants: MasterVariant[] };
|
||||
const variants = varData.variants || [];
|
||||
console.log(`Loaded ${variants.length} master variants.`);
|
||||
|
||||
// Lookup map: `${canonicalScent}:::${normalizedSize}` -> MasterVariant
|
||||
const masterLookup = new Map<string, MasterVariant>();
|
||||
for (const v of variants) {
|
||||
const cleanName = v.productName.replace(/Oil Ozone/gi, "").trim();
|
||||
const size = v.optionsStr;
|
||||
const key = `${cleanName}:::${size}`.toLowerCase();
|
||||
masterLookup.set(key, v);
|
||||
}
|
||||
|
||||
// 3. Fetch Shopee Marketplace Products
|
||||
console.log("Fetching Shopee Marketplace Products...");
|
||||
const pRes = await fetch(`${PROD_API}/tools/marketplace-products?channel=shopee&status=all&limit=400`);
|
||||
const pData = (await pRes.json()) as { items: MarketplaceProduct[]; total: number };
|
||||
const items = pData.items || [];
|
||||
console.log(`Loaded ${items.length} Shopee products.`);
|
||||
|
||||
const matchedMappings: { marketplaceProductId: number; masterVariantId: number; meta: any }[] = [];
|
||||
const heldItems: { id: number; name: string; variation: string; price: number; reason: string; detectedScent?: string | null; detectedSize?: string | null }[] = [];
|
||||
|
||||
for (const item of items) {
|
||||
// If it's a 10ml product, hold it (Master Catalog does not support 10ml)
|
||||
if (item.productName.toLowerCase().includes("10 ml") || (item.variation || "").includes("10 ml")) {
|
||||
heldItems.push({
|
||||
id: item.id,
|
||||
name: item.productName,
|
||||
variation: item.variation,
|
||||
price: item.avgPrice,
|
||||
reason: "ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml)",
|
||||
detectedSize: "10ml",
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const vStr = item.variation || "";
|
||||
const pStr = item.productName || "";
|
||||
|
||||
let scent: string | null = null;
|
||||
let size: string | null = null;
|
||||
|
||||
// Handle comma-separated variation e.g. 'ตะไคร้บ้าน,120 ml'
|
||||
if (vStr.includes(",")) {
|
||||
const parts = vStr.split(",");
|
||||
for (const part of parts) {
|
||||
if (!size) size = cleanSize(part);
|
||||
if (!scent) scent = findScent(part);
|
||||
}
|
||||
}
|
||||
|
||||
if (!size) size = cleanSize(vStr);
|
||||
if (!scent) scent = findScent(vStr);
|
||||
|
||||
if (!size) size = cleanSize(pStr);
|
||||
if (!scent) scent = findScent(pStr);
|
||||
|
||||
if (!size) {
|
||||
heldItems.push({
|
||||
id: item.id,
|
||||
name: item.productName,
|
||||
variation: item.variation,
|
||||
price: item.avgPrice,
|
||||
reason: "ไม่สามารถระบุขนาดบรรจุ (Size) ได้ชัดเจนจากชื่อสินค้าหรือ Variation",
|
||||
detectedScent: scent,
|
||||
detectedSize: null,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!scent) {
|
||||
heldItems.push({
|
||||
id: item.id,
|
||||
name: item.productName,
|
||||
variation: item.variation,
|
||||
price: item.avgPrice,
|
||||
reason: "ไม่พบชื่อกลิ่นที่ตรงกับ Master Catalog (เช่น เมลอน หรือกลิ่นที่ยังไม่มีในระบบ)",
|
||||
detectedScent: null,
|
||||
detectedSize: size,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const key = `${scent}:::${size}`.toLowerCase();
|
||||
const master = masterLookup.get(key);
|
||||
|
||||
if (!master) {
|
||||
heldItems.push({
|
||||
id: item.id,
|
||||
name: item.productName,
|
||||
variation: item.variation,
|
||||
price: item.avgPrice,
|
||||
reason: `พบกลิ่น '${scent}' ขนาด '${size}' แต่ไม่มี Master Variant ขนาดนี้ในแคตตาล็อก`,
|
||||
detectedScent: scent,
|
||||
detectedSize: size,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
matchedMappings.push({
|
||||
marketplaceProductId: item.id,
|
||||
masterVariantId: master.id,
|
||||
meta: {
|
||||
shopeeSku: item.marketplaceSku,
|
||||
shopeeName: item.productName,
|
||||
shopeeVar: item.variation,
|
||||
masterSku: master.sku,
|
||||
masterName: master.displayName,
|
||||
scent,
|
||||
size,
|
||||
hasBom: master.bomCost > 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`\n========================================`);
|
||||
console.log(`Total Shopee Products : ${items.length}`);
|
||||
console.log(`High Confidence Match : ${matchedMappings.length} (${((matchedMappings.length / items.length) * 100).toFixed(1)}%)`);
|
||||
console.log(`Held / Needs Decision : ${heldItems.length} (${((heldItems.length / items.length) * 100).toFixed(1)}%)`);
|
||||
console.log(`========================================\n`);
|
||||
|
||||
// 4. Execute Bulk Mapping on Production
|
||||
if (matchedMappings.length > 0) {
|
||||
console.log(`Applying ${matchedMappings.length} bulk mappings on production...`);
|
||||
const bulkPayload = {
|
||||
mappings: matchedMappings.map((m) => ({
|
||||
marketplaceProductId: m.marketplaceProductId,
|
||||
masterVariantId: m.masterVariantId,
|
||||
})),
|
||||
};
|
||||
|
||||
const mapRes = await fetch(`${PROD_API}/tools/marketplace-products/bulk-map`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(bulkPayload),
|
||||
});
|
||||
|
||||
const mapResult = await mapRes.json();
|
||||
console.log("Bulk Map API Response:", mapResult);
|
||||
}
|
||||
|
||||
// 5. Generate Markdown Report
|
||||
let report = `# Shopee Marketplace Product Matching & Mapping Ledger\n\n`;
|
||||
report += `* **Generated At:** ${new Date().toISOString()}\n`;
|
||||
report += `* **Target Server:** \`${PROD_API}\` (Production)\n`;
|
||||
report += `* **Total Shopee Products:** ${items.length}\n`;
|
||||
report += `* **Successfully Mapped:** ${matchedMappings.length} (${((matchedMappings.length / items.length) * 100).toFixed(1)}%)\n`;
|
||||
report += `* **Held / Requires Decision:** ${heldItems.length} (${((heldItems.length / items.length) * 100).toFixed(1)}%)\n\n`;
|
||||
|
||||
report += `## 1. Held / Unmapped Items (ต้องตัดสินใจเพิ่มเติม)\n\n`;
|
||||
report += `| ID | ชื่อสินค้า Shopee | Variation | ราคาเฉลี่ย | สาเหตุที่ Hold |\n`;
|
||||
report += `|:---|:---|:---|:---|:---|\n`;
|
||||
for (const h of heldItems) {
|
||||
report += `| ${h.id} | ${h.name.replace(/\|/g, "-")} | ${h.variation || "-"} | ฿${h.price.toFixed(2)} | ${h.reason} |\n`;
|
||||
}
|
||||
|
||||
report += `\n## 2. Sample of Successfully Mapped Items (ตัวอย่างที่เชื่อมโยงสำเร็จ)\n\n`;
|
||||
report += `| ID | Shopee SKU | กลิ่น | ขนาด | Master SKU | Master Variant | มี BOM หรือยัง |\n`;
|
||||
report += `|:---|:---|:---|:---|:---|:---|:---|\n`;
|
||||
for (const m of matchedMappings.slice(0, 30)) {
|
||||
report += `| ${m.marketplaceProductId} | ${m.meta.shopeeSku || "-"} | ${m.meta.scent} | ${m.meta.size} | \`${m.meta.masterSku}\` | ${m.meta.masterName} | ${m.meta.hasBom ? "✓ มี BOM แล้ว" : "รอสร้าง BOM"} |\n`;
|
||||
}
|
||||
if (matchedMappings.length > 30) {
|
||||
report += `| ... | ... | ... | ... | ... | *(แสดง 30 จากทั้งหมด ${matchedMappings.length} รายการ)* | ... |\n`;
|
||||
}
|
||||
|
||||
const outPath = path.resolve(process.cwd(), "SHOPEE_MATCH_AND_MAP_LEDGER.md");
|
||||
fs.writeFileSync(outPath, report, "utf8");
|
||||
console.log(`\nSaved ledger report to: ${outPath}`);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
Reference in new issue
Block a user