docs: 建品配方迁至 kb/handbook(留指针)
This commit is contained in:
1 parent
917df5b2d6
commit
ced43b19d3
9 files changed
+27
-1842
No files matched your search
+3
-168
@@ -1,169 +1,4 @@
|
||||
# 速卖通(AliExpress POP)「选品建listing」完整配方
|
||||
> **本文已迁至内网知识库**:`kb/handbook:docs/ecommerce/dianxiaomi/aliexpress.md`(2026-10-21)。
|
||||
> 请到 http://192.168.9.206:3000/kb/handbook 阅读最新版;本仓不再维护这份内容。
|
||||
|
||||
> 2026-09-30 实测跑通:**4/4 进「待发布」,采集箱 0 残留**(RV8I478D / ZQI01T5J / XSSIERBV / XAGOKS9C)。
|
||||
> 目标态 = 待发布(`dxmState=offline & dxmOfflineState=waitPublish`),**绝不点「发布」**。
|
||||
|
||||
## 0. 页面与接口事实
|
||||
|
||||
| 项 | 值 |
|
||||
|---|---|
|
||||
| 创建页 | `https://www.dianxiaomi.com/web/smt/add`(标题「店小秘--添加速卖通产品」) |
|
||||
| 编辑页 | `/web/smt/edit?id=<id>` —— **实测打不开**:`GET /api/smtProduct/edit.json?id=` 无论 id 传什么都返回同一份 12,592 字节的**添加页引导包**(modelList/unitList),不含商品数据 → 不要指望它做回读 |
|
||||
| 保存 | `POST /api/smtProduct/add.json`(点「保存」触发;**不点「发布」**) |
|
||||
| 列表 | `POST /api/smtProduct/pageList.json`,body: `pageNo&pageSize&total&searchType=0&searchValue&shopId=-1&dxmState&dxmOfflineState` |
|
||||
| 列表页 | 待发布 `/web/smt/smtProductList/offline`、采集箱 `…/draft`、在线 `…/online` |
|
||||
| 类目 | 点「选择分类」→ 弹窗标题**「选择类目」**、搜索框 `input[placeholder*=搜索分类]`、结果行 `.search-result-item` |
|
||||
| 单位 | **kg / cm**(Ozon 是 g / mm,别混) |
|
||||
| 店铺 | 10 家:BlessLuckA、Laikeet、Sunbeam、AliMingPro1、Key、Deanoyal… |
|
||||
|
||||
## 1. 执行顺序(顺序错了就被挡)
|
||||
|
||||
```
|
||||
① 店铺(必须最先!类目树随店铺/站点变)
|
||||
② 类目(中文品类词搜索 → 点结果行,弹窗自动关)
|
||||
③ 标题(insertText)
|
||||
④ 来源URL(无 label,用 placeholder 定位)
|
||||
⑤ 零售价格 / 库存数量 / 货值 / 商品编码
|
||||
⑥ 重量(kg)、包装尺寸(cm) ×3、包装后尺寸(cm) ×3
|
||||
⑦ 图片(选择图片 → 网络图片 → 粘 URL → 添加)
|
||||
⑧ 物流属性(铅笔图标弹窗 → 普货 → 确定)
|
||||
⑨ 是否原箱(下拉,必填)、报价是否含关税(radio value=1 不含关税报价)
|
||||
⑩ PC端描述(CKEditor 4)
|
||||
⑪ 保存 → 回读 pageList
|
||||
```
|
||||
|
||||
## 2. 逐字段硬知识(每条都踩过)
|
||||
|
||||
1. **不选店铺 → 「选择分类」根本打不开**(弹窗不出现,表现为 `no-modal`)。脚本里店铺选择必须在类目前。
|
||||
2. **类目搜索词必须是中文**:`旗帜→横幅(Banners)`、`托特包→Tote Bags`、`护照夹→护照套/护照夹`。
|
||||
**英文搜不到**(`Tote Bags` → 命中 0 → 保存报「请选择分类」)。
|
||||
结果行文本形如 `Luggage & Bags(箱包)/Kids' Bags(童包)/Tote Bags(托特包)`(分隔符 `/`,中英双语),
|
||||
**点行即选中并关窗**,没有可用的「选择」确认按钮。
|
||||
* 已知瑕疵:`托特包` 命中 3 条时会选到第一条「童包」,优先命中父级段(`Women Bags`)的逻辑已加,但速卖通树里未必存在该英文段 → 需要时人工在页面上点一次。
|
||||
3. **图片门槛 单边 ≥400px**:喂 10 张图,速卖通**静默只收 6 张**(300×224 / 300×195 / 367×344 / 300×213 被丢)。
|
||||
→ 建输入前先按真实尺寸过滤:`build-add-input.py --min-side 400`(会下载探尺寸)。
|
||||
模块:`.productMainImgModule`,头按钮文案「选择图片」;弹层 tab:本地图片 / **网络图片** / 图片银行 / 引用采集图片;
|
||||
网络图片 tab 的 textarea placeholder「请填写图片URL地址,多个地址用回车换行」→ 点「添加」。
|
||||
回读口令:`主图最多选用6张,已经选用了6张`(上限 **6 张主图**)。
|
||||
4. **保存必填项(缺一个就整单失败)**——错误清单来自 `MYJ.message.error` 钩子 + `.ant-form-item-explain-error`:
|
||||
|
||||
| 报错原文 | 要填什么 | 正确写法 |
|
||||
|---|---|---|
|
||||
| 请输入货值 | 货值(CNY) | 用成本价兜底(`cost_cny`) |
|
||||
| 物流属性必填! | 物流属性 | **不是普通下拉**:点 `.single-logistics-attr-edit` 铅笔 → `.batch-edit-modal` → 选「普货」→ 确定 |
|
||||
| 包装后尺寸请填写整数! | 包装后尺寸 | **整数 cm**;包装尺寸与包装后尺寸都要填,且**尺寸建议整体向上取整** |
|
||||
| 请选择报价是否含关税! | radio | `input[type=radio][value=1]` = 不含关税报价 |
|
||||
| 请输入重量 | 重量(kg) | **必须精确命中「重量(kg)」**:类目属性里还有「重量(Item Weight)」,用 `/重量/` 会填错字段(护照夹实测被拒) |
|
||||
| 请选择分类 | 类目 | 见第 2 条 |
|
||||
|
||||
5. **重量下限**:46g → 0.046 → 字段只留 2 位小数变 `0.04` → 被拒「请输入重量」。
|
||||
安全写法:`wkg = max(0.05, ceil(g/10)/100)` → 0.046→0.05、0.075→0.08、0.097→0.10。
|
||||
6. **描述是 CKEditor 4 两个实例**:`ckeditor30`=PC端描述、`ckeditor31`=无线端描述(实测确认)。
|
||||
**必须按实例所属 form-item 定位**:
|
||||
```js
|
||||
const hit = Object.keys(CKEDITOR.instances).find(k => {
|
||||
const el = CKEDITOR.instances[k].element?.$; return el && formItem.contains(el);
|
||||
});
|
||||
CKEDITOR.instances[hit].setData(html); CKEDITOR.instances[hit].updateElement();
|
||||
```
|
||||
* editable 元素的 `id` **不等于**实例名 → 用 `CKEDITOR.instances[ed.id]` 会落空;
|
||||
* 退化成「改 iframe innerHTML / contenteditable」时 model 不同步 = 白填。
|
||||
* 实测成功回读:`ckeditor:ckeditor30:len=2290`。
|
||||
7. **`pageList` 不返回 `detail` / `packageWeight` / `packageLength`**(100 行全空)→
|
||||
**不能**用它判断描述/重量/尺寸是否存进去,只能看保存时有没有报错。别把空值当「没保存」。
|
||||
|
||||
### ⚠ 类目弹窗「点了行但没关窗」→ 后面所有字段都填不进去(2026-09-30 实测)
|
||||
|
||||
`pickCategoryBySearch` 点结果行后**有时弹窗不关**。不关的弹窗会挡住后续所有点击:
|
||||
标题/价格/库存/来源URL 全部写不进去(但类目已选,看起来「像成功了」)——
|
||||
上一版就是这样,保存时报的却是别的字段,或直接存成空字段。
|
||||
**现在两处兜底**:① `closeCategoryModal()` 主动确认并 Esc/点「关闭」强关;
|
||||
② **模型自检** `smtModelGaps()` 会在保存前报出 `subject(标题)/skuPrice(价格)/ipmSkuStock(库存)` 为空 → 跳过保存。
|
||||
实测四个品修完后 `gaps=[]`(title 115–127 / price 5.3–15.9 / stock 999 / desc 1990–3252)。
|
||||
|
||||
## 3. 脚本
|
||||
|
||||
```bash
|
||||
D=/home/admin9webs/dsh-dingtalk-workspace/listing-spec
|
||||
# ① 生成输入(含 400px 图过滤 + 中文类目词)
|
||||
python3 $D/platform-shared/build-add-input.py pending-package-*.json \
|
||||
--platform 速卖通 --out /tmp/smt_input.json --min-side 400
|
||||
# ② 干跑(只填不保存,验证选择器)
|
||||
node $D/platform-smt/smtbatch.mjs --input /tmp/smt_input.json --only <oid> --dry
|
||||
# ③ 正式建listing(只保存 → 待发布)
|
||||
node $D/platform-smt/smtbatch.mjs --input /tmp/smt_input.json --only <oid>
|
||||
# ④ 回读验收(待发布有没有 + 采集箱有没有本轮新草稿)
|
||||
node $D/platform-smt/verify-landing.mjs --oids <oid1>,<oid2> --keywords "kw1,kw2"
|
||||
```
|
||||
|
||||
* `platform-shared/dxm-add-lib.mjs`:公共交互库(CDP 真实点击/滚动、setByLabel(insertText)、
|
||||
`setNumberByLabel`(原生 setter,避免小数被吃)、`pickCategoryBySearch`、`addImagesByUrl`、
|
||||
`setRichTextByLabel`(CKEditor)、`setLogisticsAttr`、`listShopOptions`、`readErrors`)。
|
||||
* `platform-shared/preflight-shops.mjs`:**每轮批量前先跑**——查每个平台有没有授权店铺。
|
||||
* `platform-shared/build-add-input.py`:把待上架 package 转成建listing输入;图按真实尺寸过滤;
|
||||
英文类目叶 → 中文搜索词映射表 `CAT_KW`(命中不了就补这张表,别在页面上手点)。
|
||||
|
||||
## 4. 验收口径(实测结果)
|
||||
|
||||
```
|
||||
RV8I478D ✅ 待发布 1 | 草稿 0 | 横幅 ¥6.65 图6
|
||||
ZQI01T5J ✅ 待发布 1 | 草稿 0 | 托特包 ¥5.30 图6
|
||||
XSSIERBV ✅ 待发布 1 | 草稿 0 | 护照套/护照夹 ¥6.90 图6
|
||||
XAGOKS9C ✅ 待发布 1 | 草稿 0 | 托特包 ¥15.90 图6
|
||||
```
|
||||
* 采集箱里**有**大量历史草稿(有些是 2022/2023 年的),按关键词搜会误报残留 →
|
||||
回读脚本只把**最近 N 分钟**建的草稿算成本轮残留(`--since-min`,默认 180)。
|
||||
* SKU 前缀不能单独当判据:RV8I478D 的 SKU 是 1688 供应商码 `ZSJLQ_1be`,不含 oid → 回读用「SKU 前缀 **或** 标题关键词」双条件。
|
||||
|
||||
## 5. 仍未解决 / 待补
|
||||
|
||||
* 类目分支优选(托特包落「童包」而非「女包」)——需要人工确认或补父级英文段。
|
||||
* `包装后尺寸` 与 `包装尺寸` 目前都填同一组向上取整值;如需精算需接速卖通计抛规则。
|
||||
* 变种(多变种)模式未做:本配方用单品/单 SKU 路径;带变种的类目需要补变种行填写。
|
||||
* 运费模板/服务模板未设置(`freightTemplateId=null`),发布前需人工在页面选或做「运费推荐生成」。
|
||||
|
||||
## 附:账号级店铺盘点(一次接口,最快)
|
||||
|
||||
`platform-shared/dxm-add-lib.mjs` 的 `readAccountShops(c)`:`GET /api/userIn.json` → `data.shopMap`
|
||||
(key=shopId,value 含 `name`/`platform`)。实测 2026-09-30(子账号):
|
||||
|
||||
```
|
||||
256 家店 → smt:61 tiktok:98 tiktokGlobal:24 aliChoice:65 ozon:5 walmart:3
|
||||
```
|
||||
|
||||
★ **页面上的店铺下拉是子集**:速卖通页只列 10 家,`shopMap` 里是 61 家。
|
||||
判断「这个登录有没有某平台店铺」用 `readAccountShops`;判断「这个店能不能在这个页面选到」才用 `listShopOptions`。
|
||||
|
||||
## 附二:Vue 3 + Pinia 模型层自检(2026-09-30 加,**保存前必做**)
|
||||
|
||||
店小秘所有平台添加页都是 **Vue 3.5.4 + Pinia**(`#app.__vue_app__.config.globalProperties.$pinia`)。
|
||||
**DOM 里显示的值 ≠ 表单模型里的值**;填了 DOM 而模型为空时点保存会「静默失败」或存成空字段。
|
||||
→ 保存前读模型自检:`smtModelSnapshot(c)` + `smtModelGaps(model)`,**gaps 非空则跳过保存**。
|
||||
|
||||
速卖通的字段映射(实测,`smtAddBasicStore.formState` / `smtAddProductStore.formState`):
|
||||
|
||||
| 业务字段 | 模型路径 |
|
||||
|---|---|
|
||||
| 店铺 / 类目 / 标题 | `smtAddBasicStore.formState.{shopId,categoryId,subject}` |
|
||||
| 价格 / **货值** / 库存 / 编码 | `smtAddProductStore.formState.{skuPrice,cargoPrice,ipmSkuStock,skuCode}` |
|
||||
| 重量 / 尺寸(长宽高) | `packageWeight` / `packageLength,packageWidth,packageHeight` |
|
||||
| 是否原箱 | `originalBox`("0"=否) |
|
||||
| **物流属性** | `specialProductTypeList`(普货 = `["1983471271:2025103"]`) |
|
||||
| 图片 | `imageList[]` |
|
||||
| **描述** | `smtAddStore.describeInfoData.detail`(PC端)/ `mobileDetail`(无线端) |
|
||||
|
||||
一次干净的 dry 实测(RV8I478D):
|
||||
```
|
||||
shopId=135223 categoryId=201717001 subjectLen=121
|
||||
skuPrice=6.65 cargoPrice=5.5 ipmSkuStock=999 skuCode=ZSJLQ_1be
|
||||
packageWeight=0.10 dims=28/16/1 originalBox=0
|
||||
specialProductTypeList=["1983471271:2025103"] images=6 descLen=1935 → gaps=[]
|
||||
```
|
||||
结论:**描述确实是写进模型的**(1935 字符,在 `describeInfoData.detail`);**物流属性的 UI 路径也确实会提交到模型**。
|
||||
之前「描述可能没保存」的疑虑到此消除;`pageList` 不返回这些字段只是列表投影问题。
|
||||
|
||||
其它平台 store 命名同构(`<platform>` = smt / shopeeGlobal / tiktokGlobal / ozonProduct):
|
||||
`<platform>AddStore`(编排+描述)· `BasicStore` · `ProductStore` · `AttrStore` · `OtherStore`
|
||||
· `SkuDataStore`/`SkuAttrStore`(变种)· `PackageStore`/`DescStore`(Shopee/Ozon)。
|
||||
公共:`activity` `global` `user` `userPlatforms` `batchImageTaskStore` `*Translate*`。
|
||||
用 `vueStores(c)` 列全部、`readPinia(c, storeId, path)` 读任意字段。
|
||||
(原文件内容已移出,历史见本仓 git 记录。)
|
||||
Reference in new issue
Block a user