# V1 测试、验收、发布准备与使用手册计划 本文档定义开发开始后必须如何验证 SmartFarm V1,以及上线前需要交付哪些操作说明。它是 G5 评审材料,不代表当前已经执行测试、部署或数据库变更。 ## 一、目标与边界 ### 1. 目标 - 每条 P0 业务决策都能对应到可执行测试。 - 每个金额、库存和状态变化都能从输入追溯到流水和最终结果。 - 重复请求、重复回调、任务重跑和并发不会重复扣库存或重复入账。 - 平台、商户、用户和服务人员不能越权访问数据。 - 智慧农业新增能力不破坏 CRMEB 普通商城与原营销活动。 - 上线、停用新入口和应用回退有明确操作顺序。 - 管理员、商户、现场人员、客服和用户都有可按步骤执行的说明。 ### 2. 暂不纳入 - 法律协议定稿。 - 税务处理和会计科目。 - 发票、税率和纳税申报。 - 生产环境真实资金清结算资质。 - IoT 真实设备协议和海量设备压测。 上述内容后置,不影响本轮对支付、退款、库存、业务账本和技术对账事实的验证。 ## 二、测试分层 | 层级 | 目标 | 主要执行者 | 阻断级别 | | --- | --- | --- | --- | | 规则单元测试 | 公式、状态迁移、截止时间和尾差 | 后端开发 | P0 | | Repository/DAO 测试 | 查询范围、锁、唯一键和汇总 | 后端开发 | P0 | | API 契约测试 | 请求、响应、错误码、权限和幂等 | 后端/测试 | P0 | | 事件与任务测试 | 重试、重复消费、游标和修复任务 | 后端/测试 | P0 | | 前端组件测试 | 表单校验、按钮状态、格式化和空错状态 | 各前端开发 | P1 | | 跨端集成测试 | 支付、去向、二次零售、结算和农业履约 | 测试/产品 | P0 | | CRMEB 回归测试 | 普通商城、营销、售后和财务不回退 | 测试 | P0 | | 性能与并发测试 | 抢购、库存、定时任务和批次结算 | 后端/运维/测试 | P0/P1 | | UAT 业务验收 | 角色是否能按真实流程完成工作 | 产品/运营/商户代表 | P0 | | 发布演练 | 升级、停用、恢复、数据校验和告警 | 后端/运维/测试 | P0 | 当前后端没有现成 PHPUnit 依赖,前端自动化覆盖也很有限。V1 冻结为:后端仓库增加 PHPUnit 单元/集成测试;三个 Vue 管理端复用现有 Jest 依赖做组件测试;新建独立 `smartfarm_qa` 工作区承载 API、四端 Playwright E2E 和 k6 性能脚本;uni-app 采用 H5 自动化加微信小程序真机验收。测试依赖只在开发任务中安装,本立项阶段不修改业务代码或依赖锁文件。 ## 三、需求到测试追踪 每条测试用例必须包含: | 字段 | 说明 | | --- | --- | | `case_id` | 稳定编号,如 `CW-ORDER-001` | | 决策依据 | `12` 中的业务决策编号 | | 页面 | `17`、`18` 中的页面编号 | | API | `19` 中的方法与路径 | | 数据对象 | `15` 中的表与关键字段 | | 事件/任务 | `20` 中的事件或任务 | | 前置数据 | 用户、商户、活动、批次、库存和时间 | | 操作步骤 | Given/When | | 预期界面 | 用户可见结果 | | 预期数据 | Then:状态、数量、金额、流水和事件 | | 幂等结果 | 同操作重放后的结果 | | 清理方式 | 测试数据隔离或重建方式 | | 优先级 | P0、P1、P2 | | 自动化状态 | 未实现、已实现、暂手工 | P0 决策没有测试编号时,G5 不得通过。 ## 四、测试环境与固定数据 ### 1. 环境建议 | 环境 | 用途 | 数据原则 | | --- | --- | --- | | `DEV` | 开发自测 | 可重建、允许模拟支付 | | `SIT` | API 与跨端集成 | 固定种子数据、第三方沙箱 | | `UAT` | 产品和业务验收 | 接近真实配置,禁止开发随意改数据 | | `PRE` | 发布演练 | 与生产拓扑和配置项结构一致 | | `PROD` | 正式环境 | 禁止使用测试账号和模拟回调 | ### 2. 固定角色账号 - 平台超级管理员。 - 云仓运营。 - 农业运营。 - 仓储履约。 - 财务操作员。 - 财务审核员。 - 平台客服。 - 商户 A 管理员。 - 商户 B 管理员。 - 农场 A 现场人员。 - 农场 B 现场人员。 - 普通用户 U1、U2、U3。 账号名称、密码和第三方密钥不写入 Git 文档;由环境密钥或测试账号清单管理。 ### 3. 固定商品与批次 | 编号 | 场景 | | --- | --- | | `SKU-CLOUD-A` | 平台自营,支持邮寄/自提/代销,批次结束统一结算 | | `SKU-CLOUD-B` | 商户供货,只支持邮寄/代销,分期收益、批次结束返本金 | | `SKU-CLOUD-C` | 商户供货,只支持代销,分期本金和收益 | | `SKU-CLOUD-D` | 活动未抢完,用于退回来源库存 | | `SKU-CLOUD-E` | 到期未售完,用于 100%/90%/80%/自定义回购 | | `SKU-NORMAL-A` | 未映射普通 SKU,用于零影响回归 | | `SKU-MAPPED-A` | `cloud_only` 二次销售映射 SKU | ### 4. 固定农业数据 - 农场 A、农场 B。 - 每个农场至少两个区域。 - 整块地块、可按面积份额地块各一组。 - 牛/羊单体资产各两只。 - 鸡/蜂批次份额各一组。 - 正常产出、减产、绝收、死亡/替换各一个生产场景。 - 一次性交付和分批交付套餐各一个。 ### 5. 默认参数边界测试 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `DEFAULT-001` | 云仓活动 SKU 未覆盖结算模式 | 使用“分期本金和收益”,发布快照一致 | | `DEFAULT-002` | 去向期限未覆盖 | 活动结束后第 3 个自然日 `23:59:59` 截止 | | `DEFAULT-003` | 自提期限未覆盖 | 去向最终确认后第 7 个自然日 `23:59:59` 截止 | | `DEFAULT-004` | 到期在途宽限未覆盖 | 到期后第 30 个自然日 `23:59:59` 建立所有权切换点 | | `DEFAULT-005` | 回购比例未覆盖 | 使用 100%,历史快照不受后改影响 | | `DEFAULT-006` | 售后观察期未覆盖 | 使用 T+7;只接受 T+0/3/7/15/30/手动 | | `DEFAULT-007` | 供货结单定时运行 | 每日 `02:00 Asia/Shanghai` 只归集上一自然日 | | `DEFAULT-008` | 自动入账边界 | 10,000/50,000/100,000 元等于阈值可自动,超过 0.01 元转审核 | | `DEFAULT-009` | 地块/资产临时预留 | 创建后 5 分钟内可确认,边界后确认与过期任务只能一方成功 | | `DEFAULT-010` | 农业套餐未选择延期容忍期 | 表单默认 7 天,只接受 0/3/7/15/30 | | `DEFAULT-011` | 农业异常用户未响应 | 第 3 个自然日结束触发快照中的等价接受或拒绝退款策略 | ## 五、云仓活动与供货测试 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `CW-ACT-001` | 创建活动草稿 | 不占用已验收库存,不产生批次 | | `CW-ACT-002` | 添加多个活动 SKU | 每个 SKU 有独立规则和稳定 `activity_sku_id` | | `CW-ACT-003` | SKU 分成不等于 10000 基点 | 发布失败,返回明确错误码 | | `CW-ACT-004` | 活动数量超过已验收量 | 发布失败,不产生部分占用 | | `CW-ACT-005` | 来源商品编辑并重建规格 | 已发布活动、订单和批次历史关联不变 | | `CW-ACT-006` | 映射 SKU 同时有普通库存 | 发布失败,V1 只允许 `cloud_only` | | `CW-ACT-007` | 发布成功 | 供货量占用、活动 SKU 和批次草稿闭合 | | `CW-ACT-008` | 发布后修改周期/分成 | 禁止覆盖历史规则,只能关闭或新建版本 | | `CW-ACT-009A` | 平台自营活动未抢完 | 释放活动占用并以内部流水转回平台来源可用库存 | | `CW-ACT-009B` | 商户供货活动未抢完且实物仍在平台仓 | 先进入 `return_pending`,完成实物退回后才恢复商户来源可用库存 | | `CW-ACT-009C` | 商户同意平台承接未抢完实物 | 转为 `platform_acquired_qty` 和平台库存流水,不再恢复商户可售数量 | | `CW-ACT-010` | 草稿来源商品保存后规格值行重建、规格文本未变 | 可重新匹配当前规格,但 `activity_sku_id` 不变 | | `CW-ACT-011` | 草稿来源规格改名或删除 | 状态为 `changed`/`missing`,发布失败 | | `CW-ACT-012` | 草稿来源出现多个候选规格 | 状态为 `ambiguous`,系统不静默选择 | | `CW-ACT-013` | 已发布来源商品删除或复制 | 仅产生来源告警,活动快照、订单、批次、持仓和账本不变 | | `CW-ACT-014` | 新活动重新选择来源规格 | 生成新的 `activity_sku_id`,不得复用历史身份 | | `CW-SUP-001` | 商户提交供货 | 只能选择本商户商品和 SKU | | `CW-SUP-002` | 其他商户读取供货详情 | 返回无权限/不存在,不泄露数据 | | `CW-SUP-003` | 部分验收 | 验收量、可分配量和拒收量可解释 | | `CW-SUP-004` | 已分配供货再降低验收量 | 拒绝操作或进入受控异常流程 | | `CW-SUP-005` | 一张申请提交多个来源 SKU | 后端拒绝;前端多选只建立多张独立草稿 | | `CW-SUP-006` | 仅保存/提交申请 | 不改来源商品库存,不产生云仓可用库存 | | `CW-SUP-007` | 平台审核通过 | 并发锁定后建立来源预留;重复审核幂等,超库存仅一方成功 | | `CW-SUP-008` | 送达数量少于审批数量 | 短缺、送达和来源预留余量均有流水并满足守恒 | | `CW-SUP-009` | 验收含合格、待检和拒收 | 三类数量之和等于送达,只有合格验收量可转云仓库存 | | `CW-SUP-010` | 申请取消或未交付释放 | 只释放剩余来源预留,不影响已验收/已分配数量 | | `CW-SUP-011` | 实物退回商户 | `return_pending → returned` 可追踪;活动占用中的数量不得直接退回 | | `CW-SUP-012` | 剩余商品由平台承接 | 转为 `platform_acquired_qty` 并有来源/目标库存流水,不与退回重复 | | `CW-SUP-013` | 平台确认责任方后商品资料变化 | 历史供货、订单和履约继续使用运费/质量/售后责任快照 | | `CW-SUP-014` | 商户尝试执行非责任动作 | 前端无按钮,直接请求仍返回权限/责任拒绝并留审计 | | `CW-SUP-015` | 任一供货状态查询 | 数量守恒式成立;不成立时阻断高风险动作并进入对账异常 | | `CW-SUP-016` | 平台从自营商品创建供货 | 仅允许商品所属商户 `is_trader=1`;供货保存为 `source_type=platform, merchant_id=NULL`,商品、SKU、来源预占、库存流水和 Outbox 同事务 | | `CW-SUP-017` | 平台选择普通商户商品冒充自营供货 | 服务端按商品所属商户重查并拒绝,不因前端参数或管理员身份绕过 | | `CW-SUP-018` | 供货余量进入实物退回 | 独立退回记录保存数量、承运、证据和状态;只有完成交接后才增加 `returned_qty` 并恢复来源库存 | | `CW-SUP-019` | 平台承接预案创建 | 只写 `pending_review` 承接记录和计算快照,不改平台库存、供货汇总或商户来源库存 | | `CW-SUP-020` | 承接制单人尝试自行复核 | 因职责分离被拒绝;复核不得修改数量、价格或目标自营 SKU | | `CW-SUP-021` | 已批准承接并发执行和重复执行 | 只有一次增加平台库存和 `platform_acquired_qty`,重复请求返回同一结果 | | `CW-SUP-022` | 同一剩余数量同时执行退回和平台承接 | 统一锁供货与处置记录后只有一方占用数量成功,`returned_qty + platform_acquired_qty` 不超过可处置量 | | `CW-MFUL-001` | 商户更正未锁定物流信息 | 当前商户必须是履约责任方;保留前后快照和审计,不修改已出库数量 | | `CW-MFUL-002` | 商户补充打包、质量或交接证据 | 只追加不可变附件关联,不能覆盖历史证据或引用其他商户附件 | | `CW-MFUL-003` | 商户验收售后退回实物 | 实收、短少、质量结果和证据闭合;差异进入统一异常,未验收数量不得直接恢复可售 | | `CW-MFUL-004` | 商户上报无法履约/短缺/质量异常 | 自动绑定履约和责任快照,创建统一异常;不得由商户提交冻结金额、影响库存或处理结果 | ## 六、首次抢购、支付与去向测试 ### 1. 抢购与支付 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `CW-ORDER-001` | 抢购校验后创建订单 | 一单一活动 SKU,保存规则快照 | | `CW-ORDER-002` | 超限购买 | 下单前失败,不扣库存 | | `CW-ORDER-003` | 多用户抢最后库存 | 成功数量不超过活动库存 | | `CW-ORDER-004` | 相同 `request_id` 重复创建 | 返回首次订单,不重复扣库存 | | `CW-ORDER-005` | 相同键不同数量 | 返回幂等冲突 | | `CW-ORDER-006` | 创建后未付款超时 | 释放活动抢购锁定,不增加 CRMEB 来源商品库存 | | `CW-ORDER-007` | 支付回调重复 10 次 | 只确认一次付款和库存流水 | | `CW-ORDER-008` | 支付 SDK 成功但后端未确认 | 前端轮询,不提前显示已付款 | | `CW-ORDER-009` | 付款后继续抢购 | 不强制立即选择去向 | | `CW-ORDER-010` | 活动结束后从订单中心选择 | 截止前仍可选择 | | `CW-ORDER-011` | 首次订单创建中任一步失败 | CRMEB 订单三层记录、绑定、首次明细、库存流水和幂等结果全部回滚 | | `CW-ORDER-012` | 首次订单支付成功 | 不增加普通商户锁定款,不生成普通订单净收入或分销佣金 | | `CW-ORDER-013` | 核心已支付但异步消费者暂时失败 | 付款事实与 Outbox 已原子保存,重试后补齐状态 | | `CW-ORDER-014` | 普通商城订单支付 | 财务、商户锁定款、通知和履约与改造前一致 | ### 2. 单一去向 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `CW-DISP-001` | 整条明细选择代销 | 全量创建一条持仓,不接受数量 | | `CW-DISP-002` | 整条明细选择自提 | 全量创建自提履约和核销凭证 | | `CW-DISP-003` | 包邮商品选择邮寄 | 全量创建邮寄履约 | | `CW-DISP-004` | 非包邮商品选择邮寄 | 先创建运费单,去向仍未最终冻结 | | `CW-DISP-005` | 补运费成功 | 校验金额/交易号/选择版本,固化地址后将邮寄设为最终去向 | | `CW-DISP-006` | 补运费超时且仍在选择期 | 返回待选择,可重新选 | | `CW-DISP-007` | 补运费超时且已过选择期 | 自动代销且不重复创建持仓 | | `CW-DISP-008` | 同时提交两种去向 | 后端拒绝 | | `CW-DISP-009` | 两设备并发选择不同去向 | 仅一个按版本成功,另一个返回冲突 | | `CW-DISP-010` | 选择截止未操作 | 自动代销,并发送一次通知 | | `CW-DISP-011` | 自提逾期 | 进入异常处理,不直接改为代销 | ### 3. 补运费支付与退款 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `CW-FREIGHT-001` | 微信公众号/H5 补运费 | 使用独立运费单,成功后只确认一次邮寄 | | `CW-FREIGHT-002` | 微信小程序补运费 | `attach=farm_cloud_freight` 正确分发,支付结果以本地状态为准 | | `CW-FREIGHT-003` | 支付宝 H5/App 补运费 | 验签后的 `total_amount`、`trade_no` 可校验并落库 | | `CW-FREIGHT-004` | 余额补运费与普通余额消费并发 | 行锁后余额、两条账单和最终余额均正确 | | `CW-FREIGHT-005` | 客户端篡改金额、订单号或用户 | 服务端忽略/拒绝,只使用本地运费单参数 | | `CW-FREIGHT-006` | 重放同一成功回调 10 次 | 只记录一次支付、一个最终去向和一张履约单 | | `CW-FREIGHT-007` | 同订单号回调金额不一致 | 不确认邮寄,进入异常并告警 | | `CW-FREIGHT-008` | 同订单号出现不同渠道交易号 | 不覆盖首次事实,进入人工复核 | | `CW-FREIGHT-009` | 支付成功事务中履约创建失败 | 整个本地事务回滚,渠道重试可恢复 | | `CW-FREIGHT-010` | 超时任务与成功回调并发 | 锁内只形成邮寄或超时结果之一 | | `CW-FREIGHT-011` | 自动代销后收到迟到支付 | 不改去向,自动创建一次全额退款 | | `CW-FREIGHT-012` | 切换支付渠道 | 旧未支付单取消,新渠道使用新运费单号 | | `CW-FREIGHT-013` | 在线退款任务重复执行 | 始终复用同一退款单号,不重复退款 | | `CW-FREIGHT-014` | 余额运费退款 | 余额增加、用户账单和退款状态在同一事务 | | `CW-FREIGHT-015` | 线下/扫码枪/组合支付请求 | V1 明确拒绝且不改变运费单 | | `CW-FREIGHT-016` | 普通商城支付、充值和退款回归 | 原事件、回调、余额和财务行为不变 | | `CW-FREIGHT-017` | 活动支付超时晚于去向截止 | `expired_at` 取去向截止;先关闭运费单再自动代销 | | `CW-FREIGHT-018` | 截止任务和最后毫秒成功回调并发 | 行锁后只产生邮寄或自动代销之一;迟到成功只生成一张退款单 | ### 4. 自提核销与逾期 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `CW-PICKUP-001` | 分两次核销整条自提明细 | 每次生成不可变核销流水,累计量和剩余量闭合 | | `CW-PICKUP-002` | 重复扫码同一核销请求 | 返回首次结果,不重复增加核销量 | | `CW-PICKUP-003` | 部分核销后到期 | 已核销量不变,仅剩余量进入逾期异常 | | `CW-PICKUP-004` | 逾期剩余量改寄/退款/回购 | 一种方案生效,数量不重复;回购只返对应本金、不计收益 | | `CW-PICKUP-005` | 商户分段取得货款资格 | 已核销、改寄收货、异常回购分别计时,退款量不结算 | ### 5. 首次退款 退款入口先按当前去向与履约阶段判定: | 当前阶段 | 退款/售后类型 | 数量规则 | 库存与去向结果 | | --- | --- | --- | --- | | 已付款待选择 | 首次购买退款 | 仅整条明细 | 冻结去向;活动仍进行则回活动库存,否则按来源归还规则处理 | | 已选代销、批次未冻结 | 首次购买退款 | 仅整条明细 | 撤销待入批次持仓,再按活动状态归还库存 | | 代销批次已开始 | 云仓经营异常 | 不走普通首次退款 | 暂停/冻结、回购、补偿或受控冲正 | | 邮寄已确认 | CRMEB 实物售后 | 可按实际未履约/退回数量部分申请 | 不允许改回待选择;按履约责任和实物退回结果恢复库存 | | 自提已确认 | CRMEB 实物售后 | 可按未核销/实际退回数量部分申请 | 核销数量不可重复退款,逾期先走协同异常 | | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `CW-REFUND-001` | 待选择或代销待入批次整条退款 | 允许,全量释放有效数量 | | `CW-REFUND-002` | 首次购买窗口申请部分数量退款 | 拒绝,不影响邮寄/自提实物售后的部分数量能力 | | `CW-REFUND-003` | 代销批次开始后普通首次退款 | 返回窗口关闭并给出当前可用异常入口 | | `CW-REFUND-004` | 退款完成事件重复 | 库存、账本和商户扣减只处理一次 | | `CW-REFUND-005` | 邮寄未发货且平台责任取消整个履约 | 商品售后和独立运费全额退款分别可对账 | | `CW-REFUND-006` | 首次退款申请后用户选去向 | 选择被冻结,拒绝或取消退款后恢复 | | `CW-REFUND-007` | 首次资金退款完成 | 不恢复 CRMEB 来源商品库存,不扣普通商户锁定款 | | `CW-REFUND-008` | 退款拒绝和用户取消两条路径 | 均解冻云仓业务状态且不重复写流水 | | `CW-REFUND-009` | 邮寄已发货后申请部分商品售后 | 按实际退回数量处理商品,不因用户改选自动退原运费 | | `CW-REFUND-010` | 自提部分核销后申请售后 | 已核销与未核销数量分别校验,不得超量退款或重复恢复库存 | | `CW-REFUND-011` | 活动结束后的首次退款完成 | 不回已结束活动可抢库存,按平台/商户来源与实物状态进入退回或承接流程 | | `CW-REFUND-012` | 首次退款与批次冻结并发 | 统一锁明细;退款先提交则不建持仓,冻结先提交则拒绝普通首次退款 | ## 七、二次零售、进度与结算测试 ### 1. 二次库存桥接 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `CW-RESALE-001` | 批次开始发布二次库存 | 桥接表、批次流水和 CRMEB 展示库存一致 | | `CW-RESALE-002` | 未映射普通 SKU 下单 | 不访问/不改变云仓库存 | | `CW-RESALE-003` | 映射 SKU 下单 | 普通订单事务内锁定云仓批次 | | `CW-RESALE-004` | 一个明细跨两个批次 | 按到期时间、批次 ID FEFO 拆分 | | `CW-RESALE-005` | 普通订单创建失败 | 分配和云仓库存全部回滚 | | `CW-RESALE-006` | 普通订单未支付关闭 | 分配释放,重新可售 | | `CW-RESALE-007` | 支付成功 | 推进原分配,不重新选择批次 | | `CW-RESALE-008` | 批次暂停 | 不再分配新订单,既有订单保持可追踪 | | `CW-RESALE-009` | 普通商品与 `cloud_only` 商品进入同一 `group_order_id` | 确认订单阶段拆成不同订单组或明确拒绝,不产生混合财务策略订单组 | | `CW-RESALE-010` | `cloud_only` 通过代客/积分/其他营销入口下单 | 入口校验拒绝,不绕过 FEFO 分配 | | `CW-RESALE-011` | 二次订单支付成功 | 不增加普通商户锁定款,实付与平台券补贴写入云仓销售快照 | | `CW-RESALE-012` | 二次退款完成 | CRMEB 镜像库存和云仓分配同时恢复,不扣普通商户锁定款 | ### 2. 有效代销售出 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `CW-EFF-001` | 二次订单已付款未完成 | 不增加有效进度 | | `CW-EFF-002` | 完成但未过观察期 | 进入观察状态,不结算 | | `CW-EFF-003` | 完成且过观察期 | 转有效销售、更新进度和节点 | | `CW-EFF-004` | 观察期内退款 | 释放/冲减,不产生有效销售 | | `CW-EFF-005` | 生效后异常退款 | 冻结未结账本并生成反向业务账本 | | `CW-EFF-006` | 使用平台券 | 平台补贴加回确认收入,不冲减用户利润基数 | | `CW-EFF-007` | 二次售价低于用户单位本金 | 差额由平台承担,不产生用户负收益 | | `CW-EFF-008` | 修复任务重复扫描 | 不重复增加有效销售 | | `CW-COST-001` | 发生仓储、物流、营销或运营成本 | 成本进入平台经营核算,不从用户本金/收益账本扣除 | | `CW-COST-002` | 平台成本高于平台分成 | 用户应得计算不变,平台侧可出现经营亏损但用户账本不为负 | | `CW-COST-003` | 查看账本计算快照 | 明确区分确认收入、已售本金、用户利润池和平台承担成本说明 | ### 3. 三种结算模式 固定示例: ```text 批次代销数量:100 件 用户 U1 持仓:40 件 用户 U2 持仓:35 件 用户 U3 持仓:25 件 用户单位本金:10.00 元 累计有效二次销售收入:按用例输入 累计已售本金:10.00 × 累计有效售出数量 累计可分配利润:max(确认收入 - 已售本金, 0) 用户利润池:累计可分配利润 × 用户利润分成率 每次应结金额:累计应得 - 历史已结 ``` 数字验收统一补充以下参数: ```text 二次有效销售单价:14.00 元/件 用户收益分成率:70% 平台收益分成率:30% 本例不含二次退款,平台券补贴已加回确认收入 ``` | 真实进度 | 有效售出 | 确认收入 | 已售本金 | 可分配利润 | 用户利润池 | | ---: | ---: | ---: | ---: | ---: | ---: | | `25%` | 25 | 350.00 | 250.00 | 100.00 | 70.00 | | `50%` | 50 | 700.00 | 500.00 | 200.00 | 140.00 | | `75%` | 75 | 1,050.00 | 750.00 | 300.00 | 210.00 | | `100%` | 100 | 1,400.00 | 1,000.00 | 400.00 | 280.00 | 各用户在四个节点的累计应得: | 用户 | 持仓占比 | `25%` 本金/收益 | `50%` 本金/收益 | `75%` 本金/收益 | `100%` 本金/收益 | | --- | ---: | ---: | ---: | ---: | ---: | | U1 | 40% | 100.00 / 28.00 | 200.00 / 56.00 | 300.00 / 84.00 | 400.00 / 112.00 | | U2 | 35% | 87.50 / 24.50 | 175.00 / 49.00 | 262.50 / 73.50 | 350.00 / 98.00 | | U3 | 25% | 62.50 / 17.50 | 125.00 / 35.00 | 187.50 / 52.50 | 250.00 / 70.00 | 模式 A 在 `25/50/75%` 的实际入账均为 `0`,`100%` 时 U1/U2/U3 分别一次入账 `512.00/448.00/320.00`。模式 B 每个节点只入账新增收益;`100%` 时再分别返还本金 `400.00/350.00/250.00`。模式 C 每个节点入账新增本金和新增收益,四次累计结果仍必须分别等于 `512.00/448.00/320.00`。 跨节点固定算例: ```text 进度从 20% 一次跳到 80% 最高触发节点:75% 真实计算进度:80% 跨越节点:[25, 50, 75] 80% 时批次累计:确认收入 1,120.00,已售本金 800.00, 可分配利润 320.00,用户利润池 224.00 ``` 模式 B 本次只生成一组 `milestone=75` 收益账本,U1/U2/U3 累计收益分别为 `89.60/78.40/56.00`;模式 C 本次本金加收益分别为 `409.60/358.40/256.00`。进度随后从 `80%` 到 `99%` 不再入账,到 `100%` 或到期时再结累计差额。 到期滞销固定算例: ```text 到期真实进度:70% 回购比例:90% 用户利润池:196.00 未售本金:300.00 回购款:270.00 ``` | 用户 | 已售本金 | 累计收益 | 未售回购款 | 最终累计应得 | | --- | ---: | ---: | ---: | ---: | | U1 | 280.00 | 78.40 | 108.00 | 466.40 | | U2 | 245.00 | 68.60 | 94.50 | 408.10 | | U3 | 175.00 | 49.00 | 67.50 | 291.50 | 三种模式最终累计应得必须相同,只允许入账时间不同。上述数字同时用于 API 示例、账本重算、对账导出和回归断言,禁止各模块自行另设算例。 批次实体未售量仍为整数 `30` 件。U1/U2/U3 的未售经济等价数量分别为 `12.000000/10.500000/7.500000`,只用于金额分摊和展示;平台库存实际转入仍为 30 件。金额必须先形成批次回购池 `270.00`,再按持仓占比和最大余数法分配,不能把经济等价数量四舍五入为实物件数。 #### 批次结束统一结算 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `CW-SET-F-001` | 25/50/75% 节点 | 只记进度,不返本金和收益 | | `CW-SET-F-002` | 100% 有效售完 | 一次返累计本金和收益 | | `CW-SET-F-003` | 到期未售完 | 已售部分与回购款一起最终结算 | #### 分期收益、批次结束返本金 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `CW-SET-PF-001` | 跨过 25% | 仅结累计收益差额 | | `CW-SET-PF-002` | 一次从 20% 到 80% | 只生成一组最高节点 `75` 账本,按真实 `80%` 累计值计算,并记录已跨 25/50/75 | | `CW-SET-PF-003` | 到期 | 返全部允许本金、最后收益和回购款 | #### 分期本金和收益 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `CW-SET-PP-001` | 25% 节点 | 返已售本金差额和收益差额 | | `CW-SET-PP-002` | 50/75/100% | 每次只结累计应得减历史已结 | | `CW-SET-PP-003` | 多用户小数尾差 | 中间高精度,最终尾差落到确定持仓 | 所有模式都必须额外验证: - 节点任务重跑。 - 账本入账任务重跑。 - 回购与最后一笔二次订单生效并发。 - 批次冻结时不入账。 - 用户累计结算金额不可为负。 - 历史账本不改金额,差异通过冲正/补记。 - 已结到 75% 后发生 5 件有效退款、真实进度回到 70% 时,按模式验证以下差额;原账本保持不变,不直接扣用户可用余额: | 模式 | 批次累计差额 | U1/U2/U3 差额 | 处理 | | --- | ---: | --- | --- | | A 统一结算 | `0.00` | `0/0/0` | 尚未正式入账,只降低预计金额 | | B 分期收益 | `-14.00` | `-5.60/-4.90/-3.50` | 先冻结未入账收益,再抵扣同持仓未来应付 | | C 分期本息 | `-64.00` | `-25.60/-22.40/-16.00` | 同时处理本金和收益的未入账/未来应付 | 最终仍无法抵扣的正常售后差额记平台风险承担,不得形成用户负账;只有审核确认的错误入账使用独立冲正。 #### 实物数量与等价权益精度 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `CW-EQTY-001` | 3 个持仓按比例承接 7 件有效销售 | 批次实物分配和库存流水始终为整数,持仓已售等价数量允许 `decimal(20,6)` | | `CW-EQTY-002` | 小数等价数量计算批次金额池 | 先按批次汇总确认销售、本金和利润池,不逐持仓独立四舍五入 | | `CW-EQTY-003` | 批次金额池分到多持仓出现分币尾差 | 使用最大余数法;余数相同时按 `holding_id` 升序,明细分值之和严格等于金额池 | | `CW-EQTY-004` | 退款、回购和节点任务交错重算 | `sold_equivalent_qty + unsold_equivalent_qty` 与有效持仓数量守恒,历史正式账本不被覆盖 | ### 4. 到期回购 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `CW-BUY-001` | 100% 回购 | 未售本金全额回购 | | `CW-BUY-002` | 90%/80% 回购 | 按规则快照计算 | | `CW-BUY-003` | 自定义比例 | 仅允许合法基点,历史规则不受后改影响 | | `CW-BUY-004` | 到期仍有未完成二次订单 | 停止新分配,等待释放/生效后再最终回购 | | `CW-BUY-005` | 回购执行重复 | 只生成一组回购、库存和账本 | | `CW-BUY-006` | 回购与退款并发 | 数量不超未售持仓,差异进入冻结 | | `CW-BUY-007` | 默认 30 天在途处置宽限 | 到截止点建立一次所有权切换,未生效在途由平台承接 | | `CW-BUY-008` | 宽限截止后极晚二次退款 | 退款风险归平台,不减少用户已审核回购或追扣余额 | | `CW-BUY-009` | 回购审核驳回 | 只能修正数量事实、关联或计算版本,不改历史单价/比例快照 | | `CW-BUY-010` | 回购事务任一步失败 | 用户账本、平台库存、所有权和 Outbox 全回滚,保持原键可重试 | | `CW-BUY-011` | 多持仓分钱尾差 | 最大余数法,余数相同按 `holding_id` 升序,明细合计等于批次池 | ### 5. 用户余额正式入账 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `CW-POST-001` | 同一账本并发执行十次 | 一条成功 `financial_posting`、一条 `UserBill`、余额只增加一次 | | `CW-POST-002` | 任务成功后重复投递 | 返回首次 `posting_id/bill_id`,不新增记录 | | `CW-POST-003` | 写余额后模拟账单创建失败 | 整个事务回滚,账本仍可用原键重试 | | `CW-POST-004` | 与用户充值并发 | 充值和云仓金额都保留,末余额正确 | | `CW-POST-005` | 与余额支付并发 | 行锁串行处理,不超扣、不覆盖入账 | | `CW-POST-006` | 单笔或日累计超过自动阈值 | 转人工复核,应结金额不变 | | `CW-POST-007` | 规则差异、退款或冻结未清 | 不创建正式入账,返回明确待办原因 | | `CW-POST-008` | 审核确认错误入账且余额充足 | 新建反向入账与减款流水,原成功记录不变 | | `CW-POST-009` | 借方冲正且余额不足 | 不部分扣款、不为负,进入待追偿并冻结后续农业入账 | | `CW-POST-010` | 金额接近扩容字段上限 | 显式容量校验,不依赖数据库截断或警告 | ## 八、商户供货货款测试 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `MER-SET-001` | 首次购买、验收、观察期均满足 | 进入商户可结算货款 | | `MER-SET-002` | 首次订单已付但未验收 | 不结算 | | `MER-SET-003` | 首次退款 | 扣减或冲正对应有效数量 | | `MER-SET-004` | 二次零售长期滞销 | 不影响已满足条件的商户货款 | | `MER-SET-005` | 用户选择三种不同去向 | 按各自首次完成时间进入观察期 | | `MER-SET-006` | 重复结算任务 | 不重复生成商户财务记录 | | `MER-SET-007` | 商户 A 查询商户 B | 拒绝且不泄露金额 | | `MER-SET-008` | 每日结单归集 | 只纳入上一自然日已具资格且未入单账本 | | `MER-SET-009` | 结单生成后来源变化 | 原明细不替换,差异进入调整与后续结单 | | `MER-SET-010` | 与普通订单自动解冻并发 | 两笔余额增加都保留,财务流水各自正确 | | `MER-SET-011` | 写商户余额后财务流水失败 | 事务全部回滚,原结单可重试 | | `MER-SET-012` | 结单超过自动阈值 | 转人工复核,不改变账本或结单金额 | | `MER-SET-013` | 全局 `mer_lock_time` 已启用 | 云仓合格货款仍直接入可用余额,不重复冻结 | | `MER-SET-014` | 审核确认错误入账但商户余额不足 | 不产生负余额,冻结后续农业货款并进入待追偿 | ## 九、租地、认养、生产和溯源测试 ### 1. 租地 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `LAND-001` | 购买整块套餐 | 支付后分配唯一有效地块占用 | | `LAND-002` | 面积份额并发分配 | 有效占用面积不超过可租面积 | | `LAND-003` | 作物不在套餐可选范围 | 下单失败 | | `LAND-004` | 套餐发布后修改 | 历史订单保留规则和承诺快照 | | `LAND-005` | 正常采收分批邮寄 | 累计交付、剩余权益和履约闭合 | | `LAND-006` | 减产/绝收 | 按比例分配合格量,再按审核方案补足、延期、替代或未履约退款 | | `LAND-007` | 收成页面 | 只允许邮寄,不显示云仓代销 | | `LAND-ALLOC-001` | 候选查询 | 不占地块容量 | | `LAND-ALLOC-002` | 建立 5 分钟预留 | 预留量计入并发容量,令牌摘要唯一 | | `LAND-ALLOC-003` | 两个订单确认最后容量 | 锁地块并重查,只有一方成功 | | `LAND-ALLOC-004` | 主动释放与过期任务并发 | 只形成一个终态,容量恢复一次且历史保留 | | `LAND-EX-001` | 生产开始后退部分未履约数量 | 使用交付退款基数累计差额,最后一笔吸收分钱尾差 | | `LAND-EX-002` | 完全等价替代且用户 3 天未响应 | 按快照自动接受,方案版本和通知事实保留 | | `LAND-EX-003` | 减量/降级/超容忍期方案未响应 | 默认拒绝并转未履约退款,不自动降低权益 | | `LAND-EX-004` | 多订单共享减产产出 | 按未履约量比例分配,整数余量按最大余数、支付时间和订单 ID 决定 | | `LAND-EX-005` | 超过套餐延期容忍期 | 用户可申请未履约退款,已履约数量不重复退款 | ### 2. 认养 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `ADOPT-001` | 大牲畜单体分配 | 同一有效周期不能重复认养 | | `ADOPT-002` | 小型养殖批次份额 | 分配份额不超批次可用份额 | | `ADOPT-003` | 个体死亡替换 | 保留原资产、异常和替换关系 | | `ADOPT-004` | 产蛋/蜂蜜分批履约 | 承诺、实际产出和累计交付闭合 | | `ADOPT-005` | 产出页面 | 只允许邮寄,不进入用户收益账本 | | `ADOPT-006` | 套餐发布后修改交付/替换规则 | 历史订单继续使用创建时认养套餐和协议快照 | | `ADOPT-ALLOC-001` | 单体候选预览 | 不占用动物资产 | | `ADOPT-ALLOC-002` | 单体 5 分钟预留并发 | 同一资产只有一条有效/预留关系 | | `ADOPT-ALLOC-003` | 批次份额预留并发 | 已分配与预留份额之和不超可分配份额 | | `ADOPT-ALLOC-004` | 替换资产 | 原分配不删除,新关系通过替换链关联 | | `ADOPT-EX-001` | 动物死亡的等价替换 | 物种、品种/性别/日龄体重、健康和交付时间均通过快照校验 | | `ADOPT-EX-002` | 非等价替换未获用户确认 | 不执行替换,3 天后按默认拒绝进入退款 | | `ADOPT-EX-003` | 批次份额减产 | 按有效订单未履约份额比例分配产出与缺口 | | `ADOPT-EX-004` | 农业未履约退款任务重跑 | 累计公式和幂等键保证数量、金额只增加一次 | ### 3. 生产、产出与履约 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `PROD-001` | 现场提交过程记录 | 草稿/待审核时用户不可见 | | `PROD-002` | 审核发布 | 用户时间线出现发布版本 | | `PROD-003` | 修改已发布记录 | 新增版本,不覆盖历史 | | `OUTPUT-001` | 合格产出分配 | 总分配不超合格可用数量 | | `OUTPUT-002` | 不合格批次 | 禁止履约分配 | | `FUL-001` | 分批发货 | 每次履约有独立数量、地址和物流 | | `FUL-002` | 包裹售后 | 只影响对应履约和权益数量 | | `FUL-003` | 平台按生产节奏主动分批 | 新增包裹运费由平台承担,不向用户二次收费 | | `FUL-004` | 包邮套餐多次正常分批 | 全部包裹均为包邮 | | `FUL-005` | 用户主动要求额外拆包/升级配送 | 用户确认独立报价后才生成补运费单 | | `FUL-006` | 分批发货前用户改地址 | 只影响未出库履约,新旧包裹各自保留地址快照 | | `FUL-007` | 同一产出重复执行分配/出库 | 分配、库存流水和包裹均按结果键幂等 | ### 4. 溯源 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `TRACE-001` | 商品扫码 | 返回当前已发布版本 | | `TRACE-002` | 用户从订单进入 | 展示订单真实生产/产出批次 | | `TRACE-003` | 草稿或驳回版本 | 公开接口不可见 | | `TRACE-004` | 撤回当前版本 | 按规则显示上一有效版本或暂停页 | | `TRACE-005` | 越权材料 | 内部材料不进入公开响应 | ### 5. 服务/现场端身份、权限与布局 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `SVC-AUTH-001` | 原客服账号无农业档案登录 | 仍进入 `/kefu/dashboard`,聊天行为不变 | | `SVC-AUTH-002` | 现场账号登录 | 复用 `service` Token,进入 `/kefu/farm/work/tasks` | | `SVC-AUTH-003` | 多工作区账号登录 | 进入工作区选择,默认入口不扩大权限 | | `SVC-AUTH-004` | 已登录后关闭 `is_open` | 下一请求 Token 失效 | | `SVC-AUTH-005` | 已登录后停用 `status` 或标记 `is_del` | 下一请求 Token 失效 | | `SVC-AUTH-006` | 无权限码直接请求路由 | 返回 `SERVICE_PERMISSION_DENIED` | | `SVC-AUTH-007` | 商户客服读取其他商户订单 ID | 按不可见返回,不泄露对象存在 | | `SVC-AUTH-008` | 范围过期或被撤销后提交 | 返回 `SERVICE_SCOPE_CHANGED`/范围拒绝,事务无写入 | | `SVC-AUTH-009` | 任务在填写中被转派 | 草稿保留,提交返回 `SERVICE_TASK_REASSIGNED` | | `SVC-AUTH-010` | 同农场未分配对象 | 仅上级范围明确覆盖时可访问 | | `SVC-AUTH-011` | 查看完整手机号 | 需要 `farm.user.pii.read` 且产生审计日志 | | `SVC-UI-001` | 原客服页 `1280x800` | 原三栏布局和主要操作无回归 | | `SVC-UI-002` | 农业桌面页 `1280x800`/`1440x900` | 无横向溢出、详情抽屉可用 | | `SVC-UI-003` | 移动作业页三种基准视口 | 无文字/按钮重叠,固定操作区不遮挡内容 | | `SVC-SCAN-001` | 摄像头识别有效码 | 后端验签、范围校验后显示对象和动作 | | `SVC-SCAN-002` | 权限拒绝/非 HTTPS/无摄像头 | 可上传图片或手工输入,不阻断作业 | | `SVC-SCAN-003` | 伪造、过期或越界码 | 不跳转对象页,返回稳定错误 | | `SVC-DRAFT-001` | 断网后保存表单 | 当前账号本机草稿完整,未向服务端伪提交 | | `SVC-DRAFT-002` | 切换账号 | 不展示其他账号草稿 | | `SVC-DRAFT-003` | 恢复网络重复提交 | 同一 `request_id` 只生成一条业务记录 | | `SVC-DRAFT-004` | IndexedDB 可保存媒体 Blob | 恢复草稿时字段和媒体完整,未上传媒体状态明确 | | `SVC-DRAFT-005` | 浏览器不支持持久 Blob/存储不足 | 字段不丢失,媒体标记为需重新选择并给出可执行提示 | | `SVC-DRAFT-006` | 恢复时范围、任务或对象版本变化 | 不覆盖服务端事实,阻断提交并允许查看/删除安全草稿 | | `SVC-DRAFT-007` | schema 版本升级 | 可迁移则保留;不可迁移只读展示并提示重建,不静默丢弃 | | `SVC-EVIDENCE-001` | 有上传权限且对象在范围内 | 返回临时附件 ID,绑定业务记录后转正式引用 | | `SVC-EVIDENCE-002` | 有上传权限但对象越界 | 拒绝上传或绑定,不形成任意文件库入口 | | `SVC-EVIDENCE-003` | 上传成功但业务未提交 | 到期清理未绑定附件,不影响已绑定证据 | | `SVC-REVIEW-001` | 审核人查看队列 | 先按对象范围过滤再分页,类型与计数一致 | | `SVC-REVIEW-002` | 审核通过/驳回 | 审核人不能修改原内容;驳回必须有原因和补充要求 | | `SVC-REVIEW-003` | 记录已撤回、已审核或版本变化 | 旧动作被拒绝,刷新后 `allowed_actions` 与真实状态一致 | | `SVC-CONTEXT-001` | 只有有效 Token 调用 `farm/context` | 可读取本人工作区,不因缺少农业业务权限产生循环拒绝 | | `SVC-CONTEXT-002` | 利用 context 返回内容构造越权对象请求 | Repository 重新校验范围并拒绝,客户端摘要不作为授权事实 | ### 6. 地图、坐标与降级 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `MAP-DATA-001` | 农场/自提点只提交经度或纬度 | 保存失败,返回 `MAP_INVALID_COORDINATE` | | `MAP-DATA-002` | 经度、纬度边界和超界值 | 合法边界可保存,超界值拒绝 | | `MAP-DATA-003` | 以 `0,0` 表示未配置 | 拒绝启用,不把默认点位当真实位置 | | `MAP-DATA-004` | 区域/地块无独立坐标 | 正常继承农场公开点位并使用示意图 | | `MAP-DATA-005` | 公开农场详情 | 只返回公开地址、农场点位和 `GCJ02`,不泄露内部地块/栏舍/仓库点位 | | `MAP-DATA-006` | 自提点修改后查看旧凭证 | 旧凭证继续显示确认时的地址、时间、联系方式和点位快照 | | `MAP-API-001` | 地址编码、逆地址和地点建议 | 返回统一 `longitude/latitude/coordinate_system`,不透传腾讯完整响应 | | `MAP-API-002` | 未登录或无地图权限调用后台辅助接口 | 拒绝访问,不形成公共代理 | | `MAP-API-003` | 提交非 `GCJ02` 坐标系 | 返回 `MAP_COORDINATE_SYSTEM_UNSUPPORTED` | | `MAP-API-004` | 腾讯超时、5xx 与配额耗尽 | 分别映射统一错误;只对超时/5xx 重试 1 次 | | `MAP-SEC-001` | 检查 API、日志、导出和前端产物 | 服务端 Key/SK 不出现;客户端仅含受白名单限制的展示 Key | | `MAP-UI-001` | 平台地址搜索、点选和手工坐标 | 三种方式保存同一规范化 GCJ02 点位 | | `MAP-UI-002` | 农场/自提点缺少有效点位 | 可保存草稿,不允许启用或发布 | | `MAP-UI-003` | 用户 H5、微信小程序和 App 打开农场导航 | 使用保存的目标点位,不先请求用户当前位置 | | `MAP-FALLBACK-001` | 用户拒绝定位或地图脚本失败 | 地址、示意图、复制和重试仍可用 | | `MAP-FALLBACK-002` | 服务/现场端地图失败 | 任务、扫码、记录、上传和提交继续可用 | | `MAP-LEGACY-001` | 旧 `/api/lbs/geocoder` 适配整改 | 首页定位、地址选择和附近门店响应保持兼容 | | `MAP-LEGACY-002` | 商户资料保存点位 | `long` 为经度、`lat` 为纬度,前端不再提交 `log` | ### 7. 页面矩阵与低保真验收 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `PAGE-COV-001` | 比对 `17` 与 `30` 页面编号 | 四端均无漏号、重号或额外编号,总计 108 个追踪对象 | | `PAGE-COV-002` | 比对 Figma Frame 与 `30` | 每个 P0 页面至少一个同编号基础 Frame,关键 Flow 有异常子 Frame | | `PAGE-STATE-001` | 桌面列表加载/空/失败/无权限 | 四种状态视觉与动作不同,不把失败或越权伪装为空 | | `PAGE-STATE-002` | 写操作版本冲突/重复提交 | 不覆盖他人结果;幂等命中展示首次成功结果 | | `PAGE-CW-001` | 云仓订单中心全状态 | 待付、确认中、待选、三去向、售后退款、关闭、异常均可区分 | | `PAGE-CW-002` | 支付结果 H5/小程序 | 均以后端查询为准,确认中/延迟回调可稍后从订单中心继续 | | `PAGE-CW-003` | 三种结算模式低保真 | 同一 SKU 只出现一种模式,节点、本金、收益和回购文案一致 | | `PAGE-LA-001` | 租地/认养下单地址与产出地址 | 下单仅为联系人,发货前重新确认地址;页面文案无歧义 | | `PAGE-SVC-001` | 服务移动页三视口 | 固定操作区、安全区、最长字段、键盘弹出均不遮挡内容 | | `PAGE-MAP-001` | 地图正常与降级 Frame | 无 Key/加载失败仍能看地址、示意图和复制/外部导航 | | `PAGE-A11Y-001` | 状态与金额表达 | 不只靠颜色;按钮可聚焦,图标有可访问名称/Tooltip | ### 8. 商品资料、范围、导入、异步操作与定时选择器 | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `MAT-001` | 已批准商品的一份当前必需材料到期 | 锁商品资料和整套当前必需材料后全量重算;`material_status` 与 `supply_eligible` 同事务更新并写审计/Outbox,不只修改单份材料 | | `MAT-002` | 用新材料替换到期材料并重新审核 | 旧材料和历史供货快照保留;只有新材料集审核通过后恢复新供货资格 | | `MAT-003` | 材料 30/7 天预警任务重复扫描 | 每个商品、材料集版本和预警阶段只通知一次;预警本身不提前改变资格 | | `SCOPE-001` | 保存范围时重新启用历史唯一行 | 请求携带当前 `scope_set_version`;更新同一唯一行而非插入重复行,响应列入 `reactivated` | | `SCOPE-002` | 一次范围保存包含新增、重启用、停用和不变项 | 全部有效行获得同一个新的集合版本;完整前后差异写审计,不能按行产生彼此不同版本 | | `SCOPE-003` | 两个管理员并发使用同一旧集合版本保存 | 只有首个成功;第二个返回版本冲突且不得部分写入 | | `ANIMAL-IMPORT-001` | 校验 1000 行动物资产文件 | 生成校验异步操作、字段映射和预览,不写动物资产;1001 行在校验阶段拒绝 | | `ANIMAL-IMPORT-002` | 文件包含格式、重复编号或越界对象 | 错误按行返回并生成受控错误文件;有任一错误时不发确认令牌 | | `ANIMAL-IMPORT-003` | 零错误后确认导入 | 重读同一附件并核对哈希、映射及目标版本;全批次同事务导入并发 `farm.animal.imported` | | `ANIMAL-IMPORT-004` | 校验后文件哈希、目标农场或栏舍版本改变 | 确认失败且整批零落库;需重新校验,不能信任浏览器回传预览行 | | `ASYNC-001` | 查询运行中、成功、部分失败和失败操作 | 四端详情统一返回物理字段;始终满足 `progress_done=success_count+failure_count<=progress_total` | | `ASYNC-002` | 异步结果包含错误文件 | `result_file_id` 只逻辑引用现有附件;响应实时返回短时签名文件,数据库不保存 URL | | `ASYNC-003` | 跨端查询同一 `operation_no` | 平台、商户、用户和服务端均重新校验对象归属/范围;越权按不可见返回,不泄露进度或错误摘要 | | `ATTACH-001` | 平台/商户提交商品、供货或溯源材料 | 只接受当前操作者可见的 `eb_system_attachment.attachment_id`;跨商户、已删除或用途不符的附件拒绝绑定 | | `ATTACH-002` | 把现场证据 ID 作为系统材料 ID 或反向提交 | 按显式 DTO 字段和目标仓库拒绝,不能因两个表恰好存在相同数字主键而串读文件 | | `TIMER-001` | 去向到期和运费到期扫描 | 去向只按订单明细 `paid_pending_choice/delivery_pending_freight + choice_deadline_at`;运费只按 `pay_status + expired_at`,不使用更新时间推断 | | `TIMER-002` | 代销批次启动和二次销售生效扫描 | 批次严格使用 `consign_ready + consign_start_at`;二次销售严格使用 `observing + observation_end_at` | | `TIMER-003` | 自提和异常确认到期扫描 | 自提按自提表 `pending/partially_verified + expires_at`;方案按 `pending_user_confirmation + confirmation_deadline_at` | | `TIMER-004` | 农业履约提醒扫描 | 只扫描 `next_reminder_at` 到期记录,按阶段读取 `address_due_at/receive_due_at`;发送成功后推进下次时间,终态清空 | | `TIMER-005` | 商品/溯源材料失效扫描 | 商品任务按已批准资料关联的当前材料集并以商品去重;溯源任务按 `trace_material.expire_at`,两者不得误用彼此状态或字段 | ## 十、权限、隐私、幂等与并发测试 ### 1. 权限 - 每个后台按钮 route name 都分别测试允许与拒绝。 - 页面可见但无操作权限时按钮隐藏,直接请求仍返回拒绝。 - 平台高风险操作测试申请人与审核人分离。 - 商户所有查询强制当前 `mer_id`。 - 用户只能读取自己的订单、持仓、账本和农业权益。 - 服务人员必须同时通过职责权限码和当前有效 `eb_farm_service_scope`。 - 商户服务人员即使被错误写入跨商户范围,也不能越过 `store_service.mer_id`。 - 授权撤销后,旧 Token 的下一次写请求也必须失败。 - 列表先按范围过滤再分页,详情越界不泄露对象是否存在。 ### 2. 隐私 - 列表默认脱敏手机号、地址和证件/报告敏感信息。 - 导出需独立权限并写审计。 - 事件 payload 不复制完整地址、用户资料和规则 JSON。 - 日志不记录支付密钥、Token、完整身份证件和敏感第三方回执。 ### 3. 幂等与并发 每个 P0 命令至少执行以下组合: 1. 串行重复两次。 2. 同一幂等键并发 10 次。 3. 不同幂等键但同一资源版本并发。 4. 成功后进程超时,客户端再次请求。 5. 事务回滚后重试。 6. 事件重复投递。 7. 任务在游标提交前中断后重跑。 预期结果必须同时检查 API、主表、流水、Outbox、消费日志和最终账本。 逻辑外键与引用完整性: | 用例编号 | 场景 | 核心预期 | | --- | --- | --- | | `DB-REF-001` | 创建引用不存在或已作废主对象的业务记录 | Repository/领域服务在写事务内拒绝,不能依赖数据库自动报外键错误 | | `DB-REF-002` | 主对象状态变化与从对象创建并发 | 按冻结锁顺序串行,最终不出现可用从记录引用不可用主对象 | | `DB-REF-003` | 作废仍被订单、账本或溯源历史引用的主数据 | 只改变可用状态并保留历史,不执行物理删除 | | `DB-REF-004` | 运行升级 `verify_after` 与每日孤儿扫描 | 90 张表的必需引用、唯一键和索引通过;发现孤儿时阻断发布或进入修复队列 | ## 十一、CRMEB 原功能回归 | 模块 | P0 回归 | | --- | --- | | 商品 | 普通商品创建、编辑规格、上下架、库存和分类 | | 普通营销 | 普通秒杀、拼团、预售、助力、积分商品 | | 优惠 | 平台券、商户券、会员价、积分抵扣 | | 购物车 | 加购、改数量、失效商品和多商户拆单 | | 订单 | 确认、创建、支付、取消、发货、核销、收货 | | 售后 | 未发货退款、退货退款、部分退款、平台退款 | | 物流 | 运费模板、包邮、快递、到店自提、电子面单 | | 商户 | 商品、订单、客服、普通账单、提现和权限 | | 用户 | 登录、地址、余额、订单、售后、消息和隐私 | | 客服 | 会话、订单、退款、物流和商品上下文 | 特别要求: - 未映射 SKU 的普通下单行为与性能基线不得明显变化。 - 普通营销活动不得误触发云仓批次分配。 - 云仓 `cloud_only` SKU 被明确禁止进入 V1 不支持的营销活动。 - 普通充值与云仓用户入账并发时不丢更新,普通余额支付和退款结果保持不变。 - 普通商户订单解冻与云仓结单并发时不丢更新,原冻结周期、普通财务流水和提现保持不变。 - 金额字段扩容前后用同一组普通财务样本核对查询、汇总、导出和余额精度。 ## 十二、性能、容量和稳定性 以下是 V1 开发验收的最低工程基线,不替代上线前按真实服务器和运营预测做容量复核: | 场景 | 固定负载 | 通过线 | | --- | --- | --- | | 云仓活动浏览 | 300 虚拟用户、持续 10 分钟 | P95 ≤ 500ms,P99 ≤ 1s,服务错误率 < 0.2% | | 单 SKU 抢购创建 | 100 并发、峰值 100 RPS、持续 5 分钟 | P95 ≤ 800ms,P99 ≤ 1.5s,超卖为 0,重复订单为 0 | | 支付/退款本地适配 | 50 并发回调,同一业务事实重复 10 次 | P95 ≤ 500ms,唯一业务结果为 1 | | 普通二次订单 FEFO | 100 并发、至少 10 个可分配批次 | P95 ≤ 800ms,跨批次数量守恒、死锁可重试且最终无悬挂 | | 批次结算 | 100,000 条持仓、4 个节点 | 单次扫描 200 条;60 分钟内闭合,无重复账本,峰值内存和慢 SQL 有报告 | | Outbox/Job 恢复 | 10,000 条积压,消费者中断后恢复 | 15 分钟内清空,重复消费不产生重复事实,死信可定位 | | 平台/商户大列表 | 100,000 条主记录及关联数据 | 首屏 P95 ≤ 1s,导出异步执行,Web 请求不加载全量 | 压测使用固定 `grafana/k6:2.0.0` 容器镜像;测试环境不得和开发人员本机浏览器负载混用。至少测试: - 同一活动 SKU 并发抢购。 - 普通商城映射 SKU 并发下单和 FEFO 行锁。 - 10 万持仓批次的进度和节点计算方案。 - 定时任务每批 200 条时的游标推进和积压恢复。 - Outbox、通知和账本队列积压。 - 导出大列表时的内存和超时。 - Swoole 常驻进程下请求状态不串用户。 - Redis/PDO 暂时不可用后的重试与告警。 性能验收报告必须记录镜像版本、Git 基线、测试数据规模、并发模型、机器配置、P50/P95/P99、错误率、数据库慢查询、Redis/队列指标和失败样本,不只写“压测通过”。若上线容量预测超过上述基线,必须按预测峰值的 1.5 倍重跑并以较高目标作为发布门。 ## 十三、缺陷等级与发布门 | 等级 | 定义 | 发布处理 | | --- | --- | --- | | `S0` | 资金、库存、数据泄露或不可恢复错误 | 必须修复,停止发布 | | `S1` | P0 主流程不可用、状态错误或越权 | 必须修复,停止发布 | | `S2` | 有替代路径但影响运营效率或部分数据展示 | 评审后决定 | | `S3` | 文案、样式和低风险体验问题 | 可列入后续版本 | G5 通过条件: - S0、S1 为 0。 - 所有 P0 用例通过。 - 重复回调、任务重跑和并发用例通过。 - CRMEB P0 回归通过。 - UAT 角色签字。 - 升级和停用/回退演练通过。 - 监控、告警和操作手册可用。 ## 十四、发布准备与回退 ### 1. 发布前 1. 冻结代码、SQL、菜单、配置和前端版本。 2. 执行备份并记录恢复点。 3. 运行 `verify_before.sql`,确认版本、表前缀、字符集、SQL 模式、金额字段类型和冲突。 4. 先部署兼容旧应用的新表/索引,并扩容现有余额及财务流水金额字段。 5. 写入菜单和配置,但保持智慧农业总开关关闭。 6. 部署后端,再部署各前端。 7. 重启 Swoole/队列/定时任务进程并核对唯一实例。 8. 执行 `verify_after.sql`,核对金额字段类型、入账唯一索引和三项自动审核阈值。 9. 检查腾讯客户端 Key 的域名/小程序白名单、服务端 Key/SK、额度告警和地图降级;扫描前端产物与日志确认没有服务端密钥。 10. 用发布验收账号走最小冒烟流程。 11. 分角色逐步开放入口和活动。 ### 2. 回退原则 - 首选关闭智慧农业入口和新定时任务,不删除数据。 - 应用版本可回退到仍能忽略扩展表的版本。 - 已产生交易后不执行删除业务表的 down SQL。 - 账本异常先冻结,禁止直接改余额。 - 普通订单适配点出现问题时,暂停 `cloud_only` SKU 销售,不影响未映射商品。 - 回退后运行对账,确认普通库存、云仓库存、支付和账本没有悬挂记录。 ### 3. 发布后观察 首日重点监控: - 云仓下单成功率和支付后待选择数量。 - 活动库存与订单锁定差异。 - 普通二次订单未分配数量。 - 支付/退款事件失败。 - 任务积压与失败重试。 - 用户和商户待入账差异。 - 地块、份额和产出容量冲突。 - API P95 和数据库慢查询。 ## 十五、分角色使用手册 每份手册同时提供: - 文档版。 - 关键页面截图版。 - 3 至 10 分钟短视频或动图版。 - 常见问题和错误提示索引。 - 文档版本、适用系统版本和更新时间。 ### 1. 平台管理员手册 目录建议: 1. 角色与权限配置。 2. 腾讯地图客户端/服务端配置、农场与自提点定位和地图异常降级。 3. 农场、区域、地块和栏舍初始化。 4. 作物、品种和套餐维护。 5. 云仓供货审核与验收。 6. 云仓活动、活动 SKU 和发布检查。 7. 云仓订单、待选去向、自提和批次。 8. 二次代销商品映射和销售进度。 9. 到期回购、异常冻结和恢复。 10. 溯源审核、发布和撤回。 11. 补运费迟到支付、退款失败和人工复核。 12. 日常对账、导出和审计查询。 ### 2. 财务操作手册 目录建议: 1. 用户本金、收益和回购账本口径。 2. 三种结算模式示例。 3. 商户供货货款条件。 4. 自动校验和人工审核边界。 5. 冻结、冲正、补记和作废。 6. 对账差异处理。 7. 重复入账排查。 8. 补运费收款、原路退款和差异对账。 9. 月度/活动/批次导出。 ### 3. 商户手册 目录建议: 1. 可供货商品和 SKU 条件。 2. 创建、修改和提交供货申请。 3. 查看审核、验收和活动占用。 4. 查看首次有效购买和退款。 5. 履约/售后责任。 6. 供货货款、结算单和对账。 7. 常见驳回原因。 ### 4. 农场现场人员手册 目录建议: 1. 客服账号登录、工作区选择和手机入口。 2. 我的任务、状态、地点和截止时间。 3. 地址、示意图、地图导航和地图不可用时的降级。 4. 摄像头扫码、图片识别和手工输入降级。 5. 农事/养殖记录录入与本机草稿。 6. 图片、视频、上传失败和补传要求。 7. 采收/产出录入与数量等式。 8. 异常上报、事件编号和补充证据。 9. 分批履约和仓库交接。 10. 调岗、任务转派、数据范围变化和无权限处理。 ### 5. 仓库履约手册 目录建议: 1. 云仓供货验收。 2. 邮寄拣货和发货。 3. 自提核销。 4. 自提点地址、地图导航和地图失败降级。 5. 产出批次入库与出库。 6. 包裹异常、补发和售后。 7. 库存差异上报。 8. 扫码失败、任务转派和离线草稿处理。 ### 6. 客服手册 目录建议: 1. 如何识别普通订单、云仓首次订单和二次零售订单。 2. 支付后为什么可以稍后选。 3. 邮寄、自提和代销的区别。 4. 补运费超时、迟到支付和原路退款状态解释。 5. 首次退款窗口。 6. 批次进度、收益、回购和冻结解释。 7. 租地/认养生产、延期和异常解释。 8. 可处理事项与必须升级平台审核的事项。 9. 隐私和敏感信息规范。 10. 原客服工作台与农业桌面工作区切换。 11. 权限或范围变化后的刷新与升级处理。 ### 7. 用户帮助中心 目录建议: 1. 如何参加集市云仓。 2. 支付后如何继续抢购和稍后选择。 3. 为什么一个订单只能选一种去向。 4. 邮寄补运费、自提和代销。 5. 补运费超时或支付后显示退款中怎么办。 6. 什么是批次进度、有效售出、本金、收益和回购。 7. 三种结算模式如何查看。 8. 首次退款和二次销售售后的区别。 9. 如何租地、认养和查看生产记录。 10. 如何查看农场位置、复制地址和打开导航。 11. 产出如何邮寄。 12. 如何查看溯源报告。 ## 十六、建议的交付文件 正式开发阶段建议建立: ```text smartfarm_docs/ testing/ traceability-matrix.xlsx api-contract-cases.md cloud-warehouse-cases.md agriculture-cases.md crmeb-regression-cases.md performance-plan.md uat-script.md release/ release-checklist.md upgrade-runbook.md rollback-runbook.md monitoring-and-alerts.md manuals/ platform-admin.md finance.md merchant.md field-operator.md warehouse.md customer-service.md user-help.md ``` 测试追踪矩阵可使用 Excel 管理,但业务规则、接口和最终结论仍应有可版本化的 Markdown 基线。 ## 十七、G5 工具与执行基线 | 编号 | 决策 | 冻结方案 | 状态 | | --- | --- | --- | --- | | `G5-001` | 后端测试框架 | `smartfarm` 使用 `phpunit/phpunit:^9.6`;该选择兼容项目声明的 PHP 8.0 下限,实际解析版本由 `composer.lock` 固定 | 已确认 | | `G5-002` | API 自动化 | 新建独立 `smartfarm_qa`,使用 `@playwright/test@1.52.0` 的 `APIRequestContext` 和 `ajv@8.17.1`;凭据只来自环境变量,不绑定 Apifox 或个人账号 | 已确认 | | `G5-003` | Web 管理端 E2E | `smartfarm_qa` 使用 Playwright 1.52.0 覆盖平台、商户、服务 PC 和用户 H5;Node.js 18+,使用 `package-lock.json` | 已确认 | | `G5-004` | 前端组件与 uni-app | 三个 Vue 管理端复用现有 Jest 3.5.3 插件;uni-app 规则优先 API 自动化、H5 用 Playwright,微信小程序关键流程使用开发者工具和真机清单 | 已确认 | | `G5-005` | 支付测试 | DEV/SIT 使用仅非生产可启用的受控支付适配器及固定回调夹具;能用官方沙箱的渠道另跑沙箱;UAT 完成真实小额支付、退款和迟到退款闭环 | 已确认 | | `G5-006` | 性能工具与目标 | 使用固定 `grafana/k6:2.0.0`,按“十二、性能、容量和稳定性”最低基线验收;上线预测更高时按峰值 1.5 倍复核 | 已确认 | | `G5-007` | 教程载体 | Markdown 是唯一版本基线;截图和 3 至 10 分钟视频作为同版本附件,禁止在文档中保存账号密码和第三方密钥 | 已确认 | 开发阶段目录冻结为: ```text smartfarm/tests/ Unit/Farm/ Integration/Farm/ Fixtures/Farm/ bootstrap.php phpunit.xml smartfarm_qa/ package.json package-lock.json playwright.config.ts tests/api/ tests/e2e/admin/ tests/e2e/merchant/ tests/e2e/service/ tests/e2e/user-h5/ tests/contracts/ fixtures/ performance/ scripts/ artifacts// ``` `smartfarm_qa` 是测试工程,不是新的业务使用端。所有测试以命令、环境名、Git SHA、数据库种子版本和证据目录五项共同标识;测试失败证据、Trace、截图和报告不得提交敏感 Token。 ## 十八、关联文档 - [12-v1-business-decisions-and-glossary.md](12-v1-business-decisions-and-glossary) - [13-cloud-warehouse-process-and-state-machines.md](13-cloud-warehouse-process-and-state-machines) - [14-land-adoption-and-traceability-prd.md](14-land-adoption-and-traceability-prd) - [15-v1-data-model-draft.md](15-v1-data-model-draft) - [16-pre-development-design-plan.md](16-pre-development-design-plan) - [18-v1-page-and-prototype-spec.md](18-v1-page-and-prototype-spec) - [19-v1-api-contract-draft.md](19-v1-api-contract-draft) - [20-v1-events-jobs-permissions-notifications.md](20-v1-events-jobs-permissions-notifications) - [31-v1-write-operation-contract-registry.md](31-v1-write-operation-contract-registry) - [32-v1-event-job-operation-registry.md](32-v1-event-job-operation-registry) - [33-v1-state-exception-transaction-matrix.md](33-v1-state-exception-transaction-matrix) - [34-v1-database-field-dictionary.md](34-v1-database-field-dictionary) - [37-v1-api-field-contract-registry.md](37-v1-api-field-contract-registry) - [21-v1-code-change-blueprint.md](21-v1-code-change-blueprint)