# Error Responses

## Purpose

定义系统统一的错误响应格式和各类错误场景，确保客户端能够一致地处理错误。

## Requirements

### Requirement: 统一错误响应格式

系统 SHALL 使用统一的错误响应格式。

#### Scenario: 标准错误格式

- **WHEN** 返回错误响应
- **THEN** SHALL 使用以下 JSON 格式：
  ```json
  {
    "error": "错误描述",
    "code": "ERROR_CODE"
  }
  ```
- **THEN** `error` 字段 SHALL 包含人类可读的错误描述
- **THEN** `code` 字段 SHALL 包含机器可读的错误代码（可选）

### Requirement: 前端提取并处理错误码

前端 SHALL 提取后端结构化错误响应中的错误码并用于错误处理。

#### Scenario: API 客户端解析结构化错误

- **WHEN** 后端返回错误响应
- **THEN** API 客户端 SHALL 尝试解析 JSON 格式 `{error: string, code?: string}`
- **THEN** 如解析成功且包含 code 字段，SHALL 创建包含 code 的 ApiError
- **THEN** 如解析失败或不包含 code，SHALL 创建不包含 code 的 ApiError

#### Scenario: ApiError 包含错误码

- **WHEN** 创建 ApiError 对象
- **THEN** ApiError 类 SHALL 包含可选的 code 字段
- **THEN** code 字段类型 SHALL 为 `string | undefined`
- **THEN** 构造函数 SHALL 接受可选的 code 参数

#### Scenario: Hooks 使用错误码映射友好消息

- **WHEN** useMutation 或其他 Hook 处理错误
- **THEN** SHALL 检查 error.code 是否存在
- **THEN** 如存在，SHALL 使用映射表转换为友好中文消息
- **THEN** 如不存在或未定义映射，SHALL 使用 error.message

#### Scenario: 错误码映射表定义

- **WHEN** 定义错误码映射表
- **THEN** 映射表 SHALL 包含以下键值对：
  - `duplicate_model` → "同一供应商下模型名称已存在"
  - `invalid_provider_id` → "供应商 ID 仅允许字母、数字、下划线，长度 1-64"
  - `immutable_field` → "供应商 ID 不允许修改"
  - `provider_not_found` → "供应商不存在"
- **THEN** 映射表 SHALL 使用 TypeScript Record 类型确保类型安全

#### Scenario: 错误码映射降级处理

- **WHEN** 后端返回新的错误码（映射表未定义）
- **THEN** 前端 SHALL 降级使用 error.message
- **THEN** 前端 SHALL NOT 抛出错误或崩溃
- **THEN** 用户 SHALL 仍能看到原始错误消息

### Requirement: provider_id 校验错误

系统 SHALL 对 provider_id 校验错误返回明确的错误信息。

#### Scenario: provider_id 包含非法字符

- **WHEN** 创建或更新供应商时，provider_id 包含非 `[a-zA-Z0-9_]` 字符
- **THEN** SHALL 返回 HTTP 400 Bad Request
- **THEN** SHALL 返回以下 JSON 格式：
  ```json
  {
    "error": "供应商 ID 仅允许字母、数字、下划线",
    "code": "INVALID_PROVIDER_ID"
  }
  ```

#### Scenario: provider_id 长度超限

- **WHEN** 创建或更新供应商时，provider_id 长度超过 64
- **THEN** SHALL 返回 HTTP 400 Bad Request
- **THEN** SHALL 返回以下 JSON 格式：
  ```json
  {
    "error": "供应商 ID 长度不能超过 64 个字符",
    "code": "INVALID_PROVIDER_ID"
  }
  ```

### Requirement: 联合唯一约束冲突错误

系统 SHALL 对联合唯一约束冲突返回明确的错误信息。

#### Scenario: 创建模型时 provider_id + model_name 组合已存在

- **WHEN** 创建模型时，provider_id + model_name 组合已存在
- **THEN** SHALL 返回 HTTP 409 Conflict
- **THEN** SHALL 返回以下 JSON 格式：
  ```json
  {
    "error": "同一供应商下模型名称已存在",
    "code": "duplicate_model"
  }
  ```

#### Scenario: 更新模型时导致 provider_id + model_name 组合冲突

- **WHEN** 更新模型时，修改 provider_id 或 model_name 导致与已有记录冲突
- **THEN** SHALL 返回 HTTP 409 Conflict
- **THEN** SHALL 返回以下 JSON 格式：
  ```json
  {
    "error": "同一供应商下模型名称已存在",
    "code": "duplicate_model"
  }
  ```

### Requirement: 资源不存在错误

系统 SHALL 对资源不存在返回明确的错误信息。

#### Scenario: 模型不存在

- **WHEN** 查询或操作不存在的模型
- **THEN** SHALL 返回 HTTP 404 Not Found
- **THEN** SHALL 返回以下 JSON 格式：
  ```json
  {
    "error": "模型未找到"
  }
  ```

#### Scenario: 供应商不存在

- **WHEN** 创建模型时指定的供应商不存在
- **THEN** SHALL 返回 HTTP 400 Bad Request
- **THEN** SHALL 返回以下 JSON 格式：
  ```json
  {
    "error": "供应商不存在"
  }
  ```

### Requirement: 统一模型 ID 格式错误

系统 SHALL 对统一模型 ID 格式错误返回明确的错误信息。

#### Scenario: 统一模型 ID 格式无效

- **WHEN** 代理请求中的 model 字段不是有效的统一模型 ID 格式
- **THEN** 请求 SHALL 走 forwardPassthrough 透传到上游（兼容未适配的客户端）
- **THEN** 不返回错误，保持与上游的兼容性

**设计理由：**
- 统一模型 ID 是 BREAKING CHANGE，部分旧客户端可能仍使用原始模型名
- 透传策略允许上游自行判断并返回错误（如 404 model not found）
- 网关作为透明代理，不应拦截所有格式非法的请求

#### Scenario: 统一模型 ID 格式有效但对应模型不存在

- **WHEN** 代理请求中的 model 字段是有效的统一模型 ID 格式（含 `/`），但数据库中找不到对应的模型
- **THEN** SHALL 返回 HTTP 404 Not Found
- **THEN** SHALL 返回以下 JSON 格式：
  ```json
  {
    "error": "模型未找到"
  }
  ```

#### Scenario: 统一模型 ID 对应的模型不存在

- **WHEN** 解析统一模型 ID 后，数据库中找不到对应的模型
- **THEN** SHALL 返回 HTTP 404 Not Found
- **THEN** SHALL 返回以下 JSON 格式：
  ```json
  {
    "error": "模型未找到"
  }
  ```

#### Scenario: 统一模型 ID 对应的模型已禁用

- **WHEN** 解析统一模型 ID 后，对应的模型 enabled 为 false
- **THEN** SHALL 返回 HTTP 404 Not Found
- **THEN** SHALL 返回以下 JSON 格式：
  ```json
  {
    "error": "模型未找到"
  }
  ```

#### Scenario: 统一模型 ID 对应的供应商已禁用

- **WHEN** 解析统一模型 ID 后，对应的供应商 enabled 为 false
- **THEN** SHALL 返回 HTTP 404 Not Found
- **THEN** SHALL 返回以下 JSON 格式：
  ```json
  {
    "error": "模型未找到"
  }
  ```

### Requirement: JSON 格式错误

系统 SHALL 对请求体 JSON 格式错误返回明确的错误信息。

#### Scenario: 请求体 JSON 格式错误

- **WHEN** 代理请求的请求体不是有效的 JSON 格式
- **THEN** SHALL 返回 HTTP 400 Bad Request
- **THEN** SHALL 返回以下 JSON 格式：
  ```json
  {
    "error": "请求体 JSON 格式错误",
    "code": "INVALID_JSON"
  }
  ```

#### Scenario: Smart Passthrough 时请求体 JSON 格式错误

- **WHEN** 同协议 Smart Passthrough 场景下，请求体 JSON 格式不正确
- **THEN** SHALL 返回 HTTP 400 Bad Request
- **THEN** SHALL 返回以下 JSON 格式：
  ```json
  {
    "error": "请求体 JSON 格式错误",
    "code": "INVALID_JSON"
  }
  ```

### Requirement: 不可变字段错误

系统 SHALL 对尝试修改不可变字段返回明确的错误信息。

#### Scenario: 尝试修改供应商 ID

- **WHEN** 更新供应商时，请求体中包含 `id` 字段
- **THEN** SHALL 返回 HTTP 400 Bad Request
- **THEN** SHALL 返回以下 JSON 格式：
  ```json
  {
    "error": "供应商 ID 不允许修改",
    "code": "IMMUTABLE_FIELD"
  }
  ```
