974 lines
24 KiB
Markdown
974 lines
24 KiB
Markdown
# sub2api-cn-relay-manager Implementation Plan
|
||
|
||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||
|
||
**Goal:** 构建一个完全独立于宿主 `sub2api` 的外部伴生控制面,在不修改宿主代码的前提下,通过安装 `model_pack`、批量导入国产模型 key、自动创建宿主资源并完成用户访问闭环,让普通用户直接通过 `sub2api` 标准 API 使用国产模型。
|
||
|
||
**Architecture:** 采用“外部控制面 + `model_pack` + 宿主 HTTP API 适配器”架构。控制面只通过 `sub2api` 公开管理 API 和标准 API 工作,负责模型包安装、宿主能力探测、资源编排、导入回滚、访问闭环验证和持续对账;宿主继续承担标准 API 网关职责,不做任何源码、数据库或运行时注入修改。
|
||
|
||
**Tech Stack:** Go 1.24、Chi、`database/sql` + SQLite 驱动、PostgreSQL 兼容预留、OpenAPI 3.1、Go `testing` + `httptest`、前端首版后置,MVP 先提供 HTTP API、CLI 和 Docker 交付物。
|
||
|
||
---
|
||
|
||
## 1. 实施边界
|
||
|
||
### 1.1 必须满足
|
||
|
||
- 不修改宿主 `sub2api` 源码
|
||
- 不 fork 宿主并运行自定义二进制
|
||
- 不直接写宿主数据库
|
||
- 不向宿主目录写入运行时代码、插件文件或配置补丁
|
||
- 只通过宿主现有管理 API 和标准 API 工作
|
||
- 首版必须完成从“导入 key”到“普通用户可实际调用”的闭环
|
||
|
||
### 1.2 首版不做
|
||
|
||
- 宿主原生插件中心
|
||
- 任意后端代码插件加载
|
||
- 代替用户签发宿主最终 API key
|
||
- 多宿主编排联邦
|
||
- 自定义计费、结算或审计系统
|
||
|
||
## 2. 目标目录结构
|
||
|
||
```text
|
||
sub2api-cn-relay-manager/
|
||
cmd/
|
||
server/main.go
|
||
cli/main.go
|
||
internal/
|
||
app/
|
||
app.go
|
||
bootstrap.go
|
||
config/
|
||
config.go
|
||
domain/
|
||
host.go
|
||
pack.go
|
||
provider.go
|
||
resource.go
|
||
import_batch.go
|
||
reconcile.go
|
||
access_closure.go
|
||
host/
|
||
sub2api/
|
||
client.go
|
||
capability_probe.go
|
||
groups.go
|
||
channels.go
|
||
plans.go
|
||
accounts.go
|
||
subscriptions.go
|
||
gateway_probe.go
|
||
pack/
|
||
manifest.go
|
||
provider_manifest.go
|
||
checksum.go
|
||
validator.go
|
||
loader.go
|
||
provision/
|
||
preview_service.go
|
||
import_service.go
|
||
rollback_service.go
|
||
naming.go
|
||
access/
|
||
planner.go
|
||
subscription_service.go
|
||
self_service_checker.go
|
||
reconcile/
|
||
runner.go
|
||
drift_checker.go
|
||
probe_runner.go
|
||
store/
|
||
migrations/
|
||
sqlite/
|
||
db.go
|
||
hosts_repo.go
|
||
host_capability_snapshots_repo.go
|
||
packs_repo.go
|
||
providers_repo.go
|
||
installs_repo.go
|
||
import_batches_repo.go
|
||
resources_repo.go
|
||
reconcile_runs_repo.go
|
||
probe_results_repo.go
|
||
access_closure_records_repo.go
|
||
api/
|
||
http/
|
||
router.go
|
||
middleware.go
|
||
hosts_handler.go
|
||
packs_handler.go
|
||
providers_handler.go
|
||
imports_handler.go
|
||
access_handler.go
|
||
reconcile_handler.go
|
||
dto/
|
||
hosts.go
|
||
packs.go
|
||
providers.go
|
||
imports.go
|
||
access.go
|
||
reconcile.go
|
||
worker/
|
||
scheduler.go
|
||
jobs.go
|
||
docs/
|
||
api/openapi.yaml
|
||
plans/
|
||
2026-05-12-sub2api-cn-relay-manager-implementation-plan.md
|
||
packs/
|
||
openai-cn-pack/
|
||
pack.json.example
|
||
providers/
|
||
deepseek.json.example
|
||
tests/
|
||
integration/
|
||
host_stub_test.go
|
||
install_pack_test.go
|
||
import_keys_test.go
|
||
access_closure_test.go
|
||
reconcile_test.go
|
||
```
|
||
|
||
## 3. 核心接口与契约
|
||
|
||
### 3.1 控制面对外 API
|
||
|
||
#### 宿主管理
|
||
|
||
- `POST /api/hosts`
|
||
- `GET /api/hosts`
|
||
- `GET /api/hosts/{host_id}`
|
||
- `POST /api/hosts/{host_id}/probe`
|
||
|
||
#### 模型包管理
|
||
|
||
- `POST /api/packs/install`
|
||
- `GET /api/packs`
|
||
- `GET /api/packs/{pack_id}`
|
||
- `GET /api/packs/{pack_id}/providers`
|
||
|
||
#### Provider 导入
|
||
|
||
- `POST /api/providers/{provider_id}/preview-import`
|
||
- `POST /api/providers/{provider_id}/import`
|
||
- `GET /api/providers/{provider_id}/import-batches`
|
||
- `GET /api/import-batches/{batch_id}`
|
||
- `POST /api/import-batches/{batch_id}/rollback`
|
||
|
||
#### 访问闭环
|
||
|
||
- `POST /api/providers/{provider_id}/access/preview`
|
||
- `POST /api/providers/{provider_id}/access/assign-subscriptions`
|
||
- `GET /api/providers/{provider_id}/access/status`
|
||
|
||
#### 对账与状态
|
||
|
||
- `POST /api/providers/{provider_id}/reconcile`
|
||
- `GET /api/providers/{provider_id}/status`
|
||
- `GET /api/providers/{provider_id}/resources`
|
||
|
||
### 3.2 宿主适配器接口
|
||
|
||
`internal/host/sub2api/client.go` 中定义单一适配接口:
|
||
|
||
```go
|
||
type HostAdapter interface {
|
||
GetHostVersion(ctx context.Context) (string, error)
|
||
ProbeCapabilities(ctx context.Context) (HostCapabilities, error)
|
||
CreateGroup(ctx context.Context, req CreateGroupRequest) (GroupRef, error)
|
||
CreateChannel(ctx context.Context, req CreateChannelRequest) (ChannelRef, error)
|
||
CreatePlan(ctx context.Context, req CreatePlanRequest) (PlanRef, error)
|
||
CreateAccount(ctx context.Context, req CreateAccountRequest) (AccountRef, error)
|
||
BatchCreateAccounts(ctx context.Context, req BatchCreateAccountsRequest) ([]AccountRef, error)
|
||
TestAccount(ctx context.Context, accountID string) (ProbeResult, error)
|
||
GetAccountModels(ctx context.Context, accountID string) ([]string, error)
|
||
AssignSubscription(ctx context.Context, req AssignSubscriptionRequest) (SubscriptionRef, error)
|
||
CheckGatewayAccess(ctx context.Context, req GatewayAccessCheckRequest) (GatewayAccessResult, error)
|
||
DeleteGroup(ctx context.Context, groupID string) error
|
||
DeleteChannel(ctx context.Context, channelID string) error
|
||
DeletePlan(ctx context.Context, planID string) error
|
||
DeleteAccount(ctx context.Context, accountID string) error
|
||
ListManagedResources(ctx context.Context, req ListManagedResourcesRequest) (ManagedResourceSnapshot, error)
|
||
}
|
||
```
|
||
|
||
约束:
|
||
|
||
- 所有宿主调用都必须通过该接口进入
|
||
- 任何绕过该接口的 HTTP 调用都视为实现缺陷
|
||
- 所有宿主 API 路径、请求体和响应体在 `internal/host/sub2api/` 内部封装,不向业务层泄漏
|
||
|
||
## 4. 状态机
|
||
|
||
### 4.1 Provider 安装状态
|
||
|
||
```text
|
||
discovered -> validated -> installed -> active
|
||
| |
|
||
v v
|
||
failed <-> degraded <-> drifted
|
||
|
|
||
v
|
||
disabled
|
||
```
|
||
|
||
状态定义:
|
||
|
||
- `discovered`:模型包已上传,尚未完成结构校验
|
||
- `validated`:包结构、校验和、版本兼容、宿主能力探测通过
|
||
- `installed`:provider 元数据和模板已写入控制面状态库
|
||
- `active`:宿主资源存在,账号探测成功,至少一种用户访问模式已验证可用
|
||
- `degraded`:核心资源存在但 smoke test、模型探测或访问闭环部分失败
|
||
- `drifted`:宿主资源与控制面记录不一致,例如资源被人工改动或删除
|
||
- `failed`:安装、导入、回滚、对账任一关键步骤失败
|
||
- `disabled`:provider 被显式停用,不再调度对账任务
|
||
|
||
### 4.2 导入批次状态
|
||
|
||
```text
|
||
pending -> previewed -> running -> succeeded
|
||
| | |
|
||
| | v
|
||
| -> partially_succeeded
|
||
v
|
||
failed -> rollback_running -> rolled_back
|
||
```
|
||
|
||
### 4.3 访问闭环状态
|
||
|
||
- `not_configured`
|
||
- `subscription_ready`
|
||
- `self_service_ready`
|
||
- `fully_ready`
|
||
- `broken`
|
||
|
||
判定规则:
|
||
|
||
- 仅 `subscription` 模式可用:`subscription_ready`
|
||
- 仅 `self-service` 模式可用:`self_service_ready`
|
||
- 两者都可用:`fully_ready`
|
||
- 两者都不可用:`broken`
|
||
|
||
## 5. 校验流程
|
||
|
||
### 5.1 宿主接入校验
|
||
|
||
请求:`POST /api/hosts`
|
||
|
||
流程:
|
||
|
||
1. 校验 `base_url`、认证信息格式
|
||
2. 拉取宿主版本
|
||
3. 探测宿主管理 API 能力:
|
||
- group 创建
|
||
- channel 创建
|
||
- plan 创建
|
||
- account 创建 / 测试 / 模型列举
|
||
- subscription 分配
|
||
4. 生成能力矩阵并落库
|
||
5. 若任一关键能力缺失,宿主状态标记为 `unsupported`
|
||
|
||
通过条件:
|
||
|
||
- 宿主版本在 `pack.json` 兼容区间内
|
||
- 所有首版硬依赖 API 均可调用
|
||
|
||
### 5.2 模型包装载校验
|
||
|
||
请求:`POST /api/packs/install`
|
||
|
||
流程:
|
||
|
||
1. 解压上传包到临时目录
|
||
2. 校验 `pack.json` 存在且字段完整
|
||
3. 校验 `providers/*.json` 至少存在一个 provider
|
||
4. 校验 `checksums.txt`
|
||
5. 校验 provider schema
|
||
6. 校验模型包声明的宿主版本兼容性
|
||
7. 将包元数据、provider 元数据写入状态库
|
||
|
||
拒绝条件:
|
||
|
||
- 缺少 `pack.json`
|
||
- provider 文件重复或 `provider_id` 冲突
|
||
- `base_url` 非 HTTPS
|
||
- `default_models` 为空
|
||
- `smoke_test_model` 不在 `default_models` 内
|
||
|
||
### 5.3 导入预检
|
||
|
||
请求:`POST /api/providers/{provider_id}/preview-import`
|
||
|
||
流程:
|
||
|
||
1. 规范化 key 列表:去空白、去重、去 BOM、过滤空行
|
||
2. 校验导入模式是否允许:`strict` 或 `partial`
|
||
3. 生成建议资源名
|
||
4. 检查宿主是否已有同名 group / channel / plan
|
||
5. 检查当前 provider 是否已有历史导入资源
|
||
6. 计算执行计划:
|
||
- create
|
||
- reuse
|
||
- conflict
|
||
7. 输出预检报告,不落宿主资源
|
||
|
||
### 5.4 导入执行
|
||
|
||
请求:`POST /api/providers/{provider_id}/import`
|
||
|
||
流程:
|
||
|
||
1. 创建批次记录,状态 `pending`
|
||
2. 执行预检,成功后切换为 `previewed`
|
||
3. 创建或复用 group
|
||
4. 创建或复用 channel
|
||
5. 按访问模式决定是否创建或复用 plan
|
||
6. 批量创建 accounts
|
||
7. 对每个 account 执行:
|
||
- 宿主 `/test`
|
||
- 宿主 `/models`
|
||
- 关键模型 smoke probe
|
||
8. 若访问模式为 `subscription`:
|
||
- 预览待分配用户列表
|
||
- 调用宿主分配接口
|
||
9. 执行网关访问探测
|
||
10. 写入资源映射、探测结果、访问闭环状态
|
||
11. 根据模式决定成功、部分成功或回滚
|
||
|
||
### 5.5 回滚校验
|
||
|
||
触发条件:
|
||
|
||
- `strict` 模式下任一关键步骤失败
|
||
- 管理员主动回滚批次
|
||
|
||
流程:
|
||
|
||
1. 读取批次关联资源
|
||
2. 按依赖逆序删除:
|
||
- subscriptions
|
||
- accounts
|
||
- channel
|
||
- plan
|
||
- group
|
||
3. 删除成功后标记 `rolled_back`
|
||
4. 若部分删除失败,批次状态标记 `failed`,provider 状态标记 `drifted`
|
||
|
||
### 5.6 对账校验
|
||
|
||
请求:`POST /api/providers/{provider_id}/reconcile`
|
||
|
||
流程:
|
||
|
||
1. 读取控制面记录的宿主资源快照
|
||
2. 拉取宿主实际资源
|
||
3. 核对 group / channel / plan / account 是否仍存在
|
||
4. 对 active account 重新执行 `/test` 和 `/models`
|
||
5. 重新执行至少一种用户访问模式探测
|
||
6. 汇总为:
|
||
- `active`
|
||
- `degraded`
|
||
- `drifted`
|
||
- `failed`
|
||
|
||
## 6. API 请求与响应最小契约
|
||
|
||
### 6.1 `POST /api/hosts`
|
||
|
||
请求体:
|
||
|
||
```json
|
||
{
|
||
"name": "prod-sub2api",
|
||
"base_url": "https://sub2api.example.com",
|
||
"auth": {
|
||
"type": "bearer",
|
||
"token": "xxxx"
|
||
}
|
||
}
|
||
```
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"host_id": "host_01",
|
||
"version": "0.1.126",
|
||
"status": "supported",
|
||
"capabilities": {
|
||
"groups": true,
|
||
"channels": true,
|
||
"plans": true,
|
||
"accounts": true,
|
||
"account_test": true,
|
||
"account_models": true,
|
||
"subscriptions": true
|
||
}
|
||
}
|
||
```
|
||
|
||
### 6.2 `POST /api/providers/{provider_id}/import`
|
||
|
||
请求体:
|
||
|
||
```json
|
||
{
|
||
"host_id": "host_01",
|
||
"mode": "partial",
|
||
"access_mode": "subscription",
|
||
"keys": [
|
||
"sk-1",
|
||
"sk-2"
|
||
],
|
||
"subscription_targets": [
|
||
{
|
||
"user_id": "user_01",
|
||
"duration_days": 30
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"batch_id": "batch_01",
|
||
"status": "succeeded",
|
||
"provider_status": "active",
|
||
"access_closure_status": "subscription_ready",
|
||
"created": {
|
||
"group_id": "g_01",
|
||
"channel_id": "c_01",
|
||
"plan_id": "p_01",
|
||
"account_ids": ["a_01", "a_02"]
|
||
}
|
||
}
|
||
```
|
||
|
||
## 7. 数据库与迁移计划
|
||
|
||
### 7.1 必建表
|
||
|
||
- `hosts`
|
||
- `host_capability_snapshots`
|
||
- `packs`
|
||
- `providers`
|
||
- `provider_installs`
|
||
- `import_batches`
|
||
- `import_batch_items`
|
||
- `managed_resources`
|
||
- `probe_results`
|
||
- `access_closure_records`
|
||
- `reconcile_runs`
|
||
|
||
### 7.2 约束
|
||
|
||
- `providers.provider_id` 在同一 `pack_id` 下唯一
|
||
- `managed_resources` 以 `host_id + provider_id + resource_type + logical_key` 唯一
|
||
- `import_batch_items.raw_key_hash` 唯一约束只用于同一批次内去重
|
||
- 密钥明文不落库,只存掩码、哈希和宿主侧 account ID
|
||
|
||
## 8. 测试策略
|
||
|
||
### 8.1 单元测试
|
||
|
||
- `internal/pack/validator.go`
|
||
- `internal/provision/naming.go`
|
||
- `internal/access/planner.go`
|
||
- `internal/reconcile/drift_checker.go`
|
||
|
||
### 8.2 宿主适配器契约测试
|
||
|
||
- 使用 `httptest.Server` 模拟 `sub2api` 管理 API
|
||
- 覆盖成功、鉴权失败、字段缺失、版本不兼容、接口漂移
|
||
|
||
### 8.3 集成测试
|
||
|
||
- 安装模型包
|
||
- 多 key 导入
|
||
- `strict` 失败回滚
|
||
- `partial` 部分成功
|
||
- `subscription` 访问闭环
|
||
- 对账发现漂移
|
||
|
||
### 8.4 验收测试
|
||
|
||
- 真实连接一套测试版 `sub2api`
|
||
- 导入 `deepseek` 两个 key
|
||
- 为测试用户分配 subscription
|
||
- 使用该用户实际标准 API key 发起一次 `/v1/chat/completions`
|
||
- 验证请求成功进入目标 provider
|
||
|
||
## 9. 分阶段里程碑
|
||
|
||
### Milestone 1:项目骨架与状态库
|
||
|
||
验收标准:
|
||
|
||
- 控制面可启动
|
||
- SQLite 可自动迁移
|
||
- `POST /api/hosts`、`GET /api/hosts` 可用
|
||
- 宿主能力探测结果可持久化
|
||
|
||
### Milestone 2:模型包安装与校验
|
||
|
||
验收标准:
|
||
|
||
- 可安装 `openai-cn-pack`
|
||
- 可列出 provider
|
||
- 版本兼容和 checksum 校验有效
|
||
- 非法 provider 包会被拒绝
|
||
|
||
### Milestone 3:多 key 导入与资源编排
|
||
|
||
验收标准:
|
||
|
||
- 可对单个 provider 执行预检
|
||
- 可批量导入多个 key
|
||
- 可创建 group / channel / plan / account
|
||
- `strict` / `partial` 两种模式行为正确
|
||
|
||
### Milestone 4:访问闭环
|
||
|
||
验收标准:
|
||
|
||
- `subscription` 模式打通
|
||
- 至少一个测试用户可通过宿主标准 API 调用目标国产模型
|
||
- 控制面可返回 `subscription_ready` 或 `fully_ready`
|
||
|
||
### Milestone 5:对账、漂移与回滚
|
||
|
||
验收标准:
|
||
|
||
- 定时对账可运行
|
||
- 宿主资源被人工删除时,provider 状态变为 `drifted`
|
||
- 可对批次执行回滚
|
||
- 回滚后状态库与宿主资源一致
|
||
|
||
### Milestone 6:独立交付与部署文档
|
||
|
||
验收标准:
|
||
|
||
- 可生成单二进制和 Docker 镜像
|
||
- 提供 `.env.example` 和最小部署说明
|
||
- 可通过 CLI 完成宿主接入、模型包装载和导入
|
||
- 文档明确宿主零改动边界和支持矩阵
|
||
|
||
## 10. 逐任务实施清单
|
||
|
||
### Task 1: 建立项目骨架与配置加载
|
||
|
||
**Files:**
|
||
- Create: `cmd/server/main.go`
|
||
- Create: `cmd/cli/main.go`
|
||
- Create: `internal/app/app.go`
|
||
- Create: `internal/app/bootstrap.go`
|
||
- Create: `internal/config/config.go`
|
||
|
||
**Step 1: 写配置加载单元测试**
|
||
|
||
- Test: `tests/integration/config_bootstrap_test.go`
|
||
- 覆盖环境变量、默认值、缺失必填项
|
||
|
||
**Step 2: 运行测试验证失败**
|
||
|
||
Run: `go test ./tests/integration -run TestConfigBootstrap -v`
|
||
Expected: FAIL,提示配置加载尚未实现
|
||
|
||
**Step 3: 写最小实现**
|
||
|
||
- 完成配置结构、启动参数、默认 SQLite DSN
|
||
|
||
**Step 4: 运行测试验证通过**
|
||
|
||
Run: `go test ./tests/integration -run TestConfigBootstrap -v`
|
||
Expected: PASS
|
||
|
||
**Step 5: Commit**
|
||
|
||
```bash
|
||
git add cmd internal tests
|
||
git commit -m "feat: bootstrap control plane app skeleton"
|
||
```
|
||
|
||
### Task 2: 建立状态库与迁移
|
||
|
||
**Files:**
|
||
- Create: `internal/store/migrations/0001_init.sql`
|
||
- Create: `internal/store/sqlite/db.go`
|
||
- Create: `internal/store/sqlite/hosts_repo.go`
|
||
- Create: `internal/store/sqlite/packs_repo.go`
|
||
- Create: `internal/store/sqlite/providers_repo.go`
|
||
|
||
**Step 1: 写迁移与仓储测试**
|
||
|
||
- Test: `tests/integration/store_init_test.go`
|
||
- 覆盖建表、唯一约束、事务回滚
|
||
|
||
**Step 2: 跑失败测试**
|
||
|
||
Run: `go test ./tests/integration -run TestStoreInit -v`
|
||
Expected: FAIL,表不存在
|
||
|
||
**Step 3: 实现最小迁移与仓储**
|
||
|
||
- 先实现 `hosts`、`packs`、`providers`
|
||
|
||
**Step 4: 跑测试**
|
||
|
||
Run: `go test ./tests/integration -run TestStoreInit -v`
|
||
Expected: PASS
|
||
|
||
**Step 5: Commit**
|
||
|
||
```bash
|
||
git add internal/store tests
|
||
git commit -m "feat: add state store migrations and repositories"
|
||
```
|
||
|
||
### Task 3: 实现宿主适配器与能力探测
|
||
|
||
**Files:**
|
||
- Create: `internal/host/sub2api/client.go`
|
||
- Create: `internal/host/sub2api/capability_probe.go`
|
||
- Create: `internal/host/sub2api/groups.go`
|
||
- Create: `internal/host/sub2api/channels.go`
|
||
- Create: `internal/host/sub2api/plans.go`
|
||
- Create: `internal/host/sub2api/accounts.go`
|
||
- Create: `internal/host/sub2api/subscriptions.go`
|
||
|
||
**Step 1: 写 `httptest` 宿主桩和适配器测试**
|
||
|
||
- Test: `tests/integration/host_stub_test.go`
|
||
- 覆盖版本获取、能力探测、错误映射
|
||
|
||
**Step 2: 先跑失败**
|
||
|
||
Run: `go test ./tests/integration -run TestSub2APIHostAdapter -v`
|
||
Expected: FAIL,适配器未实现
|
||
|
||
**Step 3: 写最小适配器实现**
|
||
|
||
- 只支持首版必需 API
|
||
|
||
**Step 4: 跑测试**
|
||
|
||
Run: `go test ./tests/integration -run TestSub2APIHostAdapter -v`
|
||
Expected: PASS
|
||
|
||
**Step 5: Commit**
|
||
|
||
```bash
|
||
git add internal/host tests
|
||
git commit -m "feat: add sub2api host adapter and capability probe"
|
||
```
|
||
|
||
### Task 4: 实现模型包协议与安装校验
|
||
|
||
**Files:**
|
||
- Create: `internal/pack/manifest.go`
|
||
- Create: `internal/pack/provider_manifest.go`
|
||
- Create: `internal/pack/checksum.go`
|
||
- Create: `internal/pack/validator.go`
|
||
- Create: `internal/pack/loader.go`
|
||
|
||
**Step 1: 写模型包校验测试**
|
||
|
||
- Test: `tests/integration/install_pack_test.go`
|
||
- 覆盖合法包、缺失字段、坏 checksum、版本不兼容
|
||
|
||
**Step 2: 跑失败**
|
||
|
||
Run: `go test ./tests/integration -run TestInstallPack -v`
|
||
Expected: FAIL,包解析和校验未实现
|
||
|
||
**Step 3: 实现最小装载器**
|
||
|
||
- 支持从 zip 和目录读取
|
||
|
||
**Step 4: 跑测试**
|
||
|
||
Run: `go test ./tests/integration -run TestInstallPack -v`
|
||
Expected: PASS
|
||
|
||
**Step 5: Commit**
|
||
|
||
```bash
|
||
git add internal/pack tests
|
||
git commit -m "feat: add model pack loader and validator"
|
||
```
|
||
|
||
### Task 5: 实现导入预检与命名策略
|
||
|
||
**Files:**
|
||
- Create: `internal/provision/preview_service.go`
|
||
- Create: `internal/provision/naming.go`
|
||
- Create: `internal/domain/import_batch.go`
|
||
|
||
**Step 1: 写预检测试**
|
||
|
||
- Test: `tests/integration/import_preview_test.go`
|
||
- 覆盖 key 规范化、冲突判定、create/reuse/conflict 输出
|
||
|
||
**Step 2: 跑失败**
|
||
|
||
Run: `go test ./tests/integration -run TestImportPreview -v`
|
||
Expected: FAIL
|
||
|
||
**Step 3: 实现预检逻辑**
|
||
|
||
- 只做只读分析,不写宿主
|
||
|
||
**Step 4: 跑测试**
|
||
|
||
Run: `go test ./tests/integration -run TestImportPreview -v`
|
||
Expected: PASS
|
||
|
||
**Step 5: Commit**
|
||
|
||
```bash
|
||
git add internal/provision internal/domain tests
|
||
git commit -m "feat: add provider import preview flow"
|
||
```
|
||
|
||
### Task 6: 实现多 key 导入、探测与回滚
|
||
|
||
**Files:**
|
||
- Create: `internal/provision/import_service.go`
|
||
- Create: `internal/provision/rollback_service.go`
|
||
- Create: `internal/store/sqlite/import_batches_repo.go`
|
||
- Create: `internal/store/sqlite/resources_repo.go`
|
||
- Create: `internal/store/sqlite/probe_results_repo.go`
|
||
|
||
**Step 1: 写导入测试**
|
||
|
||
- Test: `tests/integration/import_keys_test.go`
|
||
- 覆盖 `strict` 成功、`strict` 回滚、`partial` 部分成功
|
||
|
||
**Step 2: 跑失败**
|
||
|
||
Run: `go test ./tests/integration -run TestImportKeys -v`
|
||
Expected: FAIL
|
||
|
||
**Step 3: 实现导入与回滚服务**
|
||
|
||
- 创建宿主资源
|
||
- 记录资源映射
|
||
- 运行账号测试和模型探测
|
||
|
||
**Step 4: 跑测试**
|
||
|
||
Run: `go test ./tests/integration -run TestImportKeys -v`
|
||
Expected: PASS
|
||
|
||
**Step 5: Commit**
|
||
|
||
```bash
|
||
git add internal/provision internal/store tests
|
||
git commit -m "feat: add multi-key import and rollback flow"
|
||
```
|
||
|
||
### Task 7: 实现访问闭环
|
||
|
||
**Files:**
|
||
- Create: `internal/access/planner.go`
|
||
- Create: `internal/access/subscription_service.go`
|
||
- Create: `internal/access/self_service_checker.go`
|
||
- Create: `internal/domain/access_closure.go`
|
||
|
||
**Step 1: 写访问闭环测试**
|
||
|
||
- Test: `tests/integration/access_closure_test.go`
|
||
- 覆盖 `subscription_ready`、`self_service_ready`、`broken`
|
||
|
||
**Step 2: 跑失败**
|
||
|
||
Run: `go test ./tests/integration -run TestAccessClosure -v`
|
||
Expected: FAIL
|
||
|
||
**Step 3: 实现最小闭环服务**
|
||
|
||
- 首版先保证 `subscription` 模式打通
|
||
- `self-service` 只做绑定状态检查,不代用户签发 key
|
||
|
||
**Step 4: 跑测试**
|
||
|
||
Run: `go test ./tests/integration -run TestAccessClosure -v`
|
||
Expected: PASS
|
||
|
||
**Step 5: Commit**
|
||
|
||
```bash
|
||
git add internal/access internal/domain tests
|
||
git commit -m "feat: add access closure services"
|
||
```
|
||
|
||
### Task 8: 实现对账与漂移检测
|
||
|
||
**Files:**
|
||
- Create: `internal/reconcile/runner.go`
|
||
- Create: `internal/reconcile/drift_checker.go`
|
||
- Create: `internal/reconcile/probe_runner.go`
|
||
- Create: `internal/store/sqlite/reconcile_runs_repo.go`
|
||
|
||
**Step 1: 写对账测试**
|
||
|
||
- Test: `tests/integration/reconcile_test.go`
|
||
- 覆盖正常、资源丢失、账号失效、访问闭环破坏
|
||
|
||
**Step 2: 跑失败**
|
||
|
||
Run: `go test ./tests/integration -run TestReconcile -v`
|
||
Expected: FAIL
|
||
|
||
**Step 3: 实现对账器**
|
||
|
||
- 拉取宿主资源快照
|
||
- 比对状态库
|
||
- 重新执行探测
|
||
|
||
**Step 4: 跑测试**
|
||
|
||
Run: `go test ./tests/integration -run TestReconcile -v`
|
||
Expected: PASS
|
||
|
||
**Step 5: Commit**
|
||
|
||
```bash
|
||
git add internal/reconcile internal/store tests
|
||
git commit -m "feat: add provider reconcile and drift detection"
|
||
```
|
||
|
||
### Task 9: 暴露 HTTP API 与 OpenAPI 文档
|
||
|
||
**Files:**
|
||
- Create: `internal/api/http/router.go`
|
||
- Create: `internal/api/http/middleware.go`
|
||
- Create: `internal/api/http/hosts_handler.go`
|
||
- Create: `internal/api/http/packs_handler.go`
|
||
- Create: `internal/api/http/providers_handler.go`
|
||
- Create: `internal/api/http/imports_handler.go`
|
||
- Create: `internal/api/http/access_handler.go`
|
||
- Create: `internal/api/http/reconcile_handler.go`
|
||
- Create: `docs/api/openapi.yaml`
|
||
|
||
**Step 1: 写 handler 测试**
|
||
|
||
- Test: `tests/integration/http_api_test.go`
|
||
- 覆盖主要成功与失败路径
|
||
|
||
**Step 2: 跑失败**
|
||
|
||
Run: `go test ./tests/integration -run TestHTTPAPI -v`
|
||
Expected: FAIL
|
||
|
||
**Step 3: 实现路由和 DTO**
|
||
|
||
- 保持 handler 只做参数解析和错误映射
|
||
|
||
**Step 4: 跑测试**
|
||
|
||
Run: `go test ./tests/integration -run TestHTTPAPI -v`
|
||
Expected: PASS
|
||
|
||
**Step 5: Commit**
|
||
|
||
```bash
|
||
git add internal/api docs/api tests
|
||
git commit -m "feat: add control plane HTTP API"
|
||
```
|
||
|
||
### Task 10: 增加调度器、CLI 和端到端验收脚本
|
||
|
||
**Files:**
|
||
- Create: `internal/worker/scheduler.go`
|
||
- Create: `internal/worker/jobs.go`
|
||
- Modify: `cmd/cli/main.go`
|
||
- Create: `scripts/e2e/verify_with_host_stub.sh`
|
||
|
||
**Step 1: 写调度与 CLI 测试**
|
||
|
||
- Test: `tests/integration/cli_scheduler_test.go`
|
||
- 覆盖手动 reconcile 和定时调度
|
||
|
||
**Step 2: 跑失败**
|
||
|
||
Run: `go test ./tests/integration -run TestCLIScheduler -v`
|
||
Expected: FAIL
|
||
|
||
**Step 3: 实现最小调度与 CLI**
|
||
|
||
- 支持 `host add`、`pack install`、`provider import`、`reconcile run`
|
||
|
||
**Step 4: 跑全量测试**
|
||
|
||
Run: `go test ./...`
|
||
Expected: PASS
|
||
|
||
**Step 5: Commit**
|
||
|
||
```bash
|
||
git add cmd internal scripts tests
|
||
git commit -m "feat: add scheduler cli and e2e verification script"
|
||
```
|
||
|
||
### Task 11: 补齐独立交付物与部署文档
|
||
|
||
**Files:**
|
||
- Create: `Dockerfile`
|
||
- Create: `.env.example`
|
||
- Create: `deploy/docker-compose.yml`
|
||
- Create: `docs/deployment.md`
|
||
- Modify: `README.md`
|
||
|
||
**Step 1: 写交付物检查测试**
|
||
|
||
- Test: `tests/integration/distribution_smoke_test.go`
|
||
- 覆盖配置样例、镜像启动参数、CLI 帮助输出
|
||
|
||
**Step 2: 跑失败**
|
||
|
||
Run: `go test ./tests/integration -run TestDistributionSmoke -v`
|
||
Expected: FAIL
|
||
|
||
**Step 3: 实现最小交付物**
|
||
|
||
- 提供可运行镜像
|
||
- 提供本地 SQLite 默认部署
|
||
- 文档明确对接宿主所需环境变量
|
||
|
||
**Step 4: 跑测试**
|
||
|
||
Run: `go test ./tests/integration -run TestDistributionSmoke -v`
|
||
Expected: PASS
|
||
|
||
**Step 5: Commit**
|
||
|
||
```bash
|
||
git add Dockerfile .env.example deploy docs README.md tests
|
||
git commit -m "docs: add distribution artifacts and deployment guide"
|
||
```
|
||
|
||
## 11. 最终验收清单
|
||
|
||
- 从空状态启动控制面
|
||
- 连接一套兼容版本的 `sub2api`
|
||
- 安装 `openai-cn-pack`
|
||
- 导入 `deepseek` 两个 key
|
||
- 成功创建宿主资源并记录映射
|
||
- 至少一个账号通过测试与模型探测
|
||
- 至少一种用户访问模式验证成功
|
||
- 使用宿主标准 API 成功调用国产模型
|
||
- 人工删除一个宿主 account 后,对账将 provider 状态标记为 `drifted`
|
||
- 执行批次回滚后,宿主残留资源清理完成
|
||
|
||
## 12. 风险与收敛策略
|
||
|
||
- 宿主管理 API 漂移风险:所有 HTTP 路径和字段通过 `HostAdapter` 封装,并为版本差异保留适配层
|
||
- 宿主权限模型不稳定:在 `POST /api/hosts` 阶段强制能力探测,不满足即拒绝接入
|
||
- 不同 provider 规则差异:统一约束到 `providers/*.json`,首版只支持 OpenAI-compatible provider
|
||
- 访问闭环误判:首版必须落真实标准 API 探测,不能只以资源存在判定成功
|