# 市场进栏登记 — 测试用例

> 依据：`市场进栏登记功能需求.md`、`市场进栏登记技术方案.md`  
> **接口 Base Path**：`/tradeMarket/marketEntry`；鉴权与若依一致（Cookie / Token）。

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

| 类型 | 样本 | 要点 |
| --- | --- | --- |
| 供应商 `biz_supplier` | **S1** `id=1` 扎西合作社、**S2** `id=2` | `del_flag=0` |
| 市场栏位 `biz_market_stall` | **M1** `id=1` `stall_code=01`、**M2** `id=2` `02` | `del_flag=0` |
| 入场隔离 `biz_entry_isolation` | **EI-END** | `supplier_id=1`；`del_flag=0`；`expected_release_date <= 今天`；主表 `ear_tags=E101,E102,E103` |
| 入场隔离 | **EI-ING** | 同 S1；`expected_release_date > 今天`（**不**进可选池 A） |
| 隔离巡查 `biz_isolation_patrol` | **P-SICK** | `del_flag=0`；`sick_dead_qty>0`；`ear_tags=E102`（**全表**汇总为 B，不按供应商） |
| 隔离巡查 | **P-SICK-OTHER** | 同上；`ear_tags=E999`（其他供应商圈位巡查，仍计入 B） |
| 进栏登记 `biz_market_entry` | **ME-OTHER** | `del_flag=0`；`supplier_id=1`；子表含 `E103`（保存时占用 C，**不影响** `supplierEarTags`） |

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

```json
{
  "entryTime": "2026-05-20 14:00:00",
  "stallId": 1,
  "supplierId": 1,
  "earTagList": ["E101"]
}
```

**可选耳标池速查**

| 场景 | 公式 | 说明 |
| --- | --- | --- |
| `GET /supplierEarTags` | **A − B** | A：该供应商隔离已结束主表耳标；B：**全表**巡查病死耳标（逗号拆分） |
| 保存 R4 | **(A − B) − C** | C：他单有效进栏占用；编辑时 `excludeEntryId` 释放本条 |

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

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

---

## 一、单元测试用例

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| ZCZX-SCJLDJ-UT-001 | 耳标解析 | 逗号拆分与 trim | 单元测试 | JUnit5 | §3.4 复用 | 无 | `EarTagCodec.parse(" E101 , E102 ")` | `["E101","E102"]` |
| ZCZX-SCJLDJ-UT-002 | 耳标解析 | 本条内重复 | 单元测试 | JUnit5 | R5 | 无 | `resolveMainTags` 含重复段 | 抛异常；提示重复耳标 |
| ZCZX-SCJLDJ-UT-003 | 耳标解析 | 单段超长 | 单元测试 | JUnit5 | §3.4 | 无 | 33 字符耳标 | 校验失败 |
| ZCZX-SCJLDJ-UT-004 | 保存校验 | R6 以耳标数为准 | 单元测试 | JUnit5 | R6 | Mock 合法主数据 | `quantity=9`，`earTagList` 2 个 → `prepareAndValidate` | 通过后 `quantity=2` |
| ZCZX-SCJLDJ-UT-005 | 保存校验 | R1 无耳标 | 单元测试 | JUnit5 | R1 | Mock | 空 `earTagList` | 失败；`请至少选择一个耳标号` |
| ZCZX-SCJLDJ-UT-006 | 保存校验 | R1 合法通过 | 单元测试 | JUnit5 | R1 | Mock；池含 E101 | `MarketEntryTestSamples.normalSaveRow()` | 通过；`quantity=1` |
| ZCZX-SCJLDJ-UT-007 | 保存校验 | R4 他单占用 | 单元测试 | JUnit5 | §4.3 | 池不含 E103；occupied 含 E103 | 保存含 E103 | 失败；msg 含「已被其他进栏记录占用」 |
| ZCZX-SCJLDJ-UT-008 | 保存校验 | R4 编辑释放本条 | 单元测试 | JUnit5 | §4.3 | `id=1001`；池含 E103 | `excludeEntryId` 参与校验 | 本条 E103 可保存 |
| ZCZX-SCJLDJ-UT-009 | 保存校验 | R4 病死剔除 | 单元测试 | JUnit5 | §4.2 | 池不含 E102（B） | 保存 E102 | 失败；`不可进栏或不在可选范围` |
| ZCZX-SCJLDJ-UT-010 | 保存校验 | R4 可选池内 | 单元测试 | JUnit5 | §4 | 池含 E101 | 保存 E101 | 通过 |
| ZCZX-SCJLDJ-UT-011 | 保存校验 | R2 未来时间 | 单元测试 | JUnit5 | R2 | Mock | `entryTime` 为明天 | 失败；`不能晚于当前时间` |
| ZCZX-SCJLDJ-UT-012 | 保存校验 | R2 当前时刻 | 单元测试 | JUnit5 | R2 | Mock | `entryTime=now` | 通过 |
| ZCZX-SCJLDJ-UT-013 | 列表校验 | 日期区间颠倒 | 单元测试 | JUnit5 | §6.2 | 无 | `validateDateRange("2026-05-21","2026-05-20")` | 失败；开始不能晚于结束 |
| ZCZX-SCJLDJ-UT-014 | 保存校验 | R3 栏位已删 | 单元测试 | JUnit5+Mockito | R3 | `biz_market_stall` 不存在 | `validate` | 失败；栏位不存在或已删除 |
| ZCZX-SCJLDJ-UT-015 | 保存校验 | R3 供应商已删 | 单元测试 | JUnit5+Mockito | R3 | 供应商 `del_flag=2` | `validate` | 失败；供应商不存在或已删除 |
| ZCZX-SCJLDJ-UT-016 | 耳标池 | supplierEarTags A−B | 单元测试 | JUnit5+Mockito | §4.1～4.2、§5.1 | Mock A 含 E101,E102,E103；B 含 E102 | `listSupplierEarTagOptions(1)` | `["E101","E103"]`；不含 E102 |
| ZCZX-SCJLDJ-UT-017 | 耳标池 | 保存 (A−B)−C | 单元测试 | JUnit5+Mockito | §4.3、§5.2 | Mock 同上；C 含 E101 | `listEligible(1,null)` | 仅 `["E103"]` |
| ZCZX-SCJLDJ-UT-018 | 耳标池 | 占用集合 | 单元测试 | JUnit5+Mockito | §4.3 | Mock 子表占用 | `loadOccupiedEarTags(null)` | 含 ME-OTHER 的 E103 |
| ZCZX-SCJLDJ-UT-019 | 耳标池 | A 未结束隔离 | 单元测试 | JUnit5+Mockito | §4.1 | 仅 EI-ING 有耳标 | `listSupplierEarTagOptions` | 空列表 |
| ZCZX-SCJLDJ-UT-020 | 耳标池 | 全表 B 剔除 | 单元测试 | JUnit5+Mockito | §4.2 | A 含 E101；B 含 P-SICK-OTHER 的 E999 | `listSupplierEarTagOptions` | 不含 E999（即使非 S1 巡查） |
| ZCZX-SCJLDJ-UT-021 | Service | 新增冗余栏位号 | 单元测试 | JUnit5+Mockito | §3.2 | Mock Mapper | `insert` 合法行 | 主表 `stall_code=01`；子表 `batchInsert` |
| ZCZX-SCJLDJ-UT-022 | Service | 修改覆盖子表 | 单元测试 | JUnit5+Mockito | 技术方案 §3.2 | 已有子表 | `update` 新 `earTagList` | 先删子表再插；`quantity` 更新 |
| ZCZX-SCJLDJ-UT-023 | Service | 逻辑删除 | 单元测试 | JUnit5+Mockito | §2.2 | Mock | `remove(id)` | 主表 `del_flag=2`；子表保留 |
| ZCZX-SCJLDJ-UT-024 | Service | 删除有关联 | 单元测试 | JUnit5+Mockito | §6.5 | `hasReference=true` | `remove` | 抛 `ServiceException`；关联文案 |
| ZCZX-SCJLDJ-UT-025 | Service | 详情组装耳标 | 单元测试 | JUnit5+Mockito | §6.4 | 子表 2 行 | `selectById` | `earTagList` 2 项；`earTags` 逗号串 |
| ZCZX-SCJLDJ-UT-026 | Mapper | 列表 SQL | 单元测试 | JUnit5 | §6.2 | 无 | 检查 `BizMarketEntryMapper.xml` | `del_flag=0`；`entry_time DESC`；`GROUP_CONCAT` |
| ZCZX-SCJLDJ-UT-027 | Mapper | 巡查 B SQL | 单元测试 | JUnit5 | §5.1 | 无 | 检查 `selectSickDeadEarTagRows` | 无 `supplierId`；`sick_dead_qty>0` |
| ZCZX-SCJLDJ-UT-028 | Mapper | 耳标筛选 EXISTS | 单元测试 | JUnit5 | §6.2 | 无 | 检查 XML | 子表 `ear_tag LIKE` + `EXISTS` |

---

## 二、接口测试用例

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| ZCZX-SCJLDJ-API-001 | 列表 | 默认分页排序 | 接口测试 | Postman | §6.2 | 有进栏数据 | `GET /list` | `code=200`；≤20 条；`entryTime` 倒序 |
| ZCZX-SCJLDJ-API-002 | 列表 | 进栏日期区间 | 接口测试 | Postman | §6.2 | 已知进栏日 | `entryDateStart/End` 闭区间 | 仅范围内记录 |
| ZCZX-SCJLDJ-API-003 | 列表 | 日期区间非法 | 接口测试 | Postman | §6.2 | 已登录 | `start>end` | `code≠200`；开始不能晚于结束 |
| ZCZX-SCJLDJ-API-004 | 列表 | 栏位编号模糊 | 接口测试 | Postman | §6.2 | M1=`01` | `stallCode=1` | 含 M1 |
| ZCZX-SCJLDJ-API-005 | 列表 | 供应商精确 | 接口测试 | Postman | §6.2 | S1、S2 各有记录 | `supplierId=1` | 仅 S1 |
| ZCZX-SCJLDJ-API-006 | 列表 | 耳标号模糊 | 接口测试 | Postman | §6.2 | 子表含 E101 | `earTag=101` | 命中对应行 |
| ZCZX-SCJLDJ-API-007 | 列表 | 组合筛选 | 接口测试 | Postman | §6.2 | 已登录 | 日期+栏位+供应商+耳标 | 同时满足才命中 |
| ZCZX-SCJLDJ-API-008 | 列表 | 不含已删除 | 接口测试 | Postman | §2.2 | 某 id 已删 | `GET /list` | 无该 id |
| ZCZX-SCJLDJ-API-009 | 辅助 | 栏位下拉 | 接口测试 | Postman | §6.1 | M1、M2 有效 | `GET /stallOptions` | `[{stallId,stallCode}]`；仅 `del_flag=0` |
| ZCZX-SCJLDJ-API-010 | 辅助 | 可选耳标 A−B | 接口测试 | Postman | §4.1～4.2 | EI-END、P-SICK | `GET /supplierEarTags?supplierId=1` | 含 E101；不含 E102（病死剔除）；**含** ME-OTHER 占用的 E103 |
| ZCZX-SCJLDJ-API-011 | 辅助 | 无隔离结束数据 | 接口测试 | Postman | §4.1 | S2 无 EI-END | `supplierEarTags?supplierId=2` | `data=[]` |
| ZCZX-SCJLDJ-API-012 | 辅助 | 全表 B 剔除 | 接口测试 | Postman | §4.2 | EI-END 含 E101；P-SICK-OTHER 含 E999 | `supplierEarTags?supplierId=1` | 不含 E999、E102 |
| ZCZX-SCJLDJ-API-013 | 辅助 | 无 excludeEntryId | 接口测试 | Postman | §5.1 | 已登录 | 仅传 `supplierId` | 成功；不接受 `excludeEntryId` |
| ZCZX-SCJLDJ-API-014 | 新增 | 合法单耳标 | 接口测试 | Postman | §6.1 | 样本数据齐备 | `POST` 合法 Body | `code=200`；`quantity=1`；子表 1 行 |
| ZCZX-SCJLDJ-API-015 | 新增 | 合法双耳标 | 接口测试 | Postman | R6 | 池含 E101、E104 | `earTagList=["E101","E104"]` | `quantity=2` |
| ZCZX-SCJLDJ-API-016 | 新增 | 备注与逗号串 | 接口测试 | Postman | §3 | 已登录 | `remark`+`earTags="E101"` | 成功；与 `earTagList` 等价解析 |
| ZCZX-SCJLDJ-API-017 | 新增 | 备注超长 | 接口测试 | Postman | 技术方案 | 已登录 | `remark` 501 字 | 失败；长度提示 |
| ZCZX-SCJLDJ-API-018 | 新增 | 缺进栏时间 | 接口测试 | Postman | R1 | 已登录 | 无 `entryTime` | 失败 |
| ZCZX-SCJLDJ-API-019 | 新增 | 缺栏位/供应商 | 接口测试 | Postman | R1 | 已登录 | 缺 `stallId` 或 `supplierId` | 失败；对应必填提示 |
| ZCZX-SCJLDJ-API-020 | 新增 | 无耳标 | 接口测试 | Postman | R1 | 已登录 | 空 `earTagList` | 失败；至少一个耳标 |
| ZCZX-SCJLDJ-API-021 | 新增 | R2 未来时间 | 接口测试 | Postman | R2 | 已登录 | `entryTime` 明天 | 失败；不能晚于当前 |
| ZCZX-SCJLDJ-API-022 | 新增 | R3 无效栏位 | 接口测试 | Postman | R3 | 已删 `stallId` | `POST` | 失败；栏位不存在 |
| ZCZX-SCJLDJ-API-023 | 新增 | R3 无效供应商 | 接口测试 | Postman | R3 | 已删供应商 | `POST` | 失败；供应商不存在 |
| ZCZX-SCJLDJ-API-024 | 新增 | R5 耳标重复 | 接口测试 | Postman | R5 | 已登录 | `["E101","E101"]` | 失败；重复耳标 |
| ZCZX-SCJLDJ-API-025 | 新增 | R4 他单占用 | 接口测试 | Postman | §4.3 | ME-OTHER 占 E103；`supplierEarTags` 仍含 E103 | `POST` 含 E103 | 失败；已被占用 |
| ZCZX-SCJLDJ-API-026 | 新增 | R4 病死耳标 | 接口测试 | Postman | §4.2 | P-SICK 剔 E102 | `POST` 含 E102 | 失败；不可进栏 |
| ZCZX-SCJLDJ-API-027 | 新增 | R4 未结束隔离耳标 | 接口测试 | Postman | §4.1 | 仅 EI-ING 有 E201 | `POST` E201 | 失败；不在可选范围 |
| ZCZX-SCJLDJ-API-028 | 详情 | 有效 id | 接口测试 | Postman | §6.4 | 有 ME-OTHER | `GET /{id}` | 含 `supplierName`、`earTagList`、审计字段 |
| ZCZX-SCJLDJ-API-029 | 详情 | 无效/已删 | 接口测试 | Postman | 异常 | 已登录 | 不存在或已删 id | 失败或 `data` 空 |
| ZCZX-SCJLDJ-API-030 | 修改 | 改栏位与时间 | 接口测试 | Postman | §6.3 | 有记录 | `PUT` 改 `stallId`、`entryTime` | 成功；`stallCode` 随栏位更新 |
| ZCZX-SCJLDJ-API-031 | 修改 | 改耳标集合 | 接口测试 | Postman | §6.3 | 有记录 | `PUT` 换 `earTagList` | 子表全量覆盖；`quantity` 同步 |
| ZCZX-SCJLDJ-API-032 | 修改 | 保留原耳标 | 接口测试 | Postman | §4.3、§5.2 | 编辑本条占 E103 | `PUT` 原耳标不变 | 成功（服务端 `excludeEntryId` 释放 C） |
| ZCZX-SCJLDJ-API-033 | 修改 | 不存在 id | 接口测试 | Postman | 异常 | 已登录 | 伪造 id `PUT` | 失败 |
| ZCZX-SCJLDJ-API-034 | 删除 | 单条逻辑删 | 接口测试 | Postman | §6.5 | 无关联新行 | `DELETE /{id}` | 成功；列表不可见 |
| ZCZX-SCJLDJ-API-035 | 删除 | 释放后可保存 | 接口测试 | Postman | §4.3 | 删 ME-OTHER | `POST` 含 E103 | 成功（C 已释放；`supplierEarTags` 本已含 E103） |
| ZCZX-SCJLDJ-API-036 | 删除 | 有关联拒绝 | 接口测试 | Postman | §6.5 | `hasReference=true` | `DELETE` | 失败；存在关联业务 |
| ZCZX-SCJLDJ-API-037 | 删除 | 重复删除 | 接口测试 | Postman | 幂等 | 已删 id | 再次 `DELETE` | 失败或不存在 |
| ZCZX-SCJLDJ-API-038 | 端到端 | CRUD 串联 | 接口测试 | Postman | §7 | 已登录 | `POST`→`list`→`GET`→`PUT`→`DELETE` | 每步 `code=200` |
| ZCZX-SCJLDJ-API-039 | 鉴权 | 未登录 | 接口测试 | curl | 安全 | 无 Token | `GET /list` | 401 或统一未登录 |
| ZCZX-SCJLDJ-API-040 | 权限 | 无新增权限 | 接口测试 | Postman | §8 | 仅 list | `POST` | 无权限失败 |
| ZCZX-SCJLDJ-API-041 | 权限 | 无删除权限 | 接口测试 | Postman | §8 | 无 remove | `DELETE` | 无权限失败 |
| ZCZX-SCJLDJ-API-042 | 路由 | 辅助在详情前 | 接口测试 | Postman | 技术方案 §7 | 已登录 | `GET /stallOptions`、`/supplierEarTags` | 非 404（不被 `/{id}` 吞掉） |

---

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

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

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| ZCZX-SCJLDJ-UI-001 | 列表 | 默认展示 | UI 测试 | Playwright+Chrome | §6.2 | 已登录；有数据 | 进入「市场进栏登记」 | 列含进栏时间、栏位、供应商、耳标、数量、操作 |
| ZCZX-SCJLDJ-UI-002 | 列表 | 进栏日期筛选 | UI 测试 | Playwright+Chrome | §6.2 | 已知日期记录 | 选日期起止→搜索 | 仅范围内行 |
| ZCZX-SCJLDJ-UI-003 | 列表 | 日期区间非法 | UI 测试 | Playwright+Chrome | §6.2 | 已登录 | 开始晚于结束→搜索 | 提示修正；不查询 |
| ZCZX-SCJLDJ-UI-004 | 列表 | 栏位模糊筛选 | UI 测试 | Playwright+Chrome | §6.2 | M1=`01` | 栏位填 `1`→搜索 | 含 M1 行 |
| ZCZX-SCJLDJ-UI-005 | 列表 | 供应商精确 | UI 测试 | Playwright+Chrome | §6.2 | S1、S2 有数据 | 下拉选 S1→搜索 | 仅 S1 |
| ZCZX-SCJLDJ-UI-006 | 列表 | 耳标模糊 | UI 测试 | Playwright+Chrome | §6.2 | 含 E101 | 耳标填片段→搜索 | 命中对应行 |
| ZCZX-SCJLDJ-UI-007 | 列表 | 重置 | UI 测试 | Playwright+Chrome | §6.2 | 已筛选 | 搜索→重置 | 条件清空；列表恢复 |
| ZCZX-SCJLDJ-UI-008 | 列表 | 耳标过长省略 | UI 测试 | Playwright+Chrome | §11 | 多条耳标行 | 看列表耳标列 | 过长省略；详情见全文 |
| ZCZX-SCJLDJ-UI-009 | 新增 | 进栏时间默认 | UI 测试 | Playwright+Chrome | §3.1 | 有 add 权限 | 打开新增 | 默认≈当前时间；可选过去时间 |
| ZCZX-SCJLDJ-UI-010 | 新增 | 栏位下拉单选 | UI 测试 | Playwright+Chrome | §3.2 | M1 有效 | 打开栏位控件 | 仅下拉；不可手输编号 |
| ZCZX-SCJLDJ-UI-011 | 新增 | 未选供应商禁耳标 | UI 测试 | Playwright+Chrome | §3.4 | 新增弹窗 | 未选供应商点耳标 | 不可选或为空；提示先选供应商 |
| ZCZX-SCJLDJ-UI-012 | 新增 | 供应商模糊搜索 | UI 测试 | Playwright+Chrome | §3.3 | 有 S1 | 输入关键字→选 S1 | 选中后展示名称 |
| ZCZX-SCJLDJ-UI-013 | 新增 | 选供应商加载耳标 | UI 测试 | Playwright+Chrome | §4.1～4.2 | 样本 A−B | 选 S1 | 下拉含 E101、E103；不含 E102；**可含**已占用 E103 |
| ZCZX-SCJLDJ-UI-014 | 新增 | 数量联动 | UI 测试 | Playwright+Chrome | §3.5 | 已选 S1 | 勾选 2 个耳标 | 数量（头）=2 且只读 |
| ZCZX-SCJLDJ-UI-015 | 新增 | 更换供应商清空耳标 | UI 测试 | Playwright+Chrome | §3.4 | 已选 S1 与耳标 | 改选 S2 | 已选耳标清空；可选项按 S2 刷新 |
| ZCZX-SCJLDJ-UI-016 | 新增 | 保存成功 | UI 测试 | Playwright+Chrome | §6.1 | 权限齐备 | 填时间/栏位/S1/E101→确定 | 成功提示；列表新行；数量=1 |
| ZCZX-SCJLDJ-UI-017 | 新增 | 取消 | UI 测试 | Playwright+Chrome | §6.1 | 新增 | 填数据→取消 | 弹窗关；无新行 |
| ZCZX-SCJLDJ-UI-018 | 新增 | 无耳标校验 | UI 测试 | Playwright+Chrome | R1 | 新增 | 不选耳标→确定 | 前端或提交失败；至少一个耳标 |
| ZCZX-SCJLDJ-UI-019 | 新增 | 未来时间 | UI 测试 | Playwright+Chrome | R2 | 新增 | 进栏时间选未来→确定 | 校验失败；不能晚于当前 |
| ZCZX-SCJLDJ-UI-020 | 新增 | 无可选耳标提示 | UI 测试 | Playwright+Chrome | §10 | S2 无隔离结束 | 选 S2 看耳标 | 空列表或提示确认隔离登记 |
| ZCZX-SCJLDJ-UI-021 | 查看 | 只读全量耳标 | UI 测试 | Playwright+Chrome | §6.4 | 多条耳标记录 | 点击查看 | 耳标完整；含创建/修改信息 |
| ZCZX-SCJLDJ-UI-022 | 编辑 | 改栏位与耳标 | UI 测试 | Playwright+Chrome | §6.3 | 有记录 | 编辑→换 M2、改耳标→确定 | 列表/详情已更新 |
| ZCZX-SCJLDJ-UI-023 | 编辑 | 从详情进编辑 | UI 测试 | Playwright+Chrome | §6.4 | 查看弹窗 | 点「编辑」 | 编辑弹窗回显 |
| ZCZX-SCJLDJ-UI-024 | 删除 | 二次确认 | UI 测试 | Playwright+Chrome | §6.5 | 无关联行 | 删除→确认 | 确认语含供应商/栏位/时间或头数；行消失 |
| ZCZX-SCJLDJ-UI-025 | 删除 | 取消删除 | UI 测试 | Playwright+Chrome | §6.5 | 任意行 | 删除→取消 | 行仍在 |
| ZCZX-SCJLDJ-UI-026 | 删除 | 释放后可保存 | UI 测试 | Playwright+Chrome | §4.3 | 删 ME-OTHER | 新增选 E103 保存 | 保存成功（下拉本已含 E103） |
| ZCZX-SCJLDJ-UI-029 | 新增 | 占用耳标保存失败 | UI 测试 | Playwright+Chrome | §4.3 | ME-OTHER 占 E103 | 选 E103→确定 | 提示已被占用；不保存 |
| ZCZX-SCJLDJ-UI-027 | 端到端 | 全流程 | UI 集成 | Playwright+Chrome | §7 | 权限齐备 | 新增→筛选→查看→编辑→删除 | 无报错；数据一致 |
| ZCZX-SCJLDJ-UI-028 | 权限 | 只读角色 | UI 测试 | Playwright+Chrome | §8 | 仅 list/query | 进入模块 | 无新增/编辑/删除按钮 |

---

## 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/marketEntry')`（与菜单 `component` 一致）。  
- 列表筛选：`page.getByLabel('进栏日期')` 或占位文案；`page.getByRole('button', { name: '搜索' }).click()`。  
- 新增：`page.getByRole('button', { name: '新增' }).click()` → 选栏位下拉 → 供应商远程搜索选 S1 → 耳标多选 `E101` → 断言数量只读为 1 → `getByRole('button', { name: '确定' }).click()`。  
- 供应商切换：`await page.locator('.supplier-select').click()` 改 S2 后 `expect(earTag).toHaveCount(0)`（按实际组件 class 调整）。  
- 删除确认：`page.getByRole('button', { name: '删除' }).first().click()` → `page.getByRole('dialog').getByRole('button', { name: '确定' }).click()`。

---

## 覆盖对照

| 类别 | 单元 UT | 接口 API | 界面 UI |
| --- | --- | --- | --- |
| 正常流程 | UT-006、021～025 | API-009～016、028～032、038 | UI-009～017、021～023、027 |
| 异常流程 | UT-002～005、007、009、011～015、024 | API-017～027、029、033、036～037 | UI-003、018～019、024～025、029 |
| 业务约束 R1～R6 | UT-004～015 | API-018～027 | UI-011、014～019、029 |
| 耳标池 A−B | UT-016、019～020 | API-010～013 | UI-013、020 |
| 保存占用 C | UT-007～008、017～018 | API-025、032、035 | UI-026、029 |
| 主从表存储 | UT-021～022、025～026 | API-014～015、031 | UI-014、016 |
| 列表筛选 | UT-013、026～028 | API-002～008 | UI-002～008 |
| 逻辑删除 | UT-023 | API-008、034～037 | UI-024～026 |
| 权限/鉴权 | — | API-039～041 | UI-028 |
| 路由顺序 | — | API-042 | — |

---

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

| 需求章节 | 要点 | 主要对应用例 |
| --- | --- | --- |
| §3 字段规则 | 时间、栏位下拉、供应商、耳标、数量联动 | UT-004～006、011～012；API-013～020；UI-009～015、018～019 |
| §4 可选耳标池 | A 按供应商；B 全表病死；下拉 A−B；保存 (A−B)−C | UT-016～020；API-010～013、025～027、032、035；UI-013、020、026、029 |
| §5 R1～R6 | 保存校验 | UT-004～015；API-017～026；UI-014、018～019 |
| §6.1 新增 | CRUD 入口 | API-013～016；UI-009～017 |
| §6.2 列表 | 筛选、排序、仅有效 | API-001～008；UI-001～008 |
| §6.3 修改 | 全字段可改 | API-029～031；UI-022～023 |
| §6.4 查看 | 只读、完整耳标 | API-027；UI-008、021 |
| §6.5 删除 | 二次确认、引用、释放占用 | UT-021～022；API-033～036；UI-024～026 |
| §8 权限 | 按钮级 | API-038～040；UI-028 |
| §10 提示语 | 各失败场景 | API-017～026；UI-011、018～020 |

---

## 修订记录

| 版本 | 说明 |
| --- | --- |
| 1.0 | 初版：单元 25、接口 41、UI 28；覆盖 R1～R6、耳标池、主从表 CRUD、Playwright+Chrome；前缀 **ZCZX-SCJLDJ** |
| 1.1 | 同步需求 v1.6：`supplierEarTags` 仅 A−B（B 全表巡查）；保存 (A−B)−C；单元 28、接口 42、UI 29 |
