非常好的问题！让我详细解释一下搜索应用的调用链路和 OAuth 的关系。

## 调用链路说明

### 1. **OAuth 认证的作用**

OAuth **不是用于前端搜索调用**的，它主要用于：

```mermaid
graph LR
    A[商家安装APP] --> B[OAuth授权]
    B --> C[获取Access Token]
    C --> D[后端拉取商品数据]
    D --> E[建立ES索引]
    E --> F[搜索服务就绪]
```

**OAuth Token 的用途：**
- ✅ 你的后端调用店匠 Admin API（拉取商品、订单、客户数据）
- ✅ 注册 Webhook（接收数据变更通知）
- ❌ **不用于**前端买家搜索时的调用

### 2. **前端搜索的实际调用链路**

当买家在店铺搜索商品时：

```
买家浏览器 → 搜索框组件(Liquid/JS) → 你的搜索API → Elasticsearch → 返回结果
```

**关键点：**
- 前端 JavaScript **直接调用**你的公开搜索 API
- 不需要 OAuth Token
- 需要传递 `store_id` 参数来标识是哪个店铺

### 3. **搜索接口的两种方案**

[2 tools called]

## 详细解答

### 📍 **方案 A：前端直接调用（推荐用于公开搜索）**

**流程：**

```javascript
// 在店铺前端页面（买家浏览器中）
const response = await fetch('https://your-domain.com/api/search/products', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    query: "蓝牙耳机",
    tenant: "tenant_47167113-1",  // 店铺标识
    size: 24,
    filters: {},
    facets: ['product_type', 'vendor']
  })
});
```

**你的搜索 API 需要：**

1. **允许跨域访问（CORS）**：
```python
# Python FastAPI 示例
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 或指定店匠域名白名单
    allow_credentials=True,
    allow_methods=["POST"],
    allow_headers=["*"],
)
```

2. **根据 store_id 隔离数据**：
```python
@app.post("/api/search/products")
async def search(request: SearchRequest):
    # 从 tenant 参数提取 tenant_id
    tenant_id = extract_tenant_id(request.tenant)
    
    # 使用租户专属索引
    index_name = f"shoplazza_products_{tenant_id}"
    
    # 执行搜索
    results = es_client.search(index=index_name, body=query)
    return results
```

3. **不需要 OAuth Token 认证**（因为是公开查询）

---

### 📍 **方案 B：通过 Java 后端中转（更安全）**

**流程：**

```
买家浏览器 → Java后端(/api/search/products?storeId=xxx) → Python搜索服务 → ES
```

**Java 后端代码：**

```java
@RestController
@RequestMapping("/api/search")
public class SearchController {
    
    @PostMapping("/products")
    public ResponseEntity<SearchResponse> search(
            @RequestParam String storeId,  // 从URL参数获取店铺ID
            @RequestBody SearchRequest request) {
        
        // 1. 验证店铺ID（可选：检查域名白名单）
        ShopConfig shop = shopConfigMapper.selectByStoreId(storeId);
        if (shop == null) {
            return ResponseEntity.notFound().build();
        }
        
        // 2. 添加租户隔离参数
        request.setTenant("tenant_" + shop.getTenantId());
        
        // 3. 调用 Python 搜索服务
        SearchResponse response = restTemplate.postForObject(
            "http://localhost:6002/search/",
            request,
            SearchResponse.class
        );
        
        // 4. 记录搜索日志
        searchLogService.logSearch(shop.getId(), request.getQuery(), response.getTotal());
        
        return ResponseEntity.ok(response);
    }
}
```

**前端调用（带 store_id）：**

```javascript
const response = await fetch(
  `https://your-domain.com/api/search/products?storeId=${config.storeId}`,
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      query: "蓝牙耳机",
      size: 24,
      filters: {},
      facets: ['product_type', 'vendor']
    })
  }
);
```

---

## 🔐 OAuth 认证在整个系统中的位置

```mermaid
graph TB
    subgraph "1. 商家安装阶段（使用OAuth）"
        A[商家安装APP] --> B[OAuth授权]
        B --> C[获取Access Token]
        C --> D[存储Token到数据库]
    end
    
    subgraph "2. 数据准备阶段（使用OAuth Token）"
        D --> E[定时任务启动]
        E --> F[使用Token调用店匠API]
        F --> G[拉取商品/订单数据]
        G --> H[建立ES索引]
    end
    
    subgraph "3. 买家搜索阶段（不需要OAuth）"
        I[买家访问店铺] --> J[输入搜索词]
        J --> K[前端JS直接调用搜索API]
        K --> L[搜索ES索引]
        L --> M[返回结果]
    end
    
    H -.索引建立后.-> L
```

**关键理解：**
- **OAuth Token** = 你的后端 ↔ 店匠 Admin API（拉数据用）
- **前端搜索** = 买家浏览器 ↔ 你的搜索 API（不需要 OAuth）

---

## ✅ 你需要做的事情

### 1. **搜索 API 设计**

当前你的 Python 搜索服务应该已经支持：

```python
POST http://your-domain:6002/search/
Content-Type: application/json

{
  "query": "蓝牙耳机",
  "tenant": "tenant_1",  // 重要！用于隔离不同店铺
  "size": 20,
  "filters": {},
  "facets": ["product_type", "vendor"]
}
```

### 2. **CORS 配置**（如果前端直接调用）

在 Python FastAPI 服务中添加：

```python
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=[
        "https://*.myshoplaza.com",  # 店匠域名
        "https://your-domain.com"
    ],
    allow_methods=["POST", "GET"],
    allow_headers=["*"],
)
```

### 3. **店铺标识传递**

在前端 Liquid 模板中获取店铺域名：

```liquid
<script>
window.AI_SEARCH_CONFIG = {
  storeId: "{{ shop.domain }}",  // 店匠自动注入
  apiEndpoint: "https://your-domain.com/api/search/products"
};
</script>
```

### 4. **安全性考虑**

- ✅ 租户隔离：根据 `tenant` 参数查询不同索引
- ✅ 域名白名单：只允许店匠域名调用
- ✅ 速率限制：防止恶意请求
- ❌ **不需要**在每次搜索时验证 OAuth Token

---

## 🎯 总结

1. **OAuth 只用于后台数据同步**，不用于前端搜索
2. **前端搜索直接调用你的公开 API**，通过 `storeId` 参数隔离数据
3. **你的搜索接口可以是无认证的**，但必须做好租户隔离
4. **推荐方案**：前端 → 你的 Java 后端 → Python 搜索服务（便于日志统计和安全控制）

有其他疑问吗？