Commit 15e63baff6d2af1954047809de30dcfad04159d8

Authored by tangwang
1 parent 97a5d59d

索引文档修改

@@ -67,3 +67,5 @@ data.* @@ -67,3 +67,5 @@ data.*
67 67
68 *.log 68 *.log
69 log/ 69 log/
  70 +
  71 +*.xlsx
docs/商品导入模板说明.md 0 → 100644
@@ -0,0 +1,346 @@ @@ -0,0 +1,346 @@
  1 +# 商品导入模板说明文档
  2 +
  3 +本文档完整描述了商品导入Excel模板的结构、字段说明和示例数据。
  4 +
  5 +## 字段列表
  6 +
  7 +| 列号 | 字段名称 | 字段说明 |
  8 +|------|----------|----------|
  9 +| 1 | 商品ID | (此行导入时不可删除 )商品 ID 是系统生成的唯一标识符,新增商品无需填写 |
  10 +| 2 | 创建时间 | 创建时间的时区为当前店铺设置的时区 |
  11 +| 3 | 商品标题* | 最多255字符(同一商品的子款式标题与商品标题需一致,且中间请勿插入其他商品) |
  12 +| 4 | 商品属性* | 1.多款式商品:<br>同一个商品的首行标题填入「M」(商品主体);<br>同一个商品的非首行标题填入「P」(子款式);<br>2.单一款式商品(Single):填入「S」 |
  13 +| 5 | 商品副标题 | 最多600字符 |
  14 +| 6 | 商品描述 | 上传不带样式,若需要样式,则输入html代码 |
  15 +| 7 | SEO标题 | 最多5000字符 |
  16 +| 8 | SEO描述 | 最多5000字符 |
  17 +| 9 | SEO URL Handle | 最多支持输入255字符<br>(SEO URL handle只对SEO URL的「URL参数」部分进行更改,即“products/”后的内容,如:products/「URL参数」<br>) |
  18 +| 10 | SEO URL 重定向 | 创建URL重定向,访问修改前链接可跳转到修改后的新链接页面<br>「Y」:TRUE <br>「N」:FALSE |
  19 +| 11 | SEO关键词 | 多个关键词请用「英文逗号」隔开 |
  20 +| 12 | 商品上架 | 「Y」:上架状态<br>「N」:下架状态<br>默认为「N」 |
  21 +| 13 | 需要物流 | 「Y」:需要<br>「N」:不需要<br>默认为「Y」 |
  22 +| 14 | 商品收税 | 「Y」:需要<br>「N」:不需要<br>默认为「N」 |
  23 +| 15 | 商品spu | 最多100字符 |
  24 +| 16 | 启用虚拟销量 | 「Y」:启用<br>「N」:不启用<br>默认为「N」 |
  25 +| 17 | 虚拟销量值 | 最多输入六位数的自然数 |
  26 +| 18 | 跟踪库存 | 「Y」:跟踪<br>「N」:不跟踪<br>默认为「N」 |
  27 +| 19 | 库存规则* | 若跟踪库存为Y,则该项必填:<br>填入「1」表示:库存为0允许购买<br>填入「2」表示:库存为0不允许购买<br>填入「3」表示:库存为0自动下架 |
  28 +| 20 | 专辑名称 | 请填入已创建的手动专辑名称;<br>多个专辑请用「英文逗号」隔开 |
  29 +| 21 | 标签 | 最多输入250个标签,每个不得超过500字符,多个标签请用「英文逗号」隔开 |
  30 +| 22 | 供应商名称 | 最多20字符 |
  31 +| 23 | 供应商URL | 请输入供应商URL |
  32 +| 24 | 款式1 | 最多255字符 |
  33 +| 25 | 款式2 | 最多255字符 |
  34 +| 26 | 款式3 | 最多255字符 |
  35 +| 27 | 商品售价* | 最多输入9位正整数,2位小数 |
  36 +| 28 | 商品原价 | 最多输入9位正整数,2位小数 |
  37 +| 29 | 成本价 | 最多输入9位正整数,2位小数 |
  38 +| 30 | 商品SKU | 最多255字符 |
  39 +| 31 | 商品重量 | 最多输入10位正整数,2位小数 |
  40 +| 32 | 重量单位 | 可选单位有:kg, lb, g, oz;<br>默认为kg |
  41 +| 33 | 商品条形码 | 最多100字符 |
  42 +| 34 | 商品库存 | 只支持导入默认地点库存,如需导入其他地点的库存,请到【库存列表】导入。最多输入9位整数 |
  43 +| 35 | 尺寸信息 | 按长宽高顺序填写,用「英文逗号」隔开,单位默认为英寸 |
  44 +| 36 | 原产地国别 | 请填写国家代码 |
  45 +| 37 | HS(协调制度)代码 | 6-12纯数字 |
  46 +| 38 | 商品图片* | 商品属性为M(商品主体)与S(单一款式商品)的行可填入商品图URL:<br>1.若商品主图字段有值,则填入的图片URL为商品副图(可选填)<br>2.若商品主图字段为空,则必须至少填入一个图片URL,且首个URL为商品主图URL,其他为商品副图<br>3.填多个URL可用「英文逗号」隔开<br><br>商品属性为P(子款式)的行可填入子款式图URL:<br>1.仅支持填入一个URL作为子款式图片,填入多个时默认导入第一个;若不填入,则默认不需要图片<br>2.同一商品的部分子款式上传图片,则其余款式也需要上传,否则默认填入商品主图<br> |
  47 +| 39 | 商品备注 | 最多输入500个字 |
  48 +| 40 | 款式备注 | 最多输入20个字 |
  49 +| 41 | 商品主图 | 1.只需商品属性为M(商品主体)与S(单一款式商品)的行填写(可选填)<br>2.仅能填入一个图片URL作为商品主图,填入多个时默认只导入第一个 |
  50 +| 42 | 如需导入元字段, 请在此行增添【元字段-命名空间和密钥】作为表头,例:元字段-test.111 | 1. 请确认导入的元字段在店铺后台已创建;<br>2. 只需商品属性为M(商品主体)与S(单一款式商品)的行填写元字段值(可选填)<br>3.各类型元字段填写规范详见帮助文档:https://helpcenter.shoplazza.com/hc/zh-cn/articles/37520605874457 |
  51 +
  52 +## 示例数据
  53 +
  54 +模板中包含以下示例数据,展示了多款式商品(M+P)和单一款式商品(S)的格式:
  55 +
  56 +### 商品示例:Legendary Whitetails Men's Buck Camp Flannel Shirt
  57 +
  58 +#### 行 4 - 商品属性: M
  59 +
  60 +**基本信息:**
  61 +- 商品标题*: `Legendary Whitetails Men's Buck Camp Flannel Shirt`
  62 +- 商品属性*: `M`
  63 +- 商品副标题: `100% Cotton
  64 +We recommend ordering a size down if you prefer a closer fit
  65 +Purchase from Amazon seller Legendary Whitetails to ensure you receive an authentic Legendary Whitetails branded Buck Camp F...`
  66 +- 商品描述: `<p>A hunter&rsquo;s wardrobe is not complete without a great flannel. Our exclusive plaids are made from 100% cotton soft brushed flannel. Featuring double pleat back for ease of movement and contr...`
  67 +
  68 +**SEO信息:**
  69 +- SEO标题: `Legendary Whitetails Men's Buck Camp Flannel Shirt`
  70 +- SEO描述: `Featuring double pleat back for ease of movement and contrasting corduroy lined collar and cuffs for a great look and lasting durability.`
  71 +- SEO URL Handle: `products/maternity-lace-cardigan-dress-photo-shoot_3a09`
  72 +- SEO关键词: `新品,热卖,爆款`
  73 +
  74 +**状态设置:**
  75 +- 商品上架: `Y`
  76 +- 需要物流: `Y`
  77 +- 商品收税: `Y`
  78 +- 启用虚拟销量: `N`
  79 +- 虚拟销量值: `100`
  80 +- 跟踪库存: `Y`
  81 +- 库存规则*: `1`
  82 +
  83 +**分类信息:**
  84 +- 专辑名称: `衬衣,热卖`
  85 +- 标签: `新品,热卖,爆款`
  86 +- 供应商名称: `Amazon`
  87 +- 供应商URL: `https://www.amazon.com/Legendary-Whitetails-Buck-Flannels-Large/dp/B01KTUMBOI/ref=sr_1_1?s=fashion-mens-intl-ship&ie=UTF8&qid=1543038722&sr=1-1`
  88 +
  89 +**款式信息:**
  90 +- 款式1: `SIZE`
  91 +- 款式2: `COLOR`
  92 +
  93 +**其他信息:**
  94 +- 商品图片*: `https://m.media-amazon.com/images/S/aplus-seller-content-images-us-east-1/ATVPDKIKX0DER/A1ABYS4IVXNT9X/27b061cf-ccd1-4c4c-82fd-6519a84b60c3.jpg`
  95 +
  96 +#### 行 5 - 商品属性: P
  97 +
  98 +**基本信息:**
  99 +- 商品标题*: `Legendary Whitetails Men's Buck Camp Flannel Shirt`
  100 +- 商品属性*: `P`
  101 +
  102 +**款式信息:**
  103 +- 款式1: `S`
  104 +- 款式2: `red`
  105 +
  106 +**价格库存:**
  107 +- 商品售价*: `88.99`
  108 +- 商品原价: `149.99`
  109 +- 商品SKU: `LW-TS-RD-S1`
  110 +- 商品重量: `0.2`
  111 +- 重量单位: `kg`
  112 +- 商品库存: `100`
  113 +
  114 +#### 行 6 - 商品属性: P
  115 +
  116 +**基本信息:**
  117 +- 商品标题*: `Legendary Whitetails Men's Buck Camp Flannel Shirt`
  118 +- 商品属性*: `P`
  119 +
  120 +**款式信息:**
  121 +- 款式1: `S`
  122 +- 款式2: `black`
  123 +
  124 +**价格库存:**
  125 +- 商品售价*: `88.99`
  126 +- 商品原价: `149.99`
  127 +- 商品SKU: `LW-TS-BK-S1`
  128 +- 商品重量: `0.2`
  129 +- 重量单位: `kg`
  130 +- 商品库存: `100`
  131 +
  132 +#### 行 7 - 商品属性: P
  133 +
  134 +**基本信息:**
  135 +- 商品标题*: `Legendary Whitetails Men's Buck Camp Flannel Shirt`
  136 +- 商品属性*: `P`
  137 +
  138 +**款式信息:**
  139 +- 款式1: `S`
  140 +- 款式2: `army`
  141 +
  142 +**价格库存:**
  143 +- 商品售价*: `88.99`
  144 +- 商品原价: `149.99`
  145 +- 商品SKU: `LW-TS-AM-S1`
  146 +- 商品重量: `0.2`
  147 +- 重量单位: `kg`
  148 +- 商品库存: `100`
  149 +
  150 +#### 行 8 - 商品属性: P
  151 +
  152 +**基本信息:**
  153 +- 商品标题*: `Legendary Whitetails Men's Buck Camp Flannel Shirt`
  154 +- 商品属性*: `P`
  155 +
  156 +**款式信息:**
  157 +- 款式1: `L`
  158 +- 款式2: `red`
  159 +
  160 +**价格库存:**
  161 +- 商品售价*: `88.99`
  162 +- 商品原价: `149.99`
  163 +- 商品SKU: `LW-TS-RD-M1`
  164 +- 商品重量: `0.2`
  165 +- 重量单位: `kg`
  166 +- 商品库存: `100`
  167 +
  168 +#### 行 9 - 商品属性: P
  169 +
  170 +**基本信息:**
  171 +- 商品标题*: `Legendary Whitetails Men's Buck Camp Flannel Shirt`
  172 +- 商品属性*: `P`
  173 +
  174 +**款式信息:**
  175 +- 款式1: `L`
  176 +- 款式2: `black`
  177 +
  178 +**价格库存:**
  179 +- 商品售价*: `88.99`
  180 +- 商品原价: `149.99`
  181 +- 商品SKU: `LW-TS-BK-M1`
  182 +- 商品重量: `0.2`
  183 +- 重量单位: `kg`
  184 +- 商品库存: `100`
  185 +
  186 +#### 行 10 - 商品属性: P
  187 +
  188 +**基本信息:**
  189 +- 商品标题*: `Legendary Whitetails Men's Buck Camp Flannel Shirt`
  190 +- 商品属性*: `P`
  191 +
  192 +**款式信息:**
  193 +- 款式1: `L`
  194 +- 款式2: `army`
  195 +
  196 +**价格库存:**
  197 +- 商品售价*: `88.99`
  198 +- 商品原价: `149.99`
  199 +- 商品SKU: `LW-TS-AM-M1`
  200 +- 商品重量: `0.2`
  201 +- 重量单位: `kg`
  202 +- 商品库存: `100`
  203 +
  204 +#### 行 11 - 商品属性: P
  205 +
  206 +**基本信息:**
  207 +- 商品标题*: `Legendary Whitetails Men's Buck Camp Flannel Shirt`
  208 +- 商品属性*: `P`
  209 +
  210 +**款式信息:**
  211 +- 款式1: `XL`
  212 +- 款式2: `red`
  213 +
  214 +**价格库存:**
  215 +- 商品售价*: `88.99`
  216 +- 商品原价: `149.99`
  217 +- 商品SKU: `LW-TS-RD-L1`
  218 +- 商品重量: `0.2`
  219 +- 重量单位: `kg`
  220 +- 商品库存: `100`
  221 +
  222 +#### 行 12 - 商品属性: P
  223 +
  224 +**基本信息:**
  225 +- 商品标题*: `Legendary Whitetails Men's Buck Camp Flannel Shirt`
  226 +- 商品属性*: `P`
  227 +
  228 +**款式信息:**
  229 +- 款式1: `XL`
  230 +- 款式2: `black`
  231 +
  232 +**价格库存:**
  233 +- 商品售价*: `88.99`
  234 +- 商品原价: `149.99`
  235 +- 商品SKU: `LW-TS-BK-L1`
  236 +- 商品重量: `0.2`
  237 +- 重量单位: `kg`
  238 +- 商品库存: `100`
  239 +
  240 +#### 行 13 - 商品属性: P
  241 +
  242 +**基本信息:**
  243 +- 商品标题*: `Legendary Whitetails Men's Buck Camp Flannel Shirt`
  244 +- 商品属性*: `P`
  245 +
  246 +**款式信息:**
  247 +- 款式1: `XL`
  248 +- 款式2: `army`
  249 +
  250 +**价格库存:**
  251 +- 商品售价*: `88.99`
  252 +- 商品原价: `149.99`
  253 +- 商品SKU: `LW-TS-AM-L1`
  254 +- 商品重量: `0.2`
  255 +- 重量单位: `kg`
  256 +- 商品库存: `100`
  257 +
  258 +---
  259 +
  260 +### 商品示例:Jabra Move Wireless Stereo Headphones
  261 +
  262 +#### 行 14 - 商品属性: S
  263 +
  264 +**基本信息:**
  265 +- 商品标题*: `Jabra Move Wireless Stereo Headphones`
  266 +- 商品属性*: `S`
  267 +- 商品副标题: `Listen to your tunes and never miss a call
  268 +Ultra-lightweight and adjustable headband fits all head-types
  269 +Durable stainless steel headband and dirt resistant fabric for life on the move.40mm Dynamic...`
  270 +- 商品描述: `Clean Scandinavian design meets crisp digital sound. Get a perfect fit with the ultra-light, comfortable headband. Choose from four modern colors: Cayenne (Red), Cobalt (Blue), Coal (Black) and Gold.`
  271 +
  272 +**SEO信息:**
  273 +- SEO URL Handle: `products/maternity-rainbow-stripe-skinny-bodycon-dress_5027`
  274 +
  275 +**状态设置:**
  276 +- 商品上架: `Y`
  277 +- 需要物流: `Y`
  278 +- 商品收税: `N`
  279 +- 启用虚拟销量: `N`
  280 +- 虚拟销量值: `100`
  281 +- 跟踪库存: `N`
  282 +- 库存规则*: `2`
  283 +
  284 +**分类信息:**
  285 +- 专辑名称: `头戴式耳机`
  286 +- 标签: `耳机,头戴式,爆款`
  287 +- 供应商名称: `Amazon`
  288 +- 供应商URL: `https://www.amazon.com/Jabra-Move-Wireless-Stereo-Headphones/dp/B00MR8Z28S/ref=br_asw_pdt-2?pf_rd_m=ATVPDKIKX0DER&pf_rd_s=&pf_rd_r=8NAPN9R973J85P2KDX4G&pf_rd_t=36701&pf_rd_p=a08731ea-e1c2-4f7f-a56e...`
  289 +
  290 +**价格库存:**
  291 +- 商品售价*: `49.99`
  292 +- 商品原价: `99.99`
  293 +- 商品SKU: `B00MR8Z28S`
  294 +- 商品重量: `0.3`
  295 +- 重量单位: `lb`
  296 +- 商品库存: `500`
  297 +
  298 +**其他信息:**
  299 +- 商品图片*: `https://images-na.ssl-images-amazon.com/images/I/416k5ZUd6lL.jpg`
  300 +
  301 +---
  302 +
  303 +
  304 +## 重要说明
  305 +
  306 +### 商品属性字段说明
  307 +
  308 +商品属性字段是必填项,用于标识商品类型:
  309 +
  310 +- **M (商品主体)**: 多款式商品的首行,填写商品主体信息
  311 + - 需要填写:商品标题、描述、SEO信息、分类等商品主体信息
  312 + - 不需要填写:价格、库存、SKU等子款式信息
  313 +
  314 +- **P (子款式)**: 多款式商品的非首行,填写子款式信息
  315 + - 需要填写:商品标题(与M行一致)、款式值、价格、库存、SKU等
  316 + - 不需要填写:商品描述、SEO信息等商品主体信息
  317 +
  318 +- **S (单一款式商品)**: 单一款式商品,一行包含所有信息
  319 + - 需要填写:所有商品信息,包括标题、描述、价格、库存等
  320 +
  321 +### 字段填写规则
  322 +
  323 +1. **商品ID**: 系统自动生成,新增商品无需填写
  324 +2. **商品标题***: 必填,最多255字符。同一商品的子款式标题必须与商品主体标题一致
  325 +3. **商品属性***: 必填,只能填写 M、P 或 S
  326 +4. **商品售价***: 必填,最多9位正整数,2位小数
  327 +5. **商品SKU**: 必填,最多255字符
  328 +6. **商品图片***: 必填(M和S类型),可填写多个图片URL,用逗号分隔
  329 +7. **库存规则***: 当跟踪库存为Y时必填,可选值:1(库存为0允许购买)、2(库存为0不允许购买)、3(库存为0自动下架)
  330 +
  331 +### 数据格式要求
  332 +
  333 +- 日期时间格式:`YYYY-MM-DD HH:MM:SS`
  334 +- 布尔值:使用 `Y` 或 `N`
  335 +- 多个值:使用英文逗号分隔
  336 +- HTML内容:商品描述支持HTML代码
  337 +- URL格式:图片URL和供应商URL需为完整URL
  338 +
  339 +### 注意事项
  340 +
  341 +1. 同一商品的子款式行必须紧跟在商品主体行之后,中间不能插入其他商品
  342 +2. 商品标题在所有子款式行中必须保持一致
  343 +3. 图片URL必须是可访问的完整URL
  344 +4. 价格、库存等数值字段不能包含非数字字符(除小数点外)
  345 +5. 导入前请确保所有必填字段都已正确填写
  346 +
docs/索引字段说明.md
@@ -16,10 +16,127 @@ @@ -16,10 +16,127 @@
16 - 返回的结果格式约定为店匠系列的 SPU/SKU嵌套结构。 16 - 返回的结果格式约定为店匠系列的 SPU/SKU嵌套结构。
17 - 支撑 facet/过滤/排序业务需求:用户可以选择任何一个 keyword 或 HKText 类型的字段做筛选、聚合;也可以选择任何一个数值型字段做 Range 过滤或排序。 17 - 支撑 facet/过滤/排序业务需求:用户可以选择任何一个 keyword 或 HKText 类型的字段做筛选、聚合;也可以选择任何一个数值型字段做 Range 过滤或排序。
18 18
19 -## 数据源调研 19 +## 关键字段
20 20
21 -店匠的商品结构: 21 +参考1:spu表 & sku表、数据源《商品导入模板》
  22 +### SPU表(shoplazza_product_spu)
  23 +
  24 +主要字段:
  25 +- `id`: BIGINT - 主键ID
  26 +- `tenant_id`: BIGINT - 租户ID
  27 +- `handle`: VARCHAR(255) - URL handle
  28 +- `title`: VARCHAR(512) - 商品标题
  29 +- `brief`: VARCHAR(512) - 商品简介
  30 +- `description`: TEXT - 商品描述
  31 +- `vendor`: VARCHAR(255) - 供应商/品牌
  32 +- `category`: VARCHAR(255) - 类目
  33 +- `tags`: VARCHAR(1024) - 标签
  34 +- `seo_title`: VARCHAR(512) - SEO标题
  35 +- `seo_description`: TEXT - SEO描述
  36 +- `seo_keywords`: VARCHAR(1024) - SEO关键词
  37 +- `image_src`: VARCHAR(500) - 图片URL
  38 +- `create_time`: DATETIME - 创建时间
  39 +- `update_time`: DATETIME - 更新时间
  40 +- `shoplazza_created_at`: DATETIME - 店匠创建时间
  41 +- `shoplazza_updated_at`: DATETIME - 店匠更新时间
  42 +
  43 +spu表全部字段
  44 +"Field" "Type" "Null" "Key" "Default" "Extra"
  45 +"id" "bigint(20)" "NO" "PRI" "auto_increment"
  46 +"shop_id" "bigint(20)" "NO" "MUL" ""
  47 +"shoplazza_id" "varchar(64)" "NO" "" ""
  48 +"handle" "varchar(255)" "YES" "MUL" ""
  49 +"title" "varchar(500)" "NO" "" ""
  50 +"brief" "varchar(1000)" "YES" "" ""
  51 +"description" "text" "YES" "" ""
  52 +"spu" "varchar(100)" "YES" "" ""
  53 +"vendor" "varchar(255)" "YES" "" ""
  54 +"vendor_url" "varchar(500)" "YES" "" ""
  55 +"seo_title" "varchar(500)" "YES" "" ""
  56 +"seo_description" "text" "YES" "" ""
  57 +"seo_keywords" "text" "YES" "" ""
  58 +"image_src" "varchar(500)" "YES" "" ""
  59 +"image_width" "int(11)" "YES" "" ""
  60 +"image_height" "int(11)" "YES" "" ""
  61 +"image_path" "varchar(255)" "YES" "" ""
  62 +"image_alt" "varchar(500)" "YES" "" ""
  63 +"inventory_policy" "varchar(50)" "YES" "" ""
  64 +"inventory_quantity" "int(11)" "YES" "" "0" ""
  65 +"inventory_tracking" "tinyint(1)" "YES" "" "0" ""
  66 +"published" "tinyint(1)" "YES" "" "0" ""
  67 +"published_at" "datetime" "YES" "MUL" ""
  68 +"requires_shipping" "tinyint(1)" "YES" "" "1" ""
  69 +"taxable" "tinyint(1)" "YES" "" "0" ""
  70 +"fake_sales" "int(11)" "YES" "" "0" ""
  71 +"display_fake_sales" "tinyint(1)" "YES" "" "0" ""
  72 +"mixed_wholesale" "tinyint(1)" "YES" "" "0" ""
  73 +"need_variant_image" "tinyint(1)" "YES" "" "0" ""
  74 +"has_only_default_variant" "tinyint(1)" "YES" "" "0" ""
  75 +"tags" "text" "YES" "" ""
  76 +"note" "text" "YES" "" ""
  77 +"category" "varchar(255)" "YES" "" ""
  78 +"shoplazza_created_at" "datetime" "YES" "" ""
  79 +"shoplazza_updated_at" "datetime" "YES" "MUL" ""
  80 +"tenant_id" "bigint(20)" "NO" "MUL" ""
  81 +"creator" "varchar(64)" "YES" "" "" ""
  82 +"create_time" "datetime" "NO" "" "CURRENT_TIMESTAMP" ""
  83 +"updater" "varchar(64)" "YES" "" "" ""
  84 +"update_time" "datetime" "NO" "" "CURRENT_TIMESTAMP" "on update CURRENT_TIMESTAMP"
  85 +"deleted" "bit(1)" "NO" "" "b'0'" ""
22 86
  87 +
  88 +### SKU表(shoplazza_product_sku)
  89 +
  90 +主要字段:
  91 +- `id`: BIGINT - 主键ID(对应variant_id)
  92 +- `spu_id`: BIGINT - SPU ID(关联字段)
  93 +- `title`: VARCHAR(500) - 变体标题
  94 +- `price`: DECIMAL(10,2) - 价格
  95 +- `compare_at_price`: DECIMAL(10,2) - 原价
  96 +- `sku`: VARCHAR(100) - SKU编码
  97 +- `inventory_quantity`: INT(11) - 库存数量
  98 +- `option1`: VARCHAR(255) - 选项1
  99 +- `option2`: VARCHAR(255) - 选项2
  100 +- `option3`: VARCHAR(255) - 选项3
  101 +
  102 +sku全部字段
  103 +"Field" "Type" "Null" "Key" "Default" "Extra"
  104 +"id" "bigint(20)" "NO" "PRI" "auto_increment"
  105 +"spu_id" "bigint(20)" "NO" "MUL" ""
  106 +"shop_id" "bigint(20)" "NO" "MUL" ""
  107 +"shoplazza_id" "varchar(64)" "NO" "" ""
  108 +"shoplazza_product_id" "varchar(64)" "NO" "MUL" ""
  109 +"shoplazza_image_id" "varchar(64)" "YES" "" ""
  110 +"title" "varchar(500)" "YES" "" ""
  111 +"sku" "varchar(100)" "YES" "MUL" ""
  112 +"barcode" "varchar(100)" "YES" "" ""
  113 +"position" "int(11)" "YES" "" "0" ""
  114 +"price" "decimal(10,2)" "YES" "" ""
  115 +"compare_at_price" "decimal(10,2)" "YES" "" ""
  116 +"cost_price" "decimal(10,2)" "YES" "" ""
  117 +"option1" "varchar(255)" "YES" "" ""
  118 +"option2" "varchar(255)" "YES" "" ""
  119 +"option3" "varchar(255)" "YES" "" ""
  120 +"inventory_quantity" "int(11)" "YES" "" "0" ""
  121 +"weight" "decimal(10,2)" "YES" "" ""
  122 +"weight_unit" "varchar(10)" "YES" "" ""
  123 +"image_src" "varchar(500)" "YES" "" ""
  124 +"wholesale_price" "json" "YES" "" ""
  125 +"note" "text" "YES" "" ""
  126 +"extend" "json" "YES" "" ""
  127 +"shoplazza_created_at" "datetime" "YES" "" ""
  128 +"shoplazza_updated_at" "datetime" "YES" "" ""
  129 +"tenant_id" "bigint(20)" "NO" "MUL" ""
  130 +"creator" "varchar(64)" "YES" "" "" ""
  131 +"create_time" "datetime" "NO" "" "CURRENT_TIMESTAMP" ""
  132 +"updater" "varchar(64)" "YES" "" "" ""
  133 +"update_time" "datetime" "NO" "" "CURRENT_TIMESTAMP" "on update CURRENT_TIMESTAMP"
  134 +"deleted" "bit(1)" "NO" "" "b'0'" ""
  135 +
  136 +参考2:波哥 索引《店匠指南》 -> 商品详解
  137 +
  138 +
  139 +参考3: 店匠的商品结构 - 民丰:
23 固定字段: 140 固定字段:
24 1、必填:品名 141 1、必填:品名
25 2、非必填:副标题、类目、专辑、标签、供应商、市场 142 2、非必填:副标题、类目、专辑、标签、供应商、市场
@@ -33,7 +150,110 @@ @@ -33,7 +150,110 @@
33 150
34 商品信息:品名、颜色、尺码,基本上就这三个信息 151 商品信息:品名、颜色、尺码,基本上就这三个信息
35 152
36 -spu/sku表: 153 +
  154 +### 分类
  155 +最多三级分类,商品可以指向任何级别的分类
  156 +Field Type
  157 +category varchar(255)
  158 +category_id bigint(20)
  159 +category_google_id bigint(20)
  160 +category_level int(11)
  161 +category_path varchar(500)
  162 +
  163 +
  164 +### 属性
  165 +1. 组织ES输入数据的时候,需要为sku拼接spu的 option1 option2 option3,作为属性名称(比如“颜色”),sku的 option1 option2 option3 作为属性值(比如“白色”)
  166 +2. 有以下方案: TODO 可以选择其中一种,或者2用于填充3用于搜索:
  167 +1)铺平展开,只支持三个
  168 +attr1
  169 +attr2
  170 +attr3
  171 +option1
  172 +option2
  173 +option3
  174 +2)nested,支持多个,动态。 查询性能低
  175 +"specifications": {
  176 + "type": "nested",
  177 + "properties": {
  178 + "name": { "type": "keyword" }, // "颜色", "容量"
  179 + "value": { "type": "keyword" } // "白色", "256GB"
  180 + }
  181 +},
  182 +3)平铺展开。写入时从 specifications 提取并填充这些字段,查询性能高。
  183 +"properties": {
  184 + "color": { "type": "keyword" },
  185 + "capacity": { "type": "keyword" },
  186 + "network": { "type": "keyword" },
  187 + "edition": { "type": "keyword" }
  188 +}
  189 +
  190 +### status
  191 +1. 商品下架等状态
  192 +2. 无库存,是用status记录,提升查询效率
  193 +
  194 +### 多语言
  195 +索引:中英文(两套),如果商品资料是中文,则我们系统使用 谷歌翻译(和平台使用的翻译工具对齐) 自动翻译为英文。
  196 +检索:将非中英文,翻译成英文后,再检索英文。
  197 +
  198 +## 分面
  199 +1. 分类
  200 +2. 标签。这个是个扁平的结构,不是像属性那样k-v-pair的
  201 +不用考虑属性。这个 option1 2 3不能放在外面筛选。他的定位是spu内部的东西,不是外部用于筛选商品的。 即使外面要有那种 动态筛选 比如搜手机出品牌、款式 这种, 不是对应的这个字段,会是另外的字段。
  202 +
  203 +
  204 +### 字段预处理
  205 +需要用「英文逗号」隔开,作为list输入的字段:
  206 +SEO关键词
  207 +专辑名称
  208 +标签
  209 +尺寸信息
  210 +
  211 +
  212 +## SPU 与 SKU 的协同设计
  213 +
  214 +以下方案:
  215 +1. sku为索引单位。使用 collapse 按 spu_id 折叠
  216 +需要考虑大量的字段冗余
  217 +
  218 +2. spu为单位。 sku的title作为 spu 的sku_titles 属性。
  219 + 除了title, brielf description seo相关 cate tags vendor所有影响相关性的字段都在spu。 sku只有一个title。所以,可以以spu为单位,sku的title作为spu的一个字段,以list形式灌入,假设一个spu有三个sku,那么这个sku_titles字段有三个值,打分的时候按max取得打分,并且我们可以得到这三个sku的title匹配的得分,因此好决定sku的排序。
  220 +
  221 +3. sku 作为nested
  222 +
  223 +
  224 +
  225 +参考 [](https://blog.csdn.net/csdn_tom_168/article/details/150432666)
  226 +方案一:独立索引
  227 +spu-catalog-*:用于品牌、类目、商品介绍等宏观搜索。
  228 +sku-catalog-*:用于具体规格搜索、下单。
  229 +优势:职责分离,查询更高效。
  230 +
  231 +方案二:联合查询
  232 +GET /spu-read,sku-read/_search
  233 +适用于“模糊搜索 → 跳转详情页”的场景。
  234 +
  235 +方案三:父子文档(不推荐)
  236 +join 类型维护 SPU-SKU 关系。
  237 +性能差,维护复杂,不适用于高并发搜索场景。
  238 +
  239 +
  240 +## rank - 相关性
  241 +
  242 +
  243 +## rank - 提权
  244 +
  245 +function_score 提升相关性
  246 +```json
  247 +"function_score": {
  248 + "functions": [
  249 + { "field_value_factor": { "field": "sales_count", "factor": 0.001, "modifier": "log1p" } },
  250 + { "gauss": { "listed_at": { "scale": "30d" } } }
  251 + ],
  252 + "boost_mode": "multiply"
  253 +}
  254 +```
  255 +
  256 +
37 257
38 258
39 ## 索引基本信息 259 ## 索引基本信息
@@ -42,6 +262,17 @@ spu/sku表: @@ -42,6 +262,17 @@ spu/sku表:
42 - **索引级别**: SPU级别(商品级别) 262 - **索引级别**: SPU级别(商品级别)
43 - **数据结构**: SPU文档包含嵌套的skus数组 263 - **数据结构**: SPU文档包含嵌套的skus数组
44 264
  265 +
  266 +## 分片与副本设置
  267 +```json
  268 +# 分片数: 根据cpu数量和sku数量决定。
  269 +"settings": {
  270 + "number_of_shards": 8,
  271 + "number_of_replicas": 1,
  272 + "refresh_interval": "15s"
  273 +}
  274 +```
  275 +
45 ## 索引类型与处理说明 276 ## 索引类型与处理说明
46 277
47 ### 文本字段 278 ### 文本字段
@@ -186,7 +417,7 @@ spu/sku表: @@ -186,7 +417,7 @@ spu/sku表:
186 | 索引字段名 | ES字段类型 | 是否索引 | 数据来源表 | 表中字段名 | 表中字段类型 | Boost权重 | 是否返回 | 数据预处理 | 说明 | 417 | 索引字段名 | ES字段类型 | 是否索引 | 数据来源表 | 表中字段名 | 表中字段类型 | Boost权重 | 是否返回 | 数据预处理 | 说明 |
187 |-----------|-----------|---------|-----------|-----------|-------------|-----------|---------|-------------|------| 418 |-----------|-----------|---------|-----------|-----------|-------------|-----------|---------|-------------|------|
188 | vendor | HKText | 是 | SPU表 | vendor | VARCHAR(255) | 1.5 | 是 | | 供应商/品牌,HKText字段自动提供 `vendor.keyword` 用于过滤、聚合 | 419 | vendor | HKText | 是 | SPU表 | vendor | VARCHAR(255) | 1.5 | 是 | | 供应商/品牌,HKText字段自动提供 `vendor.keyword` 用于过滤、聚合 |
189 -| tags | HKText | 是 | SPU表 | tags | VARCHAR(1024) | 1.0 | 是 | | 标签字段,支持模糊搜索;使用 `tags.keyword` 进行精确过滤 | 420 +| tags | HKText | 是 | SPU表 | tags | VARCHAR(1024) | 1.0 | 是 | 按逗号分割为list | 标签字段,支持模糊搜索;使用 `tags.keyword` 进行精确过滤 |
190 | category | HKText | 是 | SPU表 | category | VARCHAR(255) | 1.5 | 是 | | 类目字段,使用 `category.keyword` 进行过滤/分面 | 421 | category | HKText | 是 | SPU表 | category | VARCHAR(255) | 1.5 | 是 | | 类目字段,使用 `category.keyword` 进行过滤/分面 |
191 422
192 ### 价格字段 423 ### 价格字段
@@ -306,124 +537,6 @@ spu/sku表: @@ -306,124 +537,6 @@ spu/sku表:
306 4. **向量搜索**: title_embedding字段用于语义搜索,需要配合文本查询使用 537 4. **向量搜索**: title_embedding字段用于语义搜索,需要配合文本查询使用
307 5. **Boost权重**: 不同字段的boost权重影响搜索结果的相关性排序 538 5. **Boost权重**: 不同字段的boost权重影响搜索结果的相关性排序
308 539
309 -## 数据来源表结构  
310 -  
311 -### SPU表(shoplazza_product_spu)  
312 -  
313 -主要字段:  
314 -- `id`: BIGINT - 主键ID  
315 -- `tenant_id`: BIGINT - 租户ID  
316 -- `handle`: VARCHAR(255) - URL handle  
317 -- `title`: VARCHAR(512) - 商品标题  
318 -- `brief`: VARCHAR(512) - 商品简介  
319 -- `description`: TEXT - 商品描述  
320 -- `vendor`: VARCHAR(255) - 供应商/品牌  
321 -- `category`: VARCHAR(255) - 类目  
322 -- `tags`: VARCHAR(1024) - 标签  
323 -- `seo_title`: VARCHAR(512) - SEO标题  
324 -- `seo_description`: TEXT - SEO描述  
325 -- `seo_keywords`: VARCHAR(1024) - SEO关键词  
326 -- `image_src`: VARCHAR(500) - 图片URL  
327 -- `create_time`: DATETIME - 创建时间  
328 -- `update_time`: DATETIME - 更新时间  
329 -- `shoplazza_created_at`: DATETIME - 店匠创建时间  
330 -- `shoplazza_updated_at`: DATETIME - 店匠更新时间  
331 -  
332 -spu表全部字段  
333 -"Field" "Type" "Null" "Key" "Default" "Extra"  
334 -"id" "bigint(20)" "NO" "PRI" "auto_increment"  
335 -"shop_id" "bigint(20)" "NO" "MUL" ""  
336 -"shoplazza_id" "varchar(64)" "NO" "" ""  
337 -"handle" "varchar(255)" "YES" "MUL" ""  
338 -"title" "varchar(500)" "NO" "" ""  
339 -"brief" "varchar(1000)" "YES" "" ""  
340 -"description" "text" "YES" "" ""  
341 -"spu" "varchar(100)" "YES" "" ""  
342 -"vendor" "varchar(255)" "YES" "" ""  
343 -"vendor_url" "varchar(500)" "YES" "" ""  
344 -"seo_title" "varchar(500)" "YES" "" ""  
345 -"seo_description" "text" "YES" "" ""  
346 -"seo_keywords" "text" "YES" "" ""  
347 -"image_src" "varchar(500)" "YES" "" ""  
348 -"image_width" "int(11)" "YES" "" ""  
349 -"image_height" "int(11)" "YES" "" ""  
350 -"image_path" "varchar(255)" "YES" "" ""  
351 -"image_alt" "varchar(500)" "YES" "" ""  
352 -"inventory_policy" "varchar(50)" "YES" "" ""  
353 -"inventory_quantity" "int(11)" "YES" "" "0" ""  
354 -"inventory_tracking" "tinyint(1)" "YES" "" "0" ""  
355 -"published" "tinyint(1)" "YES" "" "0" ""  
356 -"published_at" "datetime" "YES" "MUL" ""  
357 -"requires_shipping" "tinyint(1)" "YES" "" "1" ""  
358 -"taxable" "tinyint(1)" "YES" "" "0" ""  
359 -"fake_sales" "int(11)" "YES" "" "0" ""  
360 -"display_fake_sales" "tinyint(1)" "YES" "" "0" ""  
361 -"mixed_wholesale" "tinyint(1)" "YES" "" "0" ""  
362 -"need_variant_image" "tinyint(1)" "YES" "" "0" ""  
363 -"has_only_default_variant" "tinyint(1)" "YES" "" "0" ""  
364 -"tags" "text" "YES" "" ""  
365 -"note" "text" "YES" "" ""  
366 -"category" "varchar(255)" "YES" "" ""  
367 -"shoplazza_created_at" "datetime" "YES" "" ""  
368 -"shoplazza_updated_at" "datetime" "YES" "MUL" ""  
369 -"tenant_id" "bigint(20)" "NO" "MUL" ""  
370 -"creator" "varchar(64)" "YES" "" "" ""  
371 -"create_time" "datetime" "NO" "" "CURRENT_TIMESTAMP" ""  
372 -"updater" "varchar(64)" "YES" "" "" ""  
373 -"update_time" "datetime" "NO" "" "CURRENT_TIMESTAMP" "on update CURRENT_TIMESTAMP"  
374 -"deleted" "bit(1)" "NO" "" "b'0'" ""  
375 -  
376 -  
377 -  
378 -  
379 -### SKU表(shoplazza_product_sku)  
380 -  
381 -主要字段:  
382 -- `id`: BIGINT - 主键ID(对应variant_id)  
383 -- `spu_id`: BIGINT - SPU ID(关联字段)  
384 -- `title`: VARCHAR(500) - 变体标题  
385 -- `price`: DECIMAL(10,2) - 价格  
386 -- `compare_at_price`: DECIMAL(10,2) - 原价  
387 -- `sku`: VARCHAR(100) - SKU编码  
388 -- `inventory_quantity`: INT(11) - 库存数量  
389 -- `option1`: VARCHAR(255) - 选项1  
390 -- `option2`: VARCHAR(255) - 选项2  
391 -- `option3`: VARCHAR(255) - 选项3  
392 -  
393 -sku全部字段  
394 -"Field" "Type" "Null" "Key" "Default" "Extra"  
395 -"id" "bigint(20)" "NO" "PRI" "auto_increment"  
396 -"spu_id" "bigint(20)" "NO" "MUL" ""  
397 -"shop_id" "bigint(20)" "NO" "MUL" ""  
398 -"shoplazza_id" "varchar(64)" "NO" "" ""  
399 -"shoplazza_product_id" "varchar(64)" "NO" "MUL" ""  
400 -"shoplazza_image_id" "varchar(64)" "YES" "" ""  
401 -"title" "varchar(500)" "YES" "" ""  
402 -"sku" "varchar(100)" "YES" "MUL" ""  
403 -"barcode" "varchar(100)" "YES" "" ""  
404 -"position" "int(11)" "YES" "" "0" ""  
405 -"price" "decimal(10,2)" "YES" "" ""  
406 -"compare_at_price" "decimal(10,2)" "YES" "" ""  
407 -"cost_price" "decimal(10,2)" "YES" "" ""  
408 -"option1" "varchar(255)" "YES" "" ""  
409 -"option2" "varchar(255)" "YES" "" ""  
410 -"option3" "varchar(255)" "YES" "" ""  
411 -"inventory_quantity" "int(11)" "YES" "" "0" ""  
412 -"weight" "decimal(10,2)" "YES" "" ""  
413 -"weight_unit" "varchar(10)" "YES" "" ""  
414 -"image_src" "varchar(500)" "YES" "" ""  
415 -"wholesale_price" "json" "YES" "" ""  
416 -"note" "text" "YES" "" ""  
417 -"extend" "json" "YES" "" ""  
418 -"shoplazza_created_at" "datetime" "YES" "" ""  
419 -"shoplazza_updated_at" "datetime" "YES" "" ""  
420 -"tenant_id" "bigint(20)" "NO" "MUL" ""  
421 -"creator" "varchar(64)" "YES" "" "" ""  
422 -"create_time" "datetime" "NO" "" "CURRENT_TIMESTAMP" ""  
423 -"updater" "varchar(64)" "YES" "" "" ""  
424 -"update_time" "datetime" "NO" "" "CURRENT_TIMESTAMP" "on update CURRENT_TIMESTAMP"  
425 -"deleted" "bit(1)" "NO" "" "b'0'" ""  
426 -  
427 540
428 ## TODO 541 ## TODO
429 多语言问题。 542 多语言问题。
scripts/csv_to_excel.py 0 → 100755
@@ -0,0 +1,355 @@ @@ -0,0 +1,355 @@
  1 +#!/usr/bin/env python3
  2 +"""
  3 +Convert CSV data to Excel import template.
  4 +
  5 +Reads CSV file (goods_with_pic.5years_congku.csv.shuf.1w) and generates Excel file
  6 +based on the template format (商品导入模板.xlsx).
  7 +
  8 +Each CSV row corresponds to 1 SPU and 1 SKU, which will be exported as a single
  9 +S (Single variant) row in the Excel template.
  10 +"""
  11 +
  12 +import sys
  13 +import os
  14 +import csv
  15 +import random
  16 +import argparse
  17 +import re
  18 +from pathlib import Path
  19 +from datetime import datetime, timedelta
  20 +import pandas as pd
  21 +from openpyxl import load_workbook
  22 +from openpyxl.styles import Font, Alignment
  23 +from openpyxl.utils import get_column_letter
  24 +
  25 +# Add parent directory to path
  26 +sys.path.insert(0, str(Path(__file__).parent.parent))
  27 +
  28 +
  29 +def clean_value(value):
  30 + """
  31 + Clean and normalize value.
  32 +
  33 + Args:
  34 + value: Value to clean
  35 +
  36 + Returns:
  37 + Cleaned string value
  38 + """
  39 + if value is None:
  40 + return ''
  41 + value = str(value).strip()
  42 + # Remove surrounding quotes
  43 + if value.startswith('"') and value.endswith('"'):
  44 + value = value[1:-1]
  45 + return value
  46 +
  47 +
  48 +def parse_csv_row(row: dict) -> dict:
  49 + """
  50 + Parse CSV row and extract fields.
  51 +
  52 + Args:
  53 + row: CSV row dictionary
  54 +
  55 + Returns:
  56 + Parsed data dictionary
  57 + """
  58 + return {
  59 + 'skuId': clean_value(row.get('skuId', '')),
  60 + 'name': clean_value(row.get('name', '')),
  61 + 'name_pinyin': clean_value(row.get('name_pinyin', '')),
  62 + 'create_time': clean_value(row.get('create_time', '')),
  63 + 'ruSkuName': clean_value(row.get('ruSkuName', '')),
  64 + 'enSpuName': clean_value(row.get('enSpuName', '')),
  65 + 'categoryName': clean_value(row.get('categoryName', '')),
  66 + 'supplierName': clean_value(row.get('supplierName', '')),
  67 + 'brandName': clean_value(row.get('brandName', '')),
  68 + 'file_id': clean_value(row.get('file_id', '')),
  69 + 'days_since_last_update': clean_value(row.get('days_since_last_update', '')),
  70 + 'id': clean_value(row.get('id', '')),
  71 + 'imageUrl': clean_value(row.get('imageUrl', ''))
  72 + }
  73 +
  74 +
  75 +def generate_handle(title: str) -> str:
  76 + """
  77 + Generate URL-friendly handle from title.
  78 +
  79 + Args:
  80 + title: Product title
  81 +
  82 + Returns:
  83 + URL-friendly handle (ASCII only)
  84 + """
  85 + # Convert to lowercase
  86 + handle = title.lower()
  87 +
  88 + # Remove non-ASCII characters, keep only letters, numbers, spaces, and hyphens
  89 + handle = re.sub(r'[^a-z0-9\s-]', '', handle)
  90 +
  91 + # Replace spaces and multiple hyphens with single hyphen
  92 + handle = re.sub(r'[-\s]+', '-', handle)
  93 + handle = handle.strip('-')
  94 +
  95 + # Limit length
  96 + if len(handle) > 255:
  97 + handle = handle[:255]
  98 +
  99 + return handle or 'product'
  100 +
  101 +
  102 +def read_csv_file(csv_file: str) -> list:
  103 + """
  104 + Read CSV file and return list of parsed rows.
  105 +
  106 + Args:
  107 + csv_file: Path to CSV file
  108 +
  109 + Returns:
  110 + List of parsed CSV data dictionaries
  111 + """
  112 + csv_data_list = []
  113 +
  114 + with open(csv_file, 'r', encoding='utf-8') as f:
  115 + reader = csv.DictReader(f)
  116 + for row in reader:
  117 + parsed = parse_csv_row(row)
  118 + csv_data_list.append(parsed)
  119 +
  120 + return csv_data_list
  121 +
  122 +
  123 +def csv_to_excel_row(csv_data: dict) -> dict:
  124 + """
  125 + Convert CSV data row to Excel template row.
  126 +
  127 + Each CSV row represents a single product with one variant (S type in Excel).
  128 +
  129 + Args:
  130 + csv_data: Parsed CSV row data
  131 +
  132 + Returns:
  133 + Dictionary mapping Excel column names to values
  134 + """
  135 + # Parse create_time
  136 + try:
  137 + created_at = datetime.strptime(csv_data['create_time'], '%Y-%m-%d %H:%M:%S')
  138 + create_time_str = created_at.strftime('%Y-%m-%d %H:%M:%S')
  139 + except:
  140 + created_at = datetime.now() - timedelta(days=random.randint(1, 365))
  141 + create_time_str = created_at.strftime('%Y-%m-%d %H:%M:%S')
  142 +
  143 + # Generate title - use name or enSpuName
  144 + title = csv_data['name'] or csv_data['enSpuName'] or 'Product'
  145 +
  146 + # Generate handle - prefer enSpuName, then name_pinyin, then title
  147 + handle_source = csv_data['enSpuName'] or csv_data['name_pinyin'] or title
  148 + handle = generate_handle(handle_source)
  149 + if handle and not handle.startswith('products/'):
  150 + handle = f'products/{handle}'
  151 +
  152 + # Generate SEO fields
  153 + seo_title = f"{title} - {csv_data['categoryName']}" if csv_data['categoryName'] else title
  154 + seo_description = f"购买{csv_data['brandName']}{title}" if csv_data['brandName'] else title
  155 + seo_keywords_parts = [title]
  156 + if csv_data['categoryName']:
  157 + seo_keywords_parts.append(csv_data['categoryName'])
  158 + if csv_data['brandName']:
  159 + seo_keywords_parts.append(csv_data['brandName'])
  160 + seo_keywords = ','.join(seo_keywords_parts)
  161 +
  162 + # Generate tags from category and brand
  163 + tags_parts = []
  164 + if csv_data['categoryName']:
  165 + tags_parts.append(csv_data['categoryName'])
  166 + if csv_data['brandName']:
  167 + tags_parts.append(csv_data['brandName'])
  168 + tags = ','.join(tags_parts) if tags_parts else ''
  169 +
  170 + # Generate prices (similar to import_tenant2_csv.py)
  171 + price = round(random.uniform(50, 500), 2)
  172 + compare_at_price = round(price * random.uniform(1.2, 1.5), 2)
  173 + cost_price = round(price * 0.6, 2)
  174 +
  175 + # Generate random stock
  176 + inventory_quantity = random.randint(0, 100)
  177 +
  178 + # Generate random weight
  179 + weight = round(random.uniform(0.1, 5.0), 2)
  180 + weight_unit = 'kg'
  181 +
  182 + # Use ruSkuName as SKU title, fallback to name
  183 + sku_title = csv_data['ruSkuName'] or csv_data['name'] or 'SKU'
  184 +
  185 + # Use skuId as SKU code
  186 + sku_code = csv_data['skuId'] or ''
  187 +
  188 + # Generate barcode
  189 + try:
  190 + sku_id = int(csv_data['skuId'])
  191 + barcode = f"BAR{sku_id:08d}"
  192 + except:
  193 + barcode = ''
  194 +
  195 + # Build description
  196 + description = f"<p>{csv_data['name']}</p>" if csv_data['name'] else ''
  197 +
  198 + # Build brief (subtitle)
  199 + brief = csv_data['name'] or ''
  200 +
  201 + # Excel row data (mapping to Excel template columns)
  202 + excel_row = {
  203 + '商品ID': '', # Empty for new products
  204 + '创建时间': create_time_str,
  205 + '商品标题*': title,
  206 + '商品属性*': 'S', # Single variant product
  207 + '商品副标题': brief,
  208 + '商品描述': description,
  209 + 'SEO标题': seo_title,
  210 + 'SEO描述': seo_description,
  211 + 'SEO URL Handle': handle,
  212 + 'SEO URL 重定向': 'N', # Default to N
  213 + 'SEO关键词': seo_keywords,
  214 + '商品上架': 'Y', # Published by default
  215 + '需要物流': 'Y', # Requires shipping
  216 + '商品收税': 'N', # Not taxable by default
  217 + '商品spu': '', # Empty
  218 + '启用虚拟销量': 'N', # No fake sales
  219 + '虚拟销量值': '', # Empty
  220 + '跟踪库存': 'Y', # Track inventory
  221 + '库存规则*': '1', # Allow purchase when stock is 0
  222 + '专辑名称': csv_data['categoryName'] or '', # Category as album
  223 + '标签': tags,
  224 + '供应商名称': csv_data['supplierName'] or '',
  225 + '供应商URL': '', # Empty
  226 + '款式1': '', # Not used for S type
  227 + '款式2': '', # Not used for S type
  228 + '款式3': '', # Not used for S type
  229 + '商品售价*': price,
  230 + '商品原价': compare_at_price,
  231 + '成本价': cost_price,
  232 + '商品SKU': sku_code,
  233 + '商品重量': weight,
  234 + '重量单位': weight_unit,
  235 + '商品条形码': barcode,
  236 + '商品库存': inventory_quantity,
  237 + '尺寸信息': '', # Empty
  238 + '原产地国别': '', # Empty
  239 + 'HS(协调制度)代码': '', # Empty
  240 + '商品图片*': csv_data['imageUrl'] or '', # Image URL
  241 + '商品备注': '', # Empty
  242 + '款式备注': '', # Empty
  243 + '商品主图': csv_data['imageUrl'] or '', # Main image URL
  244 + }
  245 +
  246 + return excel_row
  247 +
  248 +
  249 +def create_excel_from_template(template_file: str, output_file: str, csv_data_list: list):
  250 + """
  251 + Create Excel file from template and fill with CSV data.
  252 +
  253 + Args:
  254 + template_file: Path to Excel template file
  255 + output_file: Path to output Excel file
  256 + csv_data_list: List of parsed CSV data dictionaries
  257 + """
  258 + # Load template
  259 + wb = load_workbook(template_file)
  260 + ws = wb.active # Use the active sheet (Sheet4)
  261 +
  262 + # Find header row (row 2, index 1)
  263 + header_row_idx = 2 # Row 2 in Excel (1-based, but header is at index 1 in pandas)
  264 +
  265 + # Get column mapping from header row
  266 + column_mapping = {}
  267 + for col_idx in range(1, ws.max_column + 1):
  268 + cell_value = ws.cell(row=header_row_idx, column=col_idx).value
  269 + if cell_value:
  270 + column_mapping[cell_value] = col_idx
  271 +
  272 + # Start writing data from row 4 (after header and instructions)
  273 + data_start_row = 4 # Row 4 in Excel (1-based)
  274 +
  275 + # Clear existing data rows (from row 4 onwards, but keep header and instructions)
  276 + # Find the last row with data in the template
  277 + last_template_row = ws.max_row
  278 + if last_template_row >= data_start_row:
  279 + # Clear data rows (keep header and instruction rows)
  280 + for row in range(data_start_row, last_template_row + 1):
  281 + for col in range(1, ws.max_column + 1):
  282 + ws.cell(row=row, column=col).value = None
  283 +
  284 + # Convert CSV data to Excel rows
  285 + for row_idx, csv_data in enumerate(csv_data_list):
  286 + excel_row = csv_to_excel_row(csv_data)
  287 + excel_row_num = data_start_row + row_idx
  288 +
  289 + # Write each field to corresponding column
  290 + for field_name, col_idx in column_mapping.items():
  291 + if field_name in excel_row:
  292 + cell = ws.cell(row=excel_row_num, column=col_idx)
  293 + value = excel_row[field_name]
  294 + cell.value = value
  295 +
  296 + # Set alignment for text fields
  297 + if isinstance(value, str):
  298 + cell.alignment = Alignment(vertical='top', wrap_text=True)
  299 + elif isinstance(value, (int, float)):
  300 + cell.alignment = Alignment(vertical='top')
  301 +
  302 + # Save workbook
  303 + wb.save(output_file)
  304 + print(f"Excel file created: {output_file}")
  305 + print(f" - Total rows: {len(csv_data_list)}")
  306 +
  307 +
  308 +def main():
  309 + parser = argparse.ArgumentParser(description='Convert CSV data to Excel import template')
  310 + parser.add_argument('--csv-file',
  311 + default='data/customer1/goods_with_pic.5years_congku.csv.shuf.1w',
  312 + help='CSV file path (default: data/customer1/goods_with_pic.5years_congku.csv.shuf.1w)')
  313 + parser.add_argument('--template',
  314 + default='docs/商品导入模板.xlsx',
  315 + help='Excel template file path (default: docs/商品导入模板.xlsx)')
  316 + parser.add_argument('--output',
  317 + default='商品导入数据.xlsx',
  318 + help='Output Excel file path (default: 商品导入数据.xlsx)')
  319 + parser.add_argument('--limit',
  320 + type=int,
  321 + default=None,
  322 + help='Limit number of rows to process (default: all)')
  323 +
  324 + args = parser.parse_args()
  325 +
  326 + # Check if files exist
  327 + if not os.path.exists(args.csv_file):
  328 + print(f"Error: CSV file not found: {args.csv_file}")
  329 + sys.exit(1)
  330 +
  331 + if not os.path.exists(args.template):
  332 + print(f"Error: Template file not found: {args.template}")
  333 + sys.exit(1)
  334 +
  335 + # Read CSV file
  336 + print(f"Reading CSV file: {args.csv_file}")
  337 + csv_data_list = read_csv_file(args.csv_file)
  338 + print(f"Read {len(csv_data_list)} rows from CSV")
  339 +
  340 + # Limit rows if specified
  341 + if args.limit:
  342 + csv_data_list = csv_data_list[:args.limit]
  343 + print(f"Limited to {len(csv_data_list)} rows")
  344 +
  345 + # Create Excel file
  346 + print(f"Creating Excel file from template: {args.template}")
  347 + print(f"Output file: {args.output}")
  348 + create_excel_from_template(args.template, args.output, csv_data_list)
  349 +
  350 + print(f"\nDone! Generated {len(csv_data_list)} product rows in Excel file.")
  351 +
  352 +
  353 +if __name__ == '__main__':
  354 + main()
  355 +
scripts/tenant3__csv_to_shoplazza_xlsx.sh 0 → 100644
@@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
  1 +# 激活环境
  2 +source /home/tw/miniconda3/etc/profile.d/conda.sh
  3 +conda activate searchengine
  4 +
  5 +# # 基本使用(生成所有数据)
  6 +# python scripts/csv_to_excel.py
  7 +
  8 +# # 指定输出文件
  9 +# python scripts/csv_to_excel.py --output tenant3_imports.xlsx
  10 +
  11 +# # 限制处理行数(用于测试)
  12 +# python scripts/csv_to_excel.py --limit 100
  13 +
  14 +# 指定CSV文件和模板文件
  15 +python scripts/csv_to_excel.py \
  16 + --csv-file data/customer1/goods_with_pic.5years_congku.csv.shuf.1w \
  17 + --template docs/商品导入模板.xlsx \
  18 + --output tenant3_imports.xlsx
0 \ No newline at end of file 19 \ No newline at end of file