# 休药期标准管理 — 测试用例

> 依据：`休药期标准管理功能需求.md`、`休药期标准管理技术方案.md`  
> **接口 Base Path**：`/breedingStandards/diseaseTreatment/withdrawalPeriod`；若含 `context-path` 或网关前缀须补齐。鉴权与若依一致（Cookie / Token）。  
> **权限标识（建议）**：`withdrawalPeriod:standard:list|query|add|edit|remove|syncKb|removeKb`（**无** publish / offline）。

**通用前置（无特殊说明）**：具备本模块菜单与按钮权限的账号已登录；测试环境可访问知识库或已按技术方案启用占位（如 `LOCAL-{id}`）。**药物名称**：必填，**1～20** 字符（计数口径与项目全局一致）；**全表唯一**（trim 后精确匹配）。**药物类型**：`1`抗生素 `2`抗原虫 `3`抗病毒 `4`激素。**休药期**：整数 **1～999** 天。**使用指南 / 药物作用**：必填，各 **1～200** 汉字（计数口径与项目全局一致）。**知识库同步状态**：`0` 未同步 / `1` 已同步；本模块**不设发布/下架**。

**界面（UI）测试**：借助 **Playwright** 启动 **Chromium**（与 **Google Chrome 同源内核**）；可配置 `channel: 'chrome'` 调用本机已安装的谷歌浏览器，模拟点击、输入与断言。

---

## 一、单元测试用例

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| XYP-BZ-UT-001 | 枚举/持久化 | `kb_sync_status` 合法值 | 单元测试 | JUnit5 | 仅两种稳态 | 无 | 校验/映射仅允许 `0`、`1`；写入 `2`、`-1`、`null`（若可） | 拒绝非法值；合法 `0`/`1` 可落库 |
| XYP-BZ-UT-002 | 状态守卫 | 编辑前置 | 单元测试 | JUnit5 | 需求 **4.3** / 矩阵 | 无 | `canEdit(kb=0)` true；`canEdit(kb=1)` false | 与 **§5**「已同步不可改」一致 |
| XYP-BZ-UT-003 | 状态守卫 | 删除前置 | 单元测试 | JUnit5 | 需求 **4.4** | 无 | `canDelete(kb=0)` true；`canDelete(kb=1)` false | 已同步须先移出 |
| XYP-BZ-UT-004 | 状态守卫 | 同步知识库前置 | 单元测试 | JUnit5 | 需求 **4.5** / 矩阵 | 无 | `canSyncKb(kb=0)` true；`canSyncKb(kb=1)` false | 仅未同步可同步 |
| XYP-BZ-UT-005 | 状态守卫 | 移出知识库前置 | 单元测试 | JUnit5 | 需求 **4.6** / 矩阵 | 无 | `canRemoveKb(kb=1)` true；`canRemoveKb(kb=0)` false | 仅已同步可移出 |
| XYP-BZ-UT-006 | 参数校验 | 药物名称长度 | 单元测试 | JUnit5 | 需求 **4.1** | 无 | 空串、1 字、20 字、21 字调用校验/Bean 校验 | 空/21 失败；1～20 通过 |
| XYP-BZ-UT-007 | 参数校验 | 药物类型枚举 | 单元测试 | JUnit5 | 技术方案 **§2.2** | 无 | `drugType ∈ {1,2,3,4}` 通过；`0`、`5`、`null` 失败 | 与四类一致 |
| XYP-BZ-UT-008 | 参数校验 | 休药期天数边界 | 单元测试 | JUnit5 | 需求 **4.1** | 无 | `0`、`1`、`999`、`1000`、非整数（若可输入） | `1～999` 通过；其余失败 |
| XYP-BZ-UT-009 | 参数校验 | 使用指南/药物作用字数 | 单元测试 | JUnit5 | 需求 **4.1** | 无 | 各：空、1 汉字、200 汉字、201 汉字 | 空/超上限失败；合法区间通过 |
| XYP-BZ-UT-010 | Service | 新增落库状态 | 单元测试 | JUnit5+Mockito | 需求 **4.1** | Mock Mapper 成功 | `add(合法 DTO)` | `kb_sync_status=0`；`kb_doc_id` 为空或 null |
| XYP-BZ-UT-011 | Service | 修改—已同步拒绝 | 单元测试 | JUnit5+Mockito | 需求 **2.2**、**4.3** | 库中记录 `kb=1` | `update(...)` | 业务失败；不写业务字段 |
| XYP-BZ-UT-012 | Service | 修改—未同步不调知识库 | 单元测试 | JUnit5+Mockito | 需求 **2.2**（禁止已同步下推库） | `kb=0`；Spy KB 客户端 | `update` 成功路径 | **未调用**知识库「更新/推送」类接口 |
| XYP-BZ-UT-013 | Service | 同步成功 | 单元测试 | JUnit5+Mockito | 需求 **4.5** | `kb=0`；Mock KB 返回 `docId` | `syncKb(id)` | `kb=1`；`kb_doc_id` 非空（或占位 `LOCAL-{id}`） |
| XYP-BZ-UT-014 | Service | 同步失败不翻转 | 单元测试 | JUnit5+Mockito | 需求 **4.5** | `kb=0`；Mock KB 抛错/失败码 | `syncKb(id)` | 仍为 `kb=0`；`kb_doc_id` 不变 |
| XYP-BZ-UT-015 | Service | 移出成功 | 单元测试 | JUnit5+Mockito | 需求 **4.6** | `kb=1`；Mock KB 成功 | `removeKb(id)` | `kb=0`；`kb_doc_id` 清空 |
| XYP-BZ-UT-016 | Service | 移出失败保持已同步 | 单元测试 | JUnit5+Mockito | 需求 **4.6** | `kb=1`；Mock KB 失败 | `removeKb(id)` | 仍为 `kb=1`；`kb_doc_id` 保留 |
| XYP-BZ-UT-017 | Service | 删除—未同步联动幂等删库 | 单元测试 | JUnit5+Mockito | 需求 **4.4** | `kb=0`；Mock KB | `remove(ids)` | 本地记录删除；Mock 验证发起知识库**幂等删除**（若实现为「KB 删除失败则阻断本地删」，须另补专项用例覆盖该策略） |
| XYP-BZ-UT-018 | Service | 删除—已同步拒绝 | 单元测试 | JUnit5+Mockito | 需求 **4.4** | `kb=1` | `remove(ids)` | 失败；记录仍在 |
| XYP-BZ-UT-019 | Mapper/XML（可选） | 列表 keyword 仅 drug_name | 单元测试 | MyBatis `@Test` 或集成 | 需求 **4.2**、技术方案 **3.1** | 库内造数：名称不含「甲」、使用指南含「甲」 | `list` 带 `keyword=甲` | 该条**不**出现在结果（若仅名称模糊） |

---

## 二、接口测试用例

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| XYP-BZ-API-001 | 列表 | 默认分页与排序 | 接口测试 | Postman/curl/MockMvc | 需求 **4.2** | 已登录；有多条 | `GET /withdrawalPeriod/standard/list` | `rows` 条数≤默认（如 20）；`create_time` 新在前（与技术方案建议一致） |
| XYP-BZ-API-002 | 列表 | 药物名称模糊 | 接口测试 | Postman | 需求 **4.2** | 已登录 | `keyword=` 仅命中 `drug_name` 子串 | 返回含预期行；**不**应仅靠 `usage_guide`/`drug_effect` 命中（与 **3.1** 一致） |
| XYP-BZ-API-003 | 列表 | 药物类型精确筛选 | 接口测试 | Postman | 需求 **4.2** | 已登录 | `drugType=1`（可空不传测全集） | 返回行 `drugType` 均为 1 |
| XYP-BZ-API-004 | 列表 | 关键字+类型组合 | 接口测试 | Postman | 组合查询 | 已登录 | 同时传 `keyword`、`drugType` | 同时满足 |
| XYP-BZ-API-005 | 列表 | 展示同步状态 | 接口测试 | Postman | 需求 **4.2** | 已登录；有 0/1 各一条 | `GET .../list` | 每行含 `kbSyncStatus`（或等价字段名与前端一致） |
| XYP-BZ-API-006 | 新增 | 合法提交 | 接口测试 | Postman/Swagger/MockMvc | 正常主流程 | 已登录 | `POST /withdrawalPeriod/standard` Body：`drugName`、`drugType`、`withdrawalDays`、`usageGuide`、`drugEffect` 均合法 | `code` 成功；库中 `kb_sync_status=0` |
| XYP-BZ-API-007 | 新增 | 名称非法 | 接口测试 | Postman | 需求 **4.1** | 已登录 | 名称空或 21 字或缺字段 | 失败；无新记录 |
| XYP-BZ-API-007a | 新增 | 名称重复 | 接口测试 | Postman | 需求 **4.1** 唯一 | 已存在 `drugName=药A` | `POST` 再提交 `drugName=药A` | 失败；`msg` 含「药物名称已存在」 |
| XYP-BZ-API-008 | 新增 | 类型/休药期非法 | 接口测试 | Postman | 需求 **4.1** | 已登录 | `drugType=0` 或 `withdrawalDays=0`/`1000` | 失败 |
| XYP-BZ-API-009 | 新增 | 使用指南/药物作用超长 | 接口测试 | Postman | 需求 **4.1** | 已登录 | 任一项 >200 汉字（按项目计数） | 失败；可读 `msg` |
| XYP-BZ-API-010 | 详情 | 有效 id | 接口测试 | Postman | 需求 **4.7** | 已登录 | `GET /withdrawalPeriod/standard/{id}` | 含 **4.1** 全部业务字段 + 同步状态（及 `kbDocId` 若已同步） |
| XYP-BZ-API-011 | 详情 | 无效 id | 接口测试 | Postman | 异常 | 已登录 | 不存在 id | 404 或业务失败；与项目约定一致 |
| XYP-BZ-API-012 | 修改 | 未同步成功 | 接口测试 | Postman | 需求 **4.3** | 已登录；目标 `kb=0` | `PUT /withdrawalPeriod/standard` Body 含 `id` 与合法字段 | 成功；仍为 `kb=0`（除非另调同步） |
| XYP-BZ-API-013 | 修改 | 已同步拒绝 | 接口测试 | Postman | 需求 **4.3**、矩阵 | 已登录；目标 `kb=1` | `PUT ...` 改任意业务字段 | 失败；数据不变 |
| XYP-BZ-API-013a | 修改 | 名称重复 | 接口测试 | Postman | 需求 **4.3** 唯一 | 已登录；A/B 两条未同步；改 B 的 `drugName` 为 A 的名称 | `PUT` | 失败；提示「药物名称已存在」 |
| XYP-BZ-API-014 | 删除 | 未同步成功 | 接口测试 | Postman | 需求 **4.4** | 已登录；`kb=0` | `DELETE /withdrawalPeriod/standard/{ids}` | 成功；记录消失；对端收到幂等删除（或按项目固定策略） |
| XYP-BZ-API-015 | 删除 | 已同步拒绝 | 接口测试 | Postman | 需求 **4.4** | 已登录；`kb=1` | `DELETE ...` | 失败；提示先移出 |
| XYP-BZ-API-016 | 删除 | 批量 ids | 接口测试 | Postman | 技术方案 **3.5** | 已登录；多条 `kb=0` | `DELETE .../1,2,3`（若依惯例逗号分隔） | 全部删除成功或部分失败规则与项目一致 |
| XYP-BZ-API-017 | 同步知识库 | 未同步成功 | 接口测试 | Postman | 需求 **4.5** | 已登录；`kb=0` | `POST /withdrawalPeriod/standard/syncKb/{id}` | `kb=1`；`kb_doc_id` 有值 |
| XYP-BZ-API-018 | 同步知识库 | 已同步重复调用 | 接口测试 | Postman | 矩阵 **§5** | 已登录；`kb=1` | `POST .../syncKb/{id}` | 失败；仍为已同步 |
| XYP-BZ-API-019 | 同步知识库 | 失败可重试 | 接口测试 | Postman+Mock 对端 | 需求 **4.5** | KB 不可用或返回失败；`kb=0` | `POST .../syncKb/{id}` | 仍为未同步；`msg` 可读 |
| XYP-BZ-API-020 | 移出知识库 | 已同步成功 | 接口测试 | Postman | 需求 **4.6** | 已登录；`kb=1` | `POST /withdrawalPeriod/standard/removeKb/{id}` | `kb=0`；`kb_doc_id` 空 |
| XYP-BZ-API-021 | 移出知识库 | 未同步调用 | 接口测试 | Postman | 矩阵 | 已登录；`kb=0` | `POST .../removeKb/{id}` | 失败 |
| XYP-BZ-API-022 | 移出知识库 | 失败保持已同步 | 接口测试 | Postman | 需求 **4.6** | Mock KB 失败；`kb=1` | `POST .../removeKb/{id}` | 仍为已同步 |
| XYP-BZ-API-023 | 关键路径 | 移出→改→再同步 | 接口测试 | Postman | 需求 **3.3** | 一条 `kb=1` | 顺序：`removeKb` 成功 → `PUT` 改名称 → `syncKb` 成功 | 最终 `kb=1`；知识库内容与最新业务一致 |
| XYP-BZ-API-024 | 鉴权 | 未登录 | 接口测试 | curl | 安全 | 无 Token/Cookie | `GET .../list` | 401 或重定向登录 |
| XYP-BZ-API-025 | 权限 | 无对应按钮权限 | 接口测试 | Postman | 需求 **§7** | 仅 `list` 无 `add` | `POST /withdrawalPeriod/standard` | 无权限失败 |
| XYP-BZ-API-026 | 错误提示 | 校验与 KB 失败可读 | 接口测试 | Postman | 需求 **§6** | 已登录 | 触发校验失败、同步失败 | `msg` 区分输入不合法与知识库不可用（若项目有约定码） |

---

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

**测试工具**：**Playwright** + **Chromium**（`channel: 'chrome'` 时使用本机 **Google Chrome**）。步骤与脚本映射：`page.goto`、`locator.fill`、`locator.click`、`expect`；删除/同步/移出等二次确认：`page.once('dialog')` 或页面内「确定」按钮。

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| XYP-BZ-UI-001 | 列表 | 默认分页与排序 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.2** | 已登录；数据>默认页大小 | 进入休药期标准列表页 | 每页条数与项目一致；创建时间新在前 |
| XYP-BZ-UI-002 | 列表 | 药物名称关键字 | UI 测试 | Playwright+Chrome/Chromium | 名称模糊 | 存在名称含关键词记录 | 输入关键字→查询 | 表格含该行 |
| XYP-BZ-UI-003 | 列表 | 关键字不匹配「仅指南含词」 | UI 测试 | Playwright+Chrome/Chromium | 与 **4.2**/技术方案「仅 drug_name」 | 造数：名称不含「测」、使用指南含「测」 | 关键字填「测」→查询 | **不**出现该行（验证未对指南模糊） |
| XYP-BZ-UI-004 | 列表 | 药物类型筛选 | UI 测试 | Playwright+Chrome/Chromium | 精确筛选 | 多条不同类型 | 选某一类型→查询 | 类型列均为所选 |
| XYP-BZ-UI-005 | 列表 | 组合与重置 | UI 测试 | Playwright+Chrome/Chromium | 恢复条件 | 已筛选 | 关键字+类型→查询→重置 | 恢复默认列表 |
| XYP-BZ-UI-006 | 列表 | 同步状态列与操作矩阵 | UI 测试 | Playwright+Chrome/Chromium | 需求 **§5** | 准备未同步、已同步各一条 | 逐行查看操作按钮/菜单 | **未同步**：可查、改、删、同步；**无**移出。**已同步**：可查、移出；**无**改、删、同步 |
| XYP-BZ-UI-007 | 新增 | 完整合法主流程 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.1**、**3.1** | 已登录；有新增权限 | 新增→填名称(1～20)、类型、休药期、使用指南、药物作用→提交 | 成功提示；列表出现；状态为**未同步** |
| XYP-BZ-UI-008 | 新增 | 边界与必填校验 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.1**、**§6** | 已登录 | 分别测：名称空/21 字；休药期 0/1000；指南或作用空/超长；类型未选 | 对应字段校验失败；无脏数据落库 |
| XYP-BZ-UI-009 | 详情 | 字段与同步状态展示 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.7** | 已有一条记录 | 点详情/查看 | 展示 **4.1** 全部字段 + 未同步/已同步 |
| XYP-BZ-UI-010 | 详情 | 已同步只读 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.7** | 已同步记录 | 打开详情 | 无保存式编辑或编辑入口不可用 |
| XYP-BZ-UI-011 | 详情 | 未同步可进编辑 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.7** | 未同步记录 | 详情→编辑（或列表编辑） | 可修改并保存 |
| XYP-BZ-UI-012 | 修改 | 未同步保存成功 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.3** | 未同步样本 | 改名称或休药期→保存 | 成功；仍为**未同步** |
| XYP-BZ-UI-013 | 修改 | 已同步不可改 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.3**、矩阵 | 已同步样本 | 尝试从列表/详情进入编辑 | 无编辑或保存被拒绝 |
| XYP-BZ-UI-014 | 知识库 | 同步成功 | UI 集成 | Playwright+Chrome/Chromium | 需求 **4.5** | 未同步记录 | 点「同步知识库」→确认（若有） | 状态变为**已同步**；可复查详情 `kbDocId`（若 UI 展示） |
| XYP-BZ-UI-015 | 知识库 | 已同步无「同步」入口 | UI 测试 | Playwright+Chrome/Chromium | 矩阵 | 已同步行 | 查看操作区 | 「同步」隐藏或禁用 |
| XYP-BZ-UI-016 | 知识库 | 同步失败提示与可重试 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.5**、**§6** | 未同步；KB 失败环境或 Mock | 点同步 | 明确失败文案；仍为未同步；可再次点击 |
| XYP-BZ-UI-017 | 知识库 | 移出成功 | UI 集成 | Playwright+Chrome/Chromium | 需求 **4.6** | 已同步记录 | 点「移出知识库」→确认 | 变为**未同步**；可再编辑 |
| XYP-BZ-UI-018 | 知识库 | 未同步无「移出」 | UI 测试 | Playwright+Chrome/Chromium | 矩阵 | 未同步行 | 查看操作区 | 「移出」隐藏或禁用 |
| XYP-BZ-UI-019 | 知识库 | 移出失败提示 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.6** | 已同步；KB 移出失败 | 点移出 | 仍为已同步；可读错误提示 |
| XYP-BZ-UI-020 | 删除 | 未同步删除 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.4** | 未同步记录 | 删除→二次确认确定 | 记录从列表消失 |
| XYP-BZ-UI-021 | 删除 | 已同步不可删 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.4** | 已同步记录 | 尝试删除 | 无删除或操作失败并提示先移出 |
| XYP-BZ-UI-022 | 删除 | 取消确认不删 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.4** 交互 | 未同步 | 删除→点取消 | 记录仍在 |
| XYP-BZ-UI-023 | 端到端 | 状态机全链路 | UI 集成 | Playwright+Chrome/Chromium | 需求 **3.3** | 可新建 | 新增(未同步)→同步(已同步)→详情只读→移出(未同步)→编辑保存→再同步(已同步)→移出→删除 | 每步状态与矩阵一致；最终记录删除成功 |
| XYP-BZ-UI-024 | 权限 | 只读/无写权限账号 | UI 测试 | Playwright+Chrome/Chromium | 需求 **§7** | 无 `add`/`edit` 等 | 登录进入模块 | 无新增/编辑/删除或按钮不可见；列表可查（若有 `list`） |
| XYP-BZ-UI-025 | 回归 | 无发布/下架入口 | UI 测试 | Playwright+Chrome/Chromium | 需求 **§1 边界** | 任意状态记录 | 扫页面按钮与路由 | **无**「发布」「下架」类操作 |

---

## 四、用例与需求/设计追溯（摘要）

| 需求章节 | 单元测试 | 接口测试 | UI 测试 |
| --- | --- | --- | --- |
| **4.1** 新增字段与规则 | UT-006～010 | API-006～009 | UI-007、UI-008 |
| **4.2** 列表 | UT-019 | API-001～005 | UI-001～005 |
| **4.3** 修改 | UT-002、011、012 | API-012、013 | UI-012、013 |
| **4.4** 删除与幂等通知 | UT-003、017、018 | API-014～016 | UI-020～022 |
| **4.5** 同步 | UT-004、013、014 | API-017～019 | UI-014～016 |
| **4.6** 移出 | UT-005、015、016 | API-020～022 | UI-017～019 |
| **4.7** 详情 | — | API-010、011 | UI-009～011 |
| **§5** 矩阵 | UT-002～005 | API-013、015、018、021 | UI-006、013、015、018、021 |
| **§6** 错误提示 | UT-009 | API-026 | UI-008、016、019 |
| **3.3** 关键路径 | — | API-023 | UI-023 |

---

## 五、Playwright 脚本提示（可选落地）

```typescript
// 示例：使用本机 Chrome（需已安装）；无则去掉 channel 使用内置 Chromium
import { test, expect } from '@playwright/test';

test.use({ channel: 'chrome', headless: true });

test('XYP-BZ-UI-023 摘要：新增→同步→移出→改→再同步→删', async ({ page }) => {
  // 1. 登录（URL/账号按环境配置）
  await page.goto(process.env.BASE_URL + '/login');
  await page.getByPlaceholder('用户名').fill(process.env.USER!);
  await page.getByPlaceholder('密码').fill(process.env.PASS!);
  await page.getByRole('button', { name: /登录/ }).click();
  // 2. 进入「休药期标准」菜单（菜单文案以实际为准）
  await page.getByText('休药期标准', { exact: false }).click();
  // 3. 新增、同步、移出、编辑、再同步、删除… 使用 getByRole/getByLabel 定位表单与按钮
  // 4. expect(locator).toContainText('未同步'|'已同步') 等断言
});
```

**说明**：具体菜单路径、表单 `label`、按钮文案以若依前端实现为准；建议将 `BASE_URL`、登录信息与「同步/移出」确认框选择器抽为环境变量或 Page Object，便于 CI 与多环境复用。
