merge: 拉取 origin/main(KB snapshot + 在线 FAQ 同步 + 环境文档)

This commit is contained in:
wangliang
2026-10-23 11:32:01 +08:00
30 changed files with 2083 additions and 33 deletions
+19 -7
View File
@@ -22,7 +22,7 @@
| Twenty Workspace | ✅ 已创建(admin@yehwang.com,workspace 已 ACTIVE) |
| Twenty API Key | ✅ 已在库内生成并写入 `.env`,GraphQL 验证通过 |
| 商机写入 | ✅ `create_opportunity` 实测创建 company/person/opportunity/note/task |
| 业务 RPC | ✅ 测试服 24503/24505/24506/24509 外网被封,已用 SSH 隧道(`scripts/rpc-tunnel.sh`) |
| 业务 RPC | ✅ 本地开发用 SSH 隧道(`scripts/rpc-tunnel.sh`);测试/生产应直连内网 RPC,不需要隧道 |
| 邮件通知(OTP) | ✅ 163 SMTP 已配置,实测验证码邮件已送达 163 收件箱 |
| Email 渠道 | ✅ 163 收件(imap-proxy 绕过 Unsafe Login)+ AI 回复邮件(模板)已实测 |
| IMAP 代理 | ✅ `services/imap-id-proxy`(LibreDesk 不发 IMAP ID,163 会拒绝 SELECT) |
@@ -68,7 +68,8 @@ crm-new/
├── infra/init-databases.sql # 首次初始化三个库
├── libredesk/ # LibreDesk 配置模板/渲染结果/uploads(legacy/ 为旧单机模式归档)
├── twenty/ # Twenty 部署说明(不需要源码)
├── bridge/ # 业务工具服务(原 tools)+ 单测
├── bridge/
│ └── kb/ # KB 源 Markdown(在线 FAQ 同步会更新这里) # 业务工具服务(原 tools)+ 单测
│ └── config/sites.json # inbox ID → 站点 profile(tenant/语言/类目地图)
├── services/imap-id-proxy/ # IMAP ID 注入代理(163 专用)
├── scripts/
@@ -80,18 +81,25 @@ crm-new/
│ ├── configure-email.js # 配置 163 SMTP(通知/OTP 邮件)
│ ├── add-email-inbox.js # 配置 Email Inbox + 默认外发模板 + Automation
│ ├── e2e-widget-jwt.js # 端到端验证 widget JWT + 订单工具
│ ├── rpc-tunnel.sh # 测试服 JSON-RPC 端口 SSH 隧道(bridge 依赖)
│ ├── public-https.sh # Cloudflare Tunnel 公网 HTTPS + 白名单网关
│ ├── rpc-tunnel.sh # 仅本地开发:测试服 RPC 端口 SSH 隧道
│ ├── public-https.sh # 仅本地联调:Cloudflare Quick Tunnel + 白名单网关
│ ├── set-public-url.js # 更新 LibreDesk app.root_url
│ ├── import-kb-helpcenter.js # 旧 KB -> 帮助中心文章(幂等)
│ ├── verify-rag.js # 临时助手 + preview,验证检索/引用
│ └── smoke.sh
│ ├── export-kb-snapshot.sh # 导出 kb_docs 快照
│ ├── import-kb-snapshot.sh # 无旧 bridge 时导入 KB 快照
│ ├── sync-kb-online.js # 在线 FAQ 同步 + 重建 KB(不依赖旧 bridge)
│ ├── smoke.sh
│ └── serve-web.sh # 仅本地预览 demo 页面
├── web/ # 站点接入:ai-snippet.js + demo.html(widget JWT 演示)
├── docs/ # 架构、LibreDesk 配置、接口对照、迁移说明、运维手册
├── docs/ # 架构、LibreDesk 配置、环境差异、上线/运维手册
│ ├── go-live.md # 上线文档(范围/切流/验收/监控/回滚)
│ ├── environments.md # 本地开发 vs 测试/生产:RPC/Cloudflare 隧道差异
│ ├── human-routing.md # Email/WhatsApp 人工团队分配
│ └── plan-a.md # 方案 A:bridge 接管 AI 编排进度
└── deploy/nginx.conf.example
├── deploy/
│ ├── nginx.conf.example
│ └── seeds/kb_docs.sql.gz # KB 快照(367 chunks:296 parent + 71 child)
services/public-gateway/ # 公网路径白名单反向代理(仅 webhook/帮助中心)
```
@@ -107,6 +115,8 @@ bash scripts/bootstrap.sh
bash scripts/smoke.sh
```
> `scripts/rpc-tunnel.sh`、`scripts/public-https.sh`、`scripts/serve-web.sh` 仅本地开发/联调使用;测试/生产环境不需要,见 [docs/environments.md](docs/environments.md)。
启动完成后:
1. **LibreDesk**(`System`,密码见 `.env` 的 `LIBREDESK_SYSTEM_USER_PASSWORD`)
@@ -184,6 +194,8 @@ bash scripts/serve-web.sh # http://localhost:8088/demo.html
## 运维
环境差异见 [docs/environments.md](docs/environments.md)(RPC 隧道/Cloudflare 隧道仅本地开发用)。
上线清单见 [docs/go-live.md](docs/go-live.md),常用命令见 [docs/runbook.md](docs/runbook.md)(启动/停止/日志/备份/升级/回滚/排错)。
## 测试
+15
View File
@@ -0,0 +1,15 @@
{
"enabled": true,
"timeoutMs": 15000,
"maxFaqsPerPage": 50,
"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/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" }
]
}
+73
View File
@@ -0,0 +1,73 @@
---
title: Registration & Account
category: registration
source: yehwang.com/register.html (official FAQ, verbatim)
source_url:
- https://www.yehwang.com/register.html
- https://www.yehwang.com/other-questions-1987986538
last_updated: 2026-06-25
keywords: [register, account, sign up, VAT number, Chamber of Commerce, CoC, individual, password, reset, find a shop, newsletter, images]
---
# Registration & Account
> 📎 来源 / Source: https://www.yehwang.com/register.html · 抓取 2026-06-25
### Q: Who can register as a customer at Yehwang?
Yehwang is a B2B wholesaler serving the fashion, wellness & living industries. Every customer must be in possession of a valid VAT number to open an account.
### Q: I do not have a company but I want to purchase your products. Is this possible?
Yes. A Chamber of Commerce (CoC) number is **only required for businesses based in the Netherlands**. For customers from all other regions there are no restrictions and no minimum order quantity required — you are welcome to place an order as an individual.
### Q: How do I register an account?
Click the **'Register'** button at the top right corner of our website. Fill out all your details and check that the supplied information is correct. If everything looks good, click **'Create account'**.
### Q: I created an account. Can I start shopping right away?
Yes. Once your account is created you can start shopping immediately. You will receive a confirmation email after registration. You can then log in, view all products **with prices**, and manage your information in your account settings.
### Q: I forgot my password. What now?
On the login page select **'Forgotten password'**, then enter the email address where we should send a new temporary password. If you have any problems resetting, contact **helpdesk@yehwang.com** or call **+31 318 668 171**.
### Q: How do I register for "Find a Shop"?
1. Log in to yehwang.com and select **My account** (upper right).
2. Click **My shop**.
3. Click **Add new Shop**.
4. Fill out your details and click **Continue**.
If you need help, contact **helpdesk@yehwang.com** or **+31 318 668 171**.
### Q: How do I register for the newsletter?
Use the newsletter sign-up on our website to stay up to date on weekly news, promotions, and new items.
### Q: May I use your product images?
Yes. As a customer you may use all our images, including product images, on-model shots, and images from our Instagram. Images of items you purchased are listed per order and can be downloaded as a .ZIP from your account, or saved individually from the website. If you use our images on social media, we'd love to be tagged.
### Q: I do not have a company but I do want to purchase your products, is this possible?
This website is for B2B customers only—no individual consumers. A Chamber of Commerce (CoC) number is required only for businesses based in the Netherlands. For customers from all other regions, no CoC number or minimum order quantity is required, as long as you are purchasing for commercial or professional purposes.
### Q: How can I register an account with you?
Click 'Register' at the top right of our website, fill in your details, and hit 'Create account' once everything looks correct.
### Q: I created an account with you. Can I start shopping right away?
Yes — start shopping right away. After registering you'll get a confirmation email, then you can log in to view all products with prices and manage your account settings.
### Q: I forgot my password, now what?
Select 'Forgotten password' on the login page and enter your email to receive a temporary password.Issues? Contact helpdesk@yehwang.com or call +31 318 668 171.
### Q: How can I register for Find a Shop?
1. Log in to yehwang.com and go to 'My account'.2. Click 'My store'.3. Click 'Add store'.4. Fill in your details and click Save.Need help? Contact helpdesk@yehwang.com or call +31 318 668 171.
### Q: How can I register for the newsletter?
Click here to register for the newsletter. This way you will stay up to date of weekly news, promotions and new items!
### Q: May I use your images?
Yes, as a customer, you are allowed to use all of our images. You can download them from the relevant product pages on our website or from our Instagram account.
+120
View File
@@ -0,0 +1,120 @@
---
title: Ordering — placing orders, minimum order, quantity discounts, changes
category: ordering
source: yehwang.com/order.html (official FAQ, verbatim)
source_url:
- https://www.yehwang.com/order.html
last_updated: 2026-06-25
keywords: [order, place order, minimum order, MOQ, 30 euro, combine orders, shipping per order, quantity discount, volume discount, rewards, logo-free, cancel, adjust, voucher, promo code, price drop, sold out, back in stock]
---
# Ordering
> 📎 来源 / Source: https://www.yehwang.com/order.html · 抓取 2026-06-25
### Q: How do I place an order?
Log in to your business account and click "Add to Cart" to add products to your shopping cart. Once you're done shopping, click the shopping cart in the upper-right corner to review the products you've selected.Please check your products and personal details carefully, and update them if needed. Then, select your preferred payment method and place your order.You will receive an order confirmation by email.
### Q: What is the minimum order value?
The minimum order value is €30 — order a few items or in bulk, whichever suits your needs.
### Q: Can I reserve items?
Yes, through **pre-orders** you can reserve certain products that we have already re-ordered and are on their way to our warehouse. Pre-orders must always be paid in advance. See [03-pre-order](03-pre-order.md).
### Q: Can I combine two orders?
No. It is not possible to combine two separate orders. Separate shipping costs are charged for each order because packages are processed and shipped separately. Shipping costs cannot be combined.
### Q: Do you offer quantity discounts (same product)?
Yes, larger quantities of the **same product** earn an extra discount:
- **5–9 pieces**: +3%
- **10–19 pieces**: +5%
- **20+ pieces**: +7%
These stack with our order-total volume discounts — see [09-membership-rewards](09-membership-rewards.md).
### Q: How do Yehwang's order rewards work?
To reward loyal customers, the platform returns rewards on each order, and rewards can be accumulated. Detailed rules are linked on the order rewards page on our website.
### Q: Do you have logo-free jewelry?
Yes. Since mid-2024, all new Yehwang jewelry collections are shipped without logos on packaging or labels. This allows you to sell our jewelry under your own brand and create a more personalized collection for your customers. Enjoy the freedom and flexibility to build your own jewelry line with Yehwang.
### Q: I didn't receive an order confirmation.
You should receive a confirmation immediately after placing the order. First check your spam folder. If it's still missing, your details may have been entered incorrectly — check and correct them in your account. If your details are correct and you still have no confirmation, contact **helpdesk@yehwang.com** and we'll get back to you as soon as possible.
### Q: Can I cancel or adjust my order after placing it?
No. Finalized orders cannot be cancelled. Items in a confirmed order cannot be exchanged, and items cannot be added to a finalized order. Any additions must be placed as a separate order.
### Q: How do I apply a voucher or promo code?
Discounts tied to holidays that apply to all customers (e.g. Black Friday, Daily Deals, Yehwang Day) are applied to your cart automatically. **Personal vouchers and discount codes** are added at the last checkout step — the same page where you select delivery and invoice addresses. (Note: a maximum of one code per order; sale and brand items are excluded — see [04-payment-vat-invoice](04-payment-vat-invoice.md).)
### Q: I bought an item at full price and it went on discount the next day. Can I get the difference back?
No, we do not refund the discount difference. What you can do is reorder the products at the discounted price and return the others, with return costs at your own expense. No exceptions are made.
### Q: An item is sold out — will it be back soon?
Stock varies greatly by product, and unfortunately not every sold-out item can be replenished. Click the **'Back in Stock'** notice button on the product so we can contact you if it returns.
### Q: Can I combine multiple orders?
No. Once an order has been placed, it cannot be combined with another order. Each order is processed and shipped separately, so shipping costs are charged individually and cannot be merged.
### Q: Is it possible to reserve items?
Yes. Certain products can be reserved through our pre-order service. These items have already been reordered by us and are currently on their way to our warehouse. Please note that all pre-orders must be paid in advance.
### Q: Do you offer quantity discounts?
At Yehwang, the more you buy, the more you save! When you purchase 5–9 pieces of the same product, you'll receive an extra 3% discount. Orders of 10–19 pieces enjoy an additional 5% discount, while purchases of 20+ pieces receive an extra 7% discount.
### Q: I did not receive an confirmation for my order
You should get a confirmation email right after ordering — check your spam folder if not. If your details are correct but you still didn't receive one, contact helpdesk@yehwang.com and we'll help right away.
### Q: Is it possible to cancel or adjust my order after placing it?
Once your order has been finalized, it cannot be canceled or modified. Items in confirmed orders cannot be exchanged, and additional products cannot be added. If you would like to purchase more items, please place a new order separately.
### Q: How do I apply a Yehwang voucher or promo code to my order?
Sitewide promotions, such as Black Friday, Daily Deals, and Yehwang Day, are applied automatically when eligible. To use a personal voucher or promo code, enter it during the final step of checkout on the page where you select your shipping and billing addresses.
### Q: I bought a product for the original price, but it is on discount the next day. Can I get the difference back?
No, we don't refund the price difference. You can reorder the item at the discounted price and return the original at your own expense — no exceptions.
### Q: An item is sold out. Will the sold out item be available again shortly?
Our stock varies greatly by product. Unfortunately, it is not possible to replenish each stocked-out item. Please hit the Back in Stock notice button so we can contact you when a product is back in stock.
### Q: How do I place an order?
Log in to your business account and click "Add to Cart" to add products to your shopping cart. Once you're done shopping, click the shopping cart in the upper-right corner to review the products you've selected.Please check your products and personal details carefully, and update them if needed. Then, select your preferred payment method and place your order.You will receive an order confirmation by email.
### Q: Is it possible to reserve items?
Yes. Certain products can be reserved through our pre-order service. These items have already been reordered by us and are currently on their way to our warehouse. Please note that all pre-orders must be paid in advance.
### Q: What is the minimum order value?
The minimum order value is €30 — order a few items or in bulk, whichever suits your needs.
### Q: Can I combine multiple orders?
No. Once an order has been placed, it cannot be combined with another order. Each order is processed and shipped separately, so shipping costs are charged individually and cannot be merged.
### Q: Do you offer quantity discounts?
At Yehwang, the more you buy, the more you save! When you purchase 5–9 pieces of the same product, you'll receive an extra 3% discount. Orders of 10–19 pieces enjoy an additional 5% discount, while purchases of 20+ pieces receive an extra 7% discount.
### Q: Do you have logo-free jewelry?
Yes. Since mid-2024, all new Yehwang jewelry collections are shipped without logos on packaging or labels. This allows you to sell our jewelry under your own brand and create a more personalized collection for your customers. Enjoy the freedom and flexibility to build your own jewelry line with Yehwang.
### Q: I did not receive an confirmation for my order
You should get a confirmation email right after ordering — check your spam folder if not. If your details are correct but you still didn't receive one, contact helpdesk@yehwang.com and we'll help right away.
### Q: Is it possible to cancel or adjust my order after placing it?
Once your order has been finalized, it cannot be canceled or modified. Items in confirmed orders cannot be exchanged, and additional products cannot be added. If you would like to purchase more items, please place a new order separately.
### Q: How do I apply a Yehwang voucher or promo code to my order?
Sitewide promotions, such as Black Friday, Daily Deals, and Yehwang Day, are applied automatically when eligible. To use a personal voucher or promo code, enter it during the final step of checkout on the page where you select your shipping and billing addresses.
### Q: I bought a product for the original price, but it is on discount the next day. Can I get the difference back?
No, we don't refund the price difference. You can reorder the item at the discounted price and return the original at your own expense — no exceptions.
### Q: An item is sold out. Will the sold out item be available again shortly?
Our stock varies greatly by product. Unfortunately, it is not possible to replenish each stocked-out item. Please hit the Back in Stock notice button so we can contact you when a product is back in stock.
+62
View File
@@ -0,0 +1,62 @@
---
title: Pre-order
category: pre-order
source: yehwang.com/pre-order.html (official FAQ, verbatim)
source_url:
- https://www.yehwang.com/pre-order.html
last_updated: 2026-06-25
keywords: [pre-order, preorder, reserve, out of stock, delivery week, 5% discount, minimum 50 pieces, combine, first order required]
---
# Pre-order
> 📎 来源 / Source: https://www.yehwang.com/pre-order.html · 抓取 2026-06-25
### Q: What is a pre-order?
Out-of-stock products can be reserved and paid for as pre-orders — they've already been reordered and are on their way to our warehouse. Estimated dispatch timing will be shown on each product detail page. Please note that this is an approximate timeframe only — actual delivery may arrive earlier or later than indicated.
### Q: How do I know which products are pre-order products?
Pre-order products are labeled "PRE-ORDER" on our website. You can also view the complete list on our Pre-order Products page.
### Q: How do I know when my pre-order will be delivered?
Each pre-order product displays an estimated delivery week. This refers to the expected arrival week at our warehouse, not the delivery date to your address. If your order includes products with different delivery weeks, it will be shipped once all items are available. We'll notify you by email about your shipment and any delays. Please note that delivery weeks are estimates and may change due to unforeseen circumstances. If you have any questions, contact us at helpdesk@yehwang.com.
### Q: Why can't I add a pre-order product to my cart?
Two reasons:
1. **Pre-order products cannot be combined with in-stock items** — they require two separate orders, and the minimum order value applies to each.
2. **Pre-order items are only available to existing customers** — if you have never ordered from us before, pre-order items are not visible. Place a regular order first.
### Q: Can I pre-order items that are (almost) out of stock?
Yes. Contact customer service to place a personal pre-order. The **minimum quantity for a personal pre-order is 50 pieces**. Pre-ordering clothing must first be confirmed with our supplier.
### Q: Why do I get a discount on pre-order items?
Pre-order products are available with a 5% discount because they have not yet arrived at our warehouse. Since these items require a longer wait before they can be shipped, we offer this discount to thank you for your patience. Once the products are in stock, the discounted price will return to the regular price.
### Q: Why can't I place pre-order products in my shopping cart?
Pre-order and in-stock items can't be combined — place them as separate orders, each meeting the minimum order value. Pre-order items are also only available once you've placed a regular order with us.
### Q: Can I pre-order products that are (almost) out of stock?
Yes. If you'd like to pre-order products that are out of stock or almost out of stock, please contact our customer service team. The minimum order quantity for a personal pre-order is 50 pieces. For clothing, pre-orders must first be confirmed with our supplier before they can be arranged.
### Q: What is a pre-order?
Out-of-stock products can be reserved and paid for as pre-orders — they've already been reordered and are on their way to our warehouse. Estimated dispatch timing will be shown on each product detail page. Please note that this is an approximate timeframe only — actual delivery may arrive earlier or later than indicated.
### Q: How do I know which products are pre-order products?
Pre-order products are labeled "PRE-ORDER" on our website. You can also view the complete list on our Pre-order Products page.
### Q: How do I know when my pre-order will be delivered?
Each pre-order product displays an estimated delivery week. This refers to the expected arrival week at our warehouse, not the delivery date to your address. If your order includes products with different delivery weeks, it will be shipped once all items are available. We'll notify you by email about your shipment and any delays. Please note that delivery weeks are estimates and may change due to unforeseen circumstances. If you have any questions, contact us at helpdesk@yehwang.com.
### Q: Why can't I place pre-order products in my shopping cart?
Pre-order and in-stock items can't be combined — place them as separate orders, each meeting the minimum order value. Pre-order items are also only available once you've placed a regular order with us.
### Q: Can I pre-order products that are (almost) out of stock?
Yes. If you'd like to pre-order products that are out of stock or almost out of stock, please contact our customer service team. The minimum order quantity for a personal pre-order is 50 pieces. For clothing, pre-orders must first be confirmed with our supplier before they can be arranged.
### Q: Why do I get a discount on pre-order items?
Pre-order products are available with a 5% discount because they have not yet arrived at our warehouse. Since these items require a longer wait before they can be shipped, we offer this discount to thank you for your patience. Once the products are in stock, the discounted price will return to the regular price.
+71
View File
@@ -0,0 +1,71 @@
---
title: Payment, VAT & Invoices
category: payment
source: yehwang.com/payment.html (official FAQ, verbatim)
source_url:
- https://www.yehwang.com/payment.html
last_updated: 2026-06-25
keywords: [payment, iDeal, PayPal, credit card, VISA, Sofort, Giropay, Bancontact, bank transfer, discount code, VAT, tax, VAT number, exemption, invoice, EU VAT, checkout]
---
# Payment, VAT & Invoices
> 📎 来源 / Source: https://www.yehwang.com/payment.html · 抓取 2026-06-25
### Q: What payment methods do you accept?
We accept the following online payment methods:
- iDeal
- PayPal
- Credit card
- VISA
- Sofort
- Giropay
- Bancontact
### Q: Can I pay by bank transfer?
No. Payment by bank transfer is not possible. Payment is made through our website using the methods above.
### Q: To which products can a discount code be applied?
Discount codes apply to the **total value of your order**. **Sale items and brand items are excluded.** Each discount code is valid only once, and a **maximum of one code can be used per order**.
### Q: I registered with a valid VAT number — why am I still charged VAT?
Check the following:
1. If you are a **Dutch customer** with a delivery address in the Netherlands, you always pay VAT on your orders.
2. Check that your VAT number is listed in your **My Account** details. If you have a valid VAT number that is not listed there, contact **helpdesk@yehwang.com**.
3. Select your delivery address and check whether VAT is still charged. If it still is, contact **helpdesk@yehwang.com**.
### Q: My company falls within the VAT exemption regulation. How can I receive an invoice with VAT?
If you have a valid VAT number and want to pay VAT on your orders, **contact us before placing your first order**. We will register you correctly in our system so you can start ordering.
### Q: Why do I need to pay VAT at checkout?
If your delivery address is within the EU and you did not provide a valid VAT number when entering your shipping address details, VAT will be charged at checkout. The VAT rate varies depending on the country of your delivery address. Please refer to the VAT rate table below. CountryTax rateAustria20%Belgium21%Bulgaria20%Croatia25%Cyprus19%Czech Republic21%Denmark25%Estonia20%Finland24%France20%Germany19%Greece24%Hungary27%Ireland23%Italy22%Latvia21%Lithuania21%Luxembourg17%Malta18%Poland23%Portugal23%Romania19%Slovak Republic20%Slovenia22%Spain21%Sweden25%United Kingdom20%
> ⚠️ 待核对: 官网附有"各国 VAT 税率表",需业务确认后补全到此处。
### Q: Can I have an invoice adjusted?
Unfortunately, this is not possible. Once your order has been placed, we can no longer change the details on your invoice.
### Q: I export outside the EU via a Dutch forwarder — how do I get the VAT refunded?
You pay the VAT initially. After the order is delivered to your Dutch forwarder, they give you a **proof of export** (confirming the cargo left the Netherlands). Email a copy of that document together with your IBAN to **helpdesk@yehwang.com** and we will refund the VAT as soon as possible.
### Q: What are the payment options at Yehwang?
We provide the following payment methods for orders placed in our online store:- iDeal;- Paypal;- Creditcard;- VISA;- Sofort;- Giropay;- Bancontact;
### Q: I received a discount code. To which products can this code be applied?
Discount codes apply to your order total, excluding sale and branded items. Each discount code can be used once, and only one code can be applied per order.
### Q: Is it possible to pay by bank transfer?
No, bank transfer is not available. Please complete your payment through our website. You can find more information about our payment options here.
### Q: I registered my account with a valid VAT number, why do you still charge VAT?
If VAT is still being charged after you've registered with a valid VAT number, please check the following:1. Delivery to the NetherlandsIf your delivery address is in the Netherlands, VAT is always charged, even if you have a valid VAT number.2. Verify your VAT numberMake sure your VAT number is listed in My Account. If you have a valid VAT number but it isn't displayed, please contact us at helpdesk@yehwang.com.3. Check your delivery addressSelect your delivery address again and see if VAT is still applied. If it is, please contact us at helpdesk@yehwang.com for assistance.
### 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 would like to pay VAT on your orders and receive VAT-inclusive invoices, please contact us before placing your first order. We'll update your account accordingly so VAT is applied to your future orders and invoices.
+63
View File
@@ -0,0 +1,63 @@
---
title: Shipping & Delivery (in-stock / NL warehouse)
category: shipping
source: yehwang.com/shipment.html (official FAQ, verbatim)
source_url:
- https://www.yehwang.com/shipment.html
last_updated: 2026-06-25
keywords: [shipping, delivery, PostNL, DHL, shipping cost, free shipping, dispatch time, cut-off 3pm, track and trace, Brexit, UK, import duties, VAT refund, lost package, transit time]
---
# Shipping & Delivery
> 📎 来源 / Source: https://www.yehwang.com/shipment.html · 抓取 2026-06-25
> Applies to in-stock orders shipped from our Netherlands warehouse. For items shipped from our factories, see [07-factory-direct](07-factory-direct.md).
### 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 checkout. 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, 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: What shipping costs do you charge?
Yehwang ships worldwide from the Netherlands via PostNL and DHL. Shipping costs are displayed during checkout after you select your destination country. Shipping fees are calculated based on your destination and the weight of your package, ensuring fair and transparent rates.Here's how our shipping cost system works:Destination country: Shipping costs vary depending on your destination country. Each country has its own shipping rates based on distance and local delivery costs.Package weight: The weight of your package also affects the shipping cost, as heavier packages require more resources to transport.For orders of €100 or more (excl. VAT), shipping is free to the Netherlands, Belgium, Germany, and Luxembourg. For shipping rates to other countries, please see the table below. RegionFlat RateDepends on order destination, quantity & weightFree shipping for orders over (excl. VAT)Netherlands, Belgium, Germany, Luxembourg €100,00Denmark, Norway, Serbia, Switzerland €200.00Austria, Bulgaria, Croatie, Czech Republic, Estonia, Finland, France, Greece, Hungary, Ireland, Italy,Latvia, Lithuania, Poland, Portugal, Romania, Slovakia,Slovenia, Spain, UK €200.00Cyprus €500.00US, Canada €1000.00Rest of Europe €1000.00Guadeloupe, Martinique €1000.00Asia €1000.00Africa, Oceania, Russia, Central and South-America €1000.00 For shipments outside the EU, any applicable customs clearance fees, VAT, and import duties are the responsibility of the recipient.You can also collect your order from our headquarters in Ede, the Netherlands, Monday to Friday, between 3:00 pm and 5:00 pm.The exact shipping cost will be shown in your shopping cart during checkout. If you have any questions about how shipping costs are calculated, please feel free to contact our customer service team.
> ⚠️ 待核对: 官网有按国家/重量的运费表与免邮门槛,需业务确认后补全到此处。
### Q: When will my order arrive / how fast do you dispatch?
Orders received **before 3 PM on business days are shipped within 1–3 business days**. We don't ship on weekends; orders placed Friday after 3 PM ship the following Monday. During clearance periods dispatch may take a little longer. After shipment you receive an email with a **Track and Trace** link. Transit times vary by country — check the carrier's site (via the tracking link) for an up-to-date ETA.
### Q: Will I receive a track and trace code?
Yes, you'll receive a Track & Trace code by email from PostNL. Missing it? Contact helpdesk@yehwang.com with your order number.
### Q: How does Brexit affect shipping to the UK?
We still ship to the UK; Brexit does not stop this. However, on orders over **GBP 135** (excl. VAT and shipping), UK customs may charge import duties and taxes. These costs are paid by the customer — Yehwang does not cover them.
### Q: My package hasn't been delivered. What now?
Contact us at **helpdesk@yehwang.com** with your order number. We'll open an investigation with the carrier (this can take **1–3 business days**). If the carrier declares the package lost, we refund the purchase amount once there is evidence/confirmation from the carrier that it was not delivered.
### Q: When will my order arrive?
Orders that we receive before 3 P.M. on business days will be shipped within 1–3 business days. We do not ship orders on weekends. Orders placed after 3 P.M. on Friday will be shipped the following Monday.During clearance periods, orders may not be shipped within the usual 1–3 business days and may be delayed by a few additional days.After your order has been shipped, you will receive an email containing a Track & Trace link so you can track the status of your order.Please note that transit times vary by country. Please consult the carrier's website using the Track & Trace link provided to get the most up-to-date estimated delivery time.Global DHL transit times can be found here. You can also find an indication of global transit times for PostNL here.Please note that PostNL transit times are estimates only and may occasionally be delayed by 1–2 weeks, depending on the delivery address.
### Q: How does the Brexit affect shipping
Yehwang 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 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?
VAT must be paid upfront when placing your order. Once your order has been delivered to your Dutch forwarder, you will receive a proof of export from the forwarder confirming that the goods have left the Netherlands.Please send a copy of this document, along with your IBAN number, to helpdesk@yehwang.com. Once we receive the required documents, we will process your VAT refund as soon as possible.
### Q: My package has not been delivered, what now?
How unfortunate that your package was not delivered. You may contact us via helpdesk@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.
### Q: How does Brexit affect shipping?
Yehwang ships worldwide, and Brexit does not affect our ability to ship to the UK. We continue to deliver orders to UK customers as usual.However, for orders over GBP 135 (excluding VAT and shipping costs), UK customs may charge import duties and taxes.These additional costs are the responsibility of the customer and are not covered by Yehwang.
### Q: My package has not been delivered. What should I do?
We are sorry to hear that your package has not been delivered. Please contact us at helpdesk@yehwang.com and include your order number so we can assist you.We will start an investigation with the carrier to locate your missing package. The investigation usually takes 1–3 business days.If the carrier confirms that the package has been lost, we will refund the purchase amount. The refund will be processed once we have received confirmation and supporting evidence from the carrier that the package was not delivered.
+54
View File
@@ -0,0 +1,54 @@
---
title: Returns & Refunds (in-stock / NL warehouse)
category: returns
source: yehwang.com/returns.html (official FAQ, verbatim)
source_url:
- https://www.yehwang.com/returns.html
last_updated: 2026-06-25
keywords: [return, refund, defective, incorrect, missing item, complaint, returns application, store credit, custom product, Ede, return shipping]
---
# Returns & Refunds
> 📎 来源 / Source: https://www.yehwang.com/returns.html · 抓取 2026-06-25
> Applies to in-stock orders from our NL warehouse. Factory-Direct returns differ — see [07-factory-direct](07-factory-direct.md).
### Q: I received my order but an item is defective or incorrect. What should I do?
Submit a **return request through your account**. We assess the request and, once approved, email you further information. If we confirm a manufacturing defect or wrong delivery, **we reimburse the return shipping cost**. You return the product to our headquarters in **Ede**. In some cases the product need not be returned and we reimburse directly as **store credit** or to your original payment method. Note: you **cannot** create a return request in your account for **discounted items** — instead email customer service.
### Q: How do I file a complaint / return request?
1. Go to **'My orders'** in your Yehwang account and select the order.
2. Click **'Returns Application'** and fill in the product quantity and return reason.
3. If the item is defective or incorrect, attaching a **photo is mandatory**.
After submitting, you can track it under the **'Returns'** section in 'My orders'. We respond by email **within 3 working days**, and the status changes from 'Pending' to 'Approved' or 'Rejected'.
### Q: An item from my order is missing. What should I do?
Go to **'My orders'**, select the order, click **'Returns Application'**, and choose **'Not received'** as the reason. Track it under 'Returns'. We respond within **3 working days**. If approved, **store credit is issued to your account immediately**.
### Q: Can I return / get a refund for a custom (personalized) product?
Customized products are made to your specifications and are personalized/exclusive, so they generally **do not accept unconditional returns or exchanges**. Returns/exchanges are accepted only for:
- ✓ Product quality issues (material defects, manufacturing errors, damage, etc.)
- ✓ Products significantly different from the order description / customized specifications
- ✓ Damage caused during shipping
Please read the full custom-product return policy carefully before ordering.
### Q: I received my order but one of the items is defective or incorrect. What should I do?
If you have received a defective or incorrect product, please submit a return request through your account. We will review your request and, once approved, you will receive an email with further instructions.If we confirm that the item has a manufacturing defect or was delivered incorrectly, we will reimburse the return shipping costs. You can return the product to our headquarters in Ede, the Netherlands.In some cases, returning the product may not be necessary. In this situation, we may issue a refund directly as store credit or through your original payment method.For discounted items:Return requests cannot be created through your account for discounted products. If you have received a defective or incorrect discounted item, please contact our customer service team at helpdesk@yehwang.com. Please include your order number, product item number, and photos of the issue.If a product does not meet your expectations but is not defective or incorrect, you may still return it. However, the return shipping costs will be your responsibility.Return requirementsReturned products must meet the following conditions:Products must be returned within 7 days of receipt. Please check your order carefully upon delivery.Items must be returned in their original condition and packaging.If products are not returned in their original condition and packaging, a 20% processing fee may be charged.Sale items are excluded from the return policy.Please note that complaints regarding products that you have already sold to your own customers cannot be reimbursed by Yehwang. These products cannot be returned.Returned items that do not meet the above requirements will be refused and sent back to the sender at the sender's expense.
### Q: How do I notify you of a complaint about my order?
Go to ‘My orders’ in your Yehwang account and select the order that you’re not happy with. Then hit the ‘Returns Application’ button and fill in the product quantity and return reason. In case you submit a return request because you received a defective or incorrect item it will be mandatory to attach a picture of this item.After you submit your return request you will be able to retrieve this order in the ‘Returns’ section within ‘My orders’. We will get back to you by email within 3 working days after submitting your request. You will also notice that the status of your request will have changed from ‘Pending’ to ‘Approved’ or ‘Rejected’. With this status change you will be notified whether products need to be returned to us or not.In case products do need to be returned to us this needs to be done within the following 7 days. Products need to be returned in their original state and packaging. If products are not returned in the original packaging a 20% handling fee will be charged. Items Please fill in the courier name and tracking number in your return request. The return costs are for your own account.After the products have been received by us store credit plus any possible shipping fees will be issued to your Yehwang account within 3 working days. In case defective or incorrect items do not need to be returned to us store credit will be issued immediately. You can now arrange your return fully online! Click here to read the return process.
### Q: I received my order but one of the items is missing. What should I do?
Go to “My Orders” in your Yehwang account and select the order with the missing item. Click the “Returns Application” button and enter the missing product quantity. Select “Not received” as the return reason.After submitting your return request, you can find this request under the “Returns” section in “My Orders”. We will review your request and respond by email within 3 business days. The status of your request will change from “Pending” to “Approved” or “Rejected”.If your request is approved, store credit will be issued to your Yehwang account immediately.You can now manage your return requests fully online.
### Q: I want to request a refund for a custom product. What should I do?
Customized Product Return PolicyThank you for choosing our customized product service. Because customized products are manufactured according to your specific requirements and are personalized and exclusive, our return policy differs from that of regular products. Please carefully read the following return policy before placing an order:1. Return ScopeCustomized products generally do not accept unconditional returns or exchanges.Returns or exchanges for customized products are only accepted in the following circumstances: ✓Product quality issues (such as material defects, manufacturing errors, damage, etc.).✓Products received significantly differ from the order description or customized specifications.✓Damage caused during shipping (please take photos and contact customer service immediately).2. Return Request PeriodIf there are quality issues or discrepancies with the product, please contact customer service within 7 days of receiving the product to request a return.Failure to submit a return request after 7 days will be deemed acceptance of the product and will not be accepted for return.3. Return ProcessPlease provide your order number, product photos, and a description of the problem when requesting a return.We will review your return request within 2 business days of receiving it and provide you with the follow-up procedures. After confirmation that the return conditions are met, the customer must return the item in good condition to the designated address.4. Return FeesIf the return is due to product quality issues or incorrect shipment, the seller will bear the return shipping costs.If the return is due to personal reasons (such as changes to custom requirements, mistaken orders, etc.), the customer will bear the return shipping costs and related expenses.5. Refund InstructionsOnce the returned item is confirmed to be correct and in good condition, we will process the refund within 7 business days.The refund amount is generally the purchase price, excluding the deposit (if the contract states that the deposit is non-refundable) and shipping costs (unless due to quality issues).For customized products involving mold costs or exclusive design fees, some refunds may not be refunded. The details will be subject to the contract or order confirmation. 6. Special NotesDue to the special nature of customized products, we recommend that you carefully confirm the design, specifications, quantity, and other information before placing an order. We recommend that you inspect the goods in person upon receipt and confirm that they are correct before signing for them. If there are any product defects, please retain the packaging and accessories for return or exchange. If you have any questions, please feel free to contact our customer service team at service@yehwang.com. We will be happy to assist you.
### Q: How do I submit a return request for my order?
To submit a return request, go to “My Orders” in your Yehwang account and select the order you are not satisfied with. Click the “Returns Application” button and provide the product quantity and reason for the return.If you submit a return request because you received a defective or incorrect item, you must attach a photo of the product.After submitting your return request, you can find the request under the “Returns” section in “My Orders”. We will review your request and respond by email within 3 business days. The status of your request will also change from “Pending” to “Approved” or “Rejected”. This status will indicate whether the products need to be returned to us.If a return is required, the products must be sent back within 7 days after approval. Items must be returned in their original condition and packaging. If products are not returned in their original packaging, a 20% handling fee will be charged.Please enter the courier name and tracking number in your return request. When sending the returned products, please make sure to include your order number inside the package so that we can identify and process your return correctly. Return shipping costs are the responsibility of the customer.Once we receive the returned products, store credit and any applicable shipping fees will be issued to your Yehwang account within 3 business days. If defective or incorrect items do not need to be returned, store credit will be issued immediately.You can now arrange your return fully online.
+92
View File
@@ -0,0 +1,92 @@
---
title: Factory-Direct (ships from our factories worldwide)
category: factory-direct
source: yehwang.com/factory-direct.html (official FAQ, verbatim)
source_url:
- https://www.yehwang.com/factory-direct.html
last_updated: 2026-06-25
keywords: [factory direct, factory-direct, wholesale pricing, MOQ, custom, processing time, transit time, express, standard shipping, customs, tracking, missing items, quality, all sales final, 7 days]
---
# Factory-Direct
> 📎 来源 / Source: https://www.yehwang.com/factory-direct.html · 抓取 2026-06-25
> Factory-Direct orders ship **directly from our manufacturing facilities** (not the NL warehouse) to customers worldwide. Contact for Factory-Direct: **service@yehwang.com**.
### Q: What is the Factory-Direct advantage?
We ship your order directly from our manufacturing facilities to your location. By removing the middleman and domestic warehousing, we offer: **wholesale pricing** (cost savings passed to you), **exclusive access** (first to see newest designs), and **quality oversight** (direct handling from production line to packaging).
### Q: What products are available via Factory-Direct?
Three main categories:
- **Finished Jewelry & Accessories** — earrings, necklaces, rings, bracelets, jewelry sets, plus hair accessories, phone accessories, bags, and trending apparel.
- **DIY & Jewelry Making** — beads, pendants, and raw materials/components.
- **Packing & Display** — packaging and display solutions.
### Q: Is there a Minimum Order Quantity (MOQ)?
- **Standard inventory**: most jewelry, accessories, and DIY materials start from **just 1 piece**; the specific MOQ per item is listed in its product details.
- **Custom products**: MOQ varies by design complexity and materials. Email **service@yehwang.com** with your design ideas, preferred materials, and estimated quantity — we provide a tailored quote and MOQ requirement **within 24 hours**.
### Q: What are Factory-Direct shipping times and rates?
Delivery has two stages:
- **Processing time**: 1–3 business days (quality inspection, sorting, packaging).
- **Transit time**: Express shipping **5–10 business days**; Standard shipping **10–20 business days**.
These are estimates; high-volume seasons or custom orders may need more time. Shipping cost for customized/bulk packaging orders is calculated at checkout by weight. You'll receive a tracking number/link by email once dispatched (allow 24–48 hours for it to update).
### Q: Will I pay customs/duties on Factory-Direct orders?
Since items travel internationally from our factories, customs handling applies per destination. (Tracking and customs information is provided with your shipment.)
### Q: I haven't received my Factory-Direct order. What now?
First: check your tracking link, check "hidden" delivery spots (neighbors, building manager, safe places), and wait 24 hours (carriers sometimes mark "Delivered" early). If tracking hasn't updated within **3 business days (or 7 days for international orders)** after the expected delivery date, contact **service@yehwang.com**.
### Q: My Factory-Direct order is missing items. What should I do?
Check the packaging carefully (small jewelry/DIY items are often tucked into protective pouches or inside larger items), and verify the packing slip for backordered or separately-shipped items. If still missing, email **service@yehwang.com within 48 hours** with your order number, a photo of the shipping label and box contents, and the name of the missing item.
### Q: How do you handle quality issues on Factory-Direct orders?
Every item passes a quality check before packing. If you receive a defect, **report within 7 days** of delivery and email **service@yehwang.com** with your order number and a clear photo (e.g. missing stones, chipped coating, defective DIY components). Depending on the case we provide a prepaid return label for a replacement or a full refund.
### Q: What is the Factory-Direct return & exchange policy?
Due to the cost and complexity of international shipping and customs, **all sales are final** — we do not accept returns or exchanges for non-quality reasons (e.g. change of mind). However:
- **Defective or damaged items**: notify within **7 days** for a full refund or free replacement.
- **Incorrect orders** (wrong item, color, or size): we correct it immediately.
### Q: The China Warehouse Advantage
At Yehwang, we ship factory-direct from our China warehouse, cutting out the middleman and local warehousing to offer:Wholesale Pricing: significant cost savings passed directly to you.Exclusive Access: be among the first to see our latest collections.Quality Oversight: consistent quality from production to packaging.
### Q: What products do you offer?
We offer a curated selection of fashion and creative essentials across three main categories:Finished Jewelry & Accessories: A wide range of earrings, necklaces, rings, bracelets, and coordinated jewelry sets. We also offer hair accessories, phone accessories, bags, and trending apparel.DIY & Jewelry Making: High-quality components for creators, including beads, pendants, and raw materials.Packing & Display: Professional packaging and display solutions—perfect for organizing your collection or showcasing products for sale.
### Q: Do you have a Minimum Order Quantity (MOQ)?
We aim to be flexible for both individual creators and large-scale retailers:Standard Inventory: Most of our jewelry, accessories, and DIY materials are available starting from just 1 piece. The specific MOQ for each item is clearly listed in its product details on our website.Custom Products: For custom designs or personalized apparel and jewelry, the MOQ varies based on the complexity of the design and the materials used.Interested in a custom project? Please contact our support team at service@yehwang.comwith your design ideas, preferred materials, and estimated quantity. We will provide a tailored quote and MOQ requirement within 24 hours.
### Q: Shipping, Tracking & Customs Information
Transportation destination and delivery timeWe offer worldwide shipping to most countries. Since your items are traveling internationally from our factories, delivery is divided into two stages: Processing Time: 1–3 business days. This includes quality inspection, sorting, and packaging.Transit Time: Depending on your location and chosen shipping method, transit typically takes:Express Shipping: 5–10 business daysStandard Shipping: 10–20 business daysNetherlands Transit Shipping: 10–25 business days (Orders are first shipped to our warehouse in the Netherlands before final delivery to your address.)Note: These timelines are estimates. High-volume seasons or custom orders may require additional time.Tracking Your OrderTransparency is key. Once your order has been dispatched, you will receive an email containing a tracking number and a link to our logistics partner’s portal. Please allow 24–48 hours for the tracking information to update as your package passes through the first sorting center.Customs, Duties, and TaxesBecause we ship directly from our international factories, your order may be subject to import duties, taxes, or customs fees once the package arrives in your country.Responsibility: All customs fees and taxes are the responsibility of the customer.Refused Shipments: If a shipment is refused due to unpaid customs charges, the customer will be responsible for the original shipping costs as well as any return fees incurred.""Final Sale"" Logistics ReminderAs noted in our Return Policy, we do not accept returns for a ""change of mind"" due to the high cost and complexity of international return logistics. We encourage you to review your order carefully before finalizing your purchase.If your package arrives with a quality defect, please refer to our [Quality Resolution Process] within 7 days of delivery.Address AccuracyPlease ensure that your shipping address is entered correctly in English. We cannot be held responsible for orders delivered to an incorrect address provided by the customer, or for packages that are undeliverable due to incomplete information.
### Q: What are your shipping times and rates?
We offer worldwide shipping to ensure you receive your creative supplies wherever you are.Processing Time: Orders are typically packed and shipped within 1–3 business days.Express Shipping: Estimated delivery within 5–10 business daysStandard Shipping: Estimated delivery within 10–20 business daysNetherlands Transit Shipping: 10–25 business daysTracking: You will receive a tracking number via email as soon as your package leaves our warehouse.Note: For customized or bulk packaging orders, shipping costs are calculated at checkout based on weight to ensure the fairest pricing.
### Q: What if I haven't received my order?
We’re sorry to hear your package hasn't arrived yet! Before contacting us, please try these quick steps:Check Your Tracking: Click the link in your shipping confirmation email to see the latest status and the estimated delivery date.Check the "Hidden" Spots: Occasionally, carriers leave packages with neighbors, building managers, or in "safe places" out of sight (e.g., behind a porch pillar).Wait 24 Hours: Sometimes a package is marked as "Delivered" by the carrier up to 24 hours before it actually arrives.Still no sign of it? If your tracking hasn’t updated within 3 business days (or 7 days for international orders) after the expected delivery date, please contact us at service@yehwang.comTip: Please include your Order Number in the subject line so we can investigate and resolve this for you immediately.
### Q: What should I do if my order is missing items?
We sincerely apologize for the oversight! We want to get your items to you as quickly as possible. Please follow these steps:Check the Packaging Carefully: Our DIY materials and small jewelry items are often tucked into smaller protective pouches or inside larger item pockets (e.g., inside a bag) to keep them safe during transit.Verify the Packing Slip: Check your packing slip to see if any items were marked as ""backordered"" or shipped in a separate parcel.Contact Us Within 48 Hours: If the item is definitely missing, please email us at service@yehwang.com with:Your Order NumberA photo of the shipping label and the contents of the box you receivedThe name of the missing itemOur Promise: Once we verify the error, we will either ship the missing item to you immediately at no extra cost or issue a full refund for that product—whichever you prefer!
### Q: How do you handle quality issues?
At Yehwang, we take pride in our carefully curated collections. Every item undergoes a strict quality check before it is packed. However, if you receive a product that does not meet our high standards, we are committed to making it right.If you find a defect, please follow these steps:Report within 7 days: Notify us of any damage or quality issues within 7 days of delivery.Send a Photo: Email service@yehwang.comwith your Order Number and a clear photo of the issue (e.g., missing stones, chipped coating, or defective DIY components).Our Solution: Depending on the situation and your preference, we will provide a prepaid return label for a replacement or issue a full refund for the affected item.What counts as a quality issue?Functional defects (e.g., broken zippers, non-working clasps)Structural damage (e.g., missing stones, broken chains)Receiving the wrong color or size from what was orderedPlease note: Due to the nature of handmade DIY materials and natural stones, slight variations in color or texture are normal and add to the unique character of each piece.
### Q: What is your return and exchange policy?
Due to the complexities and high costs associated with international shipping and customs, all sales are final. We do not accept returns or exchanges for non-quality-related reasons, such as a change of mind or personal preference.Our Commitment to You: While we cannot accept standard returns, your satisfaction is our priority. We stand behind the quality of our products:Defective or Damaged Items: If your order arrives with a quality issue, please notify us within 7 days. We will provide a full refund or a replacement at no cost to you.Incorrect Orders: If we sent you the wrong item, color, or size, we will correct it immediately.Shopping Tip: To ensure you are satisfied with your selection, please review product descriptions and dimensions carefully. If you have questions about a specific material, fit, or color, contact our customer service team before placing your order—we are happy to provide additional details or photos.
+57
View File
@@ -0,0 +1,57 @@
---
title: Membership tiers, volume discounts & rewards
category: membership
source: yehwang.com/membership (verbatim) + order.html + catalog snapshot 2026-06-25
source_url:
- https://www.yehwang.com/membership-307802756
- https://www.yehwang.com/order.html
- "数仓表 wa_dim.dim_user_full (快照 2026-06-25)"
last_updated: 2026-06-25
keywords: [membership, loyalty, tier, level, Bronze, Silver, Gold, Platinum, Diamond, VIP, discount, volume discount, quantity discount, rewards, spending threshold, upgrade]
---
# Membership, Volume Discounts & Rewards
> 📎 来源 / Source: https://www.yehwang.com/membership-307802756 · https://www.yehwang.com/order.html · 数据 `wa_dim.dim_user_full` · 抓取 2026-06-25
### Q: What membership tiers do you have and how do I level up?
Membership has six levels based on **cumulative historical spending** (excluding VAT and shipping). As soon as you hit a new threshold, your account is **automatically upgraded**. Each tier adds a member-bonus discount:
| Tier | Cumulative spend | Member bonus |
|---|---|---|
| Bronze | €1+ | 0% |
| Silver | €200+ | 1% |
| Gold | €1,000+ | 2% |
| Platinum | €4,000+ | 3% |
| Diamond | €10,000+ | 4% |
| VIP | €20,000+ | 5% |
(New accounts start as a New Member before reaching Bronze. Some special accounts — e.g. key accounts ("KA") — may have custom arrangements.)
### Q: What volume discounts do you offer (per order total)?
Volume discounts are based on the **total value of a single order**:
| Order total | Volume discount |
|---|---|
| €200+ | 5% |
| €500+ | 7% |
| €1,000+ | 10% |
| €2,000+ | 15% |
| €3,000+ | 20% |
| €5,000+ | 22% |
| €10,000+ | 25% |
### Q: How do the discounts stack?
The system applies discounts **sequentially**: the **volume discount** (based on order total) is applied first, then your **member bonus** (0–5% by tier) is applied on top, to maximize your savings.
### Q: Do you offer quantity discounts on the same product?
Yes — in addition to the order-total volume discount, ordering more of the **same product** earns extra:
- **5–9 pieces**: +3%
- **10–19 pieces**: +5%
- **20+ pieces**: +7%
### Q: How do order rewards work?
To reward loyal customers, the platform returns rewards on each order, which can be **accumulated**. Detailed rules are on the order rewards page on our website.
### Q: Is there a welcome discount for new members?
Yes — new registrants can receive a welcome discount (advertised as **up to 15% off**) on early orders.
+94
View File
@@ -0,0 +1,94 @@
---
title: Products catalog, materials & jewelry care
category: products
source: yehwang.com/other-questions (verbatim) + catalog snapshot 2026-06-25
source_url:
- https://www.yehwang.com/other-questions-1987986538
- "数仓表 wa_dim.dim_product_full (快照 2026-06-25)"
last_updated: 2026-06-25
keywords: [catalog, categories, price range, materials, stainless steel, gold plated, 304, 316L, nickel free, discolor, waterproof, jewelry care, selling price, minimum price, 300% margin, Gimme]
---
# Products, Materials & Jewelry Care
> 📎 来源 / Source: https://www.yehwang.com/other-questions-1987986538 · 数据 `wa_dim.dim_product_full` · 抓取 2026-06-25
## Catalog overview (live products, snapshot 2026-06-25)
| Category | Live products | Typical price (EUR) |
|---|---|---|
| Women's jewelry | ~72,800 | median ~€2.75 (€0–233) |
| Accessories | ~24,000 | median ~€2.27 |
| Clothes | ~5,000 | median ~€13.95 |
| Packing & Display | ~1,600 | median ~€1.00 |
| Other / Sale jewelry | ~970 | median ~€4.00 |
| Sports & Outdoor | ~670 | median ~€10.40 |
| Beauty & Makeup tools | ~410 | median ~€2.00 |
| High-end jewelry | ~360 | median ~€22.50 |
| Men's jewelry | ~170 | median ~€5.95 |
Top sub-categories: Earrings, Pendants, Necklaces, Bracelets, Piercing jewelry, Rings, Hair accessories, Jewelry-making supplies, Scarves, Bag charms, Glasses, Women's tops, Phone accessories, Bags.
Most-used materials: **Stainless Steel** (by far the largest), Polyester, Acrylic, Natural Stones, Titanium Alloy, PVC, Raffia.
### Q: What materials are your jewelry made of?
We use a variety of materials; we particularly favor **gold-plated metal** and **stainless steel** jewelry. The gold layer on top is **2.5 microns thick**, in 14K yellow, white, or rose gold (jewelry made before 2020 used an 18K gold layer). This layer is also applied over stainless steel; the color depends on the gold plating. Our stainless steel is **304 or 316L**. **All our jewelry is nickel free.**
### Q: Does your jewelry discolor?
**Stainless steel** jewelry is **waterproof and does not discolor** — you can shower, swim, and spray perfume on it. Other materials such as **silver 925, alloy, and copper are not waterproof** and may discolor with daily use; we do not guarantee against discoloration of silver 925, alloy, and copper.
### Q: Can I keep wearing the jewelry while playing sports or sleeping?
Although our jewelry is durable, we advise **not** wearing it while showering, sleeping, or during sports. To care for it, avoid contact with water, hairspray, and body lotion. In some cases your skin's pH can negatively affect the jewelry and cause discoloration.
### Q: Am I free to set my own selling prices?
Each product has a **minimum selling price** described on its page; you are expected to maintain this and not undercut it. We use a **300% margin** to determine the suggested selling price.
### Q: Do you offer logo-free / white-label jewelry?
Yes — Yehwang's new jewelry from mid-2024 onward has **no logos** on packaging or labels, so you can sell it under your own brand. (See also [02-ordering](02-ordering.md).)
### Q: May I use your images?
Yes! All customers may use our visuals — product shots, model images, and Instagram photos. Tag us if you share them on social media!Images of items you've purchased can be downloaded per order as a .ZIP file from your account, or individually from the website by right-clicking and saving.
### Q: What materials are used to make your jewelry?
We use a variety of materials to make our jewelry. Personally, we love gold-plated (metal with a thin layer of gold) and stainless-steel jewelry. The thin layer of gold applied to the top of the metal is 2.5 microns thick and consists of 14K yellow gold, white gold, or rose gold. (All jewelry made before 2020 is covered with a 18K gold layer) This layer is also applied over stainless steel jewelry, and the color of this jewelry is determined by the color of the gold plating. The stainless steel jewelry is made of 304 or 316L steel. All our jewelry is nickel free.
### Q: Does our jewelry discolor?
We have jewelry made of different materials. The jewelry made of stainless steel is waterproof and does not discolor. You can shower, swim and spray perfume on it. The other materials such as silver 925, alloy and copper are not waterproof and may discolor with daily use. We do not guarantee any discoloration of the materials silver 925, alloy and copper.
### Q: Can I keep wearing your jewelry while playing sports or sleeping?
The durability of our jewelry depends on the material. Stainless steel jewelry is waterproof and resistant to discoloration, so it can be worn during daily activities such as showering or exercising.However, to keep your jewelry in the best condition for longer, we recommend avoiding prolonged exposure to water, sweat, hairspray, body lotion, and other cosmetic products.In some cases, the natural pH level of your skin may also affect the jewelry and cause discoloration over time.
### Q: Am I free to determine my selling prices?
Yes, within limits — each product has a minimum selling price (using a 300% margin) that you should maintain rather than undercut.
### Q: What is Gimme?
Gimme is Yehwang's own clothing label. This brand is known for its trendy and stylish designs that perfectly align with the latest fashion trends. Gimme offers a wide range of clothing items carefully selected to meet the needs of fashion-conscious customers. Whether you are looking for casual or chic clothing, with Gimme you will always find something that suits you.
### Q: May I use your images?
Yes! All our customers are free to use our visual materials. This includes product images as well as images of products shot on models as well as any pictures posted on our Instagram account. If you use our images on social media channels, we’d love to be tagged!Images of items you have purchased through our website are listed per order and can be downloaded from your account. This means you can quickly download a .ZIP file containing all images of items included in your order.It’s also possible to download these images individually from our website by right-clicking on the images and saving them on your computer.
### Q: What materials are used to make your jewelry?
We use a variety of materials to make our jewelry. Personally, we love gold-plated (metal with a thin layer of gold) and stainless-steel jewelry. The thin layer of gold applied to the top of the metal is 2.5 microns thick and consists of 14K yellow gold, white gold, or rose gold. (All jewelry made before 2020 is covered with a 18K gold layer) This layer is also applied over stainless steel jewelry, and the color of this jewelry is determined by the color of the gold plating. The stainless steel jewelry is made of 304 or 316L steel. All our jewelry is nickel free.
### Q: Does our jewelry discolor?
We have jewelry made of different materials. The jewelry made of stainless steel is waterproof and does not discolor. You can shower, swim and spray perfume on it. The other materials such as silver 925, alloy and copper are not waterproof and may discolor with daily use. We do not guarantee any discoloration of the materials silver 925, alloy and copper.
### Q: Can I keep wearing your jewelry while playing sports or sleeping?
Although the quality of our jewelry is durable, we advise our customers not to wear our jewelry while showering or sleeping, or during sports or similar activities. The best way to care for your jewelry is to avoid contact with water, hairspray, and body lotion. In some cases, the pH level of your skin can have a negative effect on the jewelry and may cause discoloration.
### Q: Am I free to determine my selling prices?
We have a minimum selling price, which is described with each product. It is your intention to maintain this price and not to undercut the minimum sale price. We use a 300% margin to determine your selling price.
### Q: What is Gimme?
Gimme is Yehwang's own clothing label. This brand is known for its trendy and stylish designs that perfectly align with the latest fashion trends. Gimme offers a wide range of clothing items carefully selected to meet the needs of fashion-conscious customers. Whether you are looking for casual or chic clothing, with Gimme you will always find something that suits you.
### Q: Will our jewelry discolor?
Our jewelry is made from different materials, and the durability of each material varies.Jewelry made from stainless steel is waterproof and does not discolor. You can wear it while showering, swimming, or applying perfume.Other materials, such as 925 sterling silver, alloy, and copper, are not waterproof and may discolor over time with daily use. Please note that we cannot guarantee that these materials will remain free from discoloration.
+491
View File
@@ -0,0 +1,491 @@
---
title: Yehwang Website Category Map (Site Map)
category: site-map
source: www.yehwang.com sitemap.xml (auto-generated)
generated: true
source_url:
- https://www.yehwang.com/sitemap.xml
last_updated: 2026-08-14
---
Every Yehwang category page has a direct URL. Use these links to send customers straight to the right page instead of a search query.
### Q: Where can I find accessories?
Browse our accessories category: https://www.yehwang.com/accessories
### Q: Where can I find bags?
Browse our bags category: https://www.yehwang.com/accessories/bags
### Q: Where can I find bag charms?
Browse our bag charms category: https://www.yehwang.com/accessories/bags/bag-charms
### Q: Where can I find clutches?
Browse our clutches category: https://www.yehwang.com/accessories/bags/clutches
### Q: Where can I find make up bags?
Browse our make up bags category: https://www.yehwang.com/accessories/bags/make-up-bags
### Q: Where can I find shopper bags?
Browse our shopper bags category: https://www.yehwang.com/accessories/bags/shopper-bags
### Q: Where can I find shoulder bags?
Browse our shoulder bags category: https://www.yehwang.com/accessories/bags/shoulder-bags
### Q: Where can I find wholesale beach bags?
Browse our wholesale beach bags category: https://www.yehwang.com/accessories/bags/wholesale-beach-bags
### Q: Where can I find fan?
Browse our fan category: https://www.yehwang.com/accessories/fan
### Q: Where can I find gloves?
Browse our gloves category: https://www.yehwang.com/accessories/gloves
### Q: Where can I find hair accessories?
Browse our hair accessories category: https://www.yehwang.com/accessories/hair-accessories
### Q: Where can I find extensions?
Browse our extensions category: https://www.yehwang.com/accessories/hair-accessories/extensions
### Q: Where can I find hair bands?
Browse our hair bands category: https://www.yehwang.com/accessories/hair-accessories/hair-bands
### Q: Where can I find hair clips?
Browse our hair clips category: https://www.yehwang.com/accessories/hair-accessories/hair-clips
### Q: Where can I find hair ties?
Browse our hair ties category: https://www.yehwang.com/accessories/hair-accessories/hair-ties
### Q: Where can I find scrunchies?
Browse our scrunchies category: https://www.yehwang.com/accessories/hair-accessories/scrunchies
### Q: Where can I find hats and caps?
Browse our hats and caps category: https://www.yehwang.com/accessories/hats-and-caps
### Q: Where can I find keychain?
Browse our keychain category: https://www.yehwang.com/accessories/keychain
### Q: Where can I find phone cases?
Browse our phone cases category: https://www.yehwang.com/accessories/phone-accessories-226/phone-cases
### Q: Where can I find phone cords?
Browse our phone cords category: https://www.yehwang.com/accessories/phone-accessories-226/phone-cords
### Q: Where can I find scarves?
Browse our scarves category: https://www.yehwang.com/accessories/scarves
### Q: Where can I find scarves.htmls?
Browse our scarves.htmls category: https://www.yehwang.com/accessories/scarves.htmls
### Q: Where can I find scarf clips?
Browse our scarf clips category: https://www.yehwang.com/accessories/scarves/scarf-clips
### Q: Where can I find summer scarves?
Browse our summer scarves category: https://www.yehwang.com/accessories/scarves/summer-scarves
### Q: Where can I find winter scarves?
Browse our winter scarves category: https://www.yehwang.com/accessories/scarves/winter-scarves
### Q: Where can I find shoe clips?
Browse our shoe clips category: https://www.yehwang.com/accessories/shoe-clips
### Q: Where can I find socks?
Browse our socks category: https://www.yehwang.com/accessories/socks
### Q: Where can I find sunglasses?
Browse our sunglasses category: https://www.yehwang.com/accessories/sunglasses
### Q: Where can I find bestsellers?
Browse our bestsellers category: https://www.yehwang.com/bestsellers
### Q: Where can I find bestsellers accessories?
Browse our bestsellers accessories category: https://www.yehwang.com/bestsellers/bestsellers-accessories
### Q: Where can I find bestsellers clothes?
Browse our bestsellers clothes category: https://www.yehwang.com/bestsellers/bestsellers-clothes
### Q: Where can I find bestsellers jewelry?
Browse our bestsellers jewelry category: https://www.yehwang.com/bestsellers/bestsellers-jewelry
### Q: Where can I find brands?
Browse our brands category: https://www.yehwang.com/brands
### Q: Where can I find club spice?
Browse our club spice category: https://www.yehwang.com/brands/club-spice
### Q: Where can I find lilli june?
Browse our lilli june category: https://www.yehwang.com/brands/lilli-june
### Q: Where can I find lume?
Browse our lume category: https://www.yehwang.com/brands/lume
### Q: Where can I find yehwang?
Browse our yehwang category: https://www.yehwang.com/brands/yehwang
### Q: Where can I find clothes?
Browse our clothes category: https://www.yehwang.com/clothes
### Q: Where can I find blazers?
Browse our blazers category: https://www.yehwang.com/clothes/blazers
### Q: Where can I find blouses?
Browse our blouses category: https://www.yehwang.com/clothes/blouses
### Q: Where can I find clothing collections?
Browse our clothing collections category: https://www.yehwang.com/clothes/clothing-collections
### Q: Where can I find fall and winter clothes?
Browse our fall and winter clothes category: https://www.yehwang.com/clothes/clothing-collections/fall-and-winter-clothes
### Q: Where can I find summer clothes?
Browse our summer clothes category: https://www.yehwang.com/clothes/clothing-collections/summer-clothes
### Q: Where can I find denim?
Browse our denim category: https://www.yehwang.com/clothes/denim
### Q: Where can I find dresses?
Browse our dresses category: https://www.yehwang.com/clothes/dresses
### Q: Where can I find midi dresses?
Browse our midi dresses category: https://www.yehwang.com/clothes/dresses/midi-dresses
### Q: Where can I find mini dresses?
Browse our mini dresses category: https://www.yehwang.com/clothes/dresses/mini-dresses
### Q: Where can I find jackets?
Browse our jackets category: https://www.yehwang.com/clothes/jackets
### Q: Where can I find jeans?
Browse our jeans category: https://www.yehwang.com/clothes/jeans
### Q: Where can I find ripped jeans?
Browse our ripped jeans category: https://www.yehwang.com/clothes/jeans-3580/ripped-jeans
### Q: Where can I find straight jeans?
Browse our straight jeans category: https://www.yehwang.com/clothes/jeans/straight-jeans
### Q: Where can I find wide leg jeans?
Browse our wide leg jeans category: https://www.yehwang.com/clothes/jeans/wide-leg-jeans
### Q: Where can I find jumpsuits?
Browse our jumpsuits category: https://www.yehwang.com/clothes/jumpsuits
### Q: Where can I find knitwear and sweaters?
Browse our knitwear and sweaters category: https://www.yehwang.com/clothes/knitwear-and-sweaters
### Q: Where can I find sweaters?
Browse our sweaters category: https://www.yehwang.com/clothes/knitwear-and-sweaters/sweaters
### Q: Where can I find shorts?
Browse our shorts category: https://www.yehwang.com/clothes/shorts
### Q: Where can I find skirts?
Browse our skirts category: https://www.yehwang.com/clothes/skirts
### Q: Where can I find midi skirts?
Browse our midi skirts category: https://www.yehwang.com/clothes/skirts/midi-skirts
### Q: Where can I find mini skirts?
Browse our mini skirts category: https://www.yehwang.com/clothes/skirts/mini-skirts
### Q: Where can I find tops?
Browse our tops category: https://www.yehwang.com/clothes/tops
### Q: Where can I find long sleeve tops?
Browse our long sleeve tops category: https://www.yehwang.com/clothes/tops/long-sleeve-tops
### Q: Where can I find short sleeve tops?
Browse our short sleeve tops category: https://www.yehwang.com/clothes/tops/short-sleeve-tops
### Q: Where can I find sleeveless tops?
Browse our sleeveless tops category: https://www.yehwang.com/clothes/tops/sleeveless-tops
### Q: Where can I find t shirts?
Browse our t shirts category: https://www.yehwang.com/clothes/tops/t-shirts
### Q: Where can I find vest?
Browse our vest category: https://www.yehwang.com/clothes/tops/vest
### Q: Where can I find trousers?
Browse our trousers category: https://www.yehwang.com/clothes/trousers
### Q: Where can I find flaired pants?
Browse our flaired pants category: https://www.yehwang.com/clothes/trousers/flaired-pants
### Q: Where can I find skinny pants?
Browse our skinny pants category: https://www.yehwang.com/clothes/trousers/skinny-pants
### Q: Where can I find straight pants?
Browse our straight pants category: https://www.yehwang.com/clothes/trousers/straight-pants
### Q: Where can I find wide leg pants?
Browse our wide leg pants category: https://www.yehwang.com/clothes/trousers/wide-leg-pants
### Q: Where can I find dropshipping?
Browse our dropshipping category: https://www.yehwang.com/dropshipping
### Q: Where can I find factory direct wholesale en?
Browse our factory direct wholesale en category: https://www.yehwang.com/factory-direct-wholesale-en
### Q: Where can I find jewelry?
Browse our jewelry category: https://www.yehwang.com/jewelry
### Q: Where can I find anklets?
Browse our anklets category: https://www.yehwang.com/jewelry/Anklets
### Q: Where can I find advent calendars?
Browse our advent calendars category: https://www.yehwang.com/jewelry/advent-calendars
### Q: Where can I find bracelets?
Browse our bracelets category: https://www.yehwang.com/jewelry/bracelets
### Q: Where can I find bangles?
Browse our bangles category: https://www.yehwang.com/jewelry/bracelets/bangles
### Q: Where can I find bangles and cuffs bracelet?
Browse our bangles and cuffs bracelet category: https://www.yehwang.com/jewelry/bracelets/bangles-and-cuffs-bracelet
### Q: Where can I find beaded bracelets?
Browse our beaded bracelets category: https://www.yehwang.com/jewelry/bracelets/beaded-bracelets
### Q: Where can I find bracelets sets?
Browse our bracelets sets category: https://www.yehwang.com/jewelry/bracelets/bracelets-sets
### Q: Where can I find braided bracelets?
Browse our braided bracelets category: https://www.yehwang.com/jewelry/bracelets/braided-bracelets
### Q: Where can I find chain bracelets?
Browse our chain bracelets category: https://www.yehwang.com/jewelry/bracelets/chain-bracelets
### Q: Where can I find charm bracelets?
Browse our charm bracelets category: https://www.yehwang.com/jewelry/bracelets/charm-bracelets
### Q: Where can I find chunky bracelets?
Browse our chunky bracelets category: https://www.yehwang.com/jewelry/bracelets/chunky-bracelets
### Q: Where can I find hand chain bracelets?
Browse our hand chain bracelets category: https://www.yehwang.com/jewelry/bracelets/hand-chain-bracelets
### Q: Where can I find brooches?
Browse our brooches category: https://www.yehwang.com/jewelry/brooches
### Q: Where can I find cufflinks?
Browse our cufflinks category: https://www.yehwang.com/jewelry/cufflinks
### Q: Where can I find display set en?
Browse our display set en category: https://www.yehwang.com/jewelry/display-set-en
### Q: Where can I find anklets display set?
Browse our anklets display set category: https://www.yehwang.com/jewelry/display-set-en/anklets-display-set
### Q: Where can I find bracelets display set?
Browse our bracelets display set category: https://www.yehwang.com/jewelry/display-set-en/bracelets-display-set
### Q: Where can I find earrings display set?
Browse our earrings display set category: https://www.yehwang.com/jewelry/display-set-en/earrings-display-set
### Q: Where can I find necklace display set?
Browse our necklace display set category: https://www.yehwang.com/jewelry/display-set-en/necklace-display-set
### Q: Where can I find rings display set?
Browse our rings display set category: https://www.yehwang.com/jewelry/display-set-en/rings-display-set
### Q: Where can I find earrings?
Browse our earrings category: https://www.yehwang.com/jewelry/earrings
### Q: Where can I find beads earrings?
Browse our beads earrings category: https://www.yehwang.com/jewelry/earrings/beads-earrings
### Q: Where can I find birthstone earrings?
Browse our birthstone earrings category: https://www.yehwang.com/jewelry/earrings/birthstone-earrings
### Q: Where can I find drop earrings?
Browse our drop earrings category: https://www.yehwang.com/jewelry/earrings/drop-earrings
### Q: Where can I find ear cuffs?
Browse our ear cuffs category: https://www.yehwang.com/jewelry/earrings/ear-cuffs
### Q: Where can I find ear studs?
Browse our ear studs category: https://www.yehwang.com/jewelry/earrings/ear-studs
### Q: Where can I find earring set?
Browse our earring set category: https://www.yehwang.com/jewelry/earrings/earring-set
### Q: Where can I find earrings with stone?
Browse our earrings with stone category: https://www.yehwang.com/jewelry/earrings/earrings-with-stone
### Q: Where can I find hoop earrings?
Browse our hoop earrings category: https://www.yehwang.com/jewelry/earrings/hoop-earrings
### Q: Where can I find party earrings?
Browse our party earrings category: https://www.yehwang.com/jewelry/earrings/party-earrings
### Q: Where can I find studs?
Browse our studs category: https://www.yehwang.com/jewelry/earrings/studs
### Q: Where can I find jewelry collections?
Browse our jewelry collections category: https://www.yehwang.com/jewelry/jewelry-collections
### Q: Where can I find acrylic and resin collection?
Browse our acrylic and resin collection category: https://www.yehwang.com/jewelry/jewelry-collections/acrylic-and-resin-collection
### Q: Where can I find beach collection?
Browse our beach collection category: https://www.yehwang.com/jewelry/jewelry-collections/beach-collection
### Q: Where can I find flowers collection?
Browse our flowers collection category: https://www.yehwang.com/jewelry/jewelry-collections/flowers-collection
### Q: Where can I find fruits and plants collection?
Browse our fruits and plants collection category: https://www.yehwang.com/jewelry/jewelry-collections/fruits-and-plants-collection
### Q: Where can I find hearts collection?
Browse our hearts collection category: https://www.yehwang.com/jewelry/jewelry-collections/hearts-collection
### Q: Where can I find jewelry parts?
Browse our jewelry parts category: https://www.yehwang.com/jewelry/jewelry-parts
### Q: Where can I find jewelry beads?
Browse our jewelry beads category: https://www.yehwang.com/jewelry/jewelry-parts/jewelry-beads
### Q: Where can I find jewelry charms?
Browse our jewelry charms category: https://www.yehwang.com/jewelry/jewelry-parts/jewelry-charms
### Q: Where can I find jewelry working supplies?
Browse our jewelry working supplies category: https://www.yehwang.com/jewelry/jewelry-parts/jewelry-working-supplies
### Q: Where can I find men s bracelets?
Browse our men s bracelets category: https://www.yehwang.com/jewelry/men-s-bracelets
### Q: Where can I find men s jewelry?
Browse our men s jewelry category: https://www.yehwang.com/jewelry/men-s-jewelry
### Q: Where can I find men s necklaces?
Browse our men s necklaces category: https://www.yehwang.com/jewelry/men-s-necklaces
### Q: Where can I find navel piercings?
Browse our navel piercings category: https://www.yehwang.com/jewelry/navel-piercings
### Q: Where can I find necklaces?
Browse our necklaces category: https://www.yehwang.com/jewelry/necklaces
### Q: Where can I find beaded necklaces?
Browse our beaded necklaces category: https://www.yehwang.com/jewelry/necklaces/beaded-necklaces
### Q: Where can I find braided necklaces?
Browse our braided necklaces category: https://www.yehwang.com/jewelry/necklaces/braided-necklaces
### Q: Where can I find chain necklaces?
Browse our chain necklaces category: https://www.yehwang.com/jewelry/necklaces/chain-necklaces
### Q: Where can I find layered necklace?
Browse our layered necklace category: https://www.yehwang.com/jewelry/necklaces/layered-necklace
### Q: Where can I find long necklaces?
Browse our long necklaces category: https://www.yehwang.com/jewelry/necklaces/long-necklaces
### Q: Where can I find medium necklaces?
Browse our medium necklaces category: https://www.yehwang.com/jewelry/necklaces/medium-necklaces
### Q: Where can I find necklaces sets?
Browse our necklaces sets category: https://www.yehwang.com/jewelry/necklaces/necklaces-sets
### Q: Where can I find pendant necklaces?
Browse our pendant necklaces category: https://www.yehwang.com/jewelry/necklaces/pendant-necklaces
### Q: Where can I find rings?
Browse our rings category: https://www.yehwang.com/jewelry/rings
### Q: Where can I find birthstone rings?
Browse our birthstone rings category: https://www.yehwang.com/jewelry/rings/birthstone-rings
### Q: Where can I find gemstone rings?
Browse our gemstone rings category: https://www.yehwang.com/jewelry/rings/gemstone-rings
### Q: Where can I find minimalist rings?
Browse our minimalist rings category: https://www.yehwang.com/jewelry/rings/minimalist-rings
### Q: Where can I find rhinestone rings?
Browse our rhinestone rings category: https://www.yehwang.com/jewelry/rings/rhinestone-rings
### Q: Where can I find rings with sizes?
Browse our rings with sizes category: https://www.yehwang.com/jewelry/rings/rings-with-sizes
### Q: Where can I find stainless steel jewelry?
Browse our stainless steel jewelry category: https://www.yehwang.com/jewelry/stainless-steel-jewelry
### Q: Where can I find new?
Browse our new category: https://www.yehwang.com/new
### Q: Where can I find pre order?
Browse our pre order category: https://www.yehwang.com/new/pre-order
### Q: Where can I find packing and display?
Browse our packing and display category: https://www.yehwang.com/packing-and-display
### Q: Where can I find wholesale display materials?
Browse our wholesale display materials category: https://www.yehwang.com/packing-and-display/wholesale-display-materials
### Q: Where can I find displays for bracelets?
Browse our displays for bracelets category: https://www.yehwang.com/packing-and-display/wholesale-display-materials/displays-for-bracelets
### Q: Where can I find displays for earrings?
Browse our displays for earrings category: https://www.yehwang.com/packing-and-display/wholesale-display-materials/displays-for-earrings
### Q: Where can I find displays for necklaces?
Browse our displays for necklaces category: https://www.yehwang.com/packing-and-display/wholesale-display-materials/displays-for-necklaces
### Q: Where can I find displays for rings?
Browse our displays for rings category: https://www.yehwang.com/packing-and-display/wholesale-display-materials/displays-for-rings
### Q: Where can I find jewelry trays?
Browse our jewelry trays category: https://www.yehwang.com/packing-and-display/wholesale-display-materials/jewelry-trays
### Q: Where can I find wholesale packing materials?
Browse our wholesale packing materials category: https://www.yehwang.com/packing-and-display/wholesale-packing-materials
### Q: Where can I find restock request?
Browse our restock request category: https://www.yehwang.com/restock-request
### Q: Where can I find top deal?
Browse our top deal category: https://www.yehwang.com/top-deal
### Q: Where can I find 0 12 zone?
Browse our 0 12 zone category: https://www.yehwang.com/top-deal/12-12-sale/0-12-zone
### Q: Where can I find daily deals?
Browse our daily deals category: https://www.yehwang.com/top-deal/daily-deals
### Q: Where can I find new in sale?
Browse our new in sale category: https://www.yehwang.com/top-deal/new-in-sale
### Q: Where can I find 1040045430 50 off?
Browse our 1040045430 50 off category: https://www.yehwang.com/top-deal/new-in-sale/1040045430-50-off
### Q: Where can I find 20 30 off?
Browse our 20 30 off category: https://www.yehwang.com/top-deal/new-in-sale/20-30-off
### Q: Where can I find 2113133623 20 off?
Browse our 2113133623 20 off category: https://www.yehwang.com/top-deal/new-in-sale/2113133623-20-off
### Q: Where can I find 30 40 off?
Browse our 30 40 off category: https://www.yehwang.com/top-deal/new-in-sale/30-40-off
### Q: Where can I find 40 50 off?
Browse our 40 50 off category: https://www.yehwang.com/top-deal/new-in-sale/40-50-off
### Q: Where can I find sale en?
Browse our sale en category: https://www.yehwang.com/top-deal/sale-en
### Q: Where can I find accessories?
Browse our accessories category: https://www.yehwang.com/top-deal/sale-en/accessories
### Q: Where can I find jewelry?
Browse our jewelry category: https://www.yehwang.com/top-deal/sale-en/jewelry
### Q: Where can I find packing and display?
Browse our packing and display category: https://www.yehwang.com/top-deal/sale-en/packing-and-display
### Q: Where can I find wholesale service?
Browse our wholesale service category: https://www.yehwang.com/wholesale-service
### Q: Where can I find private label?
Browse our private label category: https://www.yehwang.com/wholesale-service/private-label
+81
View File
@@ -0,0 +1,81 @@
'use strict';
const fs = require('fs');
const path = require('path');
function stripHtml(value) {
return String(value || '')
.replace(/<br\s*\/?>/gi, '\n')
.replace(/<[^>]+>/g, ' ')
.replace(/&nbsp;/g, ' ')
.replace(/&amp;/g, '&')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&quot;/g, '"')
.replace(/\s+/g, ' ')
.trim();
}
function extractFaqFromLdJson(html, max = 50) {
const re = /<script[^>]*type=["']application\/ld\+json["'][^>]*>([\s\S]*?)<\/script>/gi;
let m;
while ((m = re.exec(String(html)))) {
try {
const data = JSON.parse(m[1].trim());
const candidates = Array.isArray(data) ? data : [data];
for (const item of candidates) {
const main = item && (item.mainEntity || (item['@type'] === 'FAQPage' && item.mainEntity));
if (!Array.isArray(main)) continue;
const out = main
.filter((q) => q && q.name && q.acceptedAnswer && q.acceptedAnswer.text)
.map((q) => ({ q: stripHtml(q.name), a: stripHtml(q.acceptedAnswer.text) }))
.filter((q) => q.q && q.a)
.slice(0, max);
if (out.length) return out;
}
} catch {
// 非 JSON-LD 块忽略
}
}
return [];
}
function loadFaqConfig(file) {
try {
return JSON.parse(fs.readFileSync(file, 'utf8'));
} catch {
return { enabled: false, pages: [] };
}
}
function createFaqFetcher(config = {}) {
const pages = config.pages || [];
const timeoutMs = config.timeoutMs || 15000;
const max = config.maxFaqsPerPage || 50;
async function fetchPage(url) {
const res = await fetch(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' },
signal: AbortSignal.timeout(timeoutMs),
});
if (!res.ok) throw new Error(`fetch ${url} failed: HTTP ${res.status}`);
return extractFaqFromLdJson(await res.text(), max);
}
async function fetchAll() {
const out = [];
for (const page of pages) {
try {
const questions = await fetchPage(page.url);
out.push({ ...page, questions });
} catch (err) {
out.push({ ...page, questions: [], error: err.message });
}
}
return out;
}
return { fetchPage, fetchAll, pages };
}
module.exports = { createFaqFetcher, loadFaqConfig, extractFaqFromLdJson, stripHtml };
+72
View File
@@ -0,0 +1,72 @@
'use strict';
const fs = require('fs');
function escapeRe(value) {
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function normalizeQuestion(value) {
return String(value || '').toLowerCase().replace(/[^\p{L}\p{N}]+/gu, ' ').trim();
}
// 用官网 FAQ 更新本地 markdown:保留 frontmatter、章节标题和 Q 顺序,
// 只替换已有问题的答案,新问题追加到文件末尾。
function updateMarkdownFile(filePath, questions) {
if (!fs.existsSync(filePath)) return { changed: false, reason: 'file_missing', added: 0, updated: 0 };
let content = fs.readFileSync(filePath, 'utf8');
const bodyStart = (content.match(/^---\n[\s\S]*?\n---\n/) || [''])[0].length;
const body = content.slice(bodyStart);
const existing = new Map();
let currentQ = null;
let answer = [];
const flush = () => {
if (currentQ) existing.set(normalizeQuestion(currentQ), { question: currentQ, answer: answer.join('\n').trim() });
};
for (const line of body.split('\n')) {
const q = line.match(/^###\s+Q:\s*(.*)$/);
if (q) {
flush();
currentQ = q[1].trim();
answer = [];
} else if (currentQ) {
answer.push(line);
}
}
flush();
let changed = false;
let added = 0;
let updated = 0;
const additions = [];
for (const item of questions) {
if (!item.q || !item.a) continue;
const key = normalizeQuestion(item.q);
if (existing.has(key)) {
const prev = existing.get(key);
if (prev.answer !== item.a) {
const re = new RegExp(`(### Q: ${escapeRe(prev.question)}\\n)([\\s\\S]*?)(?=\\n### Q:|$)`, 'm');
if (re.test(content)) {
content = content.replace(re, (_m, prefix) => `${prefix}${item.a}\n`);
prev.answer = item.a;
changed = true;
updated += 1;
}
}
} else {
existing.set(key, { question: item.q, answer: item.a });
additions.push(`### Q: ${item.q}\n${item.a}\n`);
added += 1;
changed = true;
}
}
if (additions.length) {
content = content.replace(/\s+$/, '') + '\n\n' + additions.join('\n');
}
if (changed) fs.writeFileSync(filePath, content, 'utf8');
return { changed, added, updated };
}
module.exports = { updateMarkdownFile };
+89
View File
@@ -0,0 +1,89 @@
'use strict';
// KB Markdown 分块:与旧 bridge rag/chunker.js 保持一致的 parent/child 结构。
const MAX_CHILD_CHARS = 500;
function parseFrontmatter(text) {
const m = String(text).match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
if (!m) return { meta: {}, body: String(text) };
const meta = {};
const fm = m[1].split('\n');
for (let i = 0; i < fm.length; i += 1) {
const kv = fm[i].match(/^(\w+):\s*(.*)$/);
if (!kv) continue;
const key = kv[1];
let value = kv[2].trim();
if (value.startsWith('[')) {
value = value.replace(/[\[\]]/g, '').split(',').map((s) => s.trim().replace(/^["']|["']$/g, '')).filter(Boolean);
} else if (value === '') {
const arr = [];
while (i + 1 < fm.length && /^\s+-\s+/.test(fm[i + 1])) {
i += 1;
const item = fm[i].replace(/^\s+-\s+/, '').trim().replace(/^["']|["']$/g, '');
if (item) arr.push(item);
}
value = arr;
} else {
value = value.replace(/^["']|["']$/g, '');
}
meta[key] = value;
}
return { meta, body: m[2] };
}
function splitChunks(body) {
const chunks = [];
let current = null;
let section = '';
for (const line of String(body).split('\n')) {
const h2 = line.match(/^##\s+(.*)$/);
if (h2) { section = h2[1].trim(); continue; }
const q = line.match(/^###\s+Q:\s*(.*)$/);
if (q) {
if (current) chunks.push(current);
current = { question: q[1].trim(), answer: '', section };
} else if (current) {
current.answer += line + '\n';
}
}
if (current) chunks.push(current);
return chunks.map((c) => ({ ...c, answer: c.answer.trim() }));
}
function sourceUrls(meta) {
const raw = meta.source_urls || meta.source_url || [];
return (Array.isArray(raw) ? raw : [raw]).map(String).filter((u) => /^https?:\/\//.test(u));
}
function createParentChildChunks(chunk, meta, sourceFile) {
const keywords = Array.isArray(meta.keywords) ? meta.keywords : [];
const embText = [keywords.join(' '), chunk.question, chunk.answer].filter(Boolean).join('\n');
const parent = {
source_file: sourceFile,
title: meta.title || '',
category: meta.category || '',
section: chunk.section,
question: chunk.question,
answer: chunk.answer,
keywords,
source_urls: sourceUrls(meta),
chunk_type: 'parent',
embText,
};
if (chunk.answer.length <= MAX_CHILD_CHARS) return { parent, children: [] };
const children = (chunk.answer.match(new RegExp(`[^\\n]{1,${MAX_CHILD_CHARS}}`, 'g')) || [chunk.answer]).map((part) => ({
source_file: sourceFile,
title: meta.title || '',
category: meta.category || '',
section: chunk.section,
question: chunk.question,
answer: part,
keywords,
source_urls: sourceUrls(meta),
chunk_type: 'child',
embText: [keywords.join(' '), chunk.question, part].filter(Boolean).join('\n'),
}));
return { parent, children };
}
module.exports = { parseFrontmatter, splitChunks, createParentChildChunks, sourceUrls, MAX_CHILD_CHARS };
+180
View File
@@ -0,0 +1,180 @@
'use strict';
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const { parseFrontmatter, splitChunks, createParentChildChunks } = require('./kb-chunker');
// 新 bridge 独立 KB 索引器:读本地 kb/*.md,生成 parent/child + embedding,
// staging 表校验后原子切换 kb_docs,旧表保留为 kb_docs_v<timestamp>。
function createKbIndexer({ pool, embeddingClient, kbDir, dim = 1024, logger = console }) {
const DIM = Number(dim) || 1024;
const KB_DIR = kbDir || path.resolve(__dirname, '..', 'kb');
async function ensureBase() {
await pool.query('CREATE EXTENSION IF NOT EXISTS vector');
await pool.query(`
CREATE TABLE IF NOT EXISTS kb_sync_runs (
id SERIAL PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
source_hash TEXT,
parent_count INT,
child_count INT,
backup_table TEXT,
note TEXT
)
`);
}
function listFiles() {
return fs.readdirSync(KB_DIR)
.filter((f) => f.endsWith('.md') && /^\d+/.test(f))
.sort();
}
function sourceHash(files) {
const h = crypto.createHash('sha256');
for (const f of files) {
h.update(f);
h.update(fs.readFileSync(path.join(KB_DIR, f)));
}
return h.digest('hex');
}
async function createStaging() {
await pool.query('DROP TABLE IF EXISTS kb_docs_staging CASCADE');
await pool.query(`
CREATE TABLE kb_docs_staging (
id SERIAL PRIMARY KEY,
source_file TEXT,
title TEXT,
category TEXT,
section TEXT,
question TEXT,
answer TEXT,
keywords TEXT[],
source_urls TEXT[],
text_vec vector(${DIM}),
parent_id INTEGER,
chunk_type TEXT DEFAULT 'parent',
keywords_text TEXT,
text_tsv tsvector
)
`);
}
async function insertDoc(table, doc, parentId) {
const text = [doc.title, doc.category, doc.section, doc.question, doc.answer, (doc.keywords || []).join(' ')].filter(Boolean).join(' ');
const r = await pool.query(
`INSERT INTO ${table}
(source_file,title,category,section,question,answer,keywords,source_urls,text_vec,parent_id,chunk_type,keywords_text,text_tsv)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9::vector,$10,$11,$12,to_tsvector('english',$13))
RETURNING id`,
[doc.source_file, doc.title, doc.category, doc.section, doc.question, doc.answer,
doc.keywords || [], doc.source_urls || [], embeddingClient.vecStr(doc.vec), parentId || null,
doc.chunk_type, (doc.keywords || []).join(' '), text]
);
return r.rows[0].id;
}
async function indexFile(table, file) {
const raw = fs.readFileSync(path.join(KB_DIR, file), 'utf8');
const { meta, body } = parseFrontmatter(raw);
const chunks = splitChunks(body);
let parents = 0;
let children = 0;
for (const chunk of chunks) {
const { parent, children: childChunks } = createParentChildChunks(chunk, meta, file);
parent.vec = await embeddingClient.embedQuery(parent.embText);
const parentId = await insertDoc(table, parent, null);
parents += 1;
for (const child of childChunks) {
child.vec = await embeddingClient.embedQuery(child.embText);
await insertDoc(table, child, parentId);
children += 1;
}
}
return { parents, children };
}
async function createStagingIndexes() {
await pool.query('CREATE INDEX kb_docs_staging_vec_idx ON kb_docs_staging USING hnsw (text_vec vector_cosine_ops)');
await pool.query('CREATE INDEX kb_docs_staging_tsv_idx ON kb_docs_staging USING GIN (text_tsv)');
await pool.query('CREATE INDEX kb_docs_staging_parent_idx ON kb_docs_staging (parent_id) WHERE parent_id IS NOT NULL');
}
async function atomicSwitch() {
const client = await pool.connect();
try {
await client.query('BEGIN');
const has = await client.query("SELECT to_regclass('public.kb_docs') IS NOT NULL AS exists");
let backup = null;
if (has.rows[0].exists) {
const stamp = new Date().toISOString().replace(/[-:TZ.]/g, '').slice(0, 14);
backup = `kb_docs_v${stamp}`;
let suffix = 0;
while ((await client.query('SELECT to_regclass($1) IS NOT NULL AS exists', [`public.${backup}`])).rows[0].exists) {
suffix += 1;
backup = `kb_docs_v${stamp}_${suffix}`;
}
await client.query(`ALTER TABLE kb_docs RENAME TO ${backup}`);
await client.query(`ALTER INDEX IF EXISTS kb_docs_vec_idx RENAME TO ${backup}_vec_idx`);
await client.query(`ALTER INDEX IF EXISTS kb_docs_tsv_idx RENAME TO ${backup}_tsv_idx`);
await client.query(`ALTER INDEX IF EXISTS kb_docs_parent_idx RENAME TO ${backup}_parent_idx`);
}
await client.query('ALTER TABLE kb_docs_staging RENAME TO kb_docs');
await client.query('ALTER INDEX IF EXISTS kb_docs_staging_vec_idx RENAME TO kb_docs_vec_idx');
await client.query('ALTER INDEX IF EXISTS kb_docs_staging_tsv_idx RENAME TO kb_docs_tsv_idx');
await client.query('ALTER INDEX IF EXISTS kb_docs_staging_parent_idx RENAME TO kb_docs_parent_idx');
await client.query('COMMIT');
return backup;
} catch (err) {
await client.query('ROLLBACK');
throw err;
} finally {
client.release();
}
}
async function reindex({ note = 'kb sync' } = {}) {
const lockClient = await pool.connect();
const lock = await lockClient.query("SELECT pg_try_advisory_lock(hashtext('bridge_kb_sync')) AS locked");
if (!lock.rows[0].locked) {
lockClient.release();
throw new Error('another KB sync is running');
}
try {
await ensureBase();
const files = listFiles();
if (!files.length) throw new Error(`no KB markdown files in ${KB_DIR}`);
const hash = sourceHash(files);
await createStaging();
let parents = 0;
let children = 0;
for (const file of files) {
const res = await indexFile('kb_docs_staging', file);
parents += res.parents;
children += res.children;
}
if (parents === 0) throw new Error('indexed 0 parent chunks');
await createStagingIndexes();
const backup = await atomicSwitch();
await pool.query(
'INSERT INTO kb_sync_runs (source_hash,parent_count,child_count,backup_table,note) VALUES ($1,$2,$3,$4,$5)',
[hash, parents, children, backup, note]
);
logger.log(JSON.stringify({ ts: new Date().toISOString(), kb_sync: 'done', parents, children, backup, files: files.length }));
return { parents, children, backup, sourceHash: hash, files: files.length };
} catch (err) {
await pool.query('DROP TABLE IF EXISTS kb_docs_staging CASCADE').catch(() => {});
throw err;
} finally {
await lockClient.query("SELECT pg_advisory_unlock(hashtext('bridge_kb_sync'))").catch(() => {});
lockClient.release();
}
}
return { reindex, listFiles, sourceHash, KB_DIR };
}
module.exports = { createKbIndexer };
+40
View File
@@ -0,0 +1,40 @@
'use strict';
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const os = require('os');
const path = require('path');
const { extractFaqFromLdJson } = require('../src/faq-fetcher');
const { updateMarkdownFile } = require('../src/faq-update');
test('faq: extracts FAQPage JSON-LD questions', () => {
const html = `<script type="application/ld+json">{"@type":"FAQPage","mainEntity":[{"name":"Q1?","acceptedAnswer":{"text":"A1"}},{"name":"Q2?","acceptedAnswer":{"text":"A2"}}]}</script>`;
const qs = extractFaqFromLdJson(html);
assert.deepEqual(qs, [{ q: 'Q1?', a: 'A1' }, { q: 'Q2?', a: 'A2' }]);
});
test('faq: updates existing answer and appends new question', () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'kb-faq-'));
const file = path.join(dir, '01.md');
fs.writeFileSync(file, '---\ntitle: T\n---\n### Q: Old?\nold answer\n');
const r = updateMarkdownFile(file, [{ q: 'Old?', a: 'new answer' }, { q: 'New?', a: 'new q answer' }]);
assert.equal(r.changed, true);
assert.equal(r.updated, 1);
assert.equal(r.added, 1);
const out = fs.readFileSync(file, 'utf8');
assert.match(out, /Old\?\nnew answer/);
assert.match(out, /New\?\nnew q answer/);
});
test('faq: normalized question match avoids duplicate append', () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'kb-faq-norm-'));
const file = path.join(dir, '01.md');
fs.writeFileSync(file, '---\ntitle: T\n---\n### Q: How does Brexit affect shipping\nold\n');
const r = updateMarkdownFile(file, [{ q: 'How does Brexit affect shipping?', a: 'new' }]);
assert.equal(r.added, 0);
assert.equal(r.updated, 1);
const out = fs.readFileSync(file, 'utf8');
assert.equal((out.match(/### Q:/g) || []).length, 1);
assert.match(out, /new/);
});
+18
View File
@@ -0,0 +1,18 @@
'use strict';
const test = require('node:test');
const assert = require('node:assert/strict');
const { parseFrontmatter, splitChunks, createParentChildChunks } = require('../src/kb-chunker');
test('kb chunker: parse frontmatter and split Q/A', () => {
const md = `---\ntitle: Test\ncategory: test\nkeywords: [ring, gold]\nsource_url: https://www.yehwang.com/x.html\n---\n## Section\n### Q: What is it?\nLong answer ${'x'.repeat(600)}\n`;
const { meta, body } = parseFrontmatter(md);
assert.equal(meta.title, 'Test');
assert.deepEqual(meta.keywords, ['ring', 'gold']);
const chunks = splitChunks(body);
assert.equal(chunks.length, 1);
const { parent, children } = createParentChildChunks(chunks[0], meta, '01-test.md');
assert.equal(parent.question, 'What is it?');
assert.equal(parent.source_urls[0], 'https://www.yehwang.com/x.html');
assert.equal(children.length, 2);
});
Binary file not shown.
+124
View File
@@ -0,0 +1,124 @@
# 环境差异:本地开发 / 测试 / 生产
> 结论:`rpc-tunnel.sh` 和 `public-https.sh` 是**本地开发联调工具**,测试/生产环境不应依赖它们。
## 1. 对比表
| 能力 | 本地开发(当前机器) | 测试 / 生产 |
|---|---|---|
| 业务 RPC | 用 SSH 隧道 `scripts/rpc-tunnel.sh`,因为 RPC 端口外网不可达 | **不需要隧道**,直接填内网 RPC 地址(服务 DNS / VPC / 专线) |
| RPC 地址 | `jsonrpc://<tunnel-gateway>:24503` 等隧道网关地址 | `jsonrpc://<rpc-host>:24503` 等真实内网地址 |
| 公网 HTTPS | Cloudflare Quick Tunnel + `scripts/public-https.sh` | **不需要 Quick Tunnel**,用正式域名 + Ingress/Nginx/ALB + TLS |
| WhatsApp webhook | 临时 Quick Tunnel URL,重启会变 | 正式域名,例如 `https://crm.example.com/webhooks/whatsapp/...` |
| 公网路径白名单 | `public-gateway`(本地 compose 服务) | 由 Ingress/Nginx 统一处理,或保留 gateway 作为内部服务 |
| Demo 页面 | `scripts/serve-web.sh`,:8088 | 不需要 |
| 数据库/Redis | compose 内 Postgres/Redis,宿主机端口仅绑 `127.0.0.1` | 使用托管数据库/Redis 或内网集群,不暴露宿主机端口 |
| 环境变量 | `.env` 指向本地/测试地址 | `.env` 指向测试/生产真实地址 |
| 密钥 | 本地 `.env` | Secret Manager / CI/CD Secret,不入库 |
## 2. RPC:本地隧道 vs 测试/生产直连
### 本地开发
RPC 端口外网不可达时,才需要:
```bash
bash scripts/rpc-tunnel.sh --apply
```
它会:
- 在本机建 SSH 隧道到远端 RPC
- 把 `.env` 的 RPC URL 改为隧道网关地址
- 重建 bridge
### 测试 / 生产
**不要运行 `rpc-tunnel.sh`。**
直接配置:
```env
RECOMMEND_URL=jsonrpc://<recommend-service>:24503
ORDER_API_URL=jsonrpc://<order-service>:24505
MEMBER_RPC_URL=jsonrpc://<member-service>:24506
INVENTORY_API_URL=jsonrpc://<inventory-service>:24509
```
要求:
- bridge 容器能直接访问这些地址
- 测试/生产网络放行对应端口
- 不要指向开发机的 SSH 隧道网关
如果测试/生产网络确实隔离,应该用 **VPC 对等连接 / 专线 / VPN / 内网反向代理**,而不是开发机上的 SSH 隧道。
## 3. 公网 HTTPS:Quick Tunnel vs 正式域名
### 本地开发
只有本地联调 WhatsApp/公网 webhook 时才需要:
```bash
bash scripts/public-https.sh
```
特点:
- Cloudflare Quick Tunnel
- URL 每次重启可能变化
- 需要重新更新 LibreDesk `root_url` 和 Meta webhook
- 只适合临时联调
### 测试 / 生产
**不要使用 Cloudflare Quick Tunnel。**
使用:
- 正式域名(`crm.example.com`)
- Ingress / Nginx / ALB / API Gateway
- 正式 TLS 证书
- 固定公网出口
配置:
```env
PUBLIC_BASE_URL=https://crm.example.com
```
并同步:
- LibreDesk `app.root_url`
- Meta WhatsApp callback URL
- `public-gateway` 的上游地址(如果保留)
### public-gateway 的去留
`services/public-gateway` 只负责路径白名单:
```
/webhooks/whatsapp/
/hc/
/widget*
/static/public/
```
测试/生产可以:
- 继续保留它,放在 Ingress 后面
- 或者把同样的路径规则直接写进 Ingress/Nginx
Quick Tunnel 只是本地把请求转发进来的手段,不是生产架构的一部分。
## 4. 测试/生产上线检查
- [ ] 未运行 `rpc-tunnel.sh`
- [ ] RPC URL 指向内网真实地址,不是 `172.23.0.1`
- [ ] 未运行 `public-https.sh`
- [ ] `PUBLIC_BASE_URL` 是正式 HTTPS 域名
- [ ] LibreDesk `root_url` 是正式域名
- [ ] Meta webhook 是正式域名
- [ ] 数据库/Redis 不暴露公网
- [ ] 不使用 `serve-web.sh` 的 8088 demo
- [ ] 密钥来自 Secret Manager / CI Secret,不在仓库和镜像里
+100 -15
View File
@@ -53,6 +53,17 @@ Email (inbox 2) / WhatsApp (inbox 3)
---
## 2.1 环境差异(本地开发 vs 测试/生产)
> 详见 [environments.md](environments.md)。
- `scripts/rpc-tunnel.sh`:**仅本地开发**,因为 RPC 端口外网不可达
- `scripts/public-https.sh`:**仅本地联调**,Cloudflare Quick Tunnel
- 测试/生产:RPC 直接配内网地址,公网 HTTPS 用正式域名 + Ingress/Nginx/ALB
- 测试/生产不应运行上述两个脚本
---
## 3. 访问入口
| 服务 | 地址 | 备注 |
@@ -97,13 +108,13 @@ Email (inbox 2) / WhatsApp (inbox 3)
- [ ] `.env` 已备份,且权限为 `600`
- [ ] `docker compose config -q` 通过
- [ ] RPC 隧道可用(商品/订单/库存)
- [ ] 本地开发:RPC 隧道可用;测试/生产:RPC 内网地址可直达
- [ ] LibreDesk webhook `Bridge AI` 为启用状态
- [ ] LibreDesk 规则 1 关闭
- [ ] 规则 4/5 指向 `Wholesale Support`
- [ ] agent1/agent2 可登录,必要时设为在线
- [ ] WhatsApp access token 未过期(当前 token 有到期时间)
- [ ] 公网 URL 与 Meta webhook 已同步
- [ ] 本地开发:Quick Tunnel 与 Meta webhook 已同步;测试/生产:正式域名与 Meta webhook 已同步
- [ ] 163 SMTP / IMAP 可用
- [ ] 已做数据库备份
- [ ] bridge 测试通过
@@ -151,10 +162,9 @@ node scripts/configure-email.js
### 6.3 数据准备
```bash
# 全量商品页站点地图(每周/商品大更新后)
node scripts/build-site-map.js www us
#### 有旧 bridge / 旧 products 库时
```bash
# 重建 KB parent-child 索引(KB 更新后;会先备份旧表)
bash scripts/rebuild-kb-index.sh
@@ -162,9 +172,80 @@ bash scripts/rebuild-kb-index.sh
node scripts/upgrade-kb-schema.js
```
### 6.4 RPC 隧道
#### 测试/生产没有旧 bridge 时(推荐)
商品/订单/库存依赖本机到内网的 RPC 隧道:
不要运行 `rebuild-kb-index.sh`。直接导入仓库里的 KB 快照:
```bash
# 默认导入到 crmnew-postgres / bridge 库
bash scripts/import-kb-snapshot.sh
# 或导入到 products 库
TARGET_PG_DB=products bash scripts/import-kb-snapshot.sh
# 验证
docker exec crmnew-postgres psql -U crmnew -d bridge -tAc \
"select count(*), count(*) filter (where chunk_type='parent'), count(*) filter (where chunk_type='child') from kb_docs;"
```
预期:
```
367 | 296 | 71
```
然后把测试环境 `.env` 的 `RAG_PG_URL` 指向该库,例如:
```env
RAG_PG_URL=postgres://crmnew:<password>@postgres:5432/bridge
```
> 快照文件:`deploy/seeds/kb_docs.sql.gz`(只包含 `kb_docs` 知识库,不含邮件/客户数据)
> 当前环境如需重新生成:`bash scripts/export-kb-snapshot.sh`
#### 在线 FAQ 同步(不依赖旧 bridge)
测试/生产如果网络可以访问官网 FAQ,可直接:
```bash
# 在宿主机执行时,RAG_PG_URL 用宿主机可达地址
RAG_PG_URL=postgres://crmnew:<password>@127.0.0.1:5440/bridge \
node scripts/sync-kb-online.js
# 如果脚本跑在 bridge 容器内,则用容器内地址
# RAG_PG_URL=postgres://crmnew:<password>@postgres:5432/bridge
```
建议 cron 每周一次:
```cron
0 3 * * 1 cd /home/dev/code/crm-new && RAG_PG_URL=... node scripts/sync-kb-online.js
```
如果官网抓取被 WAF 拦截,可先用本地源文件重建:
```bash
node scripts/sync-kb-online.js --local-only --force
```
注意:
- 需要 `RAG_PG_URL` 指向目标库
- 需要 `EMB_*`(DashScope embedding)
- 旧的 `kb_docs` 会备份为 `kb_docs_v<timestamp>`
- `bridge/kb/11-site-map.md` 目前是静态类目地图,不随官网自动更新
#### 全量商品页站点地图
```bash
node scripts/build-site-map.js www us
```
### 6.4 RPC 隧道(仅本地开发)
> 测试/生产不需要这一步;直接配置内网 RPC 地址即可。
本地 RPC 端口外网不可达时才需要:
```bash
bash scripts/rpc-tunnel.sh
@@ -172,7 +253,11 @@ bash scripts/rpc-tunnel.sh
隧道断了会导致 `search_products/search_orders/search_inventory` 失败。
### 6.5 公网 HTTPS / WhatsApp webhook
### 6.5 公网 HTTPS / WhatsApp webhook(仅本地联调)
> 测试/生产不要用 Cloudflare Quick Tunnel;使用正式域名 + Ingress/Nginx/ALB + TLS。
本地联调:
```bash
bash scripts/public-https.sh
@@ -308,10 +393,10 @@ bridge 关键日志:
### 外部依赖巡检
- RPC 隧道进程
- 本地开发:RPC 隧道进程
- 本地开发:Cloudflare tunnel 状态
- 测试/生产:内网 RPC 可达性、Ingress/证书、WhatsApp token 剩余有效期
- 163 SMTP/IMAP
- WhatsApp token 剩余有效期
- Cloudflare tunnel 状态
- 未分配/团队队列积压
---
@@ -386,8 +471,8 @@ docker compose stop bridge
### 高
1. **单点**:bridge 只有一个实例;bridge 挂了 Live chat 无 AI 回复。
2. **RPC 隧道**:依赖本机 SSH 隧道,断了商品/订单/库存工具不可用。
3. **公网 URL**:Cloudflare Quick Tunnel URL 不固定,重启可能失效。
2. **RPC 隧道(仅本地开发)**:依赖本机 SSH 隧道,断了商品/订单/库存工具不可用。测试/生产应直连内网 RPC。
3. **公网 URL(仅本地开发)**:Cloudflare Quick Tunnel URL 不固定,重启可能失效。测试/生产使用正式域名。
4. **WhatsApp token**:临时 token 有到期时间,需要提前续期。
5. **Email 历史数据为空**:`kb_mail_docs` 只有测试邮件,真实客户历史未 ingest。
@@ -453,8 +538,8 @@ TWENTY_BASE_URL / TWENTY_API_KEY
```
scripts/bootstrap.sh
scripts/rpc-tunnel.sh
scripts/public-https.sh
scripts/rpc-tunnel.sh # 仅本地开发
scripts/public-https.sh # 仅本地联调
scripts/build-site-map.js
scripts/rebuild-kb-index.sh
scripts/e2e-widget-jwt.js
+2 -1
View File
@@ -51,9 +51,10 @@ LIBREDESK_WEBHOOK_ID=1
- [x] 上下文摘要/压缩(>24 条,保留最近 16 条)
- [x] Prompt 注入防护(消息检测 + 工具结果信任边界)
- [x] 全量商品页站点地图(www 32,691 / us 22,625,商品页 32,051/22,305)
- [x] Hybrid RAG:text_tsv/parent_id/chunk_type,292 parent + 69 child,向量+全文+RRF+父块扩展
- [x] Hybrid RAG:text_tsv/parent_id/chunk_type,296 parent + 71 child,向量+全文+RRF+父块扩展
- [x] 购买意向 post-LLM 追问兜底(产品+数量触发,缺公司/联系方式时多语言硬追问)
- [x] 商机补齐:内容指纹去重、跨渠道复用/同步、阶段推进、CRM 故障 fail-closed
- [x] 在线 FAQ/KB 同步(不依赖旧 bridge,`sync-kb-online.js`)
- [x] `search_email_history`:已验证客户邮件历史,强制按 verified email scope
- [x] 发送前输出护栏:句子级自动重写 + URL 白名单 + 价格/库存数字必须有工具证据
- [x] Email/WhatsApp 人工接管:Wholesale Support 团队 + assign_team 规则(AI 不再分配)
+19 -7
View File
@@ -54,11 +54,21 @@ node scripts/setup-defaults.js
# Twenty:创建初始 workspace + 在库内生成 API key(不走 UI)
node scripts/twenty-bootstrap.js
# 重建 KB parent-child 索引(旧 bridge indexer + 回填 text_tsv,自动备份)
# 有旧 bridge 时:重建 KB parent-child 索引(自动备份)
bash scripts/rebuild-kb-index.sh
# 升级旧 kb_docs 支持 Hybrid RAG(text_tsv/parent_id/chunk_type)
node scripts/upgrade-kb-schema.js
# 没有旧 bridge 时:直接导入 KB 快照(测试/生产常用)
bash scripts/import-kb-snapshot.sh
# 或指定目标库:TARGET_PG_DB=products bash scripts/import-kb-snapshot.sh
# 在线 FAQ 同步 + 重建 KB(不依赖旧 bridge)
# 宿主机执行时 RAG_PG_URL 用 127.0.0.1:5440/bridge
RAG_PG_URL=postgres://crmnew:<password>@127.0.0.1:5440/bridge node scripts/sync-kb-online.js
# 仅用本地 bridge/kb 重建
node scripts/sync-kb-online.js --local-only --force
# 从当前环境重新生成快照(可选)
bash scripts/export-kb-snapshot.sh
# 全量商品页站点地图(官网 sitemap → bridge 库)
node scripts/build-site-map.js www us
@@ -77,10 +87,12 @@ node scripts/eval-offline.js
# 真实模型离线评测(fake LibreDesk,不写真实会话;会调 LLM/RPC)
node scripts/eval-live.js
# 公网 HTTPS(WhatsApp webhook 需要):Cloudflare Quick Tunnel + 路径白名单网关
# 公网 HTTPS(仅本地联调需要):Cloudflare Quick Tunnel + 路径白名单网关
# 测试/生产:不要跑这个脚本,使用正式域名 + Ingress/Nginx + TLS
bash scripts/public-https.sh
# 测试服 RPC 端口外网被封:建 SSH 隧道并让 bridge 指向隧道(网关 IP)
# 仅本地开发:测试服 RPC 端口外网被封时建 SSH 隧道
# 测试/生产:不要跑这个脚本,直接把 RPC URL 配成内网可达地址
bash scripts/rpc-tunnel.sh --apply
# 配置 163 SMTP(自动重启 LibreDesk)
@@ -168,6 +180,6 @@ ss -ltnp | grep -E ':(9000|3100|4100|5440|6390)\s'
| 管理员后台实时更新不工作 | 通过 http + IP 访问时 agent websocket 拒绝非 HTTPS 来源 | POC 可忽略;生产用 HTTPS 或经 localhost 访问 |
| collect_info 槽位丢失 | Redis 不可用,退回了进程内存 | 检查 `REDIS_URL=redis://redis:6379/3` 和 `docker logs crmnew-bridge` 的 lead store 告警 |
| 多站点商品推荐串站 | `bridge/config/sites.json` 的 inbox 映射没配 | 把对应 inbox 数字 ID 指到 us/eu/www profile,重建 bridge |
| WhatsApp webhook 收不到 | 没有公网 HTTPS / Meta 回调地址不对 | 跑 `bash scripts/public-https.sh`,把输出的 `.../webhooks/whatsapp/<inbox_id>` 配到 Meta;quick tunnel URL 每次重启会变 |
| WhatsApp webhook 收不到 | 没有公网 HTTPS / Meta 回调地址不对 | 本地联调跑 `bash scripts/public-https.sh`;测试/生产使用正式域名,不要用 Quick Tunnel |
| 公网访问到管理后台 | 网关白名单配置 | `services/public-gateway` 只放行 `/webhooks/whatsapp/*`、`/hc/*`、`/widget*`、`/static/public/*`,其余 404 |
| AI 回复"系统暂时无法查询订单" | 测试服 24503/24505/24506/24509 外网 TCP 被封,bridge 8s 超时 | 跑 `bash scripts/rpc-tunnel.sh --apply`,bridge RPC 指向 docker 网关(隧道只绑网关,不暴露 LAN) |
| AI 回复"系统暂时无法查询订单" | RPC 不可达,bridge 8s 超时 | 本地开发跑 `bash scripts/rpc-tunnel.sh --apply`;测试/生产检查内网 RPC 地址/网络策略,不要用开发机隧道 |
+13
View File
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
# 从已有环境导出 KB(kb_docs)快照,供没有旧 bridge 的测试/生产环境导入。
# 用法: bash scripts/export-kb-snapshot.sh [输出文件]
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
OUT="${1:-$ROOT/deploy/seeds/kb_docs.sql.gz}"
SRC_CONTAINER="${SOURCE_PG_CONTAINER:-crm-postgres}"
SRC_DB="${SOURCE_PG_DB:-products}"
SRC_USER="${SOURCE_PG_USER:-postgres}"
mkdir -p "$(dirname "$OUT")"
docker exec "$SRC_CONTAINER" pg_dump -U "$SRC_USER" -d "$SRC_DB" \
-t kb_docs --no-owner --no-privileges | gzip -9 > "$OUT"
echo "[export-kb] $SRC_CONTAINER/$SRC_DB.kb_docs -> $OUT ($(du -h "$OUT" | cut -f1))"
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
# 在没有旧 bridge 的环境导入 KB 快照(默认导入 crmnew-postgres/bridge)。
# 用法:
# bash scripts/import-kb-snapshot.sh [seed.sql.gz]
# FORCE=1 bash scripts/import-kb-snapshot.sh # 覆盖已有 kb_docs(会 DROP)
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
SEED="${1:-$ROOT/deploy/seeds/kb_docs.sql.gz}"
TARGET_CONTAINER="${TARGET_PG_CONTAINER:-crmnew-postgres}"
TARGET_DB="${TARGET_PG_DB:-bridge}"
TARGET_USER="${TARGET_PG_USER:-crmnew}"
if [ ! -f "$SEED" ]; then echo "seed not found: $SEED" >&2; exit 1; fi
# 确保数据库存在
if ! docker exec "$TARGET_CONTAINER" psql -U "$TARGET_USER" -d postgres -tAc \
"SELECT 1 FROM pg_database WHERE datname='$TARGET_DB'" | grep -q 1; then
docker exec "$TARGET_CONTAINER" psql -U "$TARGET_USER" -d postgres -c "CREATE DATABASE \"$TARGET_DB\""
fi
docker exec "$TARGET_CONTAINER" psql -U "$TARGET_USER" -d "$TARGET_DB" -c \
"CREATE EXTENSION IF NOT EXISTS vector;" >/dev/null
# 旧 dump 带 tsv trigger,先确保函数存在
docker exec -i "$TARGET_CONTAINER" psql -U "$TARGET_USER" -d "$TARGET_DB" >/dev/null <<'SQL'
CREATE OR REPLACE FUNCTION public.kb_docs_tsv_update() RETURNS trigger AS $$
BEGIN
NEW.text_tsv := to_tsvector('english',
coalesce(NEW.question,'') || ' ' || coalesce(NEW.answer,'') || ' ' ||
coalesce(array_to_string(NEW.keywords,' '),'') || ' ' ||
coalesce(NEW.title,'') || ' ' || coalesce(NEW.section,''));
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
SQL
EXISTS=$(docker exec "$TARGET_CONTAINER" psql -U "$TARGET_USER" -d "$TARGET_DB" -tAc \
"SELECT to_regclass('public.kb_docs') IS NOT NULL")
if [ "$EXISTS" = "t" ]; then
if [ "${FORCE:-0}" != "1" ]; then
echo "[import-kb] kb_docs already exists in $TARGET_DB; refusing to overwrite. Set FORCE=1 to drop it." >&2
exit 1
fi
echo "[import-kb] FORCE=1: dropping existing kb_docs in $TARGET_DB"
docker exec "$TARGET_CONTAINER" psql -U "$TARGET_USER" -d "$TARGET_DB" -c "DROP TABLE IF EXISTS kb_docs CASCADE" >/dev/null
fi
gunzip -c "$SEED" | docker exec -i "$TARGET_CONTAINER" psql -U "$TARGET_USER" -d "$TARGET_DB" -v ON_ERROR_STOP=1 >/dev/null
COUNT=$(docker exec "$TARGET_CONTAINER" psql -U "$TARGET_USER" -d "$TARGET_DB" -tAc "SELECT count(*) FROM kb_docs")
echo "[import-kb] imported kb_docs rows=$COUNT into $TARGET_CONTAINER/$TARGET_DB"
echo "[import-kb] 提示: bridge 的 RAG_PG_URL 应指向该库(容器内通常是 postgres://...@postgres:5432/$TARGET_DB)"
+2 -1
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
# 启动/重启公网 HTTPS(Cloudflare Quick Tunnel),并把 LibreDesk root_url 指过去。
# 仅本地联调:启动/重启公网 HTTPS(Cloudflare Quick Tunnel),并把 LibreDesk root_url 指过去。
# 测试/生产不要用 Quick Tunnel:使用正式域名 + Ingress/Nginx/ALB + TLS。
# 注意:quick tunnel 每次重启 URL 会变,需要重新跑本脚本(Meta 里的 webhook 也要同步改)。
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")/.."
+4
View File
@@ -1,4 +1,8 @@
#!/usr/bin/env bash
# 仅当存在旧 bridge 容器/旧 products 库时使用。
# 没有旧 bridge 的测试/生产环境请用:
# bash scripts/import-kb-snapshot.sh # 初始快照
# node scripts/sync-kb-online.js # 在线更新
# 重建旧 KB 的 parent-child 索引(调用旧 bridge 容器里的 indexer),再回填 text_tsv。
# 旧表会由 indexer 归档为 kb_docs_vN;额外先做一份 kb_docs_backup_<date> 备份。
set -euo pipefail
+2 -1
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
# 测试服 JSON-RPC 端口外网被封:在本机建 SSH 隧道,供 crm-new 的 bridge 访问。
# 仅本地开发:测试服 JSON-RPC 端口外网被封时,在本机建 SSH 隧道。
# 测试/生产不需要此隧道:把 RPC URL 直接配成内网可达地址。
#
# 默认只启动隧道并打印 .env 需要的 4 行配置;
# 加 --apply 会自动更新 .env 里的 RPC URL 并重建 bridge。
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# 本地静态测试页(widget JWT 演示)
# 仅本地预览:静态测试页(widget JWT 演示);测试/生产不需要
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")/../web"
echo "http://localhost:${WEB_PORT:-8088}/demo.html"
+73
View File
@@ -0,0 +1,73 @@
#!/usr/bin/env node
'use strict';
// 在线 FAQ/KB 同步(不依赖旧 bridge):
// 抓官网 FAQ JSON-LD → 更新 bridge/kb/*.md → parent/child + embedding → 原子切换 kb_docs
// 用法:
// node scripts/sync-kb-online.js [--force]
// 需要环境变量:
// RAG_PG_URL / EMB_BASE_URL / EMB_API_KEY / EMB_MODEL / EMB_DIM
const fs = require('fs');
const path = require('path');
const { loadConfig } = require('../bridge/src/config');
const { createEmbeddingClient } = require('../bridge/src/embedding');
const { createKbIndexer } = require('../bridge/src/kb-indexer');
const { createFaqFetcher, loadFaqConfig } = require('../bridge/src/faq-fetcher');
const { updateMarkdownFile } = require('../bridge/src/faq-update');
const ROOT = path.resolve(__dirname, '..');
const env = {};
for (const line of fs.readFileSync(path.join(ROOT, '.env'), 'utf8').split('\n')) {
if (!line.includes('=') || line.trim().startsWith('#')) continue;
const i = line.indexOf('=');
env[line.slice(0, i).trim()] = line.slice(i + 1).trim();
}
const cfg = loadConfig({ ...env, ...process.env });
const force = process.argv.includes('--force');
const localOnly = process.argv.includes('--local-only');
const KB_DIR = path.join(ROOT, 'bridge', 'kb');
const FAQ_CONFIG = path.join(ROOT, 'bridge', 'config', 'faq.json');
(async () => {
if (!cfg.ragPgUrl) throw new Error('RAG_PG_URL is not configured');
if (!cfg.embApiKey || !cfg.embBaseUrl) throw new Error('EMB_* is not configured');
const faqCfg = loadFaqConfig(FAQ_CONFIG);
if (!faqCfg.enabled) throw new Error('faq.json disabled');
let changedFiles = 0;
let added = 0;
let updated = 0;
if (!localOnly) {
const fetcher = createFaqFetcher(faqCfg);
const fetched = await fetcher.fetchAll();
for (const page of fetched) {
if (page.error || !page.questions.length) {
console.log(JSON.stringify({ ts: new Date().toISOString(), faq_fetch: 'skip', file: page.file, error: page.error || 'no_questions' }));
continue;
}
const filePath = path.join(KB_DIR, page.file);
const res = updateMarkdownFile(filePath, page.questions);
if (res.changed) changedFiles += 1;
added += res.added || 0;
updated += res.updated || 0;
console.log(JSON.stringify({ ts: new Date().toISOString(), faq_fetch: 'ok', file: page.file, questions: page.questions.length, added: res.added || 0, updated: res.updated || 0 }));
}
}
if (!localOnly && !force && changedFiles === 0) {
console.log(JSON.stringify({ ts: new Date().toISOString(), kb_sync: 'skipped', reason: 'no_faq_changes' }));
return;
}
const emb = createEmbeddingClient(cfg);
const indexer = createKbIndexer({ pool: emb.pool, embeddingClient: emb, kbDir: KB_DIR, dim: cfg.embDim });
try {
const result = await indexer.reindex({ note: `sync-kb-online ${new Date().toISOString().slice(0, 10)}` });
console.log(JSON.stringify({ ts: new Date().toISOString(), kb_sync: 'done', changed_files: changedFiles, added, updated, ...result }));
} finally {
await emb.pool.end().catch(() => {});
}
})().catch((err) => {
console.error('[sync-kb-online]', err.message);
process.exit(1);
});