# 254 Admin:定义采购运行时规格解析接口与审计模型 - 类型:需求 / 重构 - 父级大工单:#96 - 所属 MVP / 版本:#253 采购运行时 AI 规格纠偏闭环 - 状态:已完成,待用户验收 - 日期:2026-08-17 - Gitea 工单:http://ilaer.eicp.net:8418/chengma/cmautobuy/issues/254 ## 背景与目标 Client 在真实采购规格面板找不到任务尺码时,原流程只能提交失败。为了让同一次、尚未 进入不可逆阶段的采购可以取得受审计的 Admin 规格决策,本任务先冻结第五个 Client API 契约,并建立保存候选观察和最终决策的最小数据模型。 本任务不实现 AI 匹配、HTTP Handler、Client 真机候选采集或采购续跑,也不复用会改变 任务状态的失败提交接口。 ## 最终方案 - 定义一次性命令 `POST /api/v1/client/tasks/{task_id}/spec-resolution`,不增加 GET、 状态轮询、租约或心跳。 - 请求 schema v1 只包含任务/执行身份、PDD 商品、原任务规格、已选颜色、目标尺码、 当前颜色下最多 100 条可购买候选、快照哈希和观测时间;整个请求最多 64 KiB。 - 候选使用连续短编号 `c1`~`c100`。快照和幂等键都使用明确的 UTF-8 长度前缀 SHA-256 算法,Admin 必须重新计算,不能信任 Client 自报哈希。 - 响应固定为 `matched/uncertain/rejected/failed`。只有 matched 返回请求内候选的逐字 副本;来源为 `rule/ai/reused/null`,置信度使用可空的 0~10000 基点。 - MySQL v26 新增一张 `purchase_spec_resolutions`:同一行保存 pending 候选观察和最终 决策,业务唯一键为 `(task_id, attempt_id, candidate_snapshot_hash)`,另存 `request_hash` 识别同一身份的不同内容。 - Repository 提供新增、按解析编号读取、按业务身份读取、幂等复查和只完成一次决策。 候选观察先用短事务落库,规则/AI 网络调用不得占用事务;最终决策和可重放响应由后续 Handler/Service 在另一个短事务与 `idempotency_keys` 一起提交。 - 审计表不设任务和 Client 外键,避免既有硬删除流程阻塞或级联清除审计;不修改任务 状态、`pdd_products.skus_json` 或长期 `spec_mappings`。 - 表中不保存原始无障碍 XML、截图、订单号、收货信息、Cookie、Token、密码、API Key、 完整提示词或完整模型响应。 最终实现与工单方案一致。实施时进一步明确了跨 Python/Go 可复现的长度前缀哈希算法, 以及“短事务保存观察 → 事务外解析 → 短事务完成决策”的崩溃恢复边界。 ## 改了哪些 - `admin/model/model.go` - 增加解析 outcome/source 枚举、完整审计模型和完成决策模型。 - `admin/repository/mysql_db.go` - schema 版本升级到 v26,增加解析审计表、约束、索引和启动自检。 - `admin/repository/purchase_spec_resolution.go` - 增加候选观察新增、读取、幂等复查和只完成一次的参数化 Repository。 - `admin/repository/mysql_db_integration_test.go` - 覆盖 v25→v26、重复迁移、业务唯一键、请求冲突、决策只完成一次和候选上限。 - `docs/client/04-admin-api-contract.md` - 写入权威 HTTP schema、限制、哈希、响应、错误、兼容和隐私边界。 - `docs/admin/04-client-api.md` - 同步 Admin 处理顺序、事务边界、稳定错误和实现清单。 - `docs/client/03-data-model.md` - 明确 Client 后续持久化边界、原任务不可覆盖和旧 Client 兼容行为。 - `docs/admin/03-data-model.md` - 记录 MySQL v26 表结构、约束、幂等关系和数据安全边界。 ## 验收结果 | 验收标准 | 结果 | |---|---| | Client 和 Admin 文档的请求、响应、错误、幂等和兼容行为一致 | 通过 | | 新调用是一次性业务命令,无状态查询、轮询、租约或心跳 | 通过 | | 请求不含原始 XML、订单、收货信息和凭据,并限制字段、100 条候选和 64 KiB | 通过 | | 任务、尝试和候选快照唯一定位记录,相同请求可读取同一记录 | 通过 | | MySQL 8.4 全新建库、v25 升级和重复迁移通过,自检后才记录 v26 | 通过 | | 可表达规则、AI、复用、不确定、拒绝和失败,置信度保留 NULL | 通过 | | 不更新 PDD 主数据、任务状态和原四个 Client API | 通过 | | Go 1.23.0 build、test、vet 和格式检查 | 通过 | ## 测试 - 执行的命令: - `gofmt -l .` - `$env:GOTOOLCHAIN='go1.23.0'; go build ./...` - `$env:GOTOOLCHAIN='go1.23.0'; go test ./... -count=1` - `$env:GOTOOLCHAIN='go1.23.0'; go vet ./...` - 配置指向本机临时隔离的 MySQL 8.4 `_test` 库后: `go test ./repository -run 'TestMySQLMigrate_(真实MySQL8|V25升级V26且规格解析审计幂等)$' -count=1 -v` - 结果: - Go 1.23.0 格式、构建、全量测试和 vet 全部通过。 - MySQL Community Server 8.4.8 实际通过全新建库、v25→v26、重复迁移和 Repository 幂等/约束集成测试;测试使用临时 `_test` 库,未连接或修改生产库。 - **没验证到的部分**:无。HTTP Handler、AI 调用和 Client 真机行为属于 #255~#258, 不在本工单范围。 ## 相关提交 - `1241465` 定义运行时规格解析契约与审计模型