Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74d13ec181 | ||
|
|
dfc3b497f5 | ||
|
|
8ae8d2b468 | ||
|
|
093b970284 | ||
|
|
7a9ecd0b8d |
+83
-8
@@ -3,13 +3,88 @@
|
|||||||
"timeoutMs": 15000,
|
"timeoutMs": 15000,
|
||||||
"maxFaqsPerPage": 50,
|
"maxFaqsPerPage": 50,
|
||||||
"pages": [
|
"pages": [
|
||||||
{ "url": "https://www.yehwang.com/register.html", "file": "01-registration-account.md", "title": "Registration & Account", "category": "registration" },
|
{
|
||||||
{ "url": "https://www.yehwang.com/order.html", "file": "02-ordering.md", "title": "Ordering", "category": "ordering" },
|
"url": "https://www.yehwang.com/register.html",
|
||||||
{ "url": "https://www.yehwang.com/pre-order.html", "file": "03-pre-order.md", "title": "Pre-Order", "category": "pre-order" },
|
"file": "01-registration-account.md",
|
||||||
{ "url": "https://www.yehwang.com/payment.html", "file": "04-payment-vat-invoice.md", "title": "Payment, VAT & Invoices", "category": "payment" },
|
"title": "Registration & Account",
|
||||||
{ "url": "https://www.yehwang.com/shipment.html", "file": "05-shipping-delivery.md", "title": "Shipping & Delivery", "category": "shipping" },
|
"category": "registration"
|
||||||
{ "url": "https://www.yehwang.com/returns.html", "file": "06-returns-refunds.md", "title": "Returns & Refunds", "category": "returns" },
|
},
|
||||||
{ "url": "https://www.yehwang.com/factory-direct.html", "file": "07-factory-direct.md", "title": "Factory-Direct", "category": "factory-direct" },
|
{
|
||||||
{ "url": "https://www.yehwang.com/other-questions-1987986538", "file": "10-products-and-jewelry-care.md", "title": "Other Questions", "category": "other" }
|
"url": "https://www.yehwang.com/order.html",
|
||||||
|
"file": "02-ordering.md",
|
||||||
|
"title": "Ordering",
|
||||||
|
"category": "ordering"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://www.yehwang.com/pre-order.html",
|
||||||
|
"file": "03-pre-order.md",
|
||||||
|
"title": "Pre-Order",
|
||||||
|
"category": "pre-order"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://www.yehwang.com/payment.html",
|
||||||
|
"file": "04-payment-vat-invoice.md",
|
||||||
|
"title": "Payment, VAT & Invoices",
|
||||||
|
"category": "payment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://www.yehwang.com/shipment.html",
|
||||||
|
"file": "05-shipping-delivery.md",
|
||||||
|
"title": "Shipping & Delivery",
|
||||||
|
"category": "shipping"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://www.yehwang.com/returns.html",
|
||||||
|
"file": "06-returns-refunds.md",
|
||||||
|
"title": "Returns & Refunds",
|
||||||
|
"category": "returns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://www.yehwang.com/factory-direct.html",
|
||||||
|
"file": "07-factory-direct.md",
|
||||||
|
"title": "Factory-Direct",
|
||||||
|
"category": "factory-direct"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://www.yehwang.com/other-questions-1987986538",
|
||||||
|
"file": "10-products-and-jewelry-care.md",
|
||||||
|
"title": "Other Questions",
|
||||||
|
"category": "other"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://dropshipping.yehwang.com/faq/register",
|
||||||
|
"file": "12-dropshipping-register.md",
|
||||||
|
"title": "Dropshipping — Register",
|
||||||
|
"category": "dropshipping-register",
|
||||||
|
"extractor": "nuxt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://dropshipping.yehwang.com/faq/order",
|
||||||
|
"file": "13-dropshipping-order.md",
|
||||||
|
"title": "Dropshipping — Order",
|
||||||
|
"category": "dropshipping-order",
|
||||||
|
"extractor": "nuxt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://dropshipping.yehwang.com/faq/payment",
|
||||||
|
"file": "14-dropshipping-payment.md",
|
||||||
|
"title": "Dropshipping — Payment",
|
||||||
|
"category": "dropshipping-payment",
|
||||||
|
"extractor": "nuxt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://dropshipping.yehwang.com/faq/shipment",
|
||||||
|
"file": "15-dropshipping-shipment.md",
|
||||||
|
"title": "Dropshipping — Shipment",
|
||||||
|
"category": "dropshipping-shipment",
|
||||||
|
"extractor": "nuxt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://dropshipping.yehwang.com/faq/returns",
|
||||||
|
"file": "16-dropshipping-returns.md",
|
||||||
|
"title": "Dropshipping — Returns",
|
||||||
|
"category": "dropshipping-returns",
|
||||||
|
"extractor": "nuxt"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
"www": {
|
"www": {
|
||||||
"site": "www",
|
"site": "www",
|
||||||
"tenantId": 2,
|
"tenantId": 2,
|
||||||
|
"currencyCode": "EUR",
|
||||||
"searchUrl": "https://www.yehwang.com/search.html?q=",
|
"searchUrl": "https://www.yehwang.com/search.html?q=",
|
||||||
"siteMap": "site-map.json",
|
"siteMap": "site-map.json",
|
||||||
"language": "en"
|
"language": "en"
|
||||||
@@ -12,6 +13,7 @@
|
|||||||
"us": {
|
"us": {
|
||||||
"site": "us",
|
"site": "us",
|
||||||
"tenantId": 2,
|
"tenantId": 2,
|
||||||
|
"currencyCode": "USD",
|
||||||
"searchUrl": "https://us.yehwang.com/search.html?q=",
|
"searchUrl": "https://us.yehwang.com/search.html?q=",
|
||||||
"siteMap": "site-map-us.json",
|
"siteMap": "site-map-us.json",
|
||||||
"language": "en"
|
"language": "en"
|
||||||
@@ -19,6 +21,7 @@
|
|||||||
"eu": {
|
"eu": {
|
||||||
"site": "www",
|
"site": "www",
|
||||||
"tenantId": 2,
|
"tenantId": 2,
|
||||||
|
"currencyCode": "EUR",
|
||||||
"searchUrl": "https://www.yehwang.com/search.html?q=",
|
"searchUrl": "https://www.yehwang.com/search.html?q=",
|
||||||
"siteMap": "site-map.json",
|
"siteMap": "site-map.json",
|
||||||
"language": "en"
|
"language": "en"
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
title: Dropshipping — Register
|
||||||
|
category: dropshipping-register
|
||||||
|
source_url:
|
||||||
|
- https://dropshipping.yehwang.com/faq/register
|
||||||
|
last_updated: 2026-10-23
|
||||||
|
keywords: [dropshipping, dropship, register, registration, account, shopify, business buyer]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Dropshipping — Register
|
||||||
|
|
||||||
|
> 📎 Source: https://dropshipping.yehwang.com/faq/register · 抓取 2026-10-23
|
||||||
|
|
||||||
|
### Q: Who can register as a customer at Yehwang dropshipping?
|
||||||
|
Yehwang Dropshipping is a B2B wholesale supplier specializing in providing high-quality products for resellers and businesses. We welcome the following types of customers:Ambitious e-commerce beginners, Social media influencers, Startup e-commerce brands, Existing online sellers, Offline jewelry & gift shops. Note: Registration is reserved for business buyers only. We do not serve retail consumers.
|
||||||
|
|
||||||
|
### Q: I do not have a company but I do want to purchase your products, is this possible?
|
||||||
|
No, unfortunately this won’t be possible.Please follow the steps below: Application Submission: Fill in the required company information Review & Activation: Once our team approves your submitted materials, we will grant access permissions based on your specific business circumstances. Please note: The most critical requirement for this process is that your Shopify store must have successfully authorized and integrated with Yehwang. Services cannot be activated without this completed connection.
|
||||||
|
|
||||||
|
### Q: How can I register an account with you?
|
||||||
|
Hit the 'Register' button at the top right corner on our website. Fill out all your details and check if the supplied information is correct. If everything looks good, hit the ‘Create account’ button.
|
||||||
|
|
||||||
|
### Q: How to Connect Your Shopify Store to Yehwang dropshipping?
|
||||||
|
1. Set Up a Shopify Store: Ensure you have an active Shopify store ready for selling. 2. Account Registration: Register for a Yehwang account and log in. 3. Submit Your Application: Complete the partnership application form to proceed. 4. Account Activation: Your dropshipping permissions will be activated. 5. Start Selling: Sync Yehwang products to your Shopify store and begin selling!
|
||||||
|
|
||||||
|
### Q: Is uninstalling Yehwang dropshipping reversible?
|
||||||
|
If You Uninstall YEHWANG Dropshipping From Your Store, Please Note: Once the store is removed from the current YEHWANG Dropshipping account, all the data of that store on YEHWANG Dropshipping will be lost and it is not reversible.
|
||||||
|
|
||||||
|
### Q: I forgot my password, now what?
|
||||||
|
It only takes a few steps to reset your password. Select "Forgot Password" on the login page. Then fill in your email address. We will send a password modification link to the email you filled in. Click the link to reset your password. If you encounter any problems while resetting your password, contact us at helpdesk@yehwang.com or call us on +31 0610422670
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
title: Dropshipping — Order
|
||||||
|
category: dropshipping-order
|
||||||
|
source_url:
|
||||||
|
- https://dropshipping.yehwang.com/faq/order
|
||||||
|
last_updated: 2026-10-23
|
||||||
|
keywords: [dropshipping, dropship, order, ordering, MOQ, minimum order, product list, import]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Dropshipping — Order
|
||||||
|
|
||||||
|
> 📎 Source: https://dropshipping.yehwang.com/faq/order · 抓取 2026-10-23
|
||||||
|
|
||||||
|
### Q: How Do I Start Selling with Yehwang Dropshipping?
|
||||||
|
Step 1:Connect Your Store Connect your online store to add items to your store and sync orders. Step 2:Adjust Your Store Setting Edit your pricing markups and order settings from our platform's settings page. by default, product price markup is 2x product price. Step 3:Add Products To Your Import List Search for products from YEHWANG Dropshipping Step 4:Sync Products To Your Store Publish products to your online store and start selling right away!
|
||||||
|
|
||||||
|
### Q: Sync Orders From Your Store
|
||||||
|
YEHWANG Dropshipping synchronizes your store orders and ships them synchronously after payment
|
||||||
|
|
||||||
|
### Q: Will the customer know that this order is shipped from Yehwang?
|
||||||
|
All packages are shipped in unbranded, neutral packaging without any Yehwang identifiers, ensuring customers receive items as if shipped directly from your store. If you have your own brand logo, please contact us.
|
||||||
|
|
||||||
|
### Q: Do you support private label packaging?
|
||||||
|
Yes, we offer private label and customized packaging solutions. The following conditions apply: Customized stamp: €30 per order, available for customers with 100+ orders per month. Enterprise dropshipping solutions: Custom solutions are available for larger-volume dropshipping businesses. Custom packaging materials: Minimum order quantity (MOQ) starts from 5,000 pieces. Private label products: Minimum order quantity (MOQ) starts from 120 pieces. Please contact our sales team to discuss the possibilities and find a solution that best suits your business.
|
||||||
|
|
||||||
|
### Q: What is the minimum order value?
|
||||||
|
There’s no minimum order quantity—order a single item or in bulk, whichever suits your needs. Perfect for testing new products!
|
||||||
|
|
||||||
|
### Q: Which Products Support Dropshipping?
|
||||||
|
Our delivery platform spans 50+ core categories, specializing in fashion, lifestyle, and creative products. With a curated selection of 10,000+ SKUs, we empower diverse business models—from e-commerce retail and cross-border distribution to boutique stores—ensuring seamless adaptation to your unique needs.
|
||||||
|
|
||||||
|
### Q: I did not receive an confirmation for my order.
|
||||||
|
You will receive an order confirmation immediately after placing your order. If you didn’t receive an email please check your spam folder. Still no e-mail? Unfortunately it looks like something went wrong when you filled out your details. Check and change your details through your account. If you did not receive an order confirmation despite all details being correct then please contact our customer service through service@yehwang.com. We will contact you as soon as possible.
|
||||||
|
|
||||||
|
### Q: Is it possible to cancel or adjust my order after placing it?
|
||||||
|
It is not possible to cancel finalized orders. Items in your confirmed order cannot be exchanged nor can items be added to a finalized order. Any additions to your order need to be placed on a separate order.
|
||||||
|
|
||||||
|
### Q: Do you charge a service fee for dropshipping orders?
|
||||||
|
Yes. A €1.50 service fee applies to each dropshipping order. For orders with 3 or more products, an additional €0.20 fee is charged per product.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
title: Dropshipping — Payment
|
||||||
|
category: dropshipping-payment
|
||||||
|
source_url:
|
||||||
|
- https://dropshipping.yehwang.com/faq/payment
|
||||||
|
last_updated: 2026-10-23
|
||||||
|
keywords: [dropshipping, dropship, payment, pay, invoice, VAT, currency, balance, top-up]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Dropshipping — Payment
|
||||||
|
|
||||||
|
> 📎 Source: https://dropshipping.yehwang.com/faq/payment · 抓取 2026-10-23
|
||||||
|
|
||||||
|
### Q: What are the payment options at Yehwang dropshipping?
|
||||||
|
We provide the following payment methods for orders placed in our online store: - iDeal; - Paypal; - Creditcard; - VISA; - Sofort; - Giropay; - Bancontact;
|
||||||
|
|
||||||
|
### Q: Is it possible to pay by bank transfer?
|
||||||
|
It is not possible to pay by bank transfer. Payment can be made through our website.
|
||||||
|
|
||||||
|
### Q: I registered my account with a valid VAT number, why do you still charge VAT?
|
||||||
|
1. Are you a Dutch customer and is your selected delivery address in the Netherlands? Then you always pay VAT over your orders 2. Check if your VAT number is listed in the My Account details. Do you have a valid VAT number and this number is not listed in My Account? Please contact us at service@yehwang.com 3. Select your delivery address and check if VAT is still charged. If VAT is still charged please contact us at service@yehwang.com
|
||||||
|
|
||||||
|
### Q: My company falls within the VAT exemption regulation. How can I receive an invoice with VAT from you?
|
||||||
|
If you have a valid VAT number and want to pay VAT over your orders with us, please contact us before placing your first order. We will then ensure that you are properly registered in our system and you can start ordering with us.
|
||||||
|
|
||||||
|
### Q: Can I have an invoice adjusted?
|
||||||
|
Unfortunately this is not possible. Once your order has been placed, we can no longer change the information on your invoice.
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
title: Dropshipping — Shipment
|
||||||
|
category: dropshipping-shipment
|
||||||
|
source_url:
|
||||||
|
- https://dropshipping.yehwang.com/faq/shipment
|
||||||
|
last_updated: 2026-10-23
|
||||||
|
keywords: [dropshipping, dropship, shipment, shipping, delivery, tracking, warehouse, dispatch]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Dropshipping — Shipment
|
||||||
|
|
||||||
|
> 📎 Source: https://dropshipping.yehwang.com/faq/shipment · 抓取 2026-10-23
|
||||||
|
|
||||||
|
### Q: What shipping options do you offer?
|
||||||
|
For all deliveries we offer two shipment options: PostNL or DHL. You will find the actual shipping rate in the overview of your cart during check-out. For PostNL shipping rates check our other FAQ on shipping charges. If your order reaches the threshold for free shipping and you select DHL as your courier, then the PostNL fee will be deducted from the DHL fee and you only pay the surcharge. Global DHL transit times can be found here. You can find an indication of global transit times via PostNL here.
|
||||||
|
|
||||||
|
### Q: When will my order arrive?
|
||||||
|
Orders that we receive before 3 P.M. on business days will be shipped by us within 1-3 business days. We do not ship during weekends. Orders that are placed on Friday after 3 P.M. will ship the following Monday. During clearance periods, it may occur that orders don't ship within the 1-3 working days, this could take up to a few days later. After your order ships, you will receive an email containing the Track and Trace link to track the status of your order. Please note that transit times vary per country. Please consult the site of the carrier listed in the Track and Trace link to get an up to date ETA. Global DHL transit times can be found here. You can find an indication of global transit times via PostNL here. Please note that PostNL lead times are an indication only and often times unfortunately experience some delay which could take up to 1 -2 weeks depending on the delivery address.
|
||||||
|
|
||||||
|
### Q: Will I receive a track and trace code?
|
||||||
|
Yes, you will receive a track and trace code by email from Postnl. If you have not received this code you can contact us at service@yehwang.com. Do not forget to mention you order number.
|
||||||
|
|
||||||
|
### Q: How does the Brexit affect shipping?
|
||||||
|
Yehwang dropshipping ships worldwide and the Brexit does not affect this. We still ship to the UK. However, on orders over GBP 135 (excl. VAT and shipping costs) UK customs might charge you import duties and taxes. These costs are to be paid by the customer, Yehwang dropshipping doesn't cover these charges.
|
||||||
|
|
||||||
|
### Q: I am working with a Dutch forwarder to export my order to an address outside the EU. You still charge VAT over my order. How can I get the VAT amount refunded?
|
||||||
|
The VAT will be paid by you in the beginning. After the order has been delivered to your Dutch forwarder, you will receive a proof of export from them. This means that the cargo has left the Netherlands. Please send a copy of this document with your IBAN number to helpdesk@yehwang.com and we will ensure that the VAT is refunded asap.
|
||||||
|
|
||||||
|
### Q: My package has not been delivered, what now?
|
||||||
|
How unfortunate that your package was not delivered. You may contact us via service@yehwang.com. Please don't forget to include your order number. We will initiate an investigation with the carrier to find out where the missing package is. The investigation may take 1 to 3 business days. If the carrier declares the package lost, we will refund the purchase amount. We will proceed with the refund once there is evidence and contact with the carrier that the package was not delivered.
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
title: Dropshipping — Returns
|
||||||
|
category: dropshipping-returns
|
||||||
|
source_url:
|
||||||
|
- https://dropshipping.yehwang.com/faq/returns
|
||||||
|
last_updated: 2026-10-23
|
||||||
|
keywords: [dropshipping, dropship, returns, refund, return, defective, after-sales]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Dropshipping — Returns
|
||||||
|
|
||||||
|
> 📎 Source: https://dropshipping.yehwang.com/faq/returns · 抓取 2026-10-23
|
||||||
|
|
||||||
|
### Q: Return period (14 or 30 days)
|
||||||
|
Under our standard B2B policy, Yehwang does not automatically accept consumer returns within 14 or 30 days, as we operate as a wholesale supplier. In a dropshipping setup, the retailer (you) remains the contractual party toward the end customer and is therefore responsible for complying with EU/UK withdrawal regulations. We only offer you 7 days non-reasonable return from you. The same return policy on our website.
|
||||||
|
|
||||||
|
### Q: Where should customer returns be sent?
|
||||||
|
Operationally, the most efficient model is for customer returns to be sent back to you first. This allows you to: Inspect product condition * Verify compliance with your return policy * Consolidate returns if needed Direct returns to Yehwang are generally not standard in dropshipping services. You need handle all your return request directly on your shopify website.
|
||||||
|
|
||||||
|
### Q: If returns are sent to Yehwang (upon agreement)
|
||||||
|
Returns cannot be processed if we receive the direct return from your end user, because there is no order for us to track, please be sure to avoid this by making your return policy clear enough on your website.
|
||||||
|
|
||||||
|
### Q: Refund handling
|
||||||
|
In a dropshipping model: * You refund your customer directly * Yehwang can issue a credit note to you once the returned goods are received and approved (if agreed under the cooperation terms within 7 days)
|
||||||
|
|
||||||
|
### Q: Conditions for return acceptance
|
||||||
|
If returns are accepted by Yehwang, the following conditions apply: * Unworn, unused items * Original packaging intact * No signs of wear, damage, or alteration * Within agreed timeframe Restocking conditions may apply depending on volume and frequency.
|
||||||
|
|
||||||
|
### Q: Defective or damaged items
|
||||||
|
For defective or transit-damaged products: * Clear photos and order reference are required * Claims must be submitted within a short timeframe after delivery * Once verified, we will offer replacement, credit, or refund depending on the case
|
||||||
@@ -8,7 +8,7 @@ function loadSites(env) {
|
|||||||
const fallback = {
|
const fallback = {
|
||||||
defaultSite: 'www',
|
defaultSite: 'www',
|
||||||
profiles: {
|
profiles: {
|
||||||
www: { site: 'www', tenantId: null, languageId: null, currencyCode: 'USD', searchUrl: 'https://www.yehwang.com/search.html?q=', siteMap: 'site-map.json' },
|
www: { site: 'www', tenantId: null, languageId: null, currencyCode: 'EUR', searchUrl: 'https://www.yehwang.com/search.html?q=', siteMap: 'site-map.json' },
|
||||||
},
|
},
|
||||||
inboxes: {},
|
inboxes: {},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,17 +17,24 @@ function createEscalation({ client, handoffUserId = 1 }) {
|
|||||||
const text = String(reply || '').replace(/\s+$/, '');
|
const text = String(reply || '').replace(/\s+$/, '');
|
||||||
const hints = {
|
const hints = {
|
||||||
zh: '您想转人工客服吗?',
|
zh: '您想转人工客服吗?',
|
||||||
|
en: 'Would you like to speak to a human agent?',
|
||||||
de: 'Möchten Sie mit einem Mitarbeiter sprechen?',
|
de: 'Möchten Sie mit einem Mitarbeiter sprechen?',
|
||||||
fr: 'Souhaitez-vous parler à un conseiller ?',
|
fr: 'Souhaitez-vous parler à un conseiller ?',
|
||||||
it: 'Vuole parlare con un operatore?',
|
it: 'Vuole parlare con un operatore?',
|
||||||
nl: 'Wilt u met een medewerker spreken?',
|
nl: 'Wilt u met een medewerker spreken?',
|
||||||
|
es: '¿Desea hablar con un agente humano?',
|
||||||
|
tr: 'Bir insan temsilcisiyle görüşmek ister misiniz?',
|
||||||
};
|
};
|
||||||
|
// 只输出一种语言:按用户输入判断语言,识别不到时用英文;不再拼英文兜底。
|
||||||
let lang = null;
|
let lang = null;
|
||||||
if (/[\u4e00-\u9fff]/.test(query)) lang = 'zh';
|
if (/[\u4e00-\u9fff]/.test(query)) lang = 'zh';
|
||||||
|
else if (/[ğışç]/i.test(query) || /\b(merhaba|istiyorum|konuşmak|temsilci|fiyat|ürün|sipariş)\b/i.test(query)) lang = 'tr';
|
||||||
else if (/[äöüß]/i.test(query)) lang = 'de';
|
else if (/[äöüß]/i.test(query)) lang = 'de';
|
||||||
else if (/[éèêëàâùûîïôç]/i.test(query)) lang = 'fr';
|
else if (/[ñ¿¡]/i.test(query) || /\b(hola|quiero|hablar|agente|puedo|gracias|precio|pedido)\b/i.test(query)) lang = 'es';
|
||||||
|
else if (/\b(vorrei|parlare|operatore|desidera|posso|prezzo|ordine)\b/i.test(query)) lang = 'it';
|
||||||
|
else if (/[éèêëàâùûîïôç]/i.test(query) || /\b(bonjour|merci|conseiller|commande|livraison|prix|voudrais)\b/i.test(query)) lang = 'fr';
|
||||||
else if (/\b(hoe|voor|wilt|bent|geen|verzending|levering|bestelling)\b/i.test(query)) lang = 'nl';
|
else if (/\b(hoe|voor|wilt|bent|geen|verzending|levering|bestelling)\b/i.test(query)) lang = 'nl';
|
||||||
const option = lang ? `\n\n${hints[lang]} / Would you like to speak to a human agent?` : '\n\nWould you like to speak to a human agent?';
|
const option = `\n\n${hints[lang] || hints.en}`;
|
||||||
return text + option;
|
return text + option;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const vm = require('node:vm');
|
||||||
|
|
||||||
function stripHtml(value) {
|
function stripHtml(value) {
|
||||||
return String(value || '')
|
return String(value || '')
|
||||||
@@ -40,6 +41,47 @@ function extractFaqFromLdJson(html, max = 50) {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nuxt SSR 页面(如 dropshipping.yehwang.com)没有 FAQPage JSON-LD,
|
||||||
|
// FAQ 数据内嵌在 window.__NUXT__ 的 payload 里(data[].info.content = [{title, content(HTML)}])。
|
||||||
|
// 在隔离 sandbox 里求值这段自执行脚本,只取回返回对象,不接触宿主全局。
|
||||||
|
function extractNuxtPayload(html) {
|
||||||
|
const m = String(html || '').match(/__NUXT__=([\s\S]*?)<\/script>/);
|
||||||
|
if (!m) return null;
|
||||||
|
try {
|
||||||
|
return vm.runInNewContext(m[1].replace(/;\s*$/, ''), Object.create(null), { timeout: 2000 });
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractFaqFromNuxt(html, max = 50) {
|
||||||
|
const payload = extractNuxtPayload(html);
|
||||||
|
if (!payload || !payload.data) return [];
|
||||||
|
const out = [];
|
||||||
|
const seen = new Set();
|
||||||
|
const walk = (node) => {
|
||||||
|
if (out.length >= max) return;
|
||||||
|
if (Array.isArray(node)) {
|
||||||
|
for (const item of node) walk(item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!node || typeof node !== 'object') return;
|
||||||
|
if (typeof node.title === 'string' && typeof node.content === 'string') {
|
||||||
|
const q = stripHtml(node.title);
|
||||||
|
const a = stripHtml(node.content);
|
||||||
|
if (q && a && !seen.has(q)) {
|
||||||
|
seen.add(q);
|
||||||
|
out.push({ q, a });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const value of Object.values(node)) {
|
||||||
|
if (value && typeof value === 'object') walk(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
walk(payload.data);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
function loadFaqConfig(file) {
|
function loadFaqConfig(file) {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
||||||
@@ -53,20 +95,21 @@ function createFaqFetcher(config = {}) {
|
|||||||
const timeoutMs = config.timeoutMs || 15000;
|
const timeoutMs = config.timeoutMs || 15000;
|
||||||
const max = config.maxFaqsPerPage || 50;
|
const max = config.maxFaqsPerPage || 50;
|
||||||
|
|
||||||
async function fetchPage(url) {
|
async function fetchPage(page) {
|
||||||
const res = await fetch(url, {
|
const res = await fetch(page.url, {
|
||||||
headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36', Accept: 'text/html,application/xhtml+xml' },
|
headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36', Accept: 'text/html,application/xhtml+xml' },
|
||||||
signal: AbortSignal.timeout(timeoutMs),
|
signal: AbortSignal.timeout(timeoutMs),
|
||||||
});
|
});
|
||||||
if (!res.ok) throw new Error(`fetch ${url} failed: HTTP ${res.status}`);
|
if (!res.ok) throw new Error(`fetch ${page.url} failed: HTTP ${res.status}`);
|
||||||
return extractFaqFromLdJson(await res.text(), max);
|
const html = await res.text();
|
||||||
|
return page.extractor === 'nuxt' ? extractFaqFromNuxt(html, max) : extractFaqFromLdJson(html, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchAll() {
|
async function fetchAll() {
|
||||||
const out = [];
|
const out = [];
|
||||||
for (const page of pages) {
|
for (const page of pages) {
|
||||||
try {
|
try {
|
||||||
const questions = await fetchPage(page.url);
|
const questions = await fetchPage(page);
|
||||||
out.push({ ...page, questions });
|
out.push({ ...page, questions });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
out.push({ ...page, questions: [], error: err.message });
|
out.push({ ...page, questions: [], error: err.message });
|
||||||
@@ -78,4 +121,4 @@ function createFaqFetcher(config = {}) {
|
|||||||
return { fetchPage, fetchAll, pages };
|
return { fetchPage, fetchAll, pages };
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { createFaqFetcher, loadFaqConfig, extractFaqFromLdJson, stripHtml };
|
module.exports = { createFaqFetcher, loadFaqConfig, extractFaqFromLdJson, extractFaqFromNuxt, extractNuxtPayload, stripHtml };
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ TOOL DISCIPLINE:
|
|||||||
- search_email_history: only for a verified customer asking about their own past emails (quotes, previous inquiries, "I emailed you before"). Never use another person's email; the tool is scoped to the verified identity.
|
- search_email_history: only for a verified customer asking about their own past emails (quotes, previous inquiries, "I emailed you before"). Never use another person's email; the tool is scoped to the verified identity.
|
||||||
- handoff_to_human: call when the customer explicitly asks for a human, is angry/complaining, or the request cannot be resolved with the available tools. Use it instead of only saying "I'll transfer you".
|
- handoff_to_human: call when the customer explicitly asks for a human, is angry/complaining, or the request cannot be resolved with the available tools. Use it instead of only saying "I'll transfer you".
|
||||||
- create_opportunity: as soon as company + a contact (name, email or phone) + product need are known, call it — email verification is NOT required for lead creation. Call it exactly once per customer. If it returns created=false, do not create another. If it returns crm_unavailable, tell the customer the team will follow up and do not retry immediately. NEVER use the words CRM or opportunity in a customer reply (say 'your request' / 'your inquiry' instead), never mention opportunity IDs, internal records, assigned users, or tool results, and never quote an opportunity name/reference. Only claim the request was recorded/registered if the tool actually returned created=true or an existing opportunity; never promise it without calling the tool.
|
- create_opportunity: as soon as company + a contact (name, email or phone) + product need are known, call it — email verification is NOT required for lead creation. Call it exactly once per customer. If it returns created=false, do not create another. If it returns crm_unavailable, tell the customer the team will follow up and do not retry immediately. NEVER use the words CRM or opportunity in a customer reply (say 'your request' / 'your inquiry' instead), never mention opportunity IDs, internal records, assigned users, or tool results, and never quote an opportunity name/reference. Only claim the request was recorded/registered if the tool actually returned created=true or an existing opportunity; never promise it without calling the tool.
|
||||||
|
- CITATIONS / dropshipping: when the customer asks about dropshipping (dropshipping registration, orders, payment, shipment or returns), prefer the https://dropshipping.yehwang.com/faq/* URLs that appear in the search_kb source_urls. Do NOT replace them with similar www.yehwang.com pages; only fall back to www.yehwang.com when no dropshipping source is available. Cite the URL that actually supports the answer.
|
||||||
|
|
||||||
VERIFICATION (important):
|
VERIFICATION (important):
|
||||||
- Order lookup requires a verified customer. Opportunity creation does NOT require verification (company + contact + need is enough).
|
- Order lookup requires a verified customer. Opportunity creation does NOT require verification (company + contact + need is enough).
|
||||||
|
|||||||
+12
-1
@@ -5,7 +5,7 @@ const assert = require('node:assert/strict');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { extractFaqFromLdJson } = require('../src/faq-fetcher');
|
const { extractFaqFromLdJson, extractFaqFromNuxt } = require('../src/faq-fetcher');
|
||||||
const { updateMarkdownFile } = require('../src/faq-update');
|
const { updateMarkdownFile } = require('../src/faq-update');
|
||||||
|
|
||||||
test('faq: extracts FAQPage JSON-LD questions', () => {
|
test('faq: extracts FAQPage JSON-LD questions', () => {
|
||||||
@@ -38,3 +38,14 @@ test('faq: normalized question match avoids duplicate append', () => {
|
|||||||
assert.equal((out.match(/### Q:/g) || []).length, 1);
|
assert.equal((out.match(/### Q:/g) || []).length, 1);
|
||||||
assert.match(out, /new/);
|
assert.match(out, /new/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('faq: extracts FAQ from Nuxt SSR payload (no JSON-LD)', () => {
|
||||||
|
const html = '<html><script>__NUXT__=(function(a){return {data:[{info:{content:[{title:"Q1?",content:"<p>A1</p>"},{title:"Q2?",content:"<p>A2 & more</p>"}]}}]}})();</script></html>';
|
||||||
|
const qs = extractFaqFromNuxt(html);
|
||||||
|
assert.deepEqual(qs, [{ q: 'Q1?', a: 'A1' }, { q: 'Q2?', a: 'A2 & more' }]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('faq: nuxt extractor tolerates missing/broken payload', () => {
|
||||||
|
assert.deepEqual(extractFaqFromNuxt('<html>no payload</html>'), []);
|
||||||
|
assert.deepEqual(extractFaqFromNuxt('<script>__NUXT__=not-js;</script>'), []);
|
||||||
|
});
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const test = require('node:test');
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
const { SYSTEM_PROMPT, buildSystemPrompt } = require('../src/prompts');
|
||||||
|
|
||||||
|
test('prompt: dropshipping answers prefer dropshipping source urls', () => {
|
||||||
|
assert.match(SYSTEM_PROMPT, /dropshipping\.yehwang\.com\/faq\//);
|
||||||
|
assert.match(SYSTEM_PROMPT, /Do NOT replace them with similar www\.yehwang\.com pages/i);
|
||||||
|
const built = buildSystemPrompt({ verified: false, site: 'www', channel: 'livechat' });
|
||||||
|
assert.match(built, /dropshipping\.yehwang\.com\/faq\//);
|
||||||
|
});
|
||||||
@@ -569,3 +569,30 @@ test('create_opportunity: creates opportunity + note + follow-up task', async ()
|
|||||||
const personIdx = twenty.calls.findIndex((c) => c[0] === 'createPerson');
|
const personIdx = twenty.calls.findIndex((c) => c[0] === 'createPerson');
|
||||||
assert.ok(companyIdx >= 0 && personIdx > companyIdx, 'company must be created before person');
|
assert.ok(companyIdx >= 0 && personIdx > companyIdx, 'company must be created before person');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('sites.json: www/eu -> EUR, us -> USD', () => {
|
||||||
|
const sites = require('../config/sites.json');
|
||||||
|
assert.equal(sites.profiles.www.currencyCode, 'EUR');
|
||||||
|
assert.equal(sites.profiles.eu.currencyCode, 'EUR');
|
||||||
|
assert.equal(sites.profiles.us.currencyCode, 'USD');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('search_products: passes the site currency to the product RPC', async () => {
|
||||||
|
const rpc = fakeRpc(() => []);
|
||||||
|
const cfg = {
|
||||||
|
...baseCfg,
|
||||||
|
sites: {
|
||||||
|
defaultSite: 'www',
|
||||||
|
profiles: { www: { site: 'www', tenantId: 2, languageId: 1, currencyCode: 'EUR', searchUrl: 'https://www.yehwang.com/search.html?q=' } },
|
||||||
|
inboxes: { '1': 'www' },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const tools = createTools(cfg, { rpc, twenty: fakeTwenty() });
|
||||||
|
const tool = tools.find((t) => t.name === 'search_products');
|
||||||
|
await tool.handler({ query: 'rings' }, { inboxId: '1' });
|
||||||
|
const call = rpc.calls[0];
|
||||||
|
assert.equal(call.method, '/product/list');
|
||||||
|
assert.equal(call.params[0].currencyCode, 'EUR');
|
||||||
|
assert.equal(call.params[0].tenantBaseCurrency, 'EUR');
|
||||||
|
assert.equal(call.params[0].reqContext.currencyCode, 'EUR');
|
||||||
|
});
|
||||||
|
|||||||
@@ -199,3 +199,6 @@ ss -ltnp | grep -E ':(9000|3100|4100|5440|6390)\s'
|
|||||||
| WhatsApp webhook 收不到 | 没有公网 HTTPS / Meta 回调地址不对 | 本地联调跑 `bash scripts/public-https.sh`;测试/生产使用正式域名,不要用 Quick Tunnel |
|
| WhatsApp webhook 收不到 | 没有公网 HTTPS / Meta 回调地址不对 | 本地联调跑 `bash scripts/public-https.sh`;测试/生产使用正式域名,不要用 Quick Tunnel |
|
||||||
| 公网访问到管理后台 | 网关白名单配置 | `services/public-gateway` 只放行 `/webhooks/whatsapp/*`、`/hc/*`、`/widget*`、`/static/public/*`,其余 404 |
|
| 公网访问到管理后台 | 网关白名单配置 | `services/public-gateway` 只放行 `/webhooks/whatsapp/*`、`/hc/*`、`/widget*`、`/static/public/*`,其余 404 |
|
||||||
| AI 回复"系统暂时无法查询订单" | RPC 不可达,bridge 8s 超时 | 本地开发跑 `bash scripts/rpc-tunnel.sh --apply`;测试/生产检查内网 RPC 地址/网络策略,不要用开发机隧道 |
|
| AI 回复"系统暂时无法查询订单" | RPC 不可达,bridge 8s 超时 | 本地开发跑 `bash scripts/rpc-tunnel.sh --apply`;测试/生产检查内网 RPC 地址/网络策略,不要用开发机隧道 |
|
||||||
|
| WhatsApp 真实消息收不到,但 Meta "Send to My Server" 测试事件能到 | WABA 级 `override_callback_uri` 优先于 App 级 callback,且 Meta 对 override 的验证常返回 403(与目标 URL 无关) | `GET /<WABA_ID>/subscribed_apps` 确认 override;`DELETE /<WABA_ID>/subscribed_apps` 后重新 `POST`(不带 override)清掉,确认 App 级 callback 指向目标域名,再发真实消息验证 |
|
||||||
|
| 多个栈共用同一个 WhatsApp 号,消息被旧环境接走(旧环境 AI 回复、新环境无日志) | crm-new 与旧 crm-ai-demo 配置了同一 `WA_PHONE_NUMBER_ID` / WABA,旧栈 tunnel 仍在接收 | 停旧栈 WhatsApp 接收(停旧 tunnel / 禁用旧 inbox),或给新环境申请独立测试号;不要让两套同时接同一个号 |
|
||||||
|
| 新建 WhatsApp inbox 后 webhook 验证 403 `verify token mismatch` | 创建接口传入的 `webhook_verify_token` 可能未真正生效,inbox 仍是默认值;`GET /api/v1/inboxes/<id>` 返回的是掩码值,不能用来核对 | 用 `PUT /api/v1/inboxes/<id>` 带完整 config 重新保存一次,再请求 `GET /webhooks/whatsapp/<id>?hub.mode=subscribe&hub.verify_token=...&hub.challenge=...` 确认返回 200 |
|
||||||
|
|||||||
Reference in New Issue
Block a user