# 交易订单管理 — 测试用例

> 依据：`交易订单管理功能需求.md`、`交易订单管理技术方案.md`  
> **接口 Base Path**：`/tradeMarket/tradeOrder`；鉴权与若依一致（Cookie / Token）。

**通用前置（无特殊说明）**：具备本模块权限的账号已登录（`tradeMarket:tradeOrder:list|query|add|edit|remove|pay`）。关联主数据宜预置：

| 类型 | 样本 | 要点 |
| --- | --- | --- |
| 供应商 `biz_supplier` | **S1** `id=1` 扎西合作社 | `del_flag=0`；**按头计费** `fee_method=2`，`fee_standard=10` |
| 供应商 | **S2** `id=2` | `del_flag=0`；**按额计费** `fee_method=3`，`transaction_fee_rate=1`，`management_fee_rate=0.5` |
| 供应商 | **S3** `id=3` | `del_flag=0`；**按户计费** `fee_method=1` |
| 供应商 | **S4** `id=4` | `del_flag=0`；**未配置** `fee_method`（完成支付失败用） |
| 承销商 `biz_distributor` | **D1** `id=1` | `del_flag=0` |
| 市场进栏 `biz_market_entry` | **ME-S1** | `supplier_id=1`；子表耳标 **E101,E102,E103** → **M** |
| 市场进栏 | **ME-S2** | `supplier_id=2`；子表 **E201** |
| 隔离巡查 `biz_isolation_patrol` | **P-SICK** | `del_flag=0`；`sick_dead_qty>0`；耳标 **E102**（**全表 B**） |
| 交易订单（待支付） | **TO-C** | `order_status=0`；明细含 **E103** → 他单占用 **C** |
| 交易订单（待支付） | **TO-EDIT** | `id` 已知；明细 **E101**；供编辑释放 C |
| 交易订单（已完成） | **TO-DONE** | `order_status=1`；已有关联结算单 |

**合法新增 Body 示例**（2 头）：

```json
{
  "supplierId": 1,
  "distributorId": 1,
  "lineList": [
    { "earTag": "E101", "weight": 120.5, "amount": 8000.00 },
    { "earTag": "E104", "weight": 115, "amount": 7500.00 }
  ]
}
```

**可选耳标池速查**

| 场景 | 公式 | 说明 |
| --- | --- | --- |
| `GET /supplierEarTags` | **仅** `biz_market_entry_ear_tag.supplier_id` | 下拉展示用；可含他单已占用耳标 |
| 保存 R3 | **(M − B) − C** | M：有效进栏记录；B/C 服务端校验；编辑时 C 排除本单 |

**界面（UI）测试**：**Playwright** + **Chromium**（`channel: 'chrome'` 使用本机 **Google Chrome**）。组件路径：`tradeMarket/tradeOrder/index`。

**自动化对照**：`com.ruoyi.web.modules.trading.**.*TradeOrder*`、`SettlementOnPayService`（JUnit5 / MockMvc）。

---

## 一、单元测试用例

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| ZCZX-JYDDGL-UT-001 | 订单编号 | 当日首号 | 单元测试 | JUnit5 | §5.1 | 当日无未删订单 | `TradeOrderNoGenerator.next()` | `yyyyMMdd001` |
| ZCZX-JYDDGL-UT-002 | 订单编号 | 递增与左补零 | 单元测试 | JUnit5 | §3.1 | 最大序号 `008` | `next()` | 末 3 位 `009` |
| ZCZX-JYDDGL-UT-003 | 订单编号 | 超 999 拒绝 | 单元测试 | JUnit5 | §3.1 | 当日已满 999 | `next()` | 抛业务异常 |
| ZCZX-JYDDGL-UT-004 | 订单编号 | 冲突重试 | 单元测试 | JUnit5+Mockito | R8 | Mock 唯一键冲突一次 | `next()` 重试 | 第二次成功或明确失败 |
| ZCZX-JYDDGL-UT-005 | 耳标解析 | trim 与去重 | 单元测试 | JUnit5 | R4 | 无 | `EarTagCodec` 重复/空格 | 重复抛「存在重复耳标」 |
| ZCZX-JYDDGL-UT-006 | 保存校验 | R1 缺主数据 | 单元测试 | JUnit5 | R1 | Mock | 缺供应商/承销商/空 `lineList` | 失败；对应必填提示 |
| ZCZX-JYDDGL-UT-007 | 保存校验 | R2 主数据无效 | 单元测试 | JUnit5+Mockito | R2 | 供应商 `del_flag=2` | `validate` | 失败；不存在或已删除 |
| ZCZX-JYDDGL-UT-008 | 保存校验 | R3 耳标不在池 | 单元测试 | JUnit5+Mockito | §4.4 | 池不含 E102（B） | 保存 E102 | 失败；不可选或已被占用 |
| ZCZX-JYDDGL-UT-009 | 保存校验 | R3 他单占用 | 单元测试 | JUnit5+Mockito | §4.3 | C 含 E103 | 保存 E103 | 失败；已被其他订单占用 |
| ZCZX-JYDDGL-UT-010 | 保存校验 | R3 编辑释放本单 | 单元测试 | JUnit5+Mockito | §4.3 | `excludeOrderId=TO-EDIT` | 保存本单 E101 | 通过 |
| ZCZX-JYDDGL-UT-011 | 保存校验 | R3 重量金额 | 单元测试 | JUnit5 | R3 | Mock 合法池 | `weight=0` 或 `amount=-1` | 失败；须 >0 |
| ZCZX-JYDDGL-UT-012 | 保存校验 | R5～R6 汇总 | 单元测试 | JUnit5 | R5～R6 | 2 行明细 | `prepareAndValidate` | `totalHeads=2`；`totalAmount` 为两行 `amount` 之和（2 位小数）；各行 `gradeCode`/`gradeName` 按重量匹配（如 120→B/二级、115→C/三级） |
| ZCZX-JYDDGL-UT-037 | 等级匹配 | 重量落档 | 单元测试 | JUnit5+Mockito | §3.4 | 标准四档配置 | `TradeOrderGradeResolver.assignGrades` | 140→A；120.5→B；1000→失败 |
| ZCZX-JYDDGL-UT-038 | 保存校验 | 等级服务端写入 | 单元测试 | JUnit5+Mockito | R3 | 合法池；weight=120.5 | `prepareAndValidate` | 明细 `gradeCode=B`、`gradeName=二级`；请求体传入的等级字段被覆盖 |
| ZCZX-JYDDGL-UT-013 | 保存校验 | R7 已完成禁改 | 单元测试 | JUnit5 | R7 | `order_status=1` | `validateUpdate` | 失败；已完成不可修改 |
| ZCZX-JYDDGL-UT-014 | 耳标池 | `listEligible` M 按供应商 | 单元测试 | JUnit5+Mockito | §4.1 | ME-S1、ME-S2 | `listEligible(1,null)` | S1 含 E101/E103；S2 含 E201 |
| ZCZX-JYDDGL-UT-015 | 耳标池 | B 全表剔除 | 单元测试 | JUnit5+Mockito | §4.2 | M 含 E102；P-SICK 标 E102 | `listEligible(1,null)` | 不含 E102 |
| ZCZX-JYDDGL-UT-016 | 耳标池 | C 他单占用 | 单元测试 | JUnit5+Mockito | §4.3 | TO-C 占 E103 | `listEligible(1,null)` | 不含 E103 |
| ZCZX-JYDDGL-UT-017 | 耳标池 | M 为空 | 单元测试 | JUnit5+Mockito | §4.1 | 供应商无进栏 | `listEligible` | 空数组；保存提示无进栏耳标 |
| ZCZX-JYDDGL-UT-031 | 耳标下拉 | 仅进栏子表 | 单元测试 | JUnit5+Mockito | §4.4 下拉 | Mock 子表三耳标 | `listSupplierEarTagOptions(1)` | 返回 E101/E102/E103（含病死、占用，不剔 B/C） |
| ZCZX-JYDDGL-UT-018 | 结算费用 | 按户服务费 0 | 单元测试 | JUnit5 | §7.4 | S3；总金额 10000 | `calcServiceFee` | `serviceFee=0`；`payable=10000` |
| ZCZX-JYDDGL-UT-019 | 结算费用 | 按头计费 | 单元测试 | JUnit5 | §7.4 | S1；`totalHeads=3`；标准 10 | `calcServiceFee` | `serviceFee=30.00`；`payable=total−30` |
| ZCZX-JYDDGL-UT-020 | 结算费用 | 按额四舍五入 | 单元测试 | JUnit5 | §7.4 | S2；总金额 10000；费率 1%+0.5% | `calcServiceFee` | `serviceFee=150.00`（HALF_UP 2 位） |
| ZCZX-JYDDGL-UT-021 | 完成支付 | 事务成功写结算 | 单元测试 | JUnit5+Mockito | §6.3 | 待支付单；S1 | `completePay` 合法凭证 | 订单 `status=1`；`finish_time` 非空；结算 `settlement_status=0`；`deleteBySupplierIdAndEarTag(1,E101)` |
| ZCZX-JYDDGL-UT-022 | 完成支付 | 幂等拒绝 | 单元测试 | JUnit5+Mockito | §6.4 | 已有 `uk_order_id` 结算 | 再次 `completePay` | 失败；不得重复生成 |
| ZCZX-JYDDGL-UT-023 | 完成支付 | 收费未配拒绝 | 单元测试 | JUnit5+Mockito | §6.5 | S4 订单 | `completePay` | 失败；收费方式未配置；订单仍为待支付 |
| ZCZX-JYDDGL-UT-024 | 完成支付 | 结算失败回滚 | 单元测试 | JUnit5+Mockito | §6.3 | Mock 结算插入失败 | `completePay` | 订单仍为待支付；无结算记录 |
| ZCZX-JYDDGL-UT-025 | Service | 新增主从表 | 单元测试 | JUnit5+Mockito | §5 | Mock Mapper | `insert` 合法行 | `order_status=0`；`order_no` 生成；`batchInsert` 明细 |
| ZCZX-JYDDGL-UT-026 | Service | 修改覆盖明细 | 单元测试 | JUnit5+Mockito | 技术方案 §3.2 | 已有明细 | `update` 新 `lineList` | 先删子表再插；头数金额更新 |
| ZCZX-JYDDGL-UT-027 | Service | 逻辑删除 | 单元测试 | JUnit5+Mockito | §2.3 | 待支付单 | `remove` | `del_flag=2`；子表保留 |
| ZCZX-JYDDGL-UT-028 | Service | 已完成禁删 | 单元测试 | JUnit5+Mockito | §2.3 | TO-DONE | `remove` | 抛 `ServiceException` |
| ZCZX-JYDDGL-UT-029 | Mapper | 列表 SQL | 单元测试 | JUnit5 | §6.1 | 无 | 检查 `BizTradeOrderMapper.xml` | `del_flag=0`；`create_time DESC`；JOIN 双方名称 |
| ZCZX-JYDDGL-UT-030 | Mapper | 占用耳标 C SQL | 单元测试 | JUnit5 | §5.2 | 无 | 检查 `BizTradeOrderLineMapper.xml` | `order_status in (0,1)`；`excludeOrderId` 生效 |
| ZCZX-JYDDGL-UT-032 | Mapper | 删进栏耳标 SQL | 单元测试 | JUnit5 | §6.3、§5.2 | 无 | 检查 `BizMarketEntryEarTagMapper.xml` | `deleteBySupplierIdAndEarTag` 按 `supplier_id`+`ear_tag` |

**自动化已实现**：`TradeOrderNoGeneratorTest`、`SettlementFeeCalculatorTest`、`TradeOrderEarTagResolverTest`、`TradeOrderValidationTest`、`BizTradeOrderServiceImplTest`、`BizTradeOrderControllerApiTest`（MockMvc 部分 API）。**未自动化**：UT-022～024、UT-029～030、UT-032（需读 XML 或集成环境）；API-002～027、033～036、041～043；全部 UI。

---

## 二、接口测试用例

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| ZCZX-JYDDGL-API-001 | 列表 | 默认分页排序 | 接口测试 | Postman | §8.2 | 有订单数据 | `GET /list` | `code=200`；≤20 条；`create_time` 倒序 |
| ZCZX-JYDDGL-API-002 | 列表 | 订单编号模糊 | 接口测试 | Postman | §8.2 | 编号含 `20260520` | `orderNo=0520` | 命中对应行 |
| ZCZX-JYDDGL-API-003 | 列表 | 供应商精确 | 接口测试 | Postman | §8.2 | S1、S2 各有单 | `supplierId=1` | 仅 S1 订单 |
| ZCZX-JYDDGL-API-004 | 列表 | 承销商精确 | 接口测试 | Postman | §8.2 | 多承销商数据 | `distributorId=1` | 仅 D1 订单 |
| ZCZX-JYDDGL-API-005 | 列表 | 组合筛选 | 接口测试 | Postman | §8.2 | 已登录 | 编号+供应商+承销商同时传 | 同时满足才命中 |
| ZCZX-JYDDGL-API-006 | 列表 | 不含已删除 | 接口测试 | Postman | §2.3 | 某 id 已逻辑删 | `GET /list` | 无该 id |
| ZCZX-JYDDGL-API-007 | 辅助 | 耳标下拉仅子表 | 接口测试 | Postman | §4.4 下拉 | ME-S1 子表 E101,E102,E103 | `GET /supplierEarTags?supplierId=1` | **含** E101、E102、E103（不剔病死 B、不剔他单 C） |
| ZCZX-JYDDGL-API-008 | 辅助 | 下拉无 excludeOrderId | 接口测试 | Postman | 技术方案 §6.6 | 已登录 | 仅 `supplierId=1` | 成功；**不接受** `excludeOrderId` |
| ZCZX-JYDDGL-API-009 | 辅助 | 无进栏子表数据 | 接口测试 | Postman | §4.4 | 供应商无子表行 | `supplierEarTags?supplierId=99` | `data=[]` |
| ZCZX-JYDDGL-API-010 | 辅助 | 缺 supplierId | 接口测试 | Postman | 参数校验 | 已登录 | 不传 `supplierId` | 失败 |
| ZCZX-JYDDGL-API-011 | 新增 | 合法待支付单 | 接口测试 | Postman | §8.1 | 池含 E101、E104 | `POST` 合法 Body | `code=200`；`orderStatus=0`；`orderNo` 11 位；`totalHeads=2` |
| ZCZX-JYDDGL-API-012 | 新增 | 服务端覆盖汇总 | 接口测试 | Postman | R5～R6 | 已登录 | Body 故意错 `totalHeads=9` | 成功；库中头数/金额按 `lineList` 重算 |
| ZCZX-JYDDGL-API-013 | 新增 | 缺供应商/承销商 | 接口测试 | Postman | R1 | 已登录 | 缺 `supplierId` 或 `distributorId` | 失败 |
| ZCZX-JYDDGL-API-014 | 新增 | 无明细行 | 接口测试 | Postman | R1 | 已登录 | `lineList=[]` | 失败；至少一行明细 |
| ZCZX-JYDDGL-API-015 | 新增 | 耳标重复 | 接口测试 | Postman | R4 | 已登录 | 两行同 `earTag` | 失败；重复耳标 |
| ZCZX-JYDDGL-API-016 | 新增 | 病死耳标 | 接口测试 | Postman | §4.2 | P-SICK 剔 E102 | `lineList` 含 E102 | 失败；不可选 |
| ZCZX-JYDDGL-API-017 | 新增 | 他单占用耳标 | 接口测试 | Postman | §4.3 | TO-C 占 E103 | `POST` 含 E103 | 失败；已被占用 |
| ZCZX-JYDDGL-API-018 | 新增 | 重量金额非法 | 接口测试 | Postman | R3 | 已登录 | `weight=0` 或 `amount=0` | 失败 |
| ZCZX-JYDDGL-API-019 | 新增 | 无效供应商 | 接口测试 | Postman | R2 | 已删供应商 id | `POST` | 失败 |
| ZCZX-JYDDGL-API-020 | 详情 | 有效 id | 接口测试 | Postman | §8.4 | 有待支付单 | `GET /{id}` | 含 `lineList`、`supplierName`、`orderStatusName` |
| ZCZX-JYDDGL-API-021 | 详情 | 无效/已删 | 接口测试 | Postman | 异常 | 已登录 | 不存在或已删 id | 失败或 `data` 空 |
| ZCZX-JYDDGL-API-022 | 修改 | 改双方与明细 | 接口测试 | Postman | §8.3 | 待支付单 | `PUT` 换明细与承销商 | 成功；`orderNo` 不变；子表全量覆盖 |
| ZCZX-JYDDGL-API-023 | 修改 | 保留本单耳标 | 接口测试 | Postman | §4.3 | 编辑 TO-EDIT | `PUT` 仍含 E101 | 成功（C 排除本单） |
| ZCZX-JYDDGL-API-024 | 修改 | 已完成拒绝 | 接口测试 | Postman | R7 | TO-DONE | `PUT` 任意字段 | 失败；已完成不可修改 |
| ZCZX-JYDDGL-API-025 | 删除 | 待支付逻辑删 | 接口测试 | Postman | §8.5 | 无依赖新单 | `DELETE /{id}` | 成功；列表不可见 |
| ZCZX-JYDDGL-API-026 | 删除 | 释放耳标占用 | 接口测试 | Postman | §4.3 | 删占 E103 的待支付单 | `POST` 含 E103 | 成功（C 已释放） |
| ZCZX-JYDDGL-API-027 | 删除 | 已完成拒绝 | 接口测试 | Postman | §2.3 | TO-DONE | `DELETE` | 失败；不可删除 |
| ZCZX-JYDDGL-API-028 | 完成支付 | 合法按头结算 | 接口测试 | Postman | §6、§7 | 待支付 3 头 S1；先 `/common/upload` | `PUT /completePay` 带凭证 | `orderStatus=1`；`finishTime` 有值；结算待结算；`biz_market_entry_ear_tag` 本单耳标已删 |
| ZCZX-JYDDGL-API-044 | 完成支付 | 释放进栏耳标 | 接口测试 | Postman | §6.3 步骤 5 | 支付前子表有 E101 | `completePay` 后查子表 | 无 `supplier_id=1 AND ear_tag=E101` 行；再 `supplierEarTags` 不含 E101 |
| ZCZX-JYDDGL-API-029 | 完成支付 | 按额快照固化 | 接口测试 | Postman | §7.4 | S2 待支付单 | `completePay` | 结算 `fee_method=3` 与费率快照一致；改供应商主数据后结算金额不变 |
| ZCZX-JYDDGL-API-030 | 完成支付 | 缺支付凭证 | 接口测试 | Postman | §3.6 | 待支付单 | 无 `payVoucherUrl` | 失败 |
| ZCZX-JYDDGL-API-031 | 完成支付 | 收费未配置 | 接口测试 | Postman | §6.5 | S4 待支付单 | `completePay` | 失败；订单仍为待支付 |
| ZCZX-JYDDGL-API-032 | 完成支付 | 非待支付 | 接口测试 | Postman | §6.4 | TO-DONE | `completePay` | 失败 |
| ZCZX-JYDDGL-API-033 | 完成支付 | 挂单后耳标失效 | 接口测试 | Postman | §6.5、§12.6 | 建单后删进栏 E104 | `completePay` | 失败；R3 仍满足才可通过 |
| ZCZX-JYDDGL-API-034 | 完成支付 | 幂等不重复结算 | 接口测试 | Postman | §6.4 | 已成功单 | 伪造再次 `completePay` | 失败；结算条数仍为 1 |
| ZCZX-JYDDGL-API-035 | 端到端 | 待支付全流程 | 接口测试 | Postman | §9.1 | 样本齐备 | `POST`→`list`→`GET`→`PUT`→`completePay` | 每步 `code=200`；结算已生成 |
| ZCZX-JYDDGL-API-036 | 端到端 | 删单释放占用 | 接口测试 | Postman | §4.3 | 新建占 E104→删 | 再 `POST` E104 | 第二次成功 |
| ZCZX-JYDDGL-API-037 | 鉴权 | 未登录 | 接口测试 | curl | 安全 | 无 Token | `GET /list` | 401 或统一未登录 |
| ZCZX-JYDDGL-API-038 | 权限 | 无新增权限 | 接口测试 | Postman | §10 | 仅 list | `POST` | 无权限失败 |
| ZCZX-JYDDGL-API-039 | 权限 | 无完成支付 | 接口测试 | Postman | §10 | 无 pay | `PUT /completePay` | 无权限失败 |
| ZCZX-JYDDGL-API-040 | 路由 | 耳标在详情前 | 接口测试 | Postman | 技术方案 §6 | 已登录 | `GET /supplierEarTags` | 非 404（不被 `/{id}` 吞掉） |
| ZCZX-JYDDGL-API-041 | 联调 | 供应商搜索 | 接口测试 | Postman | §6.8 | 有 S1 | `GET /tradeMarket/supplier/list?supplierName=扎西` | 含 S1；供表单选用 |
| ZCZX-JYDDGL-API-042 | 联调 | 承销商搜索 | 接口测试 | Postman | §6.8 | 有 D1 | `GET /tradeMarket/distributor/list` | 含 D1 |
| ZCZX-JYDDGL-API-043 | 响应 | 失败可读 | 接口测试 | Postman | §11 | 已登录 | 触发占用、已完成改、收费未配 | `msg` 与需求示例一致可区分 |

---

## 三、界面测试用例（UI）

**测试工具**：**Playwright** + **Chromium**（`channel: 'chrome'` 使用本机 **Google Chrome**）。

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| ZCZX-JYDDGL-UI-001 | 列表 | 默认列展示 | UI 测试 | Playwright+Chrome | §8.2 | 已登录；有数据 | 进入「交易订单管理」 | 列含订单编号、创建时间、双方、头数、金额、状态、操作 |
| ZCZX-JYDDGL-UI-002 | 列表 | 订单编号筛选 | UI 测试 | Playwright+Chrome | §8.2 | 已知编号片段 | 填编号→搜索 | 仅匹配行 |
| ZCZX-JYDDGL-UI-003 | 列表 | 供应商/承销商筛选 | UI 测试 | Playwright+Chrome | §8.2 | 多供应商数据 | 分别选定→搜索 | 精确匹配对应行 |
| ZCZX-JYDDGL-UI-004 | 列表 | 重置 | UI 测试 | Playwright+Chrome | §8.2 | 已筛选 | 搜索→重置 | 条件清空；列表恢复 |
| ZCZX-JYDDGL-UI-005 | 列表 | 采购明细入口 | UI 测试 | Playwright+Chrome | §8.2 | 多行明细单 | 点「采购明细」或摘要 | 弹窗只读展示耳标、重量、金额 |
| ZCZX-JYDDGL-UI-006 | 列表 | 待支付操作按钮 | UI 测试 | Playwright+Chrome | §8.7 | 待支付行 | 看操作列 | 含打印小票、完成支付、查看、编辑、删除；无打印凭证 |
| ZCZX-JYDDGL-UI-007 | 列表 | 已完成操作按钮 | UI 测试 | Playwright+Chrome | §8.7 | 已完成行 | 看操作列 | 含打印凭证、查看；无完成支付/编辑/删除/打小票 |
| ZCZX-JYDDGL-UI-008 | 新增 | 编号只读 | UI 测试 | Playwright+Chrome | §3.1 | 有 add 权限 | 打开新增 | 无订单编号输入；保存后列表可见系统编号 |
| ZCZX-JYDDGL-UI-009 | 新增 | 未选供应商禁耳标 | UI 测试 | Playwright+Chrome | §3.4 | 新增弹窗 | 未选供应商加明细行 | 耳标不可选或提示先选供应商 |
| ZCZX-JYDDGL-UI-010 | 新增 | 选供应商加载耳标 | UI 测试 | Playwright+Chrome | §4.4 下拉 | ME-S1 子表 | 选 S1 | 下拉含 E101、E102、E103（**可含**他单占用 E103） |
| ZCZX-JYDDGL-UI-011 | 新增 | 更换供应商清空明细 | UI 测试 | Playwright+Chrome | §4.5 | 已选 S1 与行 | 改选 S2 | 明细清空；耳标按 S2 刷新 |
| ZCZX-JYDDGL-UI-012 | 新增 | 头数金额联动 | UI 测试 | Playwright+Chrome | §3.5 | 已填 2 行 | 观察总头数/总金额 | 只读；头数=2；金额=两行之和 |
| ZCZX-JYDDGL-UI-013 | 新增 | 增删明细行 | UI 测试 | Playwright+Chrome | §3.4 | 新增 | 添加行→删一行→保留 1 行保存 | 至少 1 行才可保存；联动更新 |
| ZCZX-JYDDGL-UI-014 | 新增 | 保存成功待支付 | UI 测试 | Playwright+Chrome | §8.1 | 权限齐备 | 选 S1、D1、E101+重量金额→确定 | 成功提示；列表新行状态「待支付」 |
| ZCZX-JYDDGL-UI-015 | 新增 | 取消不保存 | UI 测试 | Playwright+Chrome | §8.1 | 新增 | 填数据→取消 | 弹窗关；无新行 |
| ZCZX-JYDDGL-UI-016 | 新增 | 前端校验缺项 | UI 测试 | Playwright+Chrome | R1 | 新增 | 不选承销商/无明细→确定 | 前端或提交失败 |
| ZCZX-JYDDGL-UI-017 | 新增 | 占用耳标失败 | UI 测试 | Playwright+Chrome | §4.3 | TO-C 占 E103 | 选 E103→确定 | 提示已被占用；不保存 |
| ZCZX-JYDDGL-UI-018 | 查看 | 只读含凭证 | UI 测试 | Playwright+Chrome | §8.4 | TO-DONE | 点击查看 | 全字段只读；支付凭证可预览；无编辑入口 |
| ZCZX-JYDDGL-UI-019 | 编辑 | 改明细成功 | UI 测试 | Playwright+Chrome | §8.3 | 待支付单 | 编辑换耳标/重量→确定 | 列表与详情已更新；编号不变 |
| ZCZX-JYDDGL-UI-020 | 编辑 | 已完成不可进 | UI 测试 | Playwright+Chrome | §8.3 | TO-DONE | 列表找编辑按钮 | 无编辑按钮或点击无效 |
| ZCZX-JYDDGL-UI-021 | 删除 | 二次确认 | UI 测试 | Playwright+Chrome | §8.5 | 待支付行 | 删除→确认 | 确认语含编号/供应商/金额或头数；行消失 |
| ZCZX-JYDDGL-UI-022 | 删除 | 取消删除 | UI 测试 | Playwright+Chrome | §8.5 | 待支付行 | 删除→取消 | 行仍在 |
| ZCZX-JYDDGL-UI-023 | 删除 | 已完成不可删 | UI 测试 | Playwright+Chrome | §2.3 | TO-DONE | 找删除按钮 | 无删除按钮 |
| ZCZX-JYDDGL-UI-024 | 完成支付 | 弹窗上传凭证 | UI 测试 | Playwright+Chrome | §6.2 | 待支付单 | 完成支付→上传 jpg≤10MB→确定 | 状态变已完成；完成时间有值；可跳转或提示结算单号 |
| ZCZX-JYDDGL-UI-025 | 完成支付 | 取消不变 | UI 测试 | Playwright+Chrome | §6.2 | 待支付单 | 完成支付→取消 | 仍为待支付 |
| ZCZX-JYDDGL-UI-026 | 完成支付 | 缺凭证拦截 | UI 测试 | Playwright+Chrome | §3.6 | 待支付单 | 不上传直接确定 | 校验失败 |
| ZCZX-JYDDGL-UI-027 | 完成支付 | 收费未配提示 | UI 测试 | Playwright+Chrome | §11 | S4 待支付单 | 上传凭证→确定 | 失败提示收费未配置；仍为待支付 |
| ZCZX-JYDDGL-UI-028 | 打印 | 待支付打小票 | UI 测试 | Playwright+Chrome | §8.6 | 待支付单 | 打印小票 | 含编号、双方、明细、头数金额、创建时间；无支付凭证/完成时间 |
| ZCZX-JYDDGL-UI-029 | 打印 | 已完成打凭证 | UI 测试 | Playwright+Chrome | §8.6 | TO-DONE | 打印凭证 | 含完成时间、支付凭证图、已完成状态；无打小票 |
| ZCZX-JYDDGL-UI-030 | 端到端 | 主流程 | UI 集成 | Playwright+Chrome | §9.1 | 权限齐备 | 新增→完成支付→查看结算关联 | 订单已完成；结算待结算；进栏池无本单耳标 |
| ZCZX-JYDDGL-UI-031 | 权限 | 只读角色 | UI 测试 | Playwright+Chrome | §10 | 仅 list/query | 进入模块 | 无新增/编辑/删除/完成支付按钮 |
| ZCZX-JYDDGL-UI-032 | 完成支付 | 耳标从下拉消失 | UI 测试 | Playwright+Chrome | §6.3 步骤 5 | 支付前可选 E101 | 完成支付 E101 后新建单选 S1 | 下拉不再出现 E101 |

---

## Playwright 与谷歌浏览器（实施提示）

- 安装：`npm i -D @playwright/test`；`npx playwright install chrome`。  
- 配置：`use: { channel: 'chrome', locale: 'zh-CN', baseURL: 'http://localhost:80' }`；`storageState` 复用登录态。  
- 路由：`await page.goto('/tradeMarket/tradeOrder')`（与菜单 `component` 一致）。  
- 新增：`page.getByRole('button', { name: '新增' }).click()` → 供应商远程搜索选 S1 → 承销商选 D1 → 明细行选耳标 `E101`、填重量金额 → 断言总头数只读 → `getByRole('button', { name: '确定' }).click()`。  
- 完成支付：`getByRole('button', { name: '完成支付' }).first().click()` → `setInputFiles` 上传凭证 → 对话框「确定」。  
- 打印：监听 `page.waitForEvent('popup')` 或断言打印预览 DOM 含 `orderNo`、不含 `payVoucher`（小票场景）。

---

## 覆盖对照

| 类别 | 单元 UT | 接口 API | 界面 UI |
| --- | --- | --- | --- |
| 正常流程 | UT-012、018～021、025～026 | API-011～012、020、022～023、028～029、035 | UI-014、019、024、028～030 |
| 异常流程 | UT-006～009、013、022～024 | API-013～019、021、024、027、030～034 | UI-016～017、020、023、026～027 |
| 业务约束 R1～R8 | UT-006～013 | API-013～019、024 | UI-016、012 |
| 下拉仅子表 | UT-031 | API-007～009 | UI-010 |
| 保存 M−B−C | UT-014～017、008～010 | API-016～017、023 | UI-017 |
| 支付后删进栏耳标 | UT-021 | API-028、044 | UI-032 |
| 状态与操作矩阵 | UT-013、027～028 | API-024、027、032 | UI-006～007、020、023 |
| 完成支付与结算 | UT-018～024 | API-028～034、044 | UI-024～027、030、032 |
| 费用快照 §7.4 | UT-018～020 | API-029 | —（结算列表另模块验证） |
| 列表筛选 | UT-029 | API-001～006 | UI-002～005 |
| 逻辑删除 | UT-027 | API-025～026 | UI-021～022 |
| 权限/鉴权 | — | API-037～039 | UI-031 |
| 打印边界 | — | — | UI-028～029 |

---

## 功能需求追溯（自查）

| 需求章节 | 要点 | 主要对应用例 |
| --- | --- | --- |
| §3 字段规则 | 编号只读、双方必选、明细、联动、凭证 | UT-012；API-011～012；UI-008、012～014 |
| §4 耳标 | 下拉子表；保存 M−B−C；支付删进栏 | UT-014～017、031；API-007～009、016～017、044；UI-010～011、017、032 |
| §5 R1～R8 | 保存校验 | UT-006～013；API-013～019、024；UI-016 |
| §6 完成支付 | 凭证、事务、结算、删进栏耳标 | UT-021～024；API-028～034、044；UI-024～027、032 |
| §7 结算衔接 | 一对一、费用快照、待结算 | UT-018～021；API-028～029、035；UI-030 |
| §8 功能清单 | 列表/CRUD/打印/状态按钮 | API-001～027；UI-001～029 |
| §8.7 状态矩阵 | 待支付 vs 已完成操作 | API-024、027、032；UI-006～007、020、023 |
| §10 权限 | 按钮级 | API-037～039；UI-031 |
| §11 提示语 | 失败场景可读 | API-043；UI-017、027 |
| §12.5 协同异常 | 删进栏导致支付失败 | API-033 |

---

## 修订记录

| 版本 | 说明 |
| --- | --- |
| 1.0 | 初版：单元 30、接口 43、UI 31；覆盖 R1～R8、耳标 M−B−C、完成支付生成结算、状态矩阵、Playwright+Chrome；前缀 **ZCZX-JYDDGL** |
| 1.1 | 对齐实现 v1.2～1.4：`/supplierEarTags` 仅子表；保存仍 M−B−C；支付删进栏耳标；UT-031/032、API-007～009/044、UI-010/032；标注自动化范围 |
