9.8 KiB
9.8 KiB
Context
见 proposal.md。scripts/context-health.sh 以集合相等检查主 Specs、requirement-evidence.json 和 entry-capability-requirement-matrix.json;调查确认主 Specs 为 89 条、现有证据为 53 条,缺失 36 条,且没有过期 evidence。矩阵也缺少同一 36 条 Requirement 链接。
Goals / Non-Goals
Goals:
- 仅以当前可达源码、迁移/脚本、配置和已有归档验证记录补齐 36 条的静态可追溯证据。
- 令两个 JSON 文件分别满足脚本的 Requirement 集合双向覆盖;保持 HTTP 路由集和 Asynq 注册入口集不变。
- 每条证据在
entries、handler_consumer_job、application_service_query、domain_state_amount、store_migration_config和verification中只引用实际存在的事实。
Non-Goals:
- 不修改
openspec/specs/**、业务代码、迁移、测试、运行配置、路由或 Worker 注册。 - 不以历史 Change 的完成状态、推测的调用链或无法重现的生产结果替代当前可达证据。
- 不添加、删除或重分类既有 HTTP/async 矩阵入口。
Decisions
1. 固定缺失集合,按 capability 分组补行
以下是唯一可新增的 evidence Requirement 键;实现阶段先由同一解析规则重算,若集合变化则停止并重新调查:
| capability | 缺失 Requirement |
|---|---|
agent-funds-commission |
代理在线充值可用支付方式按支付配置判定 |
agent-open-api |
代理开放接口查询不触发可靠卡观测 |
external-integration |
富友主扫统一下单与订单查询;高频轮询外部交互日志保留边界;外部交互日志归档与留存受控执行;外部交互日志逐日物理留存 |
operations-audit |
轮询审计事实保留边界;审计归档与日留存受控执行;审计在线数据逐日物理留存;日留存独立运行日志;审计调查留存边界连续 |
package-lifecycle |
资产套餐层级投影;套餐历史整组筛选与分页;套餐历史稳定排序;物理缺失与不可展示关系区分;套餐历史读取范围与兼容边界 |
package-queue-activation |
当前主套餐过期后自动激活下一个;孤儿待生效套餐必须公平恢复;Asynq 激活结果必须准确且可重试 |
polling-load-control |
轮询全局并发与背压;正常无变化观测的低写入处理;业务变化与轮询异常仍可追溯 |
polling-operations |
停复机遵循实际生效实名策略 |
qicheng-migration-package-lifecycle-overrides |
迁移配置支持逐卡套餐生命周期覆盖;覆盖必须完整且可验证;未覆盖资产维持严格冲突阻断 |
qicheng-migration-package-usage |
迁移套餐使用记录保存计时条款快照;迁移套餐使用 SQL 满足快照完整性约束 |
refund-approval |
零金额退款审批通过;零金额退款不产生资金回款;非法退款金额仍被拒绝 |
shop-bulk-import |
导入输入必须在生成 SQL 前完成全量预检;生成的 SQL 必须在写入前校验目标库状态;批量导入 SQL 必须创建完整的店铺初始事实;导入执行必须由维护者审核后以单事务手工确认;导入结果必须可审核和重跑定位 |
2. 各类证据的直接来源
| Requirement 分组 | 入口/任务与调用链事实 | 状态、存储、迁移或验证事实 |
|---|---|---|
| 代理充值与富友(前两组中相关 2 条) | internal/handler/admin 与 internal/routes 的代理充值入口;internal/application/agentrecharge/online_creation.go;internal/infrastructure/payment/fuiou_scan.go;pkg/fuiou/scan.go |
model.AgentRechargeRecord.PaymentMethod/PaymentChannel、支付配置完整性、integrationlog.Repository;验证输出只取当前源码可命中的 PreCreate、CommonQuery、trans_stat 映射或既有归档 Change 的可复核命令。 |
| 开放接口本地查询(1 条) | 四个 GET 入口:/api/open/v1/cards/traffic、/cards/status、/cards/realname-status、/devices/traffic;internal/handler/openapi/handler.go → internal/service/agent_open_api/service.go |
对四个读取方法做静态可达性检查:不得引用观测分发/系列调度;只列实际查询的 Store/模型。 |
| 轮询低写入、轮询审计与外部交互(3 条) | 五个已注册 polling Task;internal/task/polling_base.go 和各 polling_*_handler.go;internal/infrastructure/cardobservation/series_runner.go |
internal/application/cardobservation/{apply,network,traffic}.go 的变化审计/Outbox 事实,及 internal/task/polling_integration_log.go 的失败终结事实;正常无变化仅在当前代码存在明确跳过分支时记录,不以 Requirement 文本反推。 |
| 审计/Integration 归档、清理和调查(7 条) | 已注册 constants.TaskTypeAuditDailyArchive、TaskTypeIntegrationDailyArchive、TaskTypeAuditDailyRetention;cmd/worker/main.go 的注册/总开关;internal/task/{integration_archive,audit_monthly_retention}.go |
internal/application/auditarchive/{service,integration,retention}.go;internal/infrastructure/audit/retention.go;pkg/constants/audit_archive.go;cmd/worker/main.go 的 NewRetentionLogger;internal/query/{audit,retention} 的在线边界。 |
| 套餐历史层级(5 条) | GET /api/admin/assets/{identifier}/packages 与 GET /api/c/v1/asset/package-history;后台 Service、H5 Handler 均调用 internal/query/asset/package_history.go |
internal/model/package.go 的 master_usage_id,历史 DTO 的 children/关系状态字段;Query 的整组筛选、排序、物理存在性核对和分页函数。 |
| 套餐队列激活(3 条) | internal/polling/package_activation_handler.go 的定时检查、孤儿恢复、任务投递和 HandlePackageQueueActivation;已注册 constants.TaskTypePackageQueueActivation |
tb_package_usage 的状态条件更新、master_usage_id、优先级排序、Asynq MaxRetry(3) 与 Redis 锁冲突返回错误。 |
| 轮询总并发与背压(1 条) | 五个 polling Handler 均调用 PollingBase.acquireConcurrency;调度器 processOneShard 背压跳过 |
internal/task/polling_base.go 的双计数 Lua、释放/重入队;pkg/config/config.go 的范围校验;pkg/constants/{redis,polling}.go。 |
| 停复机实名策略(1 条) | 自动、设备、手动停复机路径经 internal/service/iot_card/stop_resume_service.go;策略管理路由保持现有入口 |
isRealnameOK 的行业卡、已实名、独立卡和设备策略分支;model.IotCard/Device.RealnamePolicy 与现有套餐/流量判断。 |
| 奇成套餐迁移(5 条) | 离线迁移入口和配置加载:scripts/migration/migrate_runtime.py、lib/mapping_loader.py、lib/sql_builder.py |
package_lifecycle_overrides、tbl_card_life、multiple_active_packages;Step2 INSERT 的 expiry_base_snapshot、calendar_type_snapshot、duration_months_snapshot、duration_days_snapshot。 |
| 零金额退款(3 条) | POST /api/admin/refunds/{id}/approve 与企业微信审批共用的退款决策路径;internal/service/refund/{approval_decision,service}.go |
validateApprovedRefundAmount 拒绝负数;事务更新退款/订单;refundWalletPayment 在 amount == 0 直接返回,不写钱包回款。 |
| 店铺批量导入(5 条) | 离线 CLI scripts/migration/import_shops.py → lib/shop_import.py → lib/shop_sql.py |
CSV/配置预检、SQL 事务起始守卫、店铺/账号/角色/双钱包/审计 SQL、bcrypt 哈希、同批次错误/结果/摘要产物;运行说明明确不连接目标 PostgreSQL。 |
每个 verification 对象保留一条可直接执行的本地只读命令及其确定性源码文字命中;没有稳定文字命中的调用链,使用可重跑的只读脚本/命令输出。不得杜撰隔离库、生产库、外部渠道或已删除临时 smoke 的结果。
3. 两个 JSON 的最小同步策略
requirement-evidence.json:仅追加上述 36 个唯一键各一行;不改动现有 53 行,不新增过期键。entry-capability-requirement-matrix.json:仅把上述 36 个键链接到现有、已注册的 HTTP 或 async 行。HTTP 条目保持与 Specs 提取路由的集合相同;async 条目保持与 Worker 注册常量集合相同。- 对无 HTTP/async 入口的两类离线流程追加
cli行,入口分别为scripts/migration/migrate_runtime.py与scripts/migration/import_shops.py;现有健康脚本只比较http与async的入口集合,cli行仅承担这五条离线 Requirement 的可追溯链接,不改变该比较。不得把离线流程伪挂到 HTTP 或 Asynq 入口。 - 写入前用集合差验证恰为 36;写入后验证 evidence 键集和 matrix 链接集均等于主 Specs 键集,并确认 HTTP/async 入口集合未漂移。
4. 证据选择优先级
当前可达实现与可复跑只读输出优先;migrations/、脚本和显式配置是存储/离线事实;已归档 Change 只用于发现来源和复核历史验证边界,不能单独作为当前实现证据。无证明项保留为缺口并停止,不以相近功能、相同命名或 Spec 场景推断。
Risks / Trade-offs
polling-load-control和低写入类 Requirement 跨五个 Worker Handler;只引用共享基类无法证明每个入口均接入,必须在最终写入前逐 Handler 核对获取、释放和令牌不足重入队。- 离线迁移与店铺导入没有 HTTP/async 注册入口。将其错误塞入现有入口会使矩阵语义失真;是否允许
cli类型是唯一可能需要确认的矩阵契约缺口。 - 历史 Change 中有未完成验证任务。它们不能写成已验证事实;证据只能引用仍在仓库中的源码、脚本、迁移、配置和可重跑只读检查。
- JSON 人工编辑容易造成重复键或遗漏链接;实现阶段必须在写入前后解析并做三集合精确比较,再运行
./scripts/context-health.sh。