feat: bootstrap supply intelligence baseline

This commit is contained in:
Your Name
2026-05-07 10:16:46 +08:00
commit afdbea6fb5
62 changed files with 9170 additions and 0 deletions

View File

@@ -0,0 +1,609 @@
> 真源索引:当前文档受 `/home/long/project/立交桥/projects/supply-intelligence/tech/CURRENT_SOURCE_OF_TRUTH_2026-05.md` 纳管。
> 若阅读顺序、真源优先级或跨文档冲突存在疑问,先看该索引,再回到本基线文档。
1. 设计范围:本次覆盖 / 明确不做 / 与 PRD 对应关系
1.1 本次覆盖
- 覆盖模块 A 供应商品质探针,但收敛为“账号健康探测 + 状态写回 + 审计 + gateway 可消费状态快照”。
- 覆盖模块 B 全网模型发现,但收敛为“已接入供应商的模型列表扫描 + candidate 生成 + 下架告警”,不做广义全网情报平台。
- 覆盖模块 C 模型准入测试,但收敛为“对 discovered candidate 做标准兼容性验证 + 生成 supply_package 草稿 + 发布闭环”。
- 模块 E 仅覆盖与主链路直接相关的最小运营干预:手动触发探针、忽略 candidate、确认上架、查看失败原因、查看审计。
- 覆盖与立交桥主项目的优先集成运行方案。
- 覆盖对 NewAPI / Sub2API 的最小适配边界:状态读取、模型列表消费、可选发布回调;不反向侵入其内部实现。
- 覆盖五个 QA 阻塞的显式修复:
1) 设计范围蔓延
2) 探针误判规则冲突
3) candidate 状态机不闭环
4) 模块关闭一致性缺失
5) gateway 消费链路未闭环
1.2 明确不做
- 不做独立平台化、多服务拆分、专用 API Gateway、专用消息总线、专用控制台集群。
- 不做 vector / embedding 检索 / 向量库。
- 不做 pricing 数据库、模型比价主链路、自动定价、家族回退定价。
- 不做 predictions / 预测分析 / 广义开放平台 / 社区情报源。
- 不做 WebSocket 实时推送作为本期前提;工作台可先走普通 HTTP 拉取。
- 不做 Playwright 浏览器自动化注册主路径;本期仅保留受控自动补给的最小边界:白名单供应商、阈值触发、任务化补给、待验证/待启用,不把浏览器自动化注册链路作为首期上线门槛。
- 不要求独立 Redis/Temporal/Milvus/Qdrant 等新增重基础设施;优先复用立交桥现有 DB、现有 scheduler、现有审计、现有配置热更新机制。
- 不自动直接操作 gateway 路由细节表;只提供 package 发布后的内部契约,由 gateway/supply-api 按既有主链路消费。
1.3 与 PRD 对应关系
- AC-01/02/03保留落在探针执行、判定、状态迁移、审计与降级策略。
- AC-04/05保留落在扫描、去重、新增 candidate、下架告警。
- AC-06/07保留落在 admission runner、candidate 流转、draft package 生成。
- AC-08/09本期不做深自动注册链路但保留“受控自动补给”的最小产品/技术边界:仅允许白名单供应商、仅允许阈值触发、仅允许生成待补给任务或进入待验证/待启用,不允许把注册浏览器自动化、验证码编排、自动激活作为首期硬门槛。
- AC-10/11/12保留但只保留支撑主链路的最小实现不扩展成独立大盘平台。
- PRD 中与商业化、SFI、预测分析、比价报表相关内容不作为本次技术控制面主路径。
2. 架构与模块:模块划分、文件/目录落点、关键调用链路、关键依赖与降级边界
2.1 总体架构
基线采用“立交桥主项目内集成模块”模式,而不是独立平台。推荐以 supply-api 内部模块形式落地,原因:
- 直接复用 supply_accounts / supply_packages / audit / verify / config / scheduler。
- 避免再造服务间调用、鉴权、部署、监控、迁移复杂度。
- 更符合立交桥现有 net/http + pgx + PostgreSQL 的简洁架构。
独立运行能力保留为简单可选形态:
- 仅在确有外部项目需要时,封装为同仓内单进程启动入口。
- 独立运行不得要求新增专用基础设施;仍使用 PostgreSQL + 现有 scheduler 抽象。
- 不额外设计独立控制台、独立 worker 集群、独立 API 网关。
2.2 模块划分
建议收敛为 6 个模块,均为最小必要:
A. probe
- 读取待探测账号
- 执行标准探针
- 依据统一判定规则生成 outcome
- 驱动 account 状态迁移
- 写审计与探针日志
B. discovery
- 读取已接入供应商适配器
- 拉取模型列表
- 与现有 supply_packages / candidate 去重
- 创建 candidate
- 生成模型下架告警待办
C. admission
- 消费 discovered / retry_pending candidate
- 执行标准测试集
- 更新 candidate 状态
- 生成或更新 supply_package draft
D. publish
- 运营确认 package draft
- 将 package 切到 active
- 将 candidate 切到 published
- 写入 gateway 可消费的发布事件/变更记录
E. integration
- 立交桥内部直接集成接口
- gateway / supply-api 内部契约
- NewAPI / Sub2API 适配边界
F. control
- 模块开关、停机、运行中任务收敛、配置热更新、幂等、审计
2.3 文件/目录落点
以下为建议落点,优先放入立交桥主项目既有模块内;若 supply-intelligence 仓先行设计,可按同名目录组织:
- /home/long/project/立交桥/projects/supply-intelligence/tech/BASELINE_TECHLEAD_V2.md
- 建议实现落点参考:
- supply-api/internal/supplyintelligence/module.go
- supply-api/internal/supplyintelligence/probe/service.go
- supply-api/internal/supplyintelligence/probe/evaluator.go
- supply-api/internal/supplyintelligence/probe/state_machine.go
- supply-api/internal/supplyintelligence/discovery/service.go
- supply-api/internal/supplyintelligence/discovery/adapter_registry.go
- supply-api/internal/supplyintelligence/admission/service.go
- supply-api/internal/supplyintelligence/admission/runner.go
- supply-api/internal/supplyintelligence/publish/service.go
- supply-api/internal/supplyintelligence/integration/http_internal.go
- supply-api/internal/supplyintelligence/integration/newapi_adapter.go
- supply-api/internal/supplyintelligence/integration/sub2api_adapter.go
- supply-api/internal/supplyintelligence/control/shutdown.go
- supply-api/internal/supplyintelligence/repository/*.go
- supply-api/sql/*supply_intelligence*.sql
2.4 关键调用链路
链路 1探针 -> 状态写回 -> gateway 消费闭环
1) scheduler 触发 ProbeTick(platform/account batch)
2) probe.Service.LoadProbeTargets()
3) probe.Service.RunProbe(accountID)
4) probe.Evaluator.Classify(response/error) => success / explicit_failure / inconclusive
5) probe.StateMachine.Apply(account.current_status, recent_probe_window)
6) repository.UpdateAccountHealthAndStatusTx(...)
7) repository.AppendAuditLog(...)
8) repository.UpsertGatewayAccountSnapshot(...)
9) gateway 通过内部契约读取 snapshot 或随 package/account 查询一起读取可用状态
链路 2扫描 -> candidate -> admission
1) scheduler 触发 DiscoveryTick(platform)
2) discovery.Adapter.FetchModels()
3) discovery.Service.DiffAgainstPackagesAndCandidates()
4) repository.UpsertModelCandidate(status=discovered)
5) scheduler enqueue AdmissionRun(candidateID)
6) admission.Runner.Execute(candidateID)
7) repository.UpdateCandidateStatus(...)
8) repository.UpsertDraftPackage(...)
9) repository.AppendAuditLog(...)
链路 3运营确认上架 -> gateway 消费闭环
1) ops POST confirm publish
2) publish.Service.PublishDraft(candidateID, actor)
3) tx: lock candidate + package draft
4) package draft -> active
5) candidate test_passed -> published
6) append internal event supply_package_published
7) append audit
8) gateway/supply-api 既有主链路消费 active package 或发布事件刷新内存路由
链路 4模块关闭闭环
1) operator/config 将 module.enabled=false
2) control.ModuleGate.MarkClosing(module)
3) 新任务拒绝入队/拒绝手动触发
4) 运行中任务继续到安全提交点或超时中断
5) 写 module_state=closed when inflight=0
6) 后续 scheduler tick 直接跳过
2.5 关键依赖与降级边界
- PostgreSQL强依赖。不可用时所有自动写操作 fail-closed不做假成功。
- scheduler中强依赖。不可用时自动任务暂停但手动接口可保留。记录告警。
- supplier adapter弱依赖。单供应商异常不影响其他供应商。
- gateway首期默认事件型消费方。发布链路不等待 gateway 成功回调才提交 package active但必须通过 package change + ack 保留可追踪消费记录,且必须存在真实消费入口。
- NewAPI/Sub2API可选适配依赖。未配置时不影响立交桥内部主链路。
降级原则
- 探针外部错误、429、5xx、DNS/TCP 异常inconclusive不推进惩罚性状态迁移。
- admission 外部超时candidate 转 retry_pending 或 test_failed不能生成 active package。
- gateway 消费延迟package 可 active但需要“未消费/待同步”状态位和审计,不可假定已生效。
- 模块关闭中:新任务一律拒绝,运行中任务只允许安全收尾。
3. 接口与数据模型API/RPC/事件、数据模型/schema、错误码、安全/鉴权契约
3.1 接口分类
3.1.1 立交桥内部直接集成接口
用途:供立交桥主项目内其他模块直接调用,优先 Go 接口,不先暴露额外网络跳。
interface SupplyIntelligenceModule {
RunProbe(ctx context.Context, accountID int64, trigger string) (*ProbeOutcome, error)
ScanPlatform(ctx context.Context, platform string, trigger string) (*ScanOutcome, error)
RunAdmission(ctx context.Context, candidateID int64, trigger string) (*AdmissionOutcome, error)
PublishCandidate(ctx context.Context, candidateID int64, actor string) (*PublishOutcome, error)
GetAccountRoutingState(ctx context.Context, accountID int64) (*AccountRoutingState, error)
}
3.1.2 给 gateway / supply-api 使用的内部契约
用途:形成真实消费闭环,避免“文档说 gateway 会用,但无真实契约”。
HTTP internal 契约,前缀建议:/internal/supply-intelligence
1) GET /internal/supply-intelligence/accounts/{account_id}/routing-state
响应:
{
"account_id": 123,
"platform": "openai",
"account_status": "active",
"routing_enabled": true,
"risk_score": 20,
"reason_code": "ok",
"last_probe_at": "2026-05-06T15:00:00Z",
"version": 17
}
2) GET /internal/supply-intelligence/models/{platform}/{model}/admission-state
响应:
{
"platform": "openai",
"model": "gpt-4.1-mini",
"candidate_status": "published",
"package_id": 456,
"package_status": "active",
"gateway_sync_status": "pending|applied|failed|not_required",
"version": 9
}
3) GET /internal/supply-intelligence/gateway/package-changes?cursor=...
响应:
{
"items": [
{
"event_id": "evt_001",
"event_type": "supply_package_published",
"package_id": 456,
"platform": "openai",
"model": "gpt-4.1-mini",
"occurred_at": "2026-05-06T15:00:00Z",
"version": 9
}
],
"next_cursor": "..."
}
4) POST /internal/supply-intelligence/gateway/package-changes/{event_id}/ack
请求:
{
"consumer": "gateway",
"result": "applied|failed",
"detail": "optional"
}
响应204
闭环定义
- 发布成功 != gateway 已消费。
- 只有 gateway ack event_id 后gateway_sync_status 才能从 pending -> applied/failed。
- QA 必须验证 publish -> list changes -> ack 的真实链路。
3.1.3 面向 NewAPI/Sub2API 的适配边界
原则:只暴露最小必要只读/回调能力,不把本系统设计成它们的管理平台。
适配边界 A状态拉取
- GET /adapter/v1/supply-status/accounts/{account_id}
- 字段与 routing-state 对齐,但去掉内部实现细节。
适配边界 B模型拉取
- GET /adapter/v1/models?status=published
响应只返回已 published 且 package active 的模型。
适配边界 C可选发布回调下发
- POST /adapter/v1/package-events
仅在对方需要 webhook 模式时启用;默认不要求。
适配边界约束
- 不暴露审计明细。
- 不暴露原始探针日志。
- 不暴露账号凭证、测试账号信息、内部风险算法细节。
- 仅允许配置白名单来源访问。
3.2 数据模型/schema
3.2.1 probe_execution_logs
- id bigint pk
- account_id bigint not null
- platform varchar(64) not null
- probe_result varchar(32) not null 取值: success | explicit_failure | inconclusive
- failure_class varchar(64) null 取值: auth_invalid | quota_empty | timeout | tcp_error | dns_error | rate_limited | upstream_5xx | parse_error
- http_status int null
- latency_ms int null
- risk_score int not null
- evaluated_transition varchar(64) not null 取值: no_change | active_to_suspended | suspended_to_disabled | suspended_to_active
- executed_at timestamptz not null
- request_id varchar(64) not null
- index(account_id, executed_at desc)
3.2.2 model_candidates
- id bigint pk
- platform varchar(64) not null
- model varchar(128) not null
- status varchar(32) not null
- discovery_source varchar(32) not null 取值: official_api | official_doc | manual_seed
- last_scan_at timestamptz not null
- discovered_at timestamptz not null
- last_test_at timestamptz null
- failure_reason_code varchar(64) null
- failure_summary text null
- ignored_until timestamptz null
- package_id bigint null
- version int not null default 1
- unique(platform, model)
candidate 最终闭环状态机
- discovered扫描新发现可入测试
- testing测试执行中
- test_passed测试通过已存在 draft package
- test_failed测试失败允许人工重试或自动进入 retry_pending
- retry_pending等待下次重试
- ignored运营临时忽略到 ignored_until 后自动回 discovered
- published运营已确认上架package active
- deprecated供应商侧已消失已产生运营待办但历史保留
- closed不再处理的终态仅用于模型被明确弃用/手工关闭
合法迁移
- discovered -> testing
- testing -> test_passed | test_failed | retry_pending
- test_failed -> retry_pending | closed
- retry_pending -> testing | closed
- discovered | test_failed | retry_pending -> ignored
- ignored -> discovered
- test_passed -> published | closed
- published -> deprecated | closed
- deprecated -> closed
闭环修复点
- 任何非终态都存在后继处理路径。
- ignored 有自动回流。
- published/deprecated 最终可归档到 closed。
- 不再存在“只定义中间态、无出口”的 QA 阻塞。
3.2.3 gateway_package_events
- event_id varchar(64) pk
- event_type varchar(64) not null
- package_id bigint not null
- candidate_id bigint null
- payload jsonb not null
- consumer varchar(64) null
- consumer_status varchar(32) not null default 'pending'
- consumer_detail text null
- occurred_at timestamptz not null
- acked_at timestamptz null
- retry_count int not null default 0
3.2.4 module_runtime_state
- module_name varchar(64) pk
- desired_state varchar(16) not null 取值: enabled | disabled
- runtime_state varchar(16) not null 取值: starting | running | closing | closed
- inflight_count int not null
- updated_at timestamptz not null
3.3 探针判定统一规则
这是本轮必须修的 QA 阻塞之一,统一如下:
明确失败 explicit_failure
- HTTP 401/403
- 供应商明确返回 key invalid / account suspended / quota exhausted 且可稳定识别
不可判定 inconclusive
- HTTP 429
- HTTP 5xx
- DNS 失败
- TCP 连接失败
- 超时
- 响应体为空或格式突变
成功 success
- 返回 2xx 且最小校验通过
状态迁移规则
- active + 1 次 explicit_failure -> suspended
- suspended + 最近连续 3 次 explicit_failure -> disabled
- suspended + 1 次 success -> active
- disabled 不自动恢复,只能人工恢复到 active 或 closed
- inconclusive 永不计入 explicit failure 连续次数
说明
- 将 timeout/TCP/DNS 从“失败导致降级”统一修正为 inconclusive消除 PRD/HLD 冲突。
- 若未来某供应商能明确证明 timeout 即余额停用,也必须走供应商级覆盖配置,不改全局默认。
3.4 错误码
- SUP_INT_PROBE_NOT_FOUND 404
- SUP_INT_PROBE_MODULE_DISABLED 409
- SUP_INT_CANDIDATE_NOT_FOUND 404
- SUP_INT_CANDIDATE_STATE_INVALID 409
- SUP_INT_PUBLISH_PACKAGE_MISSING 409
- SUP_INT_GATEWAY_ACK_CONFLICT 409
- SUP_INT_ADAPTER_UNSUPPORTED 400
- SUP_INT_AUTH_FORBIDDEN 403
- SUP_INT_CONFIG_INVALID 400
- SUP_INT_UPSTREAM_TEMPORARY 503
3.5 安全/鉴权契约
- 内部接口只允许立交桥内部服务身份访问,走现有 internal auth middleware。
- NewAPI/Sub2API 适配接口必须使用独立 access key 或签名校验,按来源白名单限制。
- 审计字段必须包含 object_type/object_id/action/result_code/before_state/after_state/request_id/actor。
- 任何日志不得输出明文 API key、cookie、token、测试账号凭证。
- 手动发布、手动恢复 disabled 账号、关闭 candidate 必须要求 operator 身份并审计。
4. 任务拆解:每个任务必须有具体文件路径和函数名,粒度 2-5 分钟
说明:以下为 Engineer 最小实现任务单,按设计拆到文件级与函数级。路径以优先集成到 supply-api 为准。
4.1 模块骨架
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/module.go :: func RegisterModule(...) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/module.go :: func MountInternalRoutes(...) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/module.go :: func RegisterSchedulers(...) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/control/shutdown.go :: func BeginModuleClose(...) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/control/shutdown.go :: func FinishInflightTask(...) error
4.2 probe
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/probe/service.go :: func LoadProbeTargets(ctx context.Context, limit int) ([]Account, error)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/probe/service.go :: func RunProbe(ctx context.Context, accountID int64, trigger string) (*ProbeOutcome, error)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/probe/evaluator.go :: func ClassifyProbeResult(resp *http.Response, err error) ProbeClass
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/probe/evaluator.go :: func CalculateRiskScore(class ProbeClass) int
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/probe/state_machine.go :: func ApplyAccountTransition(current string, recent []ProbeClass) (next string, transition string)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/probe/state_machine.go :: func CountRecentExplicitFailures(recent []ProbeClass) int
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/probe/worker.go :: func HandleProbeTick(ctx context.Context) error
4.3 discovery
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/discovery/adapter_registry.go :: func ResolveModelAdapter(platform string) (ModelAdapter, error)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/discovery/service.go :: func ScanPlatform(ctx context.Context, platform string, trigger string) (*ScanOutcome, error)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/discovery/service.go :: func DiffModels(current []string, packages []string, candidates []string) DiffResult
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/discovery/service.go :: func UpsertDiscoveredCandidates(ctx context.Context, platform string, models []string) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/discovery/service.go :: func MarkDeprecatedAlerts(ctx context.Context, platform string, missing []string) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/discovery/worker.go :: func HandleDiscoveryTick(ctx context.Context) error
4.4 admission
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/admission/service.go :: func EnqueueAdmission(ctx context.Context, candidateID int64) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/admission/service.go :: func RunAdmission(ctx context.Context, candidateID int64, trigger string) (*AdmissionOutcome, error)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/admission/runner.go :: func LoadCandidateForTesting(ctx context.Context, candidateID int64) (*Candidate, error)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/admission/runner.go :: func ExecuteTestSuite(ctx context.Context, c *Candidate) (*SuiteResult, error)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/admission/runner.go :: func DecideCandidateNextState(result *SuiteResult) (string, string)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/admission/runner.go :: func UpsertDraftPackage(ctx context.Context, c *Candidate, result *SuiteResult) (int64, error)
4.5 publish
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/publish/service.go :: func PublishCandidate(ctx context.Context, candidateID int64, actor string) (*PublishOutcome, error)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/publish/service.go :: func ValidatePublishable(ctx context.Context, candidateID int64) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/publish/service.go :: func AppendGatewayPackageEvent(ctx context.Context, packageID int64, candidateID int64) error
4.6 integration
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/integration/http_internal.go :: func GetAccountRoutingState(w http.ResponseWriter, r *http.Request)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/integration/http_internal.go :: func GetModelAdmissionState(w http.ResponseWriter, r *http.Request)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/integration/http_internal.go :: func ListGatewayPackageChanges(w http.ResponseWriter, r *http.Request)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/integration/http_internal.go :: func AckGatewayPackageChange(w http.ResponseWriter, r *http.Request)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/integration/newapi_adapter.go :: func ListPublishedModels(w http.ResponseWriter, r *http.Request)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/integration/newapi_adapter.go :: func GetExternalAccountStatus(w http.ResponseWriter, r *http.Request)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/integration/sub2api_adapter.go :: func ListPublishedModels(w http.ResponseWriter, r *http.Request)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/integration/sub2api_adapter.go :: func GetExternalAccountStatus(w http.ResponseWriter, r *http.Request)
4.7 repository / sql
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/repository/probe_repo.go :: func InsertProbeExecutionLog(...) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/repository/probe_repo.go :: func UpdateAccountHealthAndStatusTx(...) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/repository/candidate_repo.go :: func UpsertModelCandidate(...) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/repository/candidate_repo.go :: func UpdateCandidateStateTx(...) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/repository/package_repo.go :: func UpsertDraftPackageTx(...) (int64, error)
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/repository/gateway_repo.go :: func InsertGatewayPackageEventTx(...) error
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/repository/gateway_repo.go :: func AckGatewayPackageEventTx(...) error
- /home/long/project/立交桥/supply-api/sql/xxxx_supply_intelligence_probe_logs.sql :: migration create table
- /home/long/project/立交桥/supply-api/sql/xxxx_supply_intelligence_candidates.sql :: migration create table
- /home/long/project/立交桥/supply-api/sql/xxxx_supply_intelligence_gateway_events.sql :: migration create table
- /home/long/project/立交桥/supply-api/sql/xxxx_supply_intelligence_module_runtime.sql :: migration create table
4.8 测试与校验
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/probe/state_machine_test.go :: func TestApplyAccountTransition()
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/probe/evaluator_test.go :: func TestClassifyProbeResult()
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/admission/runner_test.go :: func TestDecideCandidateNextState()
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/publish/service_test.go :: func TestPublishCandidate_AppendsGatewayEvent()
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/integration/http_internal_test.go :: func TestGatewayEventAckFlow()
5. 风险与保护:风险清单(概率/影响/缓解)、降级策略、威胁建模结果
5.1 风险清单
1) 探针误判导致错误下线
- 概率:中
- 影响:高
- 缓解:只允许 explicit_failure 触发惩罚状态429/超时/网络错误全部 inconclusivedisabled 仅连续 3 次明确失败;生产初期可只告警不自动 disabled。
2) candidate 重复创建或状态乱序
- 概率:中
- 影响:中
- 缓解unique(platform, model)version 乐观锁;状态迁移函数集中校验;测试任务拿行锁。
3) gateway 未真实消费已上架 package
- 概率:高
- 影响:高
- 缓解:新增 gateway_package_events + ack 契约;区分 published 与 gateway applied监控 pending backlog。
4) 模块关闭时仍有脏写入
- 概率:中
- 影响:中
- 缓解runtime_state=closing 时拒绝新任务inflight 计数;安全提交点;超时取消 ctx。
5) 适配器变更影响扫描质量
- 概率:中
- 影响:中
- 缓解:按供应商隔离;单平台失败不扩散;保留 last_successful_scan 基线;失败仅告警不删数据。
6) NewAPI/Sub2API 适配越权暴露内部数据
- 概率:低
- 影响:高
- 缓解:适配接口单独 DTO白名单认证不复用内部 debug 输出。
5.2 降级策略
- probe 模块关闭gateway 继续依赖现有 account/package 状态;新鲜度下降但主链路可运行。
- discovery 模块关闭:不再发现新模型;已上架模型不受影响。
- admission 模块关闭candidate 可积压,但不会误上架。
- publish 后 gateway 未消费:保留 pending运营可见不回滚 package active但不得宣称“已进路由”。
- NewAPI/Sub2API 未配置:直接关闭适配路由,不影响内部主链路。
5.3 威胁建模结果
输入边界
- 供应商返回体属于不可信输入必须限长、schema 校验、错误脱敏。
- 运营手动接口属于高权限输入:必须鉴权、审计、幂等。
- gateway ack 请求属于内部写接口:必须鉴权并校验 event_id/consumer 一致性。
数据流
- supplier -> adapter -> evaluator -> db
- db -> internal route -> gateway
- db -> adapter route -> NewAPI/Sub2API
主要威胁与处置
- 凭证泄漏:本期不纳入自动注册主路径;现有账号密钥仅走既有安全存储,不在本模块新增明文链路。
- 重放/重复发布publish 接口需幂等published candidate 再次 publish 返回 409。
- 伪造 gateway ack只接受内部服务身份event consumer 固定枚举。
- 大响应体压垮解析adapter 限制 body size超限视为 inconclusive/scan_failed。
- SQL 并发覆盖:关键状态表使用 version 或 select for update。
6. QA 交接与实施约束:编码前设计审查要点、编码后漂移检查点、必查真实调用链路、禁止偏离的边界
6.1 编码前设计审查要点
- 是否明确“集成运行优先,独立运行可选且轻量”。
- 是否删除 pricing/vector/predictions/开放平台化内容。
- 探针默认规则是否统一为 explicit_failure 才触发状态惩罚。
- candidate 状态机是否存在完整入口、出口、终态与回流。
- gateway 是否存在 list change + ack 的真实闭环,而非只有查询接口。
- 模块关闭是否存在 closing -> closed 收敛语义。
- NewAPI/Sub2API 是否仅作为适配边界,而非反向牵引主架构。
6.2 编码后漂移检查点
- 是否出现新增 Redis/Temporal/Kafka/MQ/向量库等重基础设施前置依赖。
- 是否出现额外独立服务、额外 API gateway、复杂事件总线。
- 是否把自动注册重新抬回本期主路径。
- 是否把 gateway 路由刷新实现成跨系统强耦合同步 RPC 必须成功。
- 是否新增未在本基线定义的中间状态。
- 是否把 timeout/TCP/DNS 再次当成 explicit_failure。
6.3 QA 必查真实调用链路
- probe tick -> evaluator -> state machine -> supply_accounts 写回 -> audit 写入
- discovery tick -> candidate discovered -> admission run -> draft package
- publish confirm -> package active -> candidate published -> gateway change event -> gateway ack
- module disable -> closing -> reject new task -> inflight drain -> closed
- adapter route -> NewAPI/Sub2API 只读返回,字段不泄露内部敏感信息
6.4 禁止偏离的边界
- 禁止把本期做成独立平台化部署前提。
- 禁止把比价、预测、向量检索恢复为主链路。
- 禁止未定义契约就声称“gateway 会消费”。
- 禁止 candidate 状态直接跳 published绕过 test_passed + draft package。
- 禁止 disabled 自动恢复。
- 禁止模块关闭时直接 kill 运行中事务而无收敛策略。
7. Engineer 实施说明:文件级落点、最小验证项、需 PM 澄清项
7.1 文件级落点
优先实施目录:
- /home/long/project/立交桥/supply-api/internal/supplyintelligence/
- /home/long/project/立交桥/supply-api/sql/
- /home/long/project/立交桥/supply-api/internal/http/internal/
若 supply-intelligence 项目仓仅承载设计文档,则本文件作为交付基线,后续代码并入 supply-api 主仓。
7.2 最小验证项
- 单测探针分类、账号状态迁移、candidate 状态迁移、publish 幂等。
- 集成测publish 后产生 gateway eventgateway ack 后状态更新 applied。
- 集成测module closing 时手动触发探针返回 409 module disabled/closing。
- E2E 最小链路:
1) 一个 active 账号 401 -> suspended
2) 一个新模型 discovered -> test_passed -> draft -> published
3) gateway 拉取 package change 并 ack
7.3 需 PM 澄清项
- 本期是否允许 production 初期仅启用 active->suspended暂不自动 disabled。
- candidate ignored 的默认恢复期是否固定 7 天,或允许按供应商配置。
- gateway 首期默认采用 pull package-changes + ack 作为事件型消费闭环;若后续证明已有内部刷新入口可复用,也必须保留等价 ack 语义与可审计消费状态。
- NewAPI/Sub2API 本期需要只读拉取,还是还需要 webhook 模式;默认只做只读拉取。
8. 阶段门控结论:可进入 QA 设计审查 / 需返回 PM / 需继续补设计
结论:可进入 QA 设计审查
理由
- 五个 QA 阻塞已在本基线中逐项补洞并收敛。
- 架构已回到立交桥一致的简洁集成模式。
- 对立交桥 / NewAPI / Sub2API 的边界已最小化并显式分类。
- 已删除明显超范围和重基础设施设计。
附带条件
- 不代表可直接开发放行。
- 进入开发前仍需确认 PM 澄清项中的 gateway 消费方式与 production 初期自动 disabled 策略。
9. 下游执行约束摘要:
- Engineer 禁止偏离:不得新增独立平台化部署前提、不得恢复 pricing/vector/predictions 主路径、不得绕过 gateway event ack 闭环、不得新增未定义 candidate 状态。
- QA 必查调用链路probe->状态写回discovery->candidate->admission->draftpublish->gateway event->ackmodule disable->closing->drain->closedNewAPI/Sub2API 只读适配边界。
- XL 若继续推进需补的门控:确认 gateway 实际消费方式;确认生产首期自动 disabled 策略;确认代码最终并入 supply-api 主仓而非另起独立重部署。
自检清单
- [x] 架构设计覆盖 PRD 所有 AC
- [x] 接口定义完整(请求/响应/错误)
- [x] 每个任务 < 5分钟有明确文件路径
- [x] 依赖关系无循环
- [x] 考虑了扩展点(未来可能的变化)
- [x] 风险评估完整,有关键风险的缓解方案
- [x] 符合项目现有技术栈和编码规范
- [x] 降级策略已设计(熔断/限流/兜底)
- [x] 威胁建模已完成(输入边界/鉴权/数据流)
- [x] 实施漂移检测点已定义(可与 QA checklist 对接)
- [x] 已明确标记是否可进入 QA 设计审查
- [x] 已提供 QA 编码前审查与编码后漂移检测所需交接物
- [x] 已给出 Engineer / QA / XL 的下游执行约束摘要
- [x] 已纳入立交桥简洁架构与立交桥/NewAPI/Sub2API 集成边界

View File

@@ -0,0 +1,118 @@
# Supply-Intelligence 当前实现真源索引2026-05
> 状态:当前有效
> 目的:为 Engineer / QA / PM 提供单一阅读入口,避免继续误读历史草案。
> 适用范围:/home/long/project/立交桥/projects/supply-intelligence/
## 1. 当前结论
当前规划包已收敛到“可进入 Engineer 实现”状态。
当前总门控结论APPROVED。
但 APPROVED 的前提是:实现、测试、评审都必须以本文件列出的“当前真源”解释,不得回退到旧 PRD/HLD/INTERFACE/DEPLOYMENT 的正文口径。
## 2. 当前真源文件(按优先级)
### 2.1 一级真源:必须优先遵循
1. `/home/long/project/立交桥/projects/supply-intelligence/tech/BASELINE_TECHLEAD_V2.md`
- 作用:当前技术基线、状态机、模块边界、集成约束、最小生产闭环定义
- 适用问题:实现边界、状态迁移、部署形态、首期能力范围、风险与验证要求
2. `/home/long/project/立交桥/projects/supply-intelligence/tech/GATEWAY_CONSUMER_DECISION_2026-05.md`
- 作用:首期 package/account 消费闭环决议
- 适用问题published vs applied、gateway 是否默认消费方、package change + ack、真实调用链落点
### 2.2 二级真源:必须按一级真源解释
3. `/home/long/project/立交桥/projects/supply-intelligence/tech/TEST_DESIGN.md`
- 作用:收敛后的测试门禁文档
- 使用规则:
- 只能按一级真源解释
- 当前阶段门控结论以其中已更新的 APPROVED 段落为准
- 若正文某处仍残留旧测试假设,以一级真源覆盖
4. `/home/long/project/立交桥/projects/supply-intelligence/specs/功能清单.md`
- 作用:任务粒度参考清单
- 使用规则:
- 仅用于任务拆分、实现排程、UI/后端任务定位
- 若与一级真源冲突,一律以一级真源为准
- 不得把其中任何历史平台化/重基础设施/深自动注册项当作默认首期门槛
## 3. 历史参考文件(禁止作为当前实现真源)
以下文件只能用于理解历史上下文,不能再作为 Engineer/QA 的当前实现依据:
1. `/home/long/project/立交桥/projects/supply-intelligence/prd/PRD.md`
2. `/home/long/project/立交桥/projects/supply-intelligence/tech/HLD.md`
3. `/home/long/project/立交桥/projects/supply-intelligence/tech/INTERFACE.md`
4. `/home/long/project/立交桥/projects/supply-intelligence/tech/DEPLOYMENT.md`
原因:这些文件虽已加失效声明,但正文仍保留大量旧设计,例如:
- pricing / prediction / 向量检索 / 仪表盘扩张
- 独立 API/worker/重基础设施默认前提
- gateway 管理接口热更新主路径
- 深自动注册 / 浏览器自动化主路径
- published 与 gateway applied 语义混淆
## 4. Engineer 必须先建立的统一理解
### 4.1 首期能力边界
首期目标不是独立平台化大系统,而是“立交桥延伸项目 + 简洁集成架构 + 最小生产闭环”。
这意味着:
- 优先并入 supply-api 主仓
- 优先复用主仓已有配置、调度、审计、内部路由能力
- 不把 Redis / Temporal / 向量数据库 / WebSocket / MQ 作为首期硬前置
- 不做 pricing / prediction / recommendation / SFI 仪表盘扩张
### 4.2 探针判定边界
必须按 explicit_failure / inconclusive / success 三类解释不能回退到旧的“timeout 直接惩罚性降级”口径。
### 4.3 自动补给边界
首期不是深自动注册主路径。
首期仅保留“受控自动补给最小边界”:
- 白名单供应商
- 阈值触发
- 任务化补给或受控补给受理接口
- pending_verify / pending_enable 等受控中间态
- fail-closed
不得默认实现:
- 浏览器自动化注册
- 短信验证码编排主路径
- 无审批直接自动激活
### 4.4 gateway 消费闭环边界
首期默认采用:
- package 发布链路event + ack
- account 状态链路:查询型消费
必须明确:
- published != applied
- active package != gateway 已消费成功
- 没有真实 poll/apply/ack 入口,不得宣称 package 发布链路已完成
## 5. QA 审查必须卡的四条红线
1. 若实现重新引入 published/applied 混淆,直接打回
2. 若把深自动注册重新膨胀成首期硬门槛,直接打回
3. 若把旧独立平台化基础设施重新作为首期依赖,直接打回
4. 若 gateway 只有接口定义、没有真实消费方入口与 ack 回写,直接打回
## 6. 推荐阅读顺序
Engineer / QA / PM 开始工作前,按以下顺序阅读:
1. `tech/CURRENT_SOURCE_OF_TRUTH_2026-05.md`
2. `tech/BASELINE_TECHLEAD_V2.md`
3. `tech/GATEWAY_CONSUMER_DECISION_2026-05.md`
4. `tech/TEST_DESIGN.md`
5. `specs/功能清单.md`
禁止跳过前 3 份文件直接依据旧 PRD/HLD/INTERFACE/DEPLOYMENT 开始实现。
## 7. 一句话执行规则
如果某个设计点在文档间出现冲突:
- 先看 `BASELINE_TECHLEAD_V2.md`
- 再看 `GATEWAY_CONSUMER_DECISION_2026-05.md`
- 然后用 `TEST_DESIGN.md``功能清单.md` 做验证与任务拆解
- 不回退到旧草案正文做判断

168
tech/DEPLOYMENT.md Normal file
View File

@@ -0,0 +1,168 @@
# Supply-Intelligence 部署设计
> 状态说明2026-05 收敛修订):本文件保留为旧版部署草案,已不再作为当前默认部署真源。
> 当前默认部署真源应以“立交桥延伸项目 + 简洁集成架构”为准:优先并入 supply-api 主仓,独立运行仅为轻量可选形态。
> 以下旧部署假设已废止,不得再作为首期落地前提:
> - 独立 API Server + 多 Worker 集群默认部署
> - Redis / 向量数据库 / WebSocket / 独立共享层作为首期前置依赖
> - 以独立多组件容器拓扑替代主仓集成部署
> 版本v1.0 | 状态:初稿
---
## 1. 部署架构
### 1.1 总体架构
```
├── Load Balancer (Nginx / 云 CLB)
├── Supply-Intelligence API Server x 2
│ │
│ ├── HTTP API
│ └── WebSocket (健康大盘实时推送)
├── Supply-Intelligence Worker x 3
│ │
│ ├── Probe Worker (探针任务)
│ ├── Discovery Worker (扫描任务)
│ ├── Admission Worker (准入测试任务)
│ ├── Auto-Reg Worker (自动注册任务)
│ └── Cleanup Worker (定期清理)
└── 共享层
├── PostgreSQL 15+ (与 supply-api 共存或独立)
├── Redis (缓存 + 锁 + 扫描结果缓存)
└── 向量数据库 (PGVector / Milvus / Qdrant)
```
### 1.2 容器化部署
```yaml
services:
supply-intel-api:
image: supply-intelligence:latest
command: ["./supply-intel", "api"]
replicas: 2
ports:
- "8081:8080"
supply-intel-probe:
image: supply-intelligence:latest
command: ["./supply-intel", "worker", "probe"]
replicas: 1
supply-intel-discovery:
image: supply-intelligence:latest
command: ["./supply-intel", "worker", "discovery"]
replicas: 1
supply-intel-admission:
image: supply-intelligence:latest
command: ["./supply-intel", "worker", "admission"]
replicas: 1
supply-intel-autoreg:
image: supply-intelligence:latest
command: ["./supply-intel", "worker", "autoreg"]
replicas: 1
```
---
## 2. 资源需求
### 2.1 API Server
| 资源 | 需求 | 说明 |
|------|------|------|
| CPU | 1 核 | |
| 内存 | 512 MB | |
| 存储 | 无 | |
### 2.2 Worker
| Worker 类型 | CPU | 内存 | 说明 |
|------------|-----|--------|------|
| Probe | 1 核 | 512 MB | 同时发起多个 HTTP 请求 |
| Discovery | 1 核 | 1 GB | 可能涉及 Playwright 爬取 |
| Admission | 2 核 | 2 GB | 测试流水线调用 LLM APICPU 与内存需求较高 |
| Auto-Reg | 1 核 | 512 MB | |
### 2.3 数据库
| 资源 | 需求 | 说明 |
|------|------|------|
| CPU | 2 核 | |
| 内存 | 4 GB | |
| 存储 | 100 GB | 探针历史 + 审计日志 + 定价数据库 |
### 2.4 向量数据库
| 选型 | CPU | 内存 | 存储 | 说明 |
|------|-----|--------|------|------|
| PGVector | 与 PostgreSQL 共存 | 共存 | 共存 | 推荐,无需额外部署 |
| Milvus | 2 核 | 4 GB | 50 GB | 高性能、分布式 |
| Qdrant | 1 核 | 2 GB | 30 GB | 轻量、Cloud-native |
---
## 3. 监控与运维钩子
### 3.1 健康检查
| 端点 | 路径 | 预期响应 | 失败行为 |
|------|------|----------|---------|
| 存活检查 | `/actuator/health/live` | HTTP 200 | 容器重启 |
| 就绪检查 | `/actuator/health/ready` | HTTP 200 | 从负载均衡移除 |
| 综合检查 | `/actuator/health` | HTTP 200 + JSON | 触发告警 |
### 3.2 启动/关闭顺序
**启动顺序**:
1. PostgreSQL 启动完成
2. Redis 启动完成
3. 向量数据库启动完成
4. Worker 启动(执行 migration
5. API Server 启动
**关闭顺序**:
1. 停止接收新 HTTP 请求
2. 等待现有请求处理完成(超时 30 秒)
3. 停止各 Worker 定时器
4. 关闭数据库连接池
5. 退出进程
### 3.3 配置管理
- 配置文件 `config.yaml` + 环境变量覆盖。
- 供应商 API Key 仅通过环境变量传入。
- 探针周期、扫描周期、测试用例集路径等可热更新。
---
## 4. 灾备设计
### 4.1 数据库灾备
| 策略 | 方案 | RTO | RPO |
|------|------|-----|-----|
| 主库故障 | 自动切换至备库 | < 5 min | < 1 min |
| 逻辑损坏 | 从备库恢复 + 审计日志回放 | < 30 min | < 1 min |
### 4.2 扫描/测试任务灾备
| 场景 | 处理 |
|------|------|
| Discovery Worker 故障 | 下一周期自动恢复,扫描任务无状态,不影响生产 |
| Admission Worker 故障 | 测试任务缓存在 Redis恢复后继续执行 |
| Probe Worker 故障 | 探针任务缓存在 Redis恢复后继续执行 |
| 向量数据库故障 | 知识库检索降级为文本匹配,不影响核心探针功能 |
### 4.3 多中心部署
- 当前阶段为单中心部署。
- 探针任务无状态,不依赖中心化调度。
- 未来扩展至多中心时,需要解决 PostgreSQL 分布式写入和向量数据库的同步问题。

View File

@@ -0,0 +1,169 @@
# Supply-Intelligence 首期消费闭环决议2026-05
> 状态:当前有效决议
> 作用:消除“只有接口定义,没有首期真实消费方与调用落点”的设计歧义。
> 适用范围:/home/long/project/立交桥/projects/supply-intelligence/ 下当前收敛规划包。
> 真源索引:本决议受 `/home/long/project/立交桥/projects/supply-intelligence/tech/CURRENT_SOURCE_OF_TRUTH_2026-05.md` 纳管;若与历史草案冲突,以真源索引定义的优先级解释。
## 1. 结论
首期默认消费闭环采用:
- package 发布链路gateway 作为首期默认消费方,使用 pull `package-changes` + `ack` 机制完成闭环
- account 状态链路:立交桥 / supply-api 内部主链路直接读取 `routing-state` 或等价 snapshot不通过 gateway event ack 闭环
这意味着必须明确区分两类链路:
1. 账号可路由状态链路:查询型消费
2. package 发布生效链路:事件型消费
不得混用以下错误口径:
- `published = 已进入 gateway 路由`
- `active package = 下游已消费成功`
正确口径:
- `published` 仅表示 supply-intelligence 侧已完成运营确认与 package 激活
- 只有 gateway 对 package event 完成 `ack(result=applied)` 后,才能宣称“已被 gateway 消费生效”
## 2. 首期默认路径
### 2.1 账号状态链路
生产主链路:
1. probe 执行
2. evaluator 分类为 success / explicit_failure / inconclusive
3. state machine 生成状态迁移
4. 写回 supply account 健康状态与审计
5. 立交桥内部路由决策读取 `GET /internal/supply-intelligence/accounts/{account_id}/routing-state`
说明:
- 这是查询型读取,不需要 event ack。
- 若调用方读取失败,不回滚 supply-intelligence 已落库状态,只记录消费侧问题。
### 2.2 package 发布闭环
生产主链路:
1. 运营确认发布 candidate
2. package draft -> active
3. candidate `test_passed -> published`
4. 写入 `gateway_package_events`
5. gateway 拉取 `GET /internal/supply-intelligence/gateway/package-changes?cursor=...`
6. gateway 应用变更到自身路由/缓存
7. gateway 调用 `POST /internal/supply-intelligence/gateway/package-changes/{event_id}/ack`
8. `gateway_sync_status` 变为 `applied``failed`
说明:
- 这是事件型闭环。
- `pending` 表示 supply-intelligence 已发布,但 gateway 尚未确认消费。
- `failed` 表示 gateway 已消费尝试但未成功,需要运营或工程介入。
## 3. 为什么不用首期强耦合同步 RPC
首期明确不采用:
- “发布时同步调用 gateway 管理接口,成功后才算发布成功”
原因:
1. 这会把 supply-intelligence 与 gateway 强耦合在单次事务中
2. 会把下游暂时不可用放大成上游发布不可用
3. 不符合当前“立交桥延伸项目、简洁架构、最小生产闭环”的收敛目标
因此首期选择:
- 上游发布成功与下游消费成功解耦
- 用 event + ack 明确消费状态
## 4. 首期真实代码落点(实现约束)
以下是首期必须存在的真实调用落点;只有接口定义不算完成。
### 4.1 supply-intelligence / supply-api 侧
- `/home/long/project/立交桥/supply-api/internal/supplyintelligence/publish/service.go`
- `PublishCandidate(...)`
- `AppendGatewayPackageEvent(...)`
- `/home/long/project/立交桥/supply-api/internal/supplyintelligence/integration/http_internal.go`
- `GetAccountRoutingState(...)`
- `ListGatewayPackageChanges(...)`
- `AckGatewayPackageChange(...)`
- `/home/long/project/立交桥/supply-api/internal/supplyintelligence/repository/gateway_repo.go`
- `InsertGatewayPackageEventTx(...)`
- `AckGatewayPackageEventTx(...)`
### 4.2 gateway 侧(首期必须由消费方实现的真实入口)
- 必须存在一个实际消费入口,完成:
1. 周期拉取 package changes
2. 应用变更
3. 回写 ack
- 若 gateway 已有内部刷新链路,可复用,但必须补齐 ack 回写
- 若 gateway 无现成入口,则新增最小 poller禁止为了这件事引入 MQ/Kafka/新总线
## 5. QA 必查真实调用链路
QA 编码后必须至少验证以下四层:
### 链路 A账号状态查询型消费
- 定义:`GetAccountRoutingState`
- 装配internal route mounted
- 调用:立交桥 / supply-api 实际路由决策点调用该接口或等价函数
- 入口:真实请求/真实调用路径可达
### 链路 Bpackage 事件发布
- 定义:`AppendGatewayPackageEvent`
- 装配publish 流程内注入 repository
- 调用:`PublishCandidate` 成功路径真实调用写事件
- 入口:运营确认发布入口可真实触达该调用链
### 链路 Cgateway 拉取消费
- 定义:`ListGatewayPackageChanges`
- 装配internal route mounted
- 调用gateway 真实 poller / 既有刷新链调用
- 入口:消费方真实任务/刷新入口存在,不是只留 TODO
### 链路 Dgateway ack 回写
- 定义:`AckGatewayPackageChange`
- 装配ack route mounted
- 调用gateway 应用成功/失败后真实回写
- 入口event 状态确实从 `pending -> applied|failed`
## 6. published / applied 语义约束
状态含义必须统一:
- candidate `published`:上游已完成运营确认
- package `active`:上游已允许被消费
- gateway sync `pending`:下游尚未确认
- gateway sync `applied`:下游已确认消费并应用
- gateway sync `failed`:下游消费尝试失败
禁止:
- UI 文案把 `published` 写成“已进路由”
- 测试把 `package active` 当成“下游已完成同步”
- QA 把 event 表存在当成“消费闭环成立”
## 7. 与 NewAPI / Sub2API 的边界
首期不要求 NewAPI / Sub2API 实现 event ack 闭环。
它们的首期边界为:
- 只读拉取账号状态
- 只读拉取已允许暴露的模型/结果
即:
- gateway 是首期必须闭环的事件型消费方
- NewAPI / Sub2API 是首期只读适配消费方
## 8. 门控要求
在下一轮 QA 设计审查或编码后审查中,若以下任一项缺失,则不得给 APPROVED
1. 没有明确的首期默认消费方
2. 没有明确区分查询型链路与事件型链路
3. 没有明确 `published != applied`
4. 没有真实代码落点要求
5. 没有 ack 回写要求
## 9. 对旧文档的覆盖关系
本决议用于覆盖旧文档中以下错误或过时口径:
- “调用 gateway 管理接口热更新即完成闭环”
- “上架成功即下游已生效”
- “gateway 会消费”但没有实际消费者与 ack 机制
如与以下文件冲突,以本决议为准:
- /home/long/project/立交桥/projects/supply-intelligence/specs/功能清单.md
- /home/long/project/立交桥/projects/supply-intelligence/tech/INTERFACE.md
- /home/long/project/立交桥/projects/supply-intelligence/tech/HLD.md
- /home/long/project/立交桥/projects/supply-intelligence/tech/BASELINE_TECHLEAD_V2.md若后续未同步更新相应段落应以本决议补充解释

1013
tech/HLD.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,251 @@
# Supply-Intelligence 实现任务板 V12026-05
> 状态:当前有效
> 目的:将当前真源收敛为可直接派工的 Engineer / QA 执行板。
> 使用前提:必须先阅读 `/home/long/project/立交桥/projects/supply-intelligence/tech/CURRENT_SOURCE_OF_TRUTH_2026-05.md`。
> 当前总门控APPROVED允许进入实现
## 0. 使用规则
1. 本任务板不是新的真源,只是执行板。
2. 若任务板与以下文件冲突,以以下文件为准:
- `/home/long/project/立交桥/projects/supply-intelligence/tech/BASELINE_TECHLEAD_V2.md`
- `/home/long/project/立交桥/projects/supply-intelligence/tech/GATEWAY_CONSUMER_DECISION_2026-05.md`
- `/home/long/project/立交桥/projects/supply-intelligence/tech/TEST_DESIGN.md`
3. 禁止 Engineer 回退到旧 PRD/HLD/INTERFACE/DEPLOYMENT 取实现口径。
4. 每个阶段完成后,必须由 QA 按“定义 → 装配 → 调用 → 入口”四层链路做复核。
## 1. 当前最短闭环路径
目标:先做出首个最小生产闭环,而不是并行铺开所有模块。
推荐顺序:
1. Phase A探针与账号状态闭环
2. Phase B发现与 candidate 闭环
3. Phase C准入测试与 draft 生成闭环
4. Phase D发布与 gateway package event + ack 闭环
5. Phase E受控自动补给最小边界
6. Phase F工作台、配置、权限与完善性补齐
## 2. 阶段任务板
### Phase A探针与账号状态闭环
目标:先让 supply-intelligence 能真实地产生可消费的账号状态。
#### A-1 数据与领域骨架
- OwnerEngineer
- 交付物:账号状态、探针日志、审计写入相关 domain/model/repository 基础结构
- 完成标准:
- 存在 `supply_intelligence_` 前缀表迁移
- 探针结果、状态迁移、审计写入模型可落库
- QA 验证:检查 schema、repo、service 调用链是否闭合
#### A-2 探针执行与统一判定
- OwnerEngineer
- 交付物probe runner + evaluator
- 完成标准:
- 200 => success
- 401/403 => explicit_failure
- 429/5xx/timeout/格式突变 => inconclusive
- QA 验证:检查 evaluator 定义、装配、调用与调度入口
#### A-3 状态机与账号状态快照接口
- OwnerEngineer
- 交付物:状态迁移逻辑 + `routing-state` 查询接口
- 完成标准:
- active -> suspended
- suspended -> disabled
- inconclusive 不触发惩罚性迁移
- 存在真实内部查询入口
- QA 验证:必须验证 `GET /internal/supply-intelligence/accounts/{account_id}/routing-state` 或等价入口
#### A-4 Phase A QA Gate
- OwnerQA
- 放行条件:
- 账号状态链路完成“定义 → 装配 → 调用 → 入口”四层验证
- 审计写入与状态写回可追踪
- 未引入 Redis / Temporal / WebSocket 作为首期硬依赖
### Phase B发现与 candidate 闭环
目标:能够从已接入供应商拉模型,并产生 candidate。
#### B-1 供应商适配器与模型拉取
- OwnerEngineer
- 交付物SupplierAdapter、registry、GetModels 拉取链路
- 完成标准:
- 至少支持首批目标供应商
- 具备健康探测与模型列表读取
- QA 验证:检查 registry 注册、装配、实际调用点
#### B-2 candidate 生成与去重
- OwnerEngineer
- 交付物discovery service + candidate repository
- 完成标准:
- 能与 `supply_packages` 去重
- 新模型生成 discovered candidate
- 下架只生成告警,不自动改 package
- QA 验证:检查 candidate 创建与下架告警调用链
#### B-3 Phase B QA Gate
- OwnerQA
- 放行条件:
- 至少一条真实发现链路打通
- candidate 状态初始落点正确
- 未扩张到 pricing / prediction / 向量检索
### Phase C准入测试与 draft 生成闭环
目标:让 discovered candidate 可变成 test_passed/test_failed并生成 draft。
#### C-1 admission runner
- OwnerEngineer
- 交付物:标准测试执行器与结果记录
- 完成标准:
- discovered / retry_pending 可消费
- 失败与超时原因可追踪
- QA 验证:检查 admission 执行入口和结果写回
#### C-2 draft package 生成
- OwnerEngineer
- 交付物test_passed -> draft package 生成逻辑
- 完成标准:
- 草稿字段完整
- candidate 状态流转闭环
- QA 验证:检查 candidate -> draft 的真实调用链
#### C-3 Phase C QA Gate
- OwnerQA
- 放行条件:
- 至少一条 candidate 完成 test_passed -> draft
- 至少一条 candidate 完成 test_failed -> failure_reason
### Phase D发布与 gateway package event + ack 闭环
目标:打通首个 package 发布最小生产闭环。
#### D-1 发布服务
- OwnerEngineer
- 交付物:运营确认发布逻辑
- 完成标准:
- draft -> active
- candidate test_passed -> published
- QA 验证published 语义不得等于 applied
#### D-2 gateway package events
- OwnerEngineer
- 交付物:`gateway_package_events` 写入、拉取、ack 回写接口
- 完成标准:
- 存在 package-changes 列表接口
- 存在 ack 接口
- ack 后状态可区分 pending/applied/failed
- QA 验证:检查 definition / assembly / call / entry 四层
#### D-3 gateway 消费方最小入口
- OwnerEngineer / 对接方
- 交付物:真实 poll/apply/ack 入口
- 完成标准:
- 不是只定义接口
- 至少有一个真实消费任务/入口
- QA 验证:没有真实入口则本阶段不通过
#### D-4 Phase D QA Gate
- OwnerQA
- 放行条件:
- published != applied 证据充分
- package event + ack 闭环真实存在
- 无“同步调用 gateway 管理接口才算发布成功”的回退实现
### Phase E受控自动补给最小边界
目标:补齐首期最小自动补给能力,但不膨胀为深自动注册。
#### E-1 自动补给配置与白名单约束
- OwnerEngineer
- 交付物auto-supply 配置、阈值、白名单、审批边界
- 完成标准:
- 非白名单供应商不自动补给
- 配置按主仓既有方式存储
- QA 验证:检查 guardrail 是否真实生效
#### E-2 自动补给任务流
- OwnerEngineer
- 交付物:补给任务创建 / 受理 / 待验证回写
- 完成标准:
- 低于阈值触发任务
- 成功后进入 pending_verify / pending_enable
- 不允许直接 active
- QA 验证:检查自动启用是否被阻断
#### E-3 fail-closed
- OwnerEngineer
- 交付物:通知网关/补给受理/KMS 异常阻断逻辑
- 完成标准:
- 失败不伪成功
- 明文不落日志/DB
- QA 验证:检查失败证据和审计闭环
#### E-4 Phase E QA Gate
- OwnerQA
- 放行条件:
- 未引入浏览器自动化注册主路径
- 未引入验证码编排主路径
- 未允许无审批直接自动激活
### Phase F工作台、配置、权限与完善性补齐
目标:补足可操作性与交付完整性,但不得改变前述主链路口径。
#### F-1 工作台最小读写能力
- OwnerEngineer
- 交付物:账号页、模型页、待处理页、确认上架、忽略、手动探针
- QA 验证:检查关键操作真实连到主链路,不是空按钮
#### F-2 配置与审计
- OwnerEngineer
- 交付物:配置读取/修改、审计日志
- QA 验证:检查配置生效路径与审计记录
#### F-3 权限与内部/外部路由边界
- OwnerEngineer
- 交付物:认证、角色权限、内部接口与外部接口分离
- QA 验证:检查 `/internal/supply-intelligence/` 与外部暴露面的边界
#### F-4 Phase F QA Gate
- OwnerQA
- 放行条件:
- 权限边界清楚
- OpenAPI 与真实路由一致
- 不新增超范围平台化能力
## 3. 明确禁止的提前扩张
以下事项在前述主链路未闭环前,禁止插队进入主开发路径:
- pricing / prediction / recommendation
- 向量数据库 / 向量检索
- SFI 仪表盘
- WebSocket 实时推送
- 独立 API + worker 集群重部署
- 浏览器自动化注册主路径
- 验证码编排主路径
- 以 Redis / Temporal 为首期硬前置
## 4. QA 统一复核问题单
每个阶段 QA 都必须回答:
1. 定义是否存在?
2. 装配是否存在?
3. 调用点是否真实存在?
4. 外部/内部入口是否真实挂载?
5. 是否出现实施漂移?
6. 是否回退到了历史草案口径?
## 5. 工程启动建议阅读顺序
1. `/home/long/project/立交桥/projects/supply-intelligence/tech/CURRENT_SOURCE_OF_TRUTH_2026-05.md`
2. `/home/long/project/立交桥/projects/supply-intelligence/tech/BASELINE_TECHLEAD_V2.md`
3. `/home/long/project/立交桥/projects/supply-intelligence/tech/GATEWAY_CONSUMER_DECISION_2026-05.md`
4. `/home/long/project/立交桥/projects/supply-intelligence/tech/TEST_DESIGN.md`
5. `/home/long/project/立交桥/projects/supply-intelligence/tech/IMPLEMENTATION_TASK_BOARD_V1_2026-05.md`
6. `/home/long/project/立交桥/projects/supply-intelligence/specs/功能清单.md`

275
tech/INTERFACE.md Normal file
View File

@@ -0,0 +1,275 @@
# Supply-Intelligence 核心接口设计
> 状态说明2026-05 收敛修订):本文件保留为旧版接口草案,已不再作为当前实现真源。
> 当前接口真源以 /home/long/project/立交桥/projects/supply-intelligence/tech/BASELINE_TECHLEAD_V2.md 为准。
> 以下旧接口定义已废止,不得继续作为实现入口:
> - pricing comparison / recommendations / predictions 相关接口
> - 与新 candidate 状态机不一致的旧状态枚举
> - 未区分 published 与 gateway applied 的旧消费口径
> 版本v1.0 | 状态:初稿
---
## 1. 内部模块间接口
### 1.1 ProbeService
```go
type ProbeService interface {
// 执行单次探针
Probe(ctx context.Context, accountID string) (*ProbeResult, error)
// 批量探针(按供应商或全量)
ProbeBatch(ctx context.Context, filter ProbeFilter) (*BatchProbeResult, error)
// 获取探针结果历史
GetProbeHistory(ctx context.Context, accountID string, limit int) ([]ProbeResult, error)
// 手动触发掠针(运营干预)
TriggerManualProbe(ctx context.Context, accountID string, actorID string) (*ProbeResult, error)
}
type ProbeResult struct {
AccountID string
Status string // active suspended disabled
RiskScore int // 0-100
RiskReason string
LatencyMs int
ResponseCode int
CheckedAt time.Time
NextCheckAt time.Time
}
type ProbeFilter struct {
Platform *string
Status *string
RiskScoreMin *int
RiskScoreMax *int
}
```
### 1.2 DiscoveryService
```go
type DiscoveryService interface {
// 执行单次全网扫描
Scan(ctx context.Context) (*ScanResult, error)
// 获取最近扫描结果
GetLastScan(ctx context.Context) (*ScanResult, error)
// 获取候选模型列表
ListCandidates(ctx context.Context, filter CandidateFilter) ([]ModelCandidate, error)
// 手动触发扫描
TriggerManualScan(ctx context.Context, actorID string) (*ScanResult, error)
// 忽略候选模型
IgnoreCandidate(ctx context.Context, candidateID string, reason string, actorID string) error
}
type ScanResult struct {
ScannedAt time.Time
Platforms []string
NewModels int
RemovedModels int
Errors []ScanError
}
type ModelCandidate struct {
ID string
Platform string
ModelID string
Status string // discovered queued testing test_passed test_failed ignored
DiscoveredAt time.Time
TestedAt *time.Time
TestResult *TestResult
}
```
### 1.3 AdmissionService
```go
type AdmissionService interface {
// 执行准入测试
RunTest(ctx context.Context, candidateID string) (*TestResult, error)
// 获取测试结果
GetTestResult(ctx context.Context, candidateID string) (*TestResult, error)
// 手动确认上架(运营干预)
Publish(ctx context.Context, candidateID string, actorID string) error
// 强制上架(测试失败但运营确认)
ForcePublish(ctx context.Context, candidateID string, reason string, actorID string) error
}
type TestResult struct {
CandidateID string
Status string // passed failed
Dimensions []TestDimension
FailedReason *string
ExecutedAt time.Time
DurationMs int
}
type TestDimension struct {
Name string
Passed bool
Detail string
}
```
### 1.4 AccountService
```go
type AccountService interface {
// 创建账号(手动或自动)
CreateAccount(ctx context.Context, req CreateAccountRequest) (*SupplyAccount, error)
// 获取账号信息
GetAccount(ctx context.Context, accountID string) (*SupplyAccount, error)
// 更新账号状态
UpdateStatus(ctx context.Context, accountID string, status string, reason string) error
// 轮换密钥
RotateKey(ctx context.Context, accountID string, actorID string) error
// 列表账号
ListAccounts(ctx context.Context, filter AccountFilter) ([]SupplyAccount, error)
}
type SupplyAccount struct {
ID string
Platform string
ProxyID string
Status string
RiskScore int
APIKeyHint string // 密钥前 4 后 4
CreatedAt time.Time
UpdatedAt time.Time
}
```
### 1.5 HealthBoardService
```go
type HealthBoardService interface {
// 获取供应商健康大盘
GetBoard(ctx context.Context, scope BoardScope) (*HealthBoard, error)
// 获取模型比价报表
GetPricingComparison(ctx context.Context, modelID string) ([]PricingComparison, error)
// 获取供应链覆盖率
GetCoverage(ctx context.Context) (*CoverageReport, error)
// 获取预测分析
GetPredictions(ctx context.Context, minConfidence float64) ([]Prediction, error)
}
type HealthBoard struct {
Accounts []AccountHealth
Candidates []CandidateSummary
Coverage float64
FreshnessIndex float64
}
```
---
## 2. 外部系统集成接口
### 2.1 与 Bridge Gateway 集成
| 方法 | 路径 | 请求 | 响应 | 说明 |
|------|------|------|------|------|
| 查询账号状态 | `GET /internal/supply-intelligence/accounts/{id}/health` | - | `ProbeResult` | Gateway 路由决策时查询 |
| 查询模型定价 | `GET /internal/supply-intelligence/pricing/{model_id}` | - | `PricingInfo` | 动态定价参考 |
| 获取推荐供应商 | `GET /internal/supply-intelligence/recommendations` | `?model={model_id}&strategy=cost` | `[]Recommendation` | 智能路由推荐 |
### 2.2 与 supply-api 集成
| 方法 | 路径 | 请求 | 响应 | 说明 |
|------|------|------|------|------|
| 读取账号列表 | `GET /internal/supply/accounts` | - | `[]SupplyAccount` | 探针器获取待检测账号 |
| 更新账号状态 | `POST /internal/supply/accounts/{id}/status` | `{"status":"suspended","reason":""}` | `{"success":true}` | 探针结果写回 |
| 读取模型列表 | `GET /internal/supply/packages` | - | `[]SupplyPackage` | 扫描比对基准 |
| 创建模型 | `POST /internal/supply/packages` | `SupplyPackage` | `{"id":""}` | 准入测试通过后上架 |
| 获取审计日志格式 | `GET /internal/supply/audit/schema` | - | `{"schema":{}}` | 审计事件格式一致 |
---
## 3. API 接口规范
### 3.1 REST API 基础
- **基础路径**: `/api/v1/supply-intelligence/`
- **内部路径** (集成模式): `/internal/supply-intelligence/`
- **内容类型**: `application/json`
- **错误响应格式**:
```json
{
"error": {
"code": "SI_PRB_4001",
"message": "供应商账号不存在",
"details": {}
}
}
```
### 3.2 核心端点
#### 探针管理
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/api/v1/supply-intelligence/probes` | 列表探针结果 |
| POST | `/api/v1/supply-intelligence/probes/{account_id}` | 手动触发探针 |
| GET | `/api/v1/supply-intelligence/probes/{account_id}/history` | 探针历史 |
#### 扫描与发现
| 方法 | 路径 | 描述 |
|------|------|------|
| POST | `/api/v1/supply-intelligence/discovery/scan` | 手动触发全网扫描 |
| GET | `/api/v1/supply-intelligence/discovery/candidates` | 列表候选模型 |
| GET | `/api/v1/supply-intelligence/discovery/candidates/{id}` | 获取候选模型详情 |
| POST | `/api/v1/supply-intelligence/discovery/candidates/{id}/ignore` | 忽略候选模型 |
#### 准入测试
| 方法 | 路径 | 描述 |
|------|------|------|
| POST | `/api/v1/supply-intelligence/admission/{candidate_id}/test` | 手动执行准入测试 |
| GET | `/api/v1/supply-intelligence/admission/{candidate_id}/result` | 获取测试结果 |
| POST | `/api/v1/supply-intelligence/admission/{candidate_id}/publish` | 确认上架 |
| POST | `/api/v1/supply-intelligence/admission/{candidate_id}/force-publish` | 强制上架 |
#### 账号管理
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/api/v1/supply-intelligence/accounts` | 列表账号 |
| POST | `/api/v1/supply-intelligence/accounts` | 创建账号 |
| GET | `/api/v1/supply-intelligence/accounts/{id}` | 获取账号 |
| POST | `/api/v1/supply-intelligence/accounts/{id}/rotate-key` | 轮换密钥 |
| POST | `/api/v1/supply-intelligence/accounts/{id}/status` | 更新状态 |
#### 健康大盘
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/api/v1/supply-intelligence/health-board` | 获取健康大盘 |
| GET | `/api/v1/supply-intelligence/pricing/{model_id}/comparison` | 模型比价 |
| GET | `/api/v1/supply-intelligence/coverage` | 供应链覆盖率 |
| GET | `/api/v1/supply-intelligence/predictions` | 预测分析 |
### 3.3 错误码定义
| 错误码 | HTTP 状态 | 说明 |
|---------|-----------|------|
| `SI_PRB_4001` | 404 | 供应商账号不存在 |
| `SI_PRB_4002` | 429 | 探针频率过高,请等待 |
| `SI_DIS_4001` | 404 | 候选模型不存在 |
| `SI_DIS_4002` | 409 | 候选模型状态不允许忽略 |
| `SI_ADM_4001` | 404 | 准入测试任务不存在 |
| `SI_ADM_4002` | 409 | 准入测试正在执行中 |
| `SI_ADM_4003` | 400 | 测试未通过,无法上架 |
| `SI_ACC_4001` | 404 | 账号不存在 |
| `SI_ACC_4002` | 409 | 账号状态不允许此操作 |
| `SI_ACC_4003` | 403 | 无权执行此操作 |
| `SI_BRD_4001` | 400 | 查询参数无效 |
### 3.4 WebSocket 接口
**路径**: `/ws/v1/supply-intelligence/board`
- 运营工作台订阅后,实时推送探针结果、候选模型变更、状态变更待办。
- 心跳间隔 30 秒。

355
tech/TEST_DESIGN.md Normal file
View File

@@ -0,0 +1,355 @@
# Supply Intelligence 测试设计方案
> 状态说明2026-05 收敛修订):本文件已转为“收敛后测试门禁文档”,必须按新基线解释。
> 若与旧 PRD/HLD/INTERFACE 的测试口径冲突,以 /home/long/project/立交桥/projects/supply-intelligence/tech/BASELINE_TECHLEAD_V2.md 与最新 PM 基线为准。
> 以下旧测试口径不得继续作为放行依据:
> - 以独立重部署、向量数据库、WebSocket、预测/比价能力为默认测试前提
> - 将自动注册深链路视为本期不可降期的默认主路径
> - 将 published 等同于 gateway 已消费生效
> 版本v1.0
> 日期2026-04-27
> 状态:初稿
> 覆盖AC-01 ~ AC-12、异常/边缘流程 FP-01 ~ FP-10、场景 S1~S4
---
## 1. 测试策略
### 1.1 测试分层模型
```
┌─────────────────────────────────────────────────┐
│ E2E Tests (黑盒) │
│ 场景:从探针调度到状态变更、从发现到上架全链路 │
│ 工具Go test + httptest + 自制 E2E runner │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ Integration Tests (灰盒) │
│ 场景Service 间协作、异步任务队列、外部 API Mock│
│ 工具Go test + testify + sqlmock + gock │
│ 覆盖率门槛service ≥ 80%, handler ≥ 80% │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ Unit Tests (白盒) │
│ 场景:状态机逻辑、探针评估、风险评分计算 │
│ 工具Go test + testify + gomock │
│ 覆盖率门槛domain ≥ 70% │
└─────────────────────────────────────────────────┘
```
### 1.2 测试通过标准
| 维度 | 标准 |
|------|------|
| 覆盖率 | domain ≥ 70%, service/handler ≥ 80% |
| 模块 A探针 | AC-01 ~ AC-03 全部通过 |
| 模块 B发现 | AC-04 ~ AC-05 全部通过 |
| 模块 C准入测试 | AC-06 ~ AC-07 全部通过 |
| 模块 D受控自动补给 | AC-08 ~ AC-09 全部通过(按首期最小边界解释) |
| 模块 E工作台 | AC-10 ~ AC-12 全部通过 |
| 异常/边缘流程 | FP-01 ~ FP-10 全部有验证测试 |
| 误报率 | 7 天连续运行 false positive ≤ 1% |
### 1.3 外部依赖 Mock
| 依赖 | Mock 方案 | 工具 |
|------|---------|------|
| **供应商 API探针目标** | Mock server 返回 200/401/403/429/500 | gock |
| **供应商模型列表 API** | Mock 返回 JSON 模型列表 | gock |
| **供应商补给接口 / 人工补录入口** | Mock 返回受理成功/400/500 | gock |
| **通知网关(飞书/邮件)** | Mock server 接收通知或确认消息 | httptest |
| **KMS 服务** | Mock 加密/解密逻辑 | 接口层 Mock |
| **Job Scheduler / 主仓调度器** | 使用主仓调度抽象或本地调度测试桩 | go test + test double |
| **supply-api 数据库** | sqlmock 拦截读写 | go-sqlmock |
---
## 2. 模块 A 测试用例(供应商品质探针)
### AC-01 探针覆盖度
| 用例 ID | 描述 | 类型 | 验证条件 |
|---------|------|------|---------|
| TA-01-01 | 15 分钟内探针覆盖率 ≥99% | Functional | Given 100 条 active/suspended 账号 When 15min 后统计 Then ≥99 条被探针 |
| TA-01-02 | suspended 账号同等探针 | Functional | Given suspended 账号 When 探针执行 Then 同样被覆盖 |
| TA-01-03 | 暂停探针账号不被覆盖 | Edge | Given 账号设置 pause_probe=true When 探针执行 Then 该账号被跳过 |
### AC-02 状态变更正确性
| 用例 ID | 描述 | 类型 | 验证条件 |
|---------|------|------|---------|
| TA-02-01 | active → suspended1次401 | Happy Path | Given active 账号 When 连续 1 次返回 401 Then 60s 内状态变为 suspended |
| TA-02-02 | suspended → disabled连续3次401 | Happy Path | Given suspended 账号 When 连续 3 次返回 401 Then 60s 内状态变为 disabled |
| TA-02-03 | 429 单次不改变状态 | Edge | Given active 账号 When 返回 429 一次 Then 15min 内状态保持 active |
| TA-02-04 | 指数退避重试逻辑 | Functional | Given 返回 429 When 探针执行 Then 按 1→2→4min 退避重试 |
| TA-02-05 | 状态机不允许 active→disabled 直变 | Edge | Given active 账号 When 连续 3 次失败 Then 不会直接变为 disabled必须先 suspended |
| TA-02-06 | 手动暂停账号状态不自动变更 | Edge | Given 账号 pause_probe=true When 供应商返回异常 Then 状态不变 |
### AC-03 误报率
| 用例 ID | 描述 | 类型 | 验证条件 |
|---------|------|------|---------|
| TA-03-01 | 7 天误报率 ≤1% | Long Run | Given 100 条正常账号 When 连续运行 7 天 Then 误变更次数 ≤7 |
| TA-03-02 | 探针与手动操作并发 | Concurrency | Given 手动修改状态的同时 When 探针执行 Then 乐观锁冲突处理正确 |
---
## 3. 模块 B 测试用例(全网模型发现)
### AC-04 新模型发现延迟
| 用例 ID | 描述 | 类型 | 验证条件 |
|---------|------|------|---------|
| TB-04-01 | 新模型在 2 扫描周期内被发现 | Functional | Given 供应商新增 model_id When 扫描执行 Then 2h 内 model_candidates 出现 discovered 记录 |
| TB-04-02 | 模型比对去重正确 | Functional | Given 已存在的 active model When 全网扫描 Then 不会重复创建 candidate |
| TB-04-03 | 模型下架告警触发 | Functional | Given active package 对应的 model_id 从供应商列表消失 When 2 扫描周期后 Then 运营工作台出现下架告警 |
### AC-05 已下架模型告警
| 用例 ID | 描述 | 类型 | 验证条件 |
|---------|------|------|---------|
| TB-05-01 | 下架模型不自动变更 package 状态 | Edge | Given model_id 消失 When 扫描执行 Then package 状态保持 active生成告警 |
| TB-05-02 | 分页获取完整模型列表 | Functional | Given 供应商返回分页 When 扫描 Then 正确处理所有分页数据 |
---
## 4. 模块 C 测试用例(模型准入测试)
### AC-06 准入测试通过
| 用例 ID | 描述 | 类型 | 验证条件 |
|---------|------|------|---------|
| TC-06-01 | discovered → test_passed + 草稿生成 | Happy Path | Given discovered candidate When 测试全部通过 Then 状态 test_passedsupply_package 草稿生成 |
| TC-06-02 | 草稿字段完整性 | Functional | Given 草稿生成 When 检查字段 Then platform/model/price/suggested 正确 |
| TC-06-03 | 准入测试 30 分钟内完成 | Performance | Given discovered candidate When 测试执行 Then ≤30min 完成 |
### AC-07 准入测试失败
| 用例 ID | 描述 | 类型 | 验证条件 |
|---------|------|------|---------|
| TC-07-01 | discovered → test_failed | Negative | Given discovered candidate When 测试返回 500 Then 30min 内状态 test_failedfailure_reason 非空 |
| TC-07-02 | 超时视为失败 | Edge | Given 测试用例 60s 无响应 When Then 整体标记为 test_failedreason = timeout |
| TC-07-03 | 测试账号 suspended 时任务失败 | Edge | Given 测试账号变为 suspended When 准入测试执行 Then 任务标记 test_failedreason = test_account_unavailable |
| TC-07-04 | ignore 账号 7 天内不重扫 | Edge | Given 运营标记 ignore When 7 天内扫描 Then 该 candidate 不出现 |
---
## 5. 模块 D 测试用例(受控自动补给)
### AC-08 受控自动补给触发与落单
| 用例 ID | 描述 | 类型 | 验证条件 |
|---------|------|------|---------|
| TD-08-01 | 可用账号数 < 阈值时触发补给任务 | Functional | Given 白名单供应商的可用账号数 < 阈值 When 系统检测 Then 10min 内生成补给任务或补给申请 |
| TD-08-02 | 非白名单供应商不自动补给 | Guardrail | Given 非白名单供应商账号不足 When 系统检测 Then 不自动触发补给,仅记录告警或人工待办 |
| TD-08-03 | 补给结果进入待验证/待启用 | Happy Path | Given 补给流程受理成功 When 补给完成 Then 新账号或候选资源进入 pending_verify / pending_enable 等受控状态,而非直接 active |
| TD-08-04 | 补给结果关联 task | Functional | Given 补给任务完成 When 检查任务记录 Then auto_supply_tasks 或等价任务状态为 completed/pending_verify |
### AC-09 受控自动补给 fail-closed
| 用例 ID | 描述 | 类型 | 验证条件 |
|---------|------|------|---------|
| TD-09-01 | 通知/补给网关不可用时 fail-closed | Resilience | Given 通知网关或补给受理接口返回 503 When 补给执行 Then 60s 内任务 failed审计日志完整无虚假成功 |
| TD-09-02 | 补给接口返回 400 | Edge | Given 补给请求参数非法或资源已存在 When 补给执行 Then 任务 failed不重复盲目重试 |
| TD-09-03 | KMS 不可用时 fail-closed | Resilience | Given KMS 超时 When 凭证加密步骤执行 Then 60s 内任务 failed明文凭证不出现在日志/DB |
| TD-09-04 | 无审批/越权配置时阻断自动启用 | Guardrail | Given 缺少审批或超出受控边界 When 补给结果回写 Then 保持 pending_verify / pending_enable不允许直接进入 active |
---
## 6. 模块 E 测试用例(运营工作台)
### AC-10 审计日志完整性
| 用例 ID | 描述 | 类型 | 验证条件 |
|---------|------|------|---------|
| TE-10-01 | 状态变更 5s 内写入审计 | Performance | Given 状态变更 When 执行完成 Then ≤5s 审计记录存在 |
| TE-10-02 | 审计字段完整性 | Functional | Given 审计记录 When 检查 Then 包含 object_type/id/action/before_state/after_state/request_id |
| TE-10-03 | 探针执行记录审计 | Functional | Given 探针执行 When 完成 Then probe_execution_logs 有记录 |
### AC-11 运营工作台干预
| 用例 ID | 描述 | 类型 | 验证条件 |
|---------|------|------|---------|
| TE-11-01 | 确认上架 draft → active | Happy Path | Given draft package When 点击确认 Then 3s 内变为 active |
| TE-11-02 | 忽略模型 7 天内不出现 | Edge | Given 点击忽略 When Then 7 天内 candidate 不出现在待处理列表 |
| TE-11-03 | 手动触发单账号探针 | Functional | Given 运营手动触发 When Then 立即执行探针,结果可见 |
| TE-11-04 | 并发操作冲突处理 | Concurrency | Given 同时点击确认和忽略 When Then 返回 409只一个生效 |
### AC-12 配置热更新
| 用例 ID | 描述 | 类型 | 验证条件 |
|---------|------|------|---------|
| TE-12-01 | 探针周期修改 60s 内生效 | Functional | Given 修改探针周期 When 下发配置 Then 60s 后新周期生效 |
---
## 7. 异常/边缘流程测试FP-01 ~ FP-10
| 用例 ID | 场景 | 验证点 | 预期行为 |
|---------|------|-------|---------|
| TFP-01 | 供应商探针 DNS/TCP 超时 | 状态不变 | 标记 inconclusive指数退避不触发状态变更 |
| TFP-02 | 供应商返回空/格式突变 | 状态不变 | 解析失败标记 inconclusive记录日志 |
| TFP-03 | 探针与手动操作并发 | 乐观锁 | 更新失败,探针记录冲突日志,下次覆盖 |
| TFP-04 | 准入测试期间测试账号 suspended | 任务标记失败 | 任务标记 test_failedreason = test_account_unavailable |
| TFP-05 | 补给接口返回 400 或资源冲突 | 任务失败 | 任务 failed不重复盲目重试审计记录完整 |
| TFP-06 | 补给成功但验证/启用失败 | pending 不变 | 账号保持 pending_verify/pending_enable任务标记 verify_failed触发告警 |
| TFP-07 | 供应商模型列表分页 500 | 整体不中断 | 已获取部分正常处理,失败页下次重试 |
| TFP-08 | 探针期间数据库不可用 | 任务失败重试 | 探针任务失败,连续 5 次失败后暂停批次,触发系统告警 |
| TFP-09 | 确认上架与忽略并发 | 409 冲突 | 只有一个生效,返回 409 |
| TFP-10 | KMS 不可用时注册 | 明文不落盘 | 加密步骤阻塞/失败,明文凭证不出现 |
---
## 8. 灰度发布验证计划
### 8.1 各 Phase 验证内容
| Phase | 交付内容 | 通过标准 | 依赖项 |
|-------|---------|---------|--------|
| **Phase 1** | 模块 A探针+ 模块 E 只读视图 | AC-01~AC-03, AC-10~AC-11只读部分 | 主仓调度能力或本地调度测试桩 |
| **Phase 2** | 模块 B发现+ 模块 C准入测试 | AC-04~AC-07 | Phase 1 + 供应商 API 清单 |
| **Phase 3** | 模块 D受控自动补给+ 模块 E 完整 | AC-08~AC-12 | Phase 1+2 + KMS/通知与补给受理链路就绪 |
### 8.2 灰度门禁
每次 Phase 升级前:
- [ ] 全部 AC 测试用例通过
- [ ] 覆盖率达标
- [ ] 灰度开关独立验证(每个开关可单独打开/关闭)
- [ ] 回滚条件演练(误报率>5% / 状态变更导致错误率上升>2%
---
## 9. 回归测试集
### 9.1 快速回归(每次 PR~10 分钟)
```
TA-01-01, TA-02-01, TA-02-02, TA-02-05,
TB-04-01, TC-06-01, TC-07-01,
TD-08-01, TD-09-01,
TE-10-01, TE-11-01
共 11 条
```
### 9.2 完整回归Phase 升级,~45 分钟)
```
TA-01-01 ~ TA-03-02全 8 条)
TB-04-01 ~ TB-05-02全 4 条)
TC-06-01 ~ TC-07-04全 4 条)
TD-08-01 ~ TD-09-03全 4 条)
TE-10-01 ~ TE-12-01全 7 条)
TFP-01 ~ TFP-10全 10 条)
共 37 条
```
---
## 10. 技术栈与集成约束验证
### 10.1 统一技术栈与双运行模式验证
| 用例 ID | 描述 | 类型 | 验证条件 |
|---------|------|------|---------|
| TSI-RUN-01 | 独立运行模式启动 | Happy Path | Given 独立 `config.yaml` 与独立数据库/Redis When 启动 `cmd/supply-intelligence/main.go` Then `/actuator/health/ready` 返回 200`/api/v1/supply-intelligence/*` 可访问 |
| TSI-RUN-02 | 集成运行模式挂载 | Integration | Given supply-api 主进程加载 `IntegrationPlugin` When 启动 Then `/internal/supply-intelligence/*` 路由与后台任务注册成功 |
| TSI-RUN-03 | 配置分离加载 | Functional | Given 独立模式与集成模式分别启动 When 读取配置 Then 独立模式只加载自身配置,集成模式合并主项目配置且不覆盖无关模块 |
| TSI-RUN-04 | 数据库前缀隔离 | Structural | Given 执行迁移 When 检查 schema Then 仅创建 `supply_intelligence_` 前缀表 |
### 10.2 独立运行与集成运行验证
### 10.3 IntegrationPlugin 与模块挂载验证
| 用例 ID | 描述 | 类型 | 验证条件 |
|---------|------|------|---------|
| TSI-PLG-01 | IntegrationPlugin 注册 HTTP 路由 | Integration | Given 集成模式 When 插件注册 Then Probe/Discovery/Admission/AutoReg/OpsWorkBench 路由挂载成功 |
| TSI-PLG-02 | 模块开关生效 | Functional | Given `enabled_modules` 关闭某模块 When 启动 Then 对应路由/worker 不注册,其他模块可用 |
| TSI-PLG-03 | 集成模式共享资源 | Integration | Given supply-api 注入共享 DB/Redis/logger When 插件启动 Then 使用共享资源且不重复初始化冲突依赖 |
### 10.3 OpenAPI 契约验证
| 用例 ID | 描述 | 类型 | 验证条件 |
|---------|------|------|---------|
| TSI-OAS-01 | OpenAPI 文档可访问 | Functional | Given 服务启动 When 请求 `/openapi.json``/docs` Then 返回 200 且包含探针、发现、准入测试、运营工作台接口 |
| TSI-OAS-02 | 路由与 OpenAPI 一致 | Contract | Given 导出的 OpenAPI 文档 When 对照 HTTP 路由 Then 请求/响应/错误码与实现一致,无缺失公开接口 |
| TSI-OAS-03 | 集成前缀可配置 | Contract | Given 集成模式配置内部前缀 When 导出文档 Then 文档反映 `/internal/supply-intelligence/` 前缀或明确区分暴露面 |
### 10.4 NewAPI / Sub2API 适配层验证
| 用例 ID | 描述 | 类型 | 验证条件 |
|---------|------|------|---------|
| TSI-ADP-01 | 供应商状态同步适配 | Contract | Given NewAPI/Sub2API 拉取供应商状态 When 调用标准化接口 Then 返回字段稳定、延迟满足约束、状态映射正确 |
| TSI-ADP-02 | 模型列表推送适配 | Contract | Given 外部系统拉取模型列表 When 调用 `/models` Then 只返回已发现且允许暴露的数据,字段与约定一致 |
| TSI-ADP-03 | 账号状态适配边界 | Contract | Given 外部系统读取账号状态 When 通过适配层执行 Then 仅返回允许暴露的状态字段,不暴露凭证/探针日志/内部风险细节 |
---
## 11. 发布门禁与阶段结论
### 11.1 发布门禁检查表
以下门禁项全部通过前,不得认定达到生产要求:
- [ ] 独立运行 / 集成运行两种模式均完成启动验证路由、worker、内部接口真实挂载
- [ ] `IntegrationPlugin`、OpenAPI、NewAPI/Sub2API 适配层合同测试全部通过
- [ ] 凭证保护经日志/DB/异常路径验证无明文KMS 不可用时 fail-closed
- [ ] 受控自动补给链路具备白名单限制、阈值触发、审批/待验证边界、重复提交阻断与审计留痕
- [ ] 状态机迁移、审计写入、Gateway package event + ack、外部只读适配链路完成一致性验证
- [ ] 首次生产放量场景遵循“只告警不自动变更状态”,并验证撤销与人工接管流程
- [ ] 调度器失效、补给受理失败、外部适配越权、错误状态传播四类高风险回归通过
- [ ] 至少一条探针、一条模型发现、一条准入测试、一条受控自动补给链路完成端到端验证
### 11.2 阶段门控结论
**当前结论APPROVED设计已可进入 Engineer 实现)**
**结论解释:**
- 本文档首页所述“收敛后测试门禁文档”口径已生效。
- 当前放行依据不再是旧 HLD/PRD/INTERFACE/DEPLOYMENT而是
- `/home/long/project/立交桥/projects/supply-intelligence/tech/BASELINE_TECHLEAD_V2.md`
- `/home/long/project/立交桥/projects/supply-intelligence/tech/GATEWAY_CONSUMER_DECISION_2026-05.md`
- 因此,本节不再沿用历史性 `REQUEST_CHANGES` 作为当前总门控。
**当前仍需在实现阶段持续验证的高风险项:**
- 凭证保护必须能证明 fail-closed且日志/审计/异常路径无明文泄漏。
- 状态同步、审计写入、package event + ack 必须形成可追踪闭环。
- 关键链路必须能完成“定义 → 装配 → 调用 → 入口”四层验证,不能只停留在接口存在。
- 自动补给按首期最小边界解释:允许白名单供应商、阈值触发、任务化补给、待验证/待启用;不把浏览器自动化深链路作为首期阻断门槛。
**实现前约束:**
- 若实现与 `BASELINE_TECHLEAD_V2.md``GATEWAY_CONSUMER_DECISION_2026-05.md` 冲突,应以两者为准并回退旧测试假设。
- 若下游消费方未落真实 poll/apply/ack 入口,不得宣称 package 发布链路已完成。
- 若 NewAPI/Sub2API 适配超出“只读/受控暴露边界”,应判定为实施漂移。
**重新转为 REQUEST_CHANGES / BLOCKED 的条件:**
- 实现阶段发现 published/applied 再次混淆。
- gateway 消费闭环缺少真实消费方入口或 ack 回写。
- 自动补给被重新扩张为首期深自动注册硬门槛。
- 核心链路无法提供四层调用链证据。
---
## 12. 性能与安全测试
### 12.1 性能基准
| 指标 | 目标值 | 测试方法 |
|------|-------|---------|
| 探针执行(单账号) | <2s | 计时 1000 次取 P99 |
| 全网扫描10 供应商) | <5min | 从调度触发到完成计 |
| 准入测试5 用例) | <30min P99 | 从 discovered 到 test_passed/failed |
| 供应商状态查询 API | <50ms P99 | 并发 100 请求 |
| 审计日志写入 | <1s P99 | 单次变更后计时 |
### 12.2 安全测试
| 测试项 | 方法 | 验证 |
|-------|------|------|
| 凭证明文保护 | 检查日志/DB/内存 dump | 无明文凭证 |
| KMS 密钥轮换 | Mock KMS 不可用 | fail-closed不暴露明文 |
| 供应商 API 限流绕过 | 连续探针超限 | 正确触发 rate limit |
| 注册接口重复提交 | 并发同一邮箱注册 | 只有一次成功,其余 failed |