1
0
Files
nex/openspec/specs/provider-management/spec.md
lanyuanxiaoyao 5d58acf5a6 fix: 修复供应商管理弹窗交互问题并去掉 API Key 脱敏
- Dialog 设置 lazy={false} 修复首次打开编辑弹窗表单为空
- API Key 改为普通字段(前端去掉 password 类型,后端去掉掩码逻辑)
- 删除模型编辑弹窗中的统一模型 ID 字段
- 简化 ProviderService.Get 签名(去掉 maskKey 参数)
- 删除 domain 和 config 层的 MaskAPIKey() 方法
- 更新前后端测试(107 单元测试 + 16 E2E 全部通过)
- 同步 delta spec 到主 spec
2026-04-22 13:13:25 +08:00

6.6 KiB
Raw Blame History

Provider Management

Purpose

管理 AI 供应商的完整生命周期包括创建、查询、更新、删除供应商配置通过分层架构Handler -> Service -> Repository处理业务逻辑和数据访问。

Requirements

Requirement: 前端适配协议字段

前端 SHALL 在供应商管理界面支持协议字段的显示和选择。

Scenario: 供应商列表返回协议字段

  • WHEN/api/providers 发送 GET 请求
  • THEN 每个供应商 SHALL 包含 protocol 字段
  • THEN protocol 值 SHALL 为 "openai" 或 "anthropic"

Scenario: 创建供应商携带协议字段

  • WHEN/api/providers 发送 POST 请求
  • THEN 请求体 SHALL 包含 protocol 字段
  • THEN protocol 值 SHALL 为 "openai" 或 "anthropic"
  • THEN 前端 SHALL 提供协议选择下拉框

Scenario: 更新供应商携带协议字段

  • WHEN/api/providers/:id 发送 PUT 请求
  • THEN 请求体 MAY 包含 protocol 字段
  • THEN protocol 值 SHALL 为 "openai" 或 "anthropic"

Requirement: 创建供应商配置

网关 SHALL 允许通过管理 API 创建新的供应商配置。

Scenario: 使用有效数据创建供应商

  • WHEN/api/providers 发送 POST 请求携带有效的供应商数据id, name, api_key, base_url, protocol
  • THEN 网关 SHALL 在数据库中创建新的供应商记录
  • THEN 网关 SHALL 返回创建的供应商api_key 为完整值),状态码为 201
  • THEN 供应商 SHALL 默认启用
  • THEN protocol 字段 SHALL 为必填项,值为 "openai" 或 "anthropic"

Scenario: 使用重复 ID 创建供应商

  • WHEN/api/providers 发送 POST 请求,携带已存在的 ID
  • THEN 网关 SHALL 返回错误,状态码为 409 (Conflict)

Scenario: 创建供应商时缺少必需字段

  • WHEN/api/providers 发送 POST 请求缺少必需字段id, name, api_key 或 base_url
  • THEN 网关 SHALL 返回错误,状态码为 400 (Bad Request)
  • THEN 错误 SHALL 指示缺少哪些字段

Scenario: 创建供应商时 ID 包含非法字符

  • WHEN/api/providers 发送 POST 请求id 包含非 [a-zA-Z0-9_] 字符
  • THEN 网关 SHALL 返回错误,状态码为 400 (Bad Request)
  • THEN 错误 SHALL 指示 id 仅允许字母、数字、下划线

Scenario: 创建供应商时 ID 过长

  • WHEN/api/providers 发送 POST 请求id 长度超过 64
  • THEN 网关 SHALL 返回错误,状态码为 400 (Bad Request)

Requirement: 供应商 ID 不允许修改

供应商 ID 是主键,用于构建统一模型 ID不允许修改。

Scenario: 尝试修改供应商 ID

  • WHEN/api/providers/:id 发送 PUT 请求,请求体中包含 id 字段
  • THEN ProviderService.Update SHALL 在 service 层校验并返回错误
  • THEN 网关 SHALL 返回错误,状态码为 400 (Bad Request)
  • THEN 错误 SHALL 指示供应商 ID 不允许修改
  • THEN 错误格式 SHALL 为:
    {
      "error": "供应商 ID 不允许修改",
      "code": "IMMUTABLE_FIELD"
    }
    

校验位置: Service 层(ProviderService.Update

  • Service 层校验保证所有调用方handler、CLI、未来 API统一遵守
  • Handler 层负责捕获 ErrImmutableField 并转换为 HTTP 400 响应

原因:

  • 供应商 ID 是主键,用于构建统一模型 IDprovider_id/model_name
  • 修改 ID 会导致所有统一模型 ID 失效
  • 客户端缓存的模型 ID 全部失效
  • 如需修改,应创建新供应商并迁移模型

Requirement: 列出所有供应商

网关 SHALL 允许获取所有供应商配置。

Scenario: 成功列出供应商

  • WHEN/api/providers 发送 GET 请求
  • THEN 网关 SHALL 返回所有供应商的列表
  • THEN 每个供应商 SHALL 包含 id, name, api_key完整值, base_url, protocol, enabled, created_at, updated_at
  • THEN api_key SHALL 返回完整值(不进行掩码处理)

变更说明: 数据访问从 config 包迁移到 ProviderRepository。API 接口保持不变。

Requirement: 获取特定供应商

网关 SHALL 允许通过 ID 获取特定供应商。

Scenario: 获取存在的供应商

  • WHEN/api/providers/:id 发送 GET 请求,携带有效的供应商 ID
  • THEN 网关 SHALL 返回供应商详情
  • THEN SHALL 包含 protocol 字段
  • THEN api_key SHALL 返回完整值(不进行掩码处理)

Scenario: 获取不存在的供应商

  • WHEN/api/providers/:id 发送 GET 请求,携带不存在的 ID
  • THEN 网关 SHALL 返回错误,状态码为 404 (Not Found)

变更说明: 通过 ProviderService 和 ProviderRepository 实现。API 接口保持不变。

Requirement: 更新供应商配置

网关 SHALL 允许更新现有供应商配置。

Scenario: 使用有效数据更新供应商

  • WHEN/api/providers/:id 发送 PUT 请求,携带有效的供应商数据
  • THEN 网关 SHALL 更新数据库中的供应商记录
  • THEN 网关 SHALL 返回更新后的供应商api_key 为完整值)
  • THEN 更新 SHALL 支持修改 protocol 字段

变更说明: 通过 ProviderService 和 ProviderRepository 实现。API 接口保持不变。

Requirement: 删除供应商配置

网关 SHALL 允许删除供应商配置。

Scenario: 删除存在的供应商

  • WHEN/api/providers/:id 发送 DELETE 请求,携带有效的供应商 ID
  • THEN 网关 SHALL 删除供应商记录
  • THEN 网关 SHALL 删除所有关联的模型CASCADE
  • THEN 网关 SHALL 返回状态码 204 (No Content)

变更说明: 通过 ProviderService 和 ProviderRepository 实现。API 接口保持不变。

Requirement: 使用 service 层处理业务逻辑

Handler SHALL 通过 ProviderService 处理业务逻辑。

Scenario: 调用 service 方法

  • WHEN handler 收到请求
  • THEN SHALL 调用对应的 ProviderService 方法Create、Get、List、Update、Delete
  • THEN SHALL 使用 domain.Provider 类型

Scenario: 错误处理

  • WHEN service 返回错误
  • THEN SHALL 转换为 HTTP 错误响应
  • THEN SHALL 使用结构化错误处理

Requirement: 使用 repository 层访问数据

Service SHALL 通过 ProviderRepository 访问数据。

Scenario: 调用 repository 方法

  • WHEN service 处理业务逻辑
  • THEN SHALL 调用对应的 ProviderRepository 方法
  • THEN SHALL 使用 domain.Provider 类型

Scenario: 数据验证

  • WHEN 创建或更新供应商
  • THEN SHALL 在 service 层验证业务规则
  • THEN SHALL 在 repository 层执行数据库操作