Commit d3a4e2594f0d1337504b0036c559c6e622dbc80f

Authored by tangwang
1 parent 1f6d15fa

相关文档

.cursor/plans/API响应格式优化与SPU索引重构.2.md 0 → 100644
... ... @@ -0,0 +1,258 @@
  1 +<!-- b5a93a00-49d7-4266-8dbf-3d3f708334ed 9b4e4bac-df19-49d4-a775-e5ec954010bd -->
  2 +# API响应格式优化与SPU索引重构
  3 +
  4 +## 概述
  5 +
  6 +重构搜索系统以支持:
  7 +
  8 +1. 外部接口友好的API响应格式(移除ES内部格式)
  9 +2. SPU维度的索引结构(包含嵌套variants数组)
  10 +3. 所有客户共用同一索引(使用tenant_id隔离)
  11 +4. 配置简化(移除MySQL相关配置,只保留ES搜索配置)
  12 +5. 添加新客户(customer2)测试数据和配置
  13 +
  14 +## Phase 1: 配置文件重构
  15 +
  16 +### 1.1 创建BASE配置文件
  17 +
  18 +**文件**: [`config/schema/base/config.yaml`](config/schema/base/config.yaml) (NEW)
  19 +
  20 +创建通用配置文件,所有使用店匠表的客户共用:
  21 +
  22 +- 移除 `mysql_config`, `main_table`, `extension_table`, `source_table`, `source_column`
  23 +- 固定索引名称:`search_products`
  24 +- SPU级别字段定义(包含嵌套variants)
  25 +- 必需字段:`tenant_id` (KEYWORD, required)
  26 +- 扁平化价格字段:`min_price`, `max_price`, `compare_at_price`
  27 +- 多语言字段:`title_zh`, `title_en`, `description_zh`, `description_en` 等
  28 +- 嵌套variants结构定义
  29 +
  30 +### 1.2 创建customer2配置文件
  31 +
  32 +**文件**: [`config/schema/customer2/config.yaml`](config/schema/customer2/config.yaml) (NEW)
  33 +
  34 +基于BASE配置,为customer2创建配置文件:
  35 +
  36 +- 继承BASE配置结构
  37 +- 定义customer2特定的字段和搜索域
  38 +- 支持多语言(中文、英文)
  39 +
  40 +### 1.3 更新配置加载器
  41 +
  42 +**文件**: [`config/config_loader.py`](config/config_loader.py)
  43 +
  44 +修改:
  45 +
  46 +- 移除 `mysql_config`, `main_table`, `extension_table` 解析
  47 +- 移除字段配置中的 `source_table`, `source_column` 解析
  48 +- 固定 `es_index_name` 为 `search_products`
  49 +- 添加 `tenant_id` 字段验证(必需字段)
  50 +- 支持嵌套字段配置(variants)
  51 +
  52 +### 1.4 更新字段类型定义
  53 +
  54 +**文件**: [`config/field_types.py`](config/field_types.py)
  55 +
  56 +修改:
  57 +
  58 +- 移除 `FieldConfig` 中的 `source_table`, `source_column`
  59 +- 添加嵌套字段支持(nested fields)
  60 +- 添加扁平化价格字段类型
  61 +
  62 +## Phase 2: 索引结构重构(SPU维度)
  63 +
  64 +### 2.1 更新Mapping生成器
  65 +
  66 +**文件**: [`indexer/mapping_generator.py`](indexer/mapping_generator.py)
  67 +
  68 +修改:
  69 +
  70 +- 生成SPU级别的mapping
  71 +- 添加 `tenant_id` 字段(KEYWORD, required)
  72 +- 添加嵌套 `variants` 字段(nested type)
  73 +- 添加扁平化价格字段(`min_price`, `max_price`, `compare_at_price`)
  74 +- 移除SKU级别的字段映射
  75 +
  76 +### 2.2 创建SPU数据转换器
  77 +
  78 +**文件**: [`indexer/spu_transformer.py`](indexer/spu_transformer.py) (NEW)
  79 +
  80 +创建SPU数据转换器:
  81 +
  82 +- 从MySQL读取SPU和SKU数据
  83 +- 按SPU聚合SKU数据为variants数组
  84 +- 计算扁平化价格字段(min_price, max_price, compare_at_price)
  85 +- 生成SPU级别的ES文档
  86 +- 注入 `tenant_id` 字段
  87 +
  88 +### 2.3 更新数据导入脚本
  89 +
  90 +**文件**: [`scripts/ingest_shoplazza.py`](scripts/ingest_shoplazza.py) (NEW)
  91 +
  92 +创建店匠数据导入脚本:
  93 +
  94 +- 从MySQL读取 `shoplazza_product_spu` 和 `shoplazza_product_sku` 表
  95 +- 按 `spu_id` 和 `tenant_id` 关联数据
  96 +- 使用SPU转换器转换数据
  97 +- 批量导入到ES索引 `search_products`
  98 +- 支持 `--tenant-id` 参数
  99 +
  100 +## Phase 3: API响应格式重构
  101 +
  102 +### 3.1 更新响应模型
  103 +
  104 +**文件**: [`api/models.py`](api/models.py)
  105 +
  106 +修改 `SearchResponse` 模型:
  107 +
  108 +- 将 `hits` 改为 `results` (List[ProductResult])
  109 +- 添加 `ProductResult` 模型(包含product_id, title, variants, relevance_score等)
  110 +- 添加 `VariantResult` 模型(包含variant_id, title, price, sku等)
  111 +- 保持 `facets` 格式
  112 +- 添加 `suggestions` 和 `related_searches` 字段(暂时返回空数组)
  113 +
  114 +### 3.2 创建结果转换器
  115 +
  116 +**文件**: [`api/result_formatter.py`](api/result_formatter.py) (NEW)
  117 +
  118 +创建结果格式化器:
  119 +
  120 +- 将ES返回的 `_hits` 格式转换为外部接口格式
  121 +- 提取SPU级别字段
  122 +- 提取嵌套variants数组
  123 +- 计算 `relevance_score`(从 `_score` 转换)
  124 +- 格式化facets结果
  125 +- 生成suggestions和related_searches(暂时返回空数组)
  126 +
  127 +### 3.3 更新搜索器
  128 +
  129 +**文件**: [`search/searcher.py`](search/searcher.py)
  130 +
  131 +修改:
  132 +
  133 +- 在搜索查询中添加 `tenant_id` 过滤(必需)
  134 +- 更新结果处理逻辑,使用结果格式化器
  135 +- 移除ES内部格式字段(`_id`, `_score`, `_source`)
  136 +- 返回格式化的外部接口格式
  137 +
  138 +### 3.4 更新API路由
  139 +
  140 +**文件**: [`api/routes/search.py`](api/routes/search.py)
  141 +
  142 +修改:
  143 +
  144 +- 添加 `tenant_id` 参数(从请求头或查询参数获取)
  145 +- 在搜索请求中添加 `tenant_id` 过滤
  146 +- 使用结果格式化器格式化响应
  147 +- 返回新的响应格式
  148 +
  149 +## Phase 4: 测试数据生成
  150 +
  151 +### 4.1 创建测试数据生成脚本
  152 +
  153 +**文件**: [`scripts/generate_test_data.py`](scripts/generate_test_data.py) (NEW)
  154 +
  155 +创建测试数据生成脚本:
  156 +
  157 +- 生成100条SPU测试数据
  158 +- 为每个SPU生成1-5个SKU变体
  159 +- 包含中文和英文标题
  160 +- 包含价格、库存、图片等字段
  161 +- 输出为MySQL INSERT语句或CSV文件
  162 +
  163 +### 4.2 创建数据导入脚本
  164 +
  165 +**文件**: [`scripts/import_test_data.py`](scripts/import_test_data.py) (NEW)
  166 +
  167 +创建数据导入脚本:
  168 +
  169 +- 连接MySQL数据库
  170 +- 导入测试数据到 `shoplazza_product_spu` 和 `shoplazza_product_sku` 表
  171 +- 设置 `tenant_id` 为customer2的ID
  172 +- 验证数据导入结果
  173 +
  174 +## Phase 5: 测试脚本和文档
  175 +
  176 +### 5.1 创建测试脚本
  177 +
  178 +**文件**: [`scripts/test_customer2.py`](scripts/test_customer2.py) (NEW)
  179 +
  180 +创建customer2测试脚本:
  181 +
  182 +- 测试数据导入
  183 +- 测试搜索API
  184 +- 测试多语言搜索
  185 +- 测试facets聚合
  186 +- 验证响应格式
  187 +
  188 +### 5.2 创建说明文档
  189 +
  190 +**文件**: [`docs/CUSTOMER2_TEST_GUIDE.md`](docs/CUSTOMER2_TEST_GUIDE.md) (NEW)
  191 +
  192 +创建customer2测试指南:
  193 +
  194 +- 数据导入步骤
  195 +- 配置说明
  196 +- API测试示例
  197 +- 常见问题解答
  198 +
  199 +## Phase 6: 更新设计文档
  200 +
  201 +### 6.1 更新设计文档
  202 +
  203 +**文件**: [`设计文档.md`](设计文档.md)
  204 +
  205 +修改:
  206 +
  207 +- 更新索引结构说明(SPU维度,所有客户共用)
  208 +- 更新配置说明(移除MySQL相关配置)
  209 +- 更新API响应格式说明
  210 +- 更新数据导入流程说明
  211 +- 添加customer2测试说明
  212 +
  213 +## 关键修改点
  214 +
  215 +1. **索引结构**:
  216 +
  217 +- 索引名称:`search_products`(所有客户共用)
  218 +- 索引粒度:SPU级别
  219 +- 租户隔离:使用 `tenant_id` 字段过滤
  220 +- 嵌套结构:`variants` 数组包含SKU数据
  221 +
  222 +2. **配置简化**:
  223 +
  224 +- 移除 `mysql_config`, `main_table`, `extension_table`
  225 +- 移除字段配置中的 `source_table`, `source_column`
  226 +- 只保留ES搜索相关配置
  227 +
  228 +3. **API响应格式**:
  229 +
  230 +- 从 `_hits`, `_source`, `_score` 改为 `results`, `product_id`, `title`, `relevance_score`
  231 +- 添加 `variants` 数组
  232 +- 添加 `suggestions` 和 `related_searches`(暂时返回空数组)
  233 +
  234 +4. **数据导入**:
  235 +
  236 +- 从MySQL读取SPU和SKU数据
  237 +- 按SPU聚合SKU数据
  238 +- 生成SPU级别的ES文档
  239 +- 注入 `tenant_id` 字段
  240 +
  241 +### To-dos
  242 +
  243 +- [ ] 创建BASE配置文件(config/schema/base/config.yaml),移除MySQL相关配置,定义SPU级别字段和嵌套variants结构
  244 +- [ ] 创建customer2配置文件(config/schema/customer2/config.yaml),基于BASE配置定义customer2特定字段
  245 +- [ ] 更新配置加载器(config/config_loader.py),移除MySQL相关配置解析,支持嵌套字段和tenant_id验证
  246 +- [ ] 更新字段类型定义(config/field_types.py),移除source_table和source_column,添加嵌套字段支持
  247 +- [ ] 更新Mapping生成器(indexer/mapping_generator.py),生成SPU级别mapping,添加tenant_id和嵌套variants字段
  248 +- [ ] 创建SPU数据转换器(indexer/spu_transformer.py),从MySQL读取SPU和SKU数据,按SPU聚合为variants数组
  249 +- [ ] 创建店匠数据导入脚本(scripts/ingest_shoplazza.py),支持从MySQL导入SPU和SKU数据到ES
  250 +- [ ] 更新响应模型(api/models.py),添加ProductResult和VariantResult模型,修改SearchResponse格式
  251 +- [ ] 创建结果格式化器(api/result_formatter.py),将ES返回格式转换为外部接口友好格式
  252 +- [ ] 更新搜索器(search/searcher.py),添加tenant_id过滤,使用结果格式化器格式化响应
  253 +- [ ] 更新API路由(api/routes/search.py),添加tenant_id参数,返回新的响应格式
  254 +- [ ] 创建测试数据生成脚本(scripts/generate_test_data.py),生成100条SPU测试数据
  255 +- [ ] 创建数据导入脚本(scripts/import_test_data.py),导入测试数据到MySQL
  256 +- [ ] 创建customer2测试脚本(scripts/test_customer2.py),测试数据导入和搜索API
  257 +- [ ] 创建customer2测试指南(docs/CUSTOMER2_TEST_GUIDE.md),包含数据导入步骤和API测试示例
  258 +- [ ] 更新设计文档(设计文档.md),更新索引结构、配置说明和API响应格式说明
0 259 \ No newline at end of file
... ...
.cursor/plans/API响应格式优化与SPU索引重构.4.md 0 → 100644
... ... @@ -0,0 +1,307 @@
  1 +<!-- 2bec2252-690d-478e-9ce8-bc9073ec23ae c3f51467-b81b-47b7-869f-3da700299e26 -->
  2 +# API响应格式优化与SPU索引重构
  3 +
  4 +## 概述
  5 +
  6 +重构搜索系统以实现:
  7 +
  8 +1. 外部接口友好的API响应格式(移除ES内部格式 `_hits`, `_source`, `_score`)
  9 +2. SPU维度的索引结构(包含嵌套variants数组)
  10 +3. 所有客户共用同一索引(使用tenant_id隔离)
  11 +4. 配置简化(完全移除MySQL相关配置,只保留ES搜索配置)
  12 +5. 创建base配置(店匠通用配置)和SPU数据导入流程
  13 +
  14 +## 核心设计原则
  15 +
  16 +- **配置只关注ES搜索**:配置文件只包含ES字段定义、查询域、排序规则等搜索相关配置
  17 +- **数据灌入流程写死**:数据来源和转换逻辑由Pipeline层(脚本)决定,不在配置中体现
  18 +- **代码简洁无冗余**:不做向后兼容,直接删除不需要的代码
  19 +
  20 +## Phase 1: 配置系统重构
  21 +
  22 +### 1.1 创建BASE配置文件
  23 +
  24 +**文件**: [`config/schema/base/config.yaml`](config/schema/base/config.yaml) (NEW)
  25 +
  26 +创建店匠通用配置文件:
  27 +
  28 +- **不包含**:`mysql_config`, `main_table`, `extension_table`, `source_table`, `source_column`
  29 +- **固定索引名称**:`search_products`
  30 +- **SPU级别字段定义**:
  31 +- `tenant_id` (KEYWORD, required)
  32 +- `product_id` (对应SPU的id)
  33 +- `title_zh`, `title_en` (多语言标题)
  34 +- `description_zh`, `description_en` (多语言描述)
  35 +- `min_price`, `max_price`, `compare_at_price` (扁平化价格)
  36 +- `vendor`, `product_type`, `tags` 等
  37 +- **嵌套variants字段定义**:
  38 +- `variants` (nested type)
  39 +- variants包含:`variant_id`, `title`, `price`, `sku`, `stock`, `options` 等
  40 +
  41 +### 1.2 移除配置中的MySQL相关字段
  42 +
  43 +**文件**: [`config/config_loader.py`](config/config_loader.py)
  44 +
  45 +修改:
  46 +
  47 +- **删除** `CustomerConfig` 中的 `mysql_config`, `main_table`, `extension_table` 字段定义
  48 +- **删除** `_parse_config` 中解析这些字段的代码
  49 +- **删除** `_parse_field_config` 中解析 `source_table`, `source_column` 的代码
  50 +- 保持其他配置解析逻辑不变
  51 +
  52 +**文件**: [`config/field_types.py`](config/field_types.py)
  53 +
  54 +修改:
  55 +
  56 +- **删除** `FieldConfig` 中的 `source_table`, `source_column` 字段定义
  57 +- 保持其他字段类型定义不变
  58 +
  59 +### 1.3 更新配置验证
  60 +
  61 +**文件**: [`config/config_loader.py`](config/config_loader.py)
  62 +
  63 +修改:
  64 +
  65 +- 添加 `tenant_id` 字段验证(必需字段)
  66 +- 移除所有与MySQL/数据源相关的验证逻辑
  67 +
  68 +## Phase 2: 索引结构重构(SPU维度)
  69 +
  70 +### 2.1 更新Mapping生成器
  71 +
  72 +**文件**: [`indexer/mapping_generator.py`](indexer/mapping_generator.py)
  73 +
  74 +修改:
  75 +
  76 +- 在 `_generate_mappings` 中:
  77 +- 添加 `tenant_id` 字段(KEYWORD, required)
  78 +- 支持嵌套 `variants` 字段(nested type)
  79 +- 支持扁平化价格字段(`min_price`, `max_price`, `compare_at_price`)
  80 +- 确保所有字段映射正确生成
  81 +
  82 +### 2.2 创建SPU数据转换器
  83 +
  84 +**文件**: [`indexer/spu_transformer.py`](indexer/spu_transformer.py) (NEW)
  85 +
  86 +创建SPU数据转换器(**不依赖配置中的source_table/source_column**):
  87 +
  88 +- **数据读取**:直接从MySQL读取 `shoplazza_product_spu` 和 `shoplazza_product_sku` 表(写死在代码中)
  89 +- **数据聚合**:按 `spu_id` 和 `tenant_id` 关联,将SKU数据聚合为variants数组
  90 +- **字段映射**:将SPU和SKU表的字段映射到ES文档字段(写死在代码中)
  91 +- **价格计算**:计算 `min_price`, `max_price`, `compare_at_price`
  92 +- **向量生成**:支持文本和图片向量生成(复用现有encoder)
  93 +- **文档生成**:生成SPU级别的ES文档,包含嵌套variants数组
  94 +- **tenant_id注入**:自动注入 `tenant_id` 字段
  95 +
  96 +### 2.3 创建店匠数据导入脚本
  97 +
  98 +**文件**: [`scripts/ingest_shoplazza.py`](scripts/ingest_shoplazza.py) (NEW)
  99 +
  100 +创建店匠数据导入脚本:
  101 +
  102 +- 连接MySQL数据库(连接信息通过参数或环境变量传入)
  103 +- 读取 `shoplazza_product_spu` 和 `shoplazza_product_sku` 表
  104 +- 使用SPU转换器转换数据
  105 +- 批量导入到ES索引 `search_products`
  106 +- 支持 `--tenant-id` 参数(必需)
  107 +- 支持 `--recreate` 参数(重建索引)
  108 +- 支持 `--batch-size` 参数
  109 +
  110 +## Phase 3: API响应格式重构
  111 +
  112 +### 3.1 更新响应模型
  113 +
  114 +**文件**: [`api/models.py`](api/models.py)
  115 +
  116 +修改:
  117 +
  118 +- **创建** `VariantResult` 模型:
  119 +- `variant_id`, `title`, `price`, `sku`, `stock`, `options` 等
  120 +- **创建** `ProductResult` 模型:
  121 +- `product_id`, `title`, `handle`, `description`, `vendor`, `product_type`, `tags`
  122 +- `price`, `compare_at_price`, `currency`, `image_url`, `in_stock`
  123 +- `variants` (List[VariantResult])
  124 +- `relevance_score` (float, 0-1)
  125 +- **修改** `SearchResponse` 模型:
  126 +- 将 `hits` 改为 `results` (List[ProductResult])
  127 +- 添加 `suggestions` (List[str])
  128 +- 添加 `related_searches` (List[str])
  129 +- 保持 `facets`, `total`, `took_ms` 等字段
  130 +
  131 +### 3.2 创建结果格式化器
  132 +
  133 +**文件**: [`api/result_formatter.py`](api/result_formatter.py) (NEW)
  134 +
  135 +创建结果格式化器:
  136 +
  137 +- **方法** `format_search_results(es_hits, max_score) -> List[ProductResult]`:
  138 +- 将ES返回的 `_hits` 格式转换为 `ProductResult` 列表
  139 +- 提取SPU级别字段(从 `_source` 中提取)
  140 +- 提取嵌套variants数组(从 `_source.variants` 中提取)
  141 +- 计算 `relevance_score`(从 `_score` 归一化到0-1,基于max_score)
  142 +- 处理缺失字段(提供默认值)
  143 +- **方法** `format_facets(es_aggregations, facet_configs) -> List[FacetResult]`:
  144 +- 格式化facets结果(保持现有逻辑)
  145 +- **方法** `generate_suggestions(query, results) -> List[str]`:
  146 +- 生成搜索建议(暂时返回空数组)
  147 +- **方法** `generate_related_searches(query, results) -> List[str]`:
  148 +- 生成相关搜索(暂时返回空数组)
  149 +
  150 +### 3.3 更新搜索器
  151 +
  152 +**文件**: [`search/searcher.py`](search/searcher.py)
  153 +
  154 +修改:
  155 +
  156 +- **添加** `tenant_id` 参数到 `search` 方法(必需)
  157 +- **修改** 查询构建:在filter中添加 `tenant_id` 过滤(必需)
  158 +- **修改** 结果处理:
  159 +- 使用 `ResultFormatter` 格式化结果
  160 +- 返回 `results` 而不是 `hits`
  161 +- 移除ES内部格式字段(`_id`, `_score`, `_source`)
  162 +- **修改** `SearchResult` 类:
  163 +- 将 `hits` 改为 `results` (List[ProductResult])
  164 +- 添加 `suggestions` 和 `related_searches` 字段
  165 +
  166 +### 3.4 更新API路由
  167 +
  168 +**文件**: [`api/routes/search.py`](api/routes/search.py)
  169 +
  170 +修改:
  171 +
  172 +- **添加** `tenant_id` 参数获取:
  173 +- 优先从请求头 `X-Tenant-ID` 获取
  174 +- 其次从查询参数 `tenant_id` 获取
  175 +- 如果都没有,返回400错误
  176 +- **修改** 搜索调用:
  177 +- 传递 `tenant_id` 给 `searcher.search()`
  178 +- **修改** 响应构建:
  179 +- 使用格式化后的 `results`
  180 +- 添加 `suggestions` 和 `related_searches`(暂时返回空数组)
  181 +
  182 +## Phase 4: DataTransformer重构
  183 +
  184 +### 4.1 重构DataTransformer(向后兼容,用于customer1)
  185 +
  186 +**文件**: [`indexer/data_transformer.py`](indexer/data_transformer.py)
  187 +
  188 +修改:
  189 +
  190 +- **保持** 现有逻辑(用于customer1等旧配置)
  191 +- **添加** 检查:如果 `field.source_column` 不存在,跳过该字段(向后兼容)
  192 +- **注意**:base配置不使用DataTransformer,使用SPU转换器
  193 +
  194 +## Phase 5: 测试数据生成
  195 +
  196 +### 5.1 创建测试数据生成脚本
  197 +
  198 +**文件**: [`scripts/generate_test_data.py`](scripts/generate_test_data.py) (NEW)
  199 +
  200 +创建测试数据生成脚本:
  201 +
  202 +- 生成100条SPU测试数据(符合 `shoplazza_product_spu` 表结构)
  203 +- 为每个SPU生成1-5个SKU变体(符合 `shoplazza_product_sku` 表结构)
  204 +- 包含中文和英文标题
  205 +- 包含价格、库存、图片等字段
  206 +- 输出为MySQL INSERT语句或CSV文件
  207 +- 设置 `tenant_id` 为指定值
  208 +
  209 +### 5.2 创建数据导入脚本
  210 +
  211 +**文件**: [`scripts/import_test_data.py`](scripts/import_test_data.py) (NEW)
  212 +
  213 +创建数据导入脚本:
  214 +
  215 +- 连接MySQL数据库
  216 +- 导入测试数据到 `shoplazza_product_spu` 和 `shoplazza_product_sku` 表
  217 +- 设置 `tenant_id` 为base客户的ID(如 "base" 或 "1")
  218 +- 验证数据导入结果
  219 +
  220 +## Phase 6: 测试脚本和文档
  221 +
  222 +### 6.1 创建测试脚本
  223 +
  224 +**文件**: [`scripts/test_base.py`](scripts/test_base.py) (NEW)
  225 +
  226 +创建base配置测试脚本:
  227 +
  228 +- 测试数据导入(使用ingest_shoplazza.py)
  229 +- 测试搜索API(验证tenant_id过滤)
  230 +- 测试多语言搜索
  231 +- 测试facets聚合
  232 +- **验证响应格式**:
  233 +- 确认返回 `results` 而不是 `hits`
  234 +- 确认每个result包含 `product_id`, `title`, `variants`, `relevance_score`
  235 +- 确认variants数组格式正确
  236 +- 确认没有ES内部格式字段(`_id`, `_score`, `_source`)
  237 +
  238 +### 6.2 创建说明文档
  239 +
  240 +**文件**: [`docs/BASE_CONFIG_GUIDE.md`](docs/BASE_CONFIG_GUIDE.md) (NEW)
  241 +
  242 +创建base配置测试指南:
  243 +
  244 +- 数据导入步骤
  245 +- 配置说明(强调不包含MySQL配置)
  246 +- API测试示例(包含tenant_id参数)
  247 +- 响应格式说明
  248 +- 常见问题解答
  249 +
  250 +## Phase 7: 更新设计文档
  251 +
  252 +### 7.1 更新设计文档
  253 +
  254 +**文件**: [`设计文档.md`](设计文档.md)
  255 +
  256 +修改:
  257 +
  258 +- 更新索引结构说明(SPU维度,所有客户共用 `search_products` 索引)
  259 +- 更新配置说明(完全移除MySQL相关配置,只保留ES搜索配置)
  260 +- 更新API响应格式说明(results格式,非hits格式)
  261 +- 更新数据导入流程说明(Pipeline层决定数据源,配置不包含数据源信息)
  262 +- 添加base配置说明
  263 +- 添加tenant_id隔离说明
  264 +
  265 +## 关键修改点总结
  266 +
  267 +1. **配置系统**:
  268 +
  269 +- 完全移除 `mysql_config`, `main_table`, `extension_table`, `source_table`, `source_column`
  270 +- 配置只包含ES搜索相关配置
  271 +- 代码简洁,无冗余,无向后兼容逻辑
  272 +
  273 +2. **数据转换**:
  274 +
  275 +- SPU转换器直接从MySQL读取,不依赖配置
  276 +- 数据映射逻辑写死在代码中
  277 +- 支持嵌套variants数组
  278 +
  279 +3. **索引结构**:
  280 +
  281 +- SPU级别索引
  282 +- 嵌套variants字段
  283 +- tenant_id字段(必需)
  284 +
  285 +4. **API响应**:
  286 +
  287 +- 从 `hits` 改为 `results`
  288 +- 从 `_id`, `_score`, `_source` 改为 `product_id`, `relevance_score`, 结构化字段
  289 +- 包含 `variants` 数组
  290 +
  291 +5. **租户隔离**:
  292 +
  293 +- 所有客户共用 `search_products` 索引
  294 +- 使用 `tenant_id` 字段过滤
  295 +- API必须提供 `tenant_id` 参数
  296 +
  297 +### To-dos
  298 +
  299 +- [ ] Create ResponseTransformer to convert ES hits to Shoplazza format (results, facets, suggestions, related_searches)
  300 +- [ ] Update API models: add VariantOption, ProductVariant, ProductResult, update SearchResponse with new format
  301 +- [ ] Update search route to use ResponseTransformer and return Shoplazza format
  302 +- [ ] Create script to generate 100 SPU+SKU test records for customer2 in Shoplazza tables
  303 +- [ ] Create customer2 config.yaml with search-only fields (no pipeline details)
  304 +- [ ] Create or update SPUDataTransformer to join SPU+SKU and create nested variants structure
  305 +- [ ] Create customer2 ingestion script that loads from MySQL and uses SPU transformer
  306 +- [ ] Create test script and documentation for customer2 setup and testing
  307 +- [ ] Update design document: SPU-level indexing, unified index, config separation, pipeline decisions
0 308 \ No newline at end of file
... ...
.cursor/plans/API响应格式优化与SPU索引重构.5.最终执行.md 0 → 100644
... ... @@ -0,0 +1,317 @@
  1 +<!-- 2bec2252-690d-478e-9ce8-bc9073ec23ae e6d47a17-19ec-429f-9ffe-054cc6563eec -->
  2 +# API响应格式优化与SPU索引重构
  3 +
  4 +## 概述
  5 +
  6 +重构搜索系统以实现:
  7 +
  8 +1. 外部接口友好的API响应格式(移除ES内部格式 `_hits`, `_source`, `_score`)
  9 +2. SPU维度的索引结构(包含嵌套variants数组)
  10 +3. 所有客户共用同一索引(使用tenant_id隔离)
  11 +4. 配置简化(完全移除MySQL相关配置,只保留ES搜索配置)
  12 +5. 创建base配置(店匠通用配置)和SPU数据导入流程
  13 +
  14 +## 核心设计原则
  15 +
  16 +- **配置只关注ES搜索**:配置文件只包含ES字段定义、查询域、排序规则等搜索相关配置
  17 +- **数据灌入流程写死**:数据来源和转换逻辑由Pipeline层(脚本)决定,不在配置中体现
  18 +- **代码简洁无冗余**:不做向后兼容,直接删除不需要的代码
  19 +
  20 +## Phase 1: 配置系统重构
  21 +
  22 +### 1.1 创建BASE配置文件
  23 +
  24 +**文件**: [`config/schema/base/config.yaml`](config/schema/base/config.yaml) (NEW)
  25 +
  26 +创建店匠通用配置文件:
  27 +
  28 +- **不包含**:`mysql_config`, `main_table`, `extension_table`, `source_table`, `source_column`
  29 +- **固定索引名称**:`search_products`
  30 +- **SPU级别字段定义**(只包含对搜索有帮助的字段):
  31 +- `tenant_id` (KEYWORD, required) - 租户隔离
  32 +- `product_id` (KEYWORD) - 商品ID
  33 +- `handle` (KEYWORD) - 商品handle,用于精确匹配
  34 +- `title` (TEXT, chinese_ecommerce) - 标题,主要搜索字段
  35 +- `brief` (TEXT, chinese_ecommerce) - 简介,辅助搜索
  36 +- `description` (TEXT, chinese_ecommerce) - 描述,辅助搜索
  37 +- `seo_title` (TEXT, chinese_ecommerce) - SEO标题,提升相关性
  38 +- `seo_description` (TEXT, chinese_ecommerce) - SEO描述,提升相关性
  39 +- `seo_keywords` (TEXT, chinese_ecommerce) - SEO关键词,提升相关性
  40 +- `vendor` (TEXT + KEYWORD) - 供应商/品牌,搜索和过滤
  41 +- `product_type` (TEXT + KEYWORD) - 商品类型,搜索和过滤
  42 +- `tags` (TEXT + KEYWORD) - 标签,搜索和过滤
  43 +- `category` (TEXT + KEYWORD) - 类目,搜索和过滤
  44 +- `min_price`, `max_price`, `compare_at_price` (FLOAT) - 扁平化价格,用于范围过滤
  45 +- `image_url` (KEYWORD) - 主图URL,用于显示(不参与搜索)
  46 +- **嵌套variants字段定义**:
  47 +- `variants` (nested type)
  48 +- variants包含:`variant_id`, `title`, `price`, `sku`, `stock`, `options` 等
  49 +- **注意**:不包含业务逻辑字段(published, inventory_policy, requires_shipping, taxable等)
  50 +
  51 +### 1.2 移除配置中的MySQL相关字段
  52 +
  53 +**文件**: [`config/config_loader.py`](config/config_loader.py)
  54 +
  55 +修改:
  56 +
  57 +- **删除** `CustomerConfig` 中的 `mysql_config`, `main_table`, `extension_table` 字段定义
  58 +- **删除** `_parse_config` 中解析这些字段的代码
  59 +- **删除** `_parse_field_config` 中解析 `source_table`, `source_column` 的代码
  60 +- 保持其他配置解析逻辑不变
  61 +
  62 +**文件**: [`config/field_types.py`](config/field_types.py)
  63 +
  64 +修改:
  65 +
  66 +- **删除** `FieldConfig` 中的 `source_table`, `source_column` 字段定义
  67 +- 保持其他字段类型定义不变
  68 +
  69 +### 1.3 更新配置验证
  70 +
  71 +**文件**: [`config/config_loader.py`](config/config_loader.py)
  72 +
  73 +修改:
  74 +
  75 +- 添加 `tenant_id` 字段验证(必需字段)
  76 +- 移除所有与MySQL/数据源相关的验证逻辑
  77 +
  78 +## Phase 2: 索引结构重构(SPU维度)
  79 +
  80 +### 2.1 更新Mapping生成器
  81 +
  82 +**文件**: [`indexer/mapping_generator.py`](indexer/mapping_generator.py)
  83 +
  84 +修改:
  85 +
  86 +- 在 `_generate_mappings` 中:
  87 +- 添加 `tenant_id` 字段(KEYWORD, required)
  88 +- 支持嵌套 `variants` 字段(nested type)
  89 +- 支持扁平化价格字段(`min_price`, `max_price`, `compare_at_price`)
  90 +- 确保所有字段映射正确生成
  91 +
  92 +### 2.2 创建SPU数据转换器
  93 +
  94 +**文件**: [`indexer/spu_transformer.py`](indexer/spu_transformer.py) (NEW)
  95 +
  96 +创建SPU数据转换器(**不依赖配置中的source_table/source_column**):
  97 +
  98 +- **数据读取**:直接从MySQL读取 `shoplazza_product_spu` 和 `shoplazza_product_sku` 表(写死在代码中)
  99 +- **数据聚合**:按 `spu_id` 和 `tenant_id` 关联,将SKU数据聚合为variants数组
  100 +- **字段映射**:将SPU和SKU表的字段映射到ES文档字段(写死在代码中)
  101 +- **价格计算**:计算 `min_price`, `max_price`, `compare_at_price`
  102 +- **向量生成**:支持文本和图片向量生成(复用现有encoder)
  103 +- **文档生成**:生成SPU级别的ES文档,包含嵌套variants数组
  104 +- **tenant_id注入**:自动注入 `tenant_id` 字段
  105 +
  106 +### 2.3 创建店匠数据导入脚本
  107 +
  108 +**文件**: [`scripts/ingest_shoplazza.py`](scripts/ingest_shoplazza.py) (NEW)
  109 +
  110 +创建店匠数据导入脚本:
  111 +
  112 +- 连接MySQL数据库(连接信息通过参数或环境变量传入)
  113 +- 读取 `shoplazza_product_spu` 和 `shoplazza_product_sku` 表
  114 +- 使用SPU转换器转换数据
  115 +- 批量导入到ES索引 `search_products`
  116 +- 支持 `--tenant-id` 参数(必需)
  117 +- 支持 `--recreate` 参数(重建索引)
  118 +- 支持 `--batch-size` 参数
  119 +
  120 +## Phase 3: API响应格式重构
  121 +
  122 +### 3.1 更新响应模型
  123 +
  124 +**文件**: [`api/models.py`](api/models.py)
  125 +
  126 +修改:
  127 +
  128 +- **创建** `VariantResult` 模型:
  129 +- `variant_id`, `title`, `price`, `sku`, `stock`, `options` 等
  130 +- **创建** `ProductResult` 模型:
  131 +- `product_id`, `title`, `handle`, `description`, `vendor`, `product_type`, `tags`
  132 +- `price`, `compare_at_price`, `currency`, `image_url`, `in_stock`
  133 +- `variants` (List[VariantResult])
  134 +- `relevance_score` (float, 0-1)
  135 +- **修改** `SearchResponse` 模型:
  136 +- 将 `hits` 改为 `results` (List[ProductResult])
  137 +- 添加 `suggestions` (List[str])
  138 +- 添加 `related_searches` (List[str])
  139 +- 保持 `facets`, `total`, `took_ms` 等字段
  140 +
  141 +### 3.2 创建结果格式化器
  142 +
  143 +**文件**: [`api/result_formatter.py`](api/result_formatter.py) (NEW)
  144 +
  145 +创建结果格式化器:
  146 +
  147 +- **方法** `format_search_results(es_hits, max_score) -> List[ProductResult]`:
  148 +- 将ES返回的 `_hits` 格式转换为 `ProductResult` 列表
  149 +- 提取SPU级别字段(从 `_source` 中提取)
  150 +- 提取嵌套variants数组(从 `_source.variants` 中提取)
  151 +- 计算 `relevance_score`(从 `_score` 归一化到0-1,基于max_score)
  152 +- 处理缺失字段(提供默认值)
  153 +- **方法** `format_facets(es_aggregations, facet_configs) -> List[FacetResult]`:
  154 +- 格式化facets结果(保持现有逻辑)
  155 +- **方法** `generate_suggestions(query, results) -> List[str]`:
  156 +- 生成搜索建议(暂时返回空数组)
  157 +- **方法** `generate_related_searches(query, results) -> List[str]`:
  158 +- 生成相关搜索(暂时返回空数组)
  159 +
  160 +### 3.3 更新搜索器
  161 +
  162 +**文件**: [`search/searcher.py`](search/searcher.py)
  163 +
  164 +修改:
  165 +
  166 +- **添加** `tenant_id` 参数到 `search` 方法(必需)
  167 +- **修改** 查询构建:在filter中添加 `tenant_id` 过滤(必需)
  168 +- **修改** 结果处理:
  169 +- 使用 `ResultFormatter` 格式化结果
  170 +- 返回 `results` 而不是 `hits`
  171 +- 移除ES内部格式字段(`_id`, `_score`, `_source`)
  172 +- **修改** `SearchResult` 类:
  173 +- 将 `hits` 改为 `results` (List[ProductResult])
  174 +- 添加 `suggestions` 和 `related_searches` 字段
  175 +
  176 +### 3.4 更新API路由
  177 +
  178 +**文件**: [`api/routes/search.py`](api/routes/search.py)
  179 +
  180 +修改:
  181 +
  182 +- **添加** `tenant_id` 参数获取:
  183 +- 优先从请求头 `X-Tenant-ID` 获取
  184 +- 其次从查询参数 `tenant_id` 获取
  185 +- 如果都没有,返回400错误
  186 +- **修改** 搜索调用:
  187 +- 传递 `tenant_id` 给 `searcher.search()`
  188 +- **修改** 响应构建:
  189 +- 使用格式化后的 `results`
  190 +- 添加 `suggestions` 和 `related_searches`(暂时返回空数组)
  191 +
  192 +## Phase 4: DataTransformer重构
  193 +
  194 +### 4.1 重构DataTransformer(向后兼容,用于customer1)
  195 +
  196 +**文件**: [`indexer/data_transformer.py`](indexer/data_transformer.py)
  197 +
  198 +修改:
  199 +
  200 +- **保持** 现有逻辑(用于customer1等旧配置)
  201 +- **添加** 检查:如果 `field.source_column` 不存在,跳过该字段(向后兼容)
  202 +- **注意**:base配置不使用DataTransformer,使用SPU转换器
  203 +
  204 +## Phase 5: 测试数据生成
  205 +
  206 +### 5.1 创建测试数据生成脚本
  207 +
  208 +**文件**: [`scripts/generate_test_data.py`](scripts/generate_test_data.py) (NEW)
  209 +
  210 +创建测试数据生成脚本:
  211 +
  212 +- 生成100条SPU测试数据(符合 `shoplazza_product_spu` 表结构)
  213 +- 为每个SPU生成1-5个SKU变体(符合 `shoplazza_product_sku` 表结构)
  214 +- 包含中文和英文标题
  215 +- 包含价格、库存、图片等字段
  216 +- 输出为MySQL INSERT语句或CSV文件
  217 +- 设置 `tenant_id` 为指定值
  218 +
  219 +### 5.2 创建数据导入脚本
  220 +
  221 +**文件**: [`scripts/import_test_data.py`](scripts/import_test_data.py) (NEW)
  222 +
  223 +创建数据导入脚本:
  224 +
  225 +- 连接MySQL数据库
  226 +- 导入测试数据到 `shoplazza_product_spu` 和 `shoplazza_product_sku` 表
  227 +- 设置 `tenant_id` 为base客户的ID(如 "base" 或 "1")
  228 +- 验证数据导入结果
  229 +
  230 +## Phase 6: 测试脚本和文档
  231 +
  232 +### 6.1 创建测试脚本
  233 +
  234 +**文件**: [`scripts/test_base.py`](scripts/test_base.py) (NEW)
  235 +
  236 +创建base配置测试脚本:
  237 +
  238 +- 测试数据导入(使用ingest_shoplazza.py)
  239 +- 测试搜索API(验证tenant_id过滤)
  240 +- 测试多语言搜索
  241 +- 测试facets聚合
  242 +- **验证响应格式**:
  243 +- 确认返回 `results` 而不是 `hits`
  244 +- 确认每个result包含 `product_id`, `title`, `variants`, `relevance_score`
  245 +- 确认variants数组格式正确
  246 +- 确认没有ES内部格式字段(`_id`, `_score`, `_source`)
  247 +
  248 +### 6.2 创建说明文档
  249 +
  250 +**文件**: [`docs/BASE_CONFIG_GUIDE.md`](docs/BASE_CONFIG_GUIDE.md) (NEW)
  251 +
  252 +创建base配置测试指南:
  253 +
  254 +- 数据导入步骤
  255 +- 配置说明(强调不包含MySQL配置)
  256 +- API测试示例(包含tenant_id参数)
  257 +- 响应格式说明
  258 +- 常见问题解答
  259 +
  260 +## Phase 7: 更新设计文档
  261 +
  262 +### 7.1 更新设计文档
  263 +
  264 +**文件**: [`设计文档.md`](设计文档.md)
  265 +
  266 +修改:
  267 +
  268 +- 更新索引结构说明(SPU维度,所有客户共用 `search_products` 索引)
  269 +- 更新配置说明(完全移除MySQL相关配置,只保留ES搜索配置)
  270 +- 更新API响应格式说明(results格式,非hits格式)
  271 +- 更新数据导入流程说明(Pipeline层决定数据源,配置不包含数据源信息)
  272 +- 添加base配置说明
  273 +- 添加tenant_id隔离说明
  274 +
  275 +## 关键修改点总结
  276 +
  277 +1. **配置系统**:
  278 +
  279 +- 完全移除 `mysql_config`, `main_table`, `extension_table`, `source_table`, `source_column`
  280 +- 配置只包含ES搜索相关配置
  281 +- 代码简洁,无冗余,无向后兼容逻辑
  282 +
  283 +2. **数据转换**:
  284 +
  285 +- SPU转换器直接从MySQL读取,不依赖配置
  286 +- 数据映射逻辑写死在代码中
  287 +- 支持嵌套variants数组
  288 +
  289 +3. **索引结构**:
  290 +
  291 +- SPU级别索引
  292 +- 嵌套variants字段
  293 +- tenant_id字段(必需)
  294 +
  295 +4. **API响应**:
  296 +
  297 +- 从 `hits` 改为 `results`
  298 +- 从 `_id`, `_score`, `_source` 改为 `product_id`, `relevance_score`, 结构化字段
  299 +- 包含 `variants` 数组
  300 +
  301 +5. **租户隔离**:
  302 +
  303 +- 所有客户共用 `search_products` 索引
  304 +- 使用 `tenant_id` 字段过滤
  305 +- API必须提供 `tenant_id` 参数
  306 +
  307 +### To-dos
  308 +
  309 +- [ ] Create ResponseTransformer to convert ES hits to Shoplazza format (results, facets, suggestions, related_searches)
  310 +- [ ] Update API models: add VariantOption, ProductVariant, ProductResult, update SearchResponse with new format
  311 +- [ ] Update search route to use ResponseTransformer and return Shoplazza format
  312 +- [ ] Create script to generate 100 SPU+SKU test records for customer2 in Shoplazza tables
  313 +- [ ] Create customer2 config.yaml with search-only fields (no pipeline details)
  314 +- [ ] Create or update SPUDataTransformer to join SPU+SKU and create nested variants structure
  315 +- [ ] Create customer2 ingestion script that loads from MySQL and uses SPU transformer
  316 +- [ ] Create test script and documentation for customer2 setup and testing
  317 +- [ ] Update design document: SPU-level indexing, unified index, config separation, pipeline decisions
0 318 \ No newline at end of file
... ...
.cursor/plans/API响应格式优化与SPU索引重构.纠正.md 0 → 100644
... ... @@ -0,0 +1,232 @@
  1 +# API响应格式优化与SPU索引重构
  2 +
  3 +## 概述
  4 +
  5 +重构搜索系统以支持:
  6 +
  7 +1. 外部接口友好的API响应格式(移除ES内部格式)
  8 +2. SPU维度的索引结构(包含嵌套variants数组)
  9 +3. 所有客户共用同一索引(使用tenant_id隔离)
  10 +4. 配置简化(移除MySQL相关配置,只保留ES搜索配置)
  11 +5. 添加新客户(customer2)测试数据和配置
  12 +
  13 +## Phase 1: 配置文件重构
  14 +
  15 +### 1.1 创建BASE配置文件
  16 +
  17 +**文件**: [`config/schema/base/config.yaml`](config/schema/base/config.yaml) (NEW)
  18 +
  19 +创建通用配置文件,所有使用店匠表的客户共用:
  20 +
  21 +- 移除 `mysql_config`, `main_table`, `extension_table`, `source_table`, `source_column`
  22 +- 固定索引名称:`search_products`
  23 +- SPU级别字段定义(包含嵌套variants)
  24 +- 必需字段:`tenant_id` (KEYWORD, required)
  25 +- 扁平化价格字段:`min_price`, `max_price`, `compare_at_price`
  26 +- 多语言字段:`title_zh`, `title_en`, `description_zh`, `description_en` 等
  27 +- 嵌套variants结构定义
  28 +
  29 +务必注意:对于店匠的店铺,我们应该都通用的用base配置即可,不需要另外单独针对某个店铺新建一套配置,也就是所有店匠的商品都用这个base配置。
  30 +但是允许有其他某个深度定制客户有自己的索引,比如当前有一套索引customer1,我们不用动他,他的数据灌入流程是另外一份,写死的,他的索引配置也是另外一份。也就是我们的搜索服务可以支持多种配置,索引结构变了、字段变了,就是多一分配置,只是店匠多数用户都用base配置即可。
  31 +下面,如果有用到 customer2 的,应该就是指base客户,并注意帮我纠正。
  32 +
  33 +### 1.3 更新配置加载器
  34 +
  35 +**文件**: [`config/config_loader.py`](config/config_loader.py)
  36 +
  37 +修改:
  38 +
  39 +- 移除 `mysql_config`, `main_table`, `extension_table` 解析
  40 +- 移除字段配置中的 `source_table`, `source_column` 解析
  41 +- 固定 `es_index_name` 为 `search_products`
  42 +- 添加 `tenant_id` 字段验证(必需字段)
  43 +- 支持嵌套字段配置(variants)
  44 +
  45 +### 1.4 更新字段类型定义
  46 +
  47 +**文件**: [`config/field_types.py`](config/field_types.py)
  48 +
  49 +修改:
  50 +
  51 +- 移除 `FieldConfig` 中的 `source_table`, `source_column`
  52 +- 添加嵌套字段支持(nested fields)
  53 +- 添加扁平化价格字段类型
  54 +
  55 +## Phase 2: 索引结构重构(SPU维度)
  56 +
  57 +### 2.1 更新Mapping生成器
  58 +
  59 +**文件**: [`indexer/mapping_generator.py`](indexer/mapping_generator.py)
  60 +
  61 +修改:
  62 +
  63 +- 生成SPU级别的mapping
  64 +- 添加 `tenant_id` 字段(KEYWORD, required)
  65 +- 添加嵌套 `variants` 字段(nested type)
  66 +- 添加扁平化价格字段(`min_price`, `max_price`, `compare_at_price`)
  67 +- 移除SKU级别的字段映射
  68 +
  69 +### 2.2 创建SPU数据转换器
  70 +
  71 +**文件**: [`indexer/spu_transformer.py`](indexer/spu_transformer.py) (NEW)
  72 +
  73 +创建SPU数据转换器:
  74 +
  75 +- 从MySQL读取SPU和SKU数据
  76 +- 按SPU聚合SKU数据为variants数组
  77 +- 计算扁平化价格字段(min_price, max_price, compare_at_price)
  78 +- 生成SPU级别的ES文档
  79 +- 注入 `tenant_id` 字段
  80 +
  81 +### 2.3 更新数据导入脚本
  82 +
  83 +**文件**: [`scripts/ingest_shoplazza.py`](scripts/ingest_shoplazza.py) (NEW)
  84 +
  85 +创建店匠数据导入脚本:
  86 +
  87 +- 从MySQL读取 `shoplazza_product_spu` 和 `shoplazza_product_sku` 表
  88 +- 按 `spu_id` 和 `tenant_id` 关联数据
  89 +- 使用SPU转换器转换数据
  90 +- 批量导入到ES索引 `search_products`
  91 +- 支持 `--tenant-id` 参数
  92 +
  93 +## Phase 3: API响应格式重构
  94 +
  95 +### 3.1 更新响应模型
  96 +
  97 +**文件**: [`api/models.py`](api/models.py)
  98 +
  99 +修改 `SearchResponse` 模型:
  100 +
  101 +- 将 `hits` 改为 `results` (List[ProductResult])
  102 +- 添加 `ProductResult` 模型(包含product_id, title, variants, relevance_score等)
  103 +- 添加 `VariantResult` 模型(包含variant_id, title, price, sku等)
  104 +- 保持 `facets` 格式
  105 +- 添加 `suggestions` 和 `related_searches` 字段(暂时返回空数组)
  106 +
  107 +### 3.2 创建结果转换器
  108 +
  109 +**文件**: [`api/result_formatter.py`](api/result_formatter.py) (NEW)
  110 +
  111 +创建结果格式化器:
  112 +
  113 +- 将ES返回的 `_hits` 格式转换为外部接口格式
  114 +- 提取SPU级别字段
  115 +- 提取嵌套variants数组
  116 +- 计算 `relevance_score`(从 `_score` 转换)
  117 +- 格式化facets结果
  118 +- 生成suggestions和related_searches(暂时返回空数组)
  119 +
  120 +### 3.3 更新搜索器
  121 +
  122 +**文件**: [`search/searcher.py`](search/searcher.py)
  123 +
  124 +修改:
  125 +
  126 +- 在搜索查询中添加 `tenant_id` 过滤(必需)
  127 +- 更新结果处理逻辑,使用结果格式化器
  128 +- 移除ES内部格式字段(`_id`, `_score`, `_source`)
  129 +- 返回格式化的外部接口格式
  130 +
  131 +### 3.4 更新API路由
  132 +
  133 +**文件**: [`api/routes/search.py`](api/routes/search.py)
  134 +
  135 +修改:
  136 +
  137 +- 添加 `tenant_id` 参数(从请求头或查询参数获取)
  138 +- 在搜索请求中添加 `tenant_id` 过滤
  139 +- 使用结果格式化器格式化响应
  140 +- 返回新的响应格式
  141 +
  142 +## Phase 4: 测试数据生成
  143 +
  144 +### 4.1 创建测试数据生成脚本
  145 +
  146 +**文件**: [`scripts/generate_test_data.py`](scripts/generate_test_data.py) (NEW)
  147 +
  148 +创建测试数据生成脚本:
  149 +
  150 +- 生成100条SPU测试数据
  151 +- 为每个SPU生成1-5个SKU变体
  152 +- 包含中文和英文标题
  153 +- 包含价格、库存、图片等字段
  154 +- 输出为MySQL INSERT语句或CSV文件
  155 +
  156 +### 4.2 创建数据导入脚本
  157 +
  158 +**文件**: [`scripts/import_test_data.py`](scripts/import_test_data.py) (NEW)
  159 +
  160 +创建数据导入脚本:
  161 +
  162 +- 连接MySQL数据库
  163 +- 导入测试数据到 `shoplazza_product_spu` 和 `shoplazza_product_sku` 表
  164 +- 设置 `tenant_id` 为customer2的ID
  165 +- 验证数据导入结果
  166 +
  167 +## Phase 5: 测试脚本和文档
  168 +
  169 +### 5.1 创建测试脚本
  170 +
  171 +**文件**: [`scripts/test_customer2.py`](scripts/test_customer2.py) (NEW)
  172 +
  173 +创建customer2测试脚本:
  174 +
  175 +- 测试数据导入
  176 +- 测试搜索API
  177 +- 测试多语言搜索
  178 +- 测试facets聚合
  179 +- 验证响应格式
  180 +
  181 +### 5.2 创建说明文档
  182 +
  183 +**文件**: [`docs/CUSTOMER2_TEST_GUIDE.md`](docs/CUSTOMER2_TEST_GUIDE.md) (NEW)
  184 +
  185 +创建customer2测试指南:
  186 +
  187 +- 数据导入步骤
  188 +- 配置说明
  189 +- API测试示例
  190 +- 常见问题解答
  191 +
  192 +## Phase 6: 更新设计文档
  193 +
  194 +### 6.1 更新设计文档
  195 +
  196 +**文件**: [`设计文档.md`](设计文档.md)
  197 +
  198 +修改:
  199 +
  200 +- 更新索引结构说明(SPU维度,所有客户共用)
  201 +- 更新配置说明(移除MySQL相关配置)
  202 +- 更新API响应格式说明
  203 +- 更新数据导入流程说明
  204 +- 添加customer2测试说明
  205 +
  206 +## 关键修改点
  207 +
  208 +1. **索引结构**:
  209 +
  210 +- 索引名称:`search_products`(所有客户共用)
  211 +- 索引粒度:SPU级别
  212 +- 租户隔离:使用 `tenant_id` 字段过滤
  213 +- 嵌套结构:`variants` 数组包含SKU数据
  214 +
  215 +2. **配置简化**:
  216 +
  217 +- 移除 `mysql_config`, `main_table`, `extension_table`
  218 +- 移除字段配置中的 `source_table`, `source_column`
  219 +- 只保留ES搜索相关配置
  220 +
  221 +3. **API响应格式**:
  222 +
  223 +- 从 `_hits`, `_source`, `_score` 改为 `results`, `product_id`, `title`, `relevance_score`
  224 +- 添加 `variants` 数组
  225 +- 添加 `suggestions` 和 `related_searches`(暂时返回空数组)
  226 +
  227 +4. **数据导入**:
  228 +
  229 +- 从MySQL读取SPU和SKU数据
  230 +- 按SPU聚合SKU数据
  231 +- 生成SPU级别的ES文档
  232 +- 注入 `tenant_id` 字段
0 233 \ No newline at end of file
... ...
.cursor/plans/API响应格式优化与SPU索引重构3.md 0 → 100644
... ... @@ -0,0 +1,252 @@
  1 +<!-- 2bec2252-690d-478e-9ce8-bc9073ec23ae cee9ef6a-2da8-41b5-b37e-12464a8e129a -->
  2 +# API响应格式优化与SPU索引重构
  3 +
  4 +## 概述
  5 +
  6 +重构搜索系统以支持:
  7 +
  8 +1. 外部接口友好的API响应格式(移除ES内部格式)
  9 +2. SPU维度的索引结构(包含嵌套variants数组)
  10 +3. 所有客户共用同一索引(使用tenant_id隔离)
  11 +4. 配置简化(移除MySQL相关配置,只保留ES搜索配置)
  12 +5. 创建base配置(店匠通用配置)和测试数据
  13 +
  14 +## Phase 1: 配置文件重构
  15 +
  16 +### 1.1 创建BASE配置文件
  17 +
  18 +**文件**: [`config/schema/base/config.yaml`](config/schema/base/config.yaml) (NEW)
  19 +
  20 +创建通用配置文件,所有使用店匠表的客户共用:
  21 +
  22 +- 移除 `mysql_config`, `main_table`, `extension_table`
  23 +- 移除字段配置中的 `source_table`, `source_column`
  24 +- 固定索引名称:`search_products`
  25 +- SPU级别字段定义(包含嵌套variants)
  26 +- 必需字段:`tenant_id` (KEYWORD, required)
  27 +- 扁平化价格字段:`min_price`, `max_price`, `compare_at_price`
  28 +- 多语言字段:`title_zh`, `title_en`, `description_zh`, `description_en` 等
  29 +- 嵌套variants结构定义
  30 +
  31 +**注意**:店匠的店铺都使用base配置,不需要单独配置。允许其他深度定制客户(如customer1)有自己的配置。
  32 +
  33 +### 1.2 更新配置加载器
  34 +
  35 +**文件**: [`config/config_loader.py`](config/config_loader.py)
  36 +
  37 +修改:
  38 +
  39 +- 移除 `mysql_config`, `main_table`, `extension_table` 解析(设为可选,向后兼容)
  40 +- 移除字段配置中的 `source_table`, `source_column` 解析(设为可选)
  41 +- 支持固定 `es_index_name` 为 `search_products`(base配置)
  42 +- 添加 `tenant_id` 字段验证(必需字段)
  43 +- 支持嵌套字段配置(variants)
  44 +
  45 +### 1.3 更新字段类型定义
  46 +
  47 +**文件**: [`config/field_types.py`](config/field_types.py)
  48 +
  49 +修改:
  50 +
  51 +- `FieldConfig` 中的 `source_table`, `source_column` 设为可选(向后兼容)
  52 +- 确保嵌套字段支持(nested fields)正常工作
  53 +- 添加扁平化价格字段类型支持
  54 +
  55 +## Phase 2: 索引结构重构(SPU维度)
  56 +
  57 +### 2.1 更新Mapping生成器
  58 +
  59 +**文件**: [`indexer/mapping_generator.py`](indexer/mapping_generator.py)
  60 +
  61 +修改:
  62 +
  63 +- 生成SPU级别的mapping
  64 +- 添加 `tenant_id` 字段(KEYWORD, required)
  65 +- 添加嵌套 `variants` 字段(nested type)
  66 +- 添加扁平化价格字段(`min_price`, `max_price`, `compare_at_price`)
  67 +- 支持SPU级别字段映射
  68 +
  69 +### 2.2 创建SPU数据转换器
  70 +
  71 +**文件**: [`indexer/spu_transformer.py`](indexer/spu_transformer.py) (NEW)
  72 +
  73 +创建SPU数据转换器:
  74 +
  75 +- 从MySQL读取SPU和SKU数据(`shoplazza_product_spu` 和 `shoplazza_product_sku` 表)
  76 +- 按SPU聚合SKU数据为variants数组
  77 +- 计算扁平化价格字段(min_price, max_price, compare_at_price)
  78 +- 生成SPU级别的ES文档
  79 +- 注入 `tenant_id` 字段
  80 +
  81 +### 2.3 创建店匠数据导入脚本
  82 +
  83 +**文件**: [`scripts/ingest_shoplazza.py`](scripts/ingest_shoplazza.py) (NEW)
  84 +
  85 +创建店匠数据导入脚本:
  86 +
  87 +- 从MySQL读取 `shoplazza_product_spu` 和 `shoplazza_product_sku` 表
  88 +- 按 `spu_id` 和 `tenant_id` 关联数据
  89 +- 使用SPU转换器转换数据
  90 +- 批量导入到ES索引 `search_products`
  91 +- 支持 `--tenant-id` 参数
  92 +
  93 +## Phase 3: API响应格式重构
  94 +
  95 +### 3.1 更新响应模型
  96 +
  97 +**文件**: [`api/models.py`](api/models.py)
  98 +
  99 +修改 `SearchResponse` 模型:
  100 +
  101 +- 将 `hits` 改为 `results` (List[ProductResult])
  102 +- 添加 `ProductResult` 模型(包含product_id, title, variants, relevance_score等)
  103 +- 添加 `VariantResult` 模型(包含variant_id, title, price, sku等)
  104 +- 保持 `facets` 格式
  105 +- 添加 `suggestions` 和 `related_searches` 字段(暂时返回空数组)
  106 +
  107 +### 3.2 创建结果转换器
  108 +
  109 +**文件**: [`api/result_formatter.py`](api/result_formatter.py) (NEW)
  110 +
  111 +创建结果格式化器:
  112 +
  113 +- 将ES返回的 `_hits` 格式转换为外部接口格式
  114 +- 提取SPU级别字段
  115 +- 提取嵌套variants数组
  116 +- 计算 `relevance_score`(从 `_score` 转换,归一化到0-1)
  117 +- 格式化facets结果
  118 +- 生成suggestions和related_searches(暂时返回空数组)
  119 +
  120 +### 3.3 更新搜索器
  121 +
  122 +**文件**: [`search/searcher.py`](search/searcher.py)
  123 +
  124 +修改:
  125 +
  126 +- 在搜索查询中添加 `tenant_id` 过滤(必需,从请求参数或配置获取)
  127 +- 更新结果处理逻辑,使用结果格式化器
  128 +- 移除ES内部格式字段(`_id`, `_score`, `_source`)
  129 +- 返回格式化的外部接口格式
  130 +
  131 +### 3.4 更新API路由
  132 +
  133 +**文件**: [`api/routes/search.py`](api/routes/search.py)
  134 +
  135 +修改:
  136 +
  137 +- 添加 `tenant_id` 参数(从请求头 `X-Tenant-ID` 或查询参数 `tenant_id` 获取)
  138 +- 在搜索请求中添加 `tenant_id` 过滤
  139 +- 使用结果格式化器格式化响应
  140 +- 返回新的响应格式
  141 +
  142 +## Phase 4: 测试数据生成
  143 +
  144 +### 4.1 创建测试数据生成脚本
  145 +
  146 +**文件**: [`scripts/generate_test_data.py`](scripts/generate_test_data.py) (NEW)
  147 +
  148 +创建测试数据生成脚本:
  149 +
  150 +- 生成100条SPU测试数据
  151 +- 为每个SPU生成1-5个SKU变体
  152 +- 包含中文和英文标题
  153 +- 包含价格、库存、图片等字段
  154 +- 输出为MySQL INSERT语句或CSV文件
  155 +
  156 +### 4.2 创建数据导入脚本
  157 +
  158 +**文件**: [`scripts/import_test_data.py`](scripts/import_test_data.py) (NEW)
  159 +
  160 +创建数据导入脚本:
  161 +
  162 +- 连接MySQL数据库
  163 +- 导入测试数据到 `shoplazza_product_spu` 和 `shoplazza_product_sku` 表
  164 +- 设置 `tenant_id` 为base客户的ID(如 "base" 或 "1")
  165 +- 验证数据导入结果
  166 +
  167 +## Phase 5: 测试脚本和文档
  168 +
  169 +### 5.1 创建测试脚本
  170 +
  171 +**文件**: [`scripts/test_base.py`](scripts/test_base.py) (NEW)
  172 +
  173 +创建base配置测试脚本:
  174 +
  175 +- 测试数据导入
  176 +- 测试搜索API
  177 +- 测试多语言搜索
  178 +- 测试facets聚合
  179 +- 验证响应格式(results格式,非_hits格式)
  180 +- 验证tenant_id过滤
  181 +
  182 +### 5.2 创建说明文档
  183 +
  184 +**文件**: [`docs/BASE_CONFIG_GUIDE.md`](docs/BASE_CONFIG_GUIDE.md) (NEW)
  185 +
  186 +创建base配置测试指南:
  187 +
  188 +- 数据导入步骤
  189 +- 配置说明
  190 +- API测试示例
  191 +- 常见问题解答
  192 +
  193 +## Phase 6: 更新设计文档
  194 +
  195 +### 6.1 更新设计文档
  196 +
  197 +**文件**: [`设计文档.md`](设计文档.md)
  198 +
  199 +修改:
  200 +
  201 +- 更新索引结构说明(SPU维度,所有客户共用 `search_products` 索引)
  202 +- 更新配置说明(移除MySQL相关配置,只保留ES搜索配置)
  203 +- 更新API响应格式说明(results格式,非_hits格式)
  204 +- 更新数据导入流程说明(Pipeline层决定数据源,配置不包含数据源信息)
  205 +- 添加base配置说明
  206 +
  207 +## 关键修改点
  208 +
  209 +1. **索引结构**:
  210 +
  211 +- 索引名称:`search_products`(所有客户共用)
  212 +- 索引粒度:SPU级别
  213 +- 租户隔离:使用 `tenant_id` 字段过滤
  214 +- 嵌套结构:`variants` 数组包含SKU数据
  215 +
  216 +2. **配置简化**:
  217 +
  218 +- 移除 `mysql_config`, `main_table`, `extension_table`(设为可选,向后兼容)
  219 +- 移除字段配置中的 `source_table`, `source_column`(设为可选)
  220 +- 只保留ES搜索相关配置
  221 +
  222 +3. **API响应格式**:
  223 +
  224 +- 从 `_hits`, `_source`, `_score` 改为 `results`, `product_id`, `title`, `relevance_score`
  225 +- 添加 `variants` 数组
  226 +- 添加 `suggestions` 和 `related_searches`(暂时返回空数组)
  227 +
  228 +4. **数据导入**:
  229 +
  230 +- 从MySQL读取SPU和SKU数据(Pipeline层决定)
  231 +- 按SPU聚合SKU数据
  232 +- 生成SPU级别的ES文档
  233 +- 注入 `tenant_id` 字段
  234 +
  235 +5. **配置架构**:
  236 +
  237 +- 配置文件只包含搜索引擎关注的内容
  238 +- 数据来源由Pipeline层(脚本/工具)决定
  239 +- 转换器类型由Pipeline选择,不由配置决定
  240 +- 配置文件不引入mysql到ES层次的东西
  241 +
  242 +### To-dos
  243 +
  244 +- [ ] Create ResponseTransformer to convert ES hits to Shoplazza format (results, facets, suggestions, related_searches)
  245 +- [ ] Update API models: add VariantOption, ProductVariant, ProductResult, update SearchResponse with new format
  246 +- [ ] Update search route to use ResponseTransformer and return Shoplazza format
  247 +- [ ] Create script to generate 100 SPU+SKU test records for customer2 in Shoplazza tables
  248 +- [ ] Create customer2 config.yaml with search-only fields (no pipeline details)
  249 +- [ ] Create or update SPUDataTransformer to join SPU+SKU and create nested variants structure
  250 +- [ ] Create customer2 ingestion script that loads from MySQL and uses SPU transformer
  251 +- [ ] Create test script and documentation for customer2 setup and testing
  252 +- [ ] Update design document: SPU-level indexing, unified index, config separation, pipeline decisions
0 253 \ No newline at end of file
... ...