<!-- 2bec2252-690d-478e-9ce8-bc9073ec23ae cee9ef6a-2da8-41b5-b37e-12464a8e129a -->
# API响应格式优化与SPU索引重构

## 概述

重构搜索系统以支持：

1. 外部接口友好的API响应格式（移除ES内部格式）
2. SPU维度的索引结构（包含嵌套variants数组）
3. 所有客户共用同一索引（使用tenant_id隔离）
4. 配置简化（移除MySQL相关配置，只保留ES搜索配置）
5. 创建base配置（店匠通用配置）和测试数据

## Phase 1: 配置文件重构

### 1.1 创建BASE配置文件

**文件**: [`config/schema/base/config.yaml`](config/schema/base/config.yaml) (NEW)

创建通用配置文件，所有使用店匠表的客户共用：

- 移除 `mysql_config`, `main_table`, `extension_table`
- 移除字段配置中的 `source_table`, `source_column`
- 固定索引名称：`search_products`
- SPU级别字段定义（包含嵌套variants）
- 必需字段：`tenant_id` (KEYWORD, required)
- 扁平化价格字段：`min_price`, `max_price`, `compare_at_price`
- 多语言字段：`title_zh`, `title_en`, `description_zh`, `description_en` 等
- 嵌套variants结构定义

**注意**：店匠的店铺都使用base配置，不需要单独配置。允许其他深度定制客户（如tenant1）有自己的配置。

### 1.2 更新配置加载器

**文件**: [`config/config_loader.py`](config/config_loader.py)

修改：

- 移除 `mysql_config`, `main_table`, `extension_table` 解析（设为可选，向后兼容）
- 移除字段配置中的 `source_table`, `source_column` 解析（设为可选）
- 支持固定 `es_index_name` 为 `search_products`（base配置）
- 添加 `tenant_id` 字段验证（必需字段）
- 支持嵌套字段配置（variants）

### 1.3 更新字段类型定义

**文件**: [`config/field_types.py`](config/field_types.py)

修改：

- `FieldConfig` 中的 `source_table`, `source_column` 设为可选（向后兼容）
- 确保嵌套字段支持（nested fields）正常工作
- 添加扁平化价格字段类型支持

## Phase 2: 索引结构重构（SPU维度）

### 2.1 更新Mapping生成器

**文件**: [`indexer/mapping_generator.py`](indexer/mapping_generator.py)

修改：

- 生成SPU级别的mapping
- 添加 `tenant_id` 字段（KEYWORD, required）
- 添加嵌套 `variants` 字段（nested type）
- 添加扁平化价格字段（`min_price`, `max_price`, `compare_at_price`）
- 支持SPU级别字段映射

### 2.2 创建SPU数据转换器

**文件**: [`indexer/spu_transformer.py`](indexer/spu_transformer.py) (NEW)

创建SPU数据转换器：

- 从MySQL读取SPU和SKU数据（`shoplazza_product_spu` 和 `shoplazza_product_sku` 表）
- 按SPU聚合SKU数据为variants数组
- 计算扁平化价格字段（min_price, max_price, compare_at_price）
- 生成SPU级别的ES文档
- 注入 `tenant_id` 字段

### 2.3 创建店匠数据导入脚本

**文件**: [`scripts/ingest_shoplazza.py`](scripts/ingest_shoplazza.py) (NEW)

创建店匠数据导入脚本：

- 从MySQL读取 `shoplazza_product_spu` 和 `shoplazza_product_sku` 表
- 按 `spu_id` 和 `tenant_id` 关联数据
- 使用SPU转换器转换数据
- 批量导入到ES索引 `search_products`
- 支持 `--tenant-id` 参数

## Phase 3: API响应格式重构

### 3.1 更新响应模型

**文件**: [`api/models.py`](api/models.py)

修改 `SearchResponse` 模型：

- 将 `hits` 改为 `results` (List[ProductResult])
- 添加 `ProductResult` 模型（包含product_id, title, variants, relevance_score等）
- 添加 `VariantResult` 模型（包含variant_id, title, price, sku等）
- 保持 `facets` 格式
- 添加 `suggestions` 和 `related_searches` 字段（暂时返回空数组）

### 3.2 创建结果转换器

**文件**: [`api/result_formatter.py`](api/result_formatter.py) (NEW)

创建结果格式化器：

- 将ES返回的 `_hits` 格式转换为外部接口格式
- 提取SPU级别字段
- 提取嵌套variants数组
- 计算 `relevance_score`（从 `_score` 转换，归一化到0-1）
- 格式化facets结果
- 生成suggestions和related_searches（暂时返回空数组）

### 3.3 更新搜索器

**文件**: [`search/searcher.py`](search/searcher.py)

修改：

- 在搜索查询中添加 `tenant_id` 过滤（必需，从请求参数或配置获取）
- 更新结果处理逻辑，使用结果格式化器
- 移除ES内部格式字段（`_id`, `_score`, `_source`）
- 返回格式化的外部接口格式

### 3.4 更新API路由

**文件**: [`api/routes/search.py`](api/routes/search.py)

修改：

- 添加 `tenant_id` 参数（从请求头 `X-Tenant-ID` 或查询参数 `tenant_id` 获取）
- 在搜索请求中添加 `tenant_id` 过滤
- 使用结果格式化器格式化响应
- 返回新的响应格式

## Phase 4: 测试数据生成

### 4.1 创建测试数据生成脚本

**文件**: [`scripts/generate_test_data.py`](scripts/generate_test_data.py) (NEW)

创建测试数据生成脚本：

- 生成100条SPU测试数据
- 为每个SPU生成1-5个SKU变体
- 包含中文和英文标题
- 包含价格、库存、图片等字段
- 输出为MySQL INSERT语句或CSV文件

### 4.2 创建数据导入脚本

**文件**: [`scripts/import_test_data.py`](scripts/import_test_data.py) (NEW)

创建数据导入脚本：

- 连接MySQL数据库
- 导入测试数据到 `shoplazza_product_spu` 和 `shoplazza_product_sku` 表
- 设置 `tenant_id` 为base客户的ID（如 "base" 或 "1"）
- 验证数据导入结果

## Phase 5: 测试脚本和文档

### 5.1 创建测试脚本

**文件**: [`scripts/test_base.py`](scripts/test_base.py) (NEW)

创建base配置测试脚本：

- 测试数据导入
- 测试搜索API
- 测试多语言搜索
- 测试facets聚合
- 验证响应格式（results格式，非_hits格式）
- 验证tenant_id过滤

### 5.2 创建说明文档

**文件**: [`docs/BASE_CONFIG_GUIDE.md`](docs/BASE_CONFIG_GUIDE.md) (NEW)

创建base配置测试指南：

- 数据导入步骤
- 配置说明
- API测试示例
- 常见问题解答

## Phase 6: 更新设计文档

### 6.1 更新设计文档

**文件**: [`设计文档.md`](设计文档.md)

修改：

- 更新索引结构说明（SPU维度，所有客户共用 `search_products` 索引）
- 更新配置说明（移除MySQL相关配置，只保留ES搜索配置）
- 更新API响应格式说明（results格式，非_hits格式）
- 更新数据导入流程说明（Pipeline层决定数据源，配置不包含数据源信息）
- 添加base配置说明

## 关键修改点

1. **索引结构**：

- 索引名称：`search_products`（所有客户共用）
- 索引粒度：SPU级别
- 租户隔离：使用 `tenant_id` 字段过滤
- 嵌套结构：`variants` 数组包含SKU数据

2. **配置简化**：

- 移除 `mysql_config`, `main_table`, `extension_table`（设为可选，向后兼容）
- 移除字段配置中的 `source_table`, `source_column`（设为可选）
- 只保留ES搜索相关配置

3. **API响应格式**：

- 从 `_hits`, `_source`, `_score` 改为 `results`, `product_id`, `title`, `relevance_score`
- 添加 `variants` 数组
- 添加 `suggestions` 和 `related_searches`（暂时返回空数组）

4. **数据导入**：

- 从MySQL读取SPU和SKU数据（Pipeline层决定）
- 按SPU聚合SKU数据
- 生成SPU级别的ES文档
- 注入 `tenant_id` 字段

5. **配置架构**：

- 配置文件只包含搜索引擎关注的内容
- 数据来源由Pipeline层（脚本/工具）决定
- 转换器类型由Pipeline选择，不由配置决定
- 配置文件不引入mysql到ES层次的东西

### To-dos

- [ ] Create ResponseTransformer to convert ES hits to Shoplazza format (results, facets, suggestions, related_searches)
- [ ] Update API models: add VariantOption, ProductVariant, ProductResult, update SearchResponse with new format
- [ ] Update search route to use ResponseTransformer and return Shoplazza format
- [ ] Create script to generate 100 SPU+SKU test records for tenant2 in Shoplazza tables
- [ ] Create tenant2 config.yaml with search-only fields (no pipeline details)
- [ ] Create or update SPUDataTransformer to join SPU+SKU and create nested variants structure
- [ ] Create tenant2 ingestion script that loads from MySQL and uses SPU transformer
- [ ] Create test script and documentation for tenant2 setup and testing
- [ ] Update design document: SPU-level indexing, unified index, config separation, pipeline decisions