# 药物禁配标准管理 — 测试用例

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

**通用前置（无特殊说明）**：具备本模块菜单与按钮权限的账号已登录；测试环境可访问知识库或已按技术方案启用占位（如 `LOCAL-{id}`）。**主药名称**：必填，**1～20** 字符（计数口径与项目全局一致）；**允许重复**。**配伍药物**：至少 **1** 条；每条名称 **1～20** 字符；trim 后**互不相同**且**均不等于主药**；顺序与保存一致。**配伍结果**：选填，**≤50** 字符。**知识库同步状态**：`0` 未同步 / `1` 已同步；本模块**不设发布/下架**。列表默认 **keyword 仅匹配主药名**（不匹配配伍名，除非需求已扩展并验收）。

**知识库说明**：后端当前可为**占位**实现（同步即成功写 `LOCAL-{id}`）。含「知识库调用失败」的用例在占位阶段可标为 **待 HTTP 对接后执行**；或通过对 Service 编排层 Mock 失败路径单独补测。

**界面（UI）测试**：借助 **Playwright** 启动 **Chromium**（与 **Google Chrome 同源内核**）；可配置 `channel: 'chrome'` 调用本机已安装的谷歌浏览器，模拟点击、输入与断言。前端菜单路由以实际若依页面为准（未上线页面时步骤中菜单名写为**占位**，落地后替换）。

---

## 一、单元测试用例

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| JP-BZ-UT-001 | 枚举/持久化 | `kb_sync_status` 合法值 | 单元测试 | JUnit5 | 仅两种稳态 | 无 | 校验/映射仅允许 `0`、`1`；拒绝 `2`、`-1` 等 | 非法值拒绝；`0`/`1` 合法 |
| JP-BZ-UT-002 | 状态守卫 | 编辑前置 | 单元测试 | JUnit5 | 需求 **4.3**、操作矩阵 | 无 | `canEdit(kb=0)` true；`canEdit(kb=1)` false | 已同步不可改 |
| JP-BZ-UT-003 | 状态守卫 | 删除前置 | 单元测试 | JUnit5 | 需求 **4.4** | 无 | `canDelete(kb=0)` true；`canDelete(kb=1)` false | 已同步须先移出 |
| JP-BZ-UT-004 | 状态守卫 | 同步知识库前置 | 单元测试 | JUnit5 | 需求 **4.5**、矩阵 | 无 | `canSyncKb(kb=0)` true；`canSyncKb(kb=1)` false | 仅未同步可同步 |
| JP-BZ-UT-005 | 状态守卫 | 移出知识库前置 | 单元测试 | JUnit5 | 需求 **4.6**、矩阵 | 无 | `canRemoveKb(kb=1)` true；`canRemoveKb(kb=0)` false | 仅已同步可移出 |
| JP-BZ-UT-006 | 参数校验 | 主药名称长度 | 单元测试 | JUnit5 | 需求 **4.1** | 无 | 空串、21 字、`validateMainDrugName`；合法 1～20 字 | 空/超长失败；合法通过 |
| JP-BZ-UT-007 | 参数校验 | 配伍结果长度 | 单元测试 | JUnit5 | 需求 **4.1** | 无 | `null`、全空白、50 字、51 字 | `null`/空白/50 通过；51 失败 |
| JP-BZ-UT-008 | 参数校验 | 配伍列表至少一条 | 单元测试 | JUnit5 | 需求 **2.3**、**4.1** | `companions=[]` | `validateCompanions` | 抛错（至少一条） |
| JP-BZ-UT-009 | 参数校验 | 配伍名同条重复 | 单元测试 | JUnit5 | 需求 **2.3** | 两条配伍 trim 后同名 | `validateCompanions` | 抛错（不能重复） |
| JP-BZ-UT-010 | 参数校验 | 配伍名与主药相同 | 单元测试 | JUnit5 | 需求 **2.3** | 配伍名与主药 trim 后相同 | `validateCompanions` | 抛错（不能与主药相同） |
| JP-BZ-UT-011 | 参数校验 | `trimFieldsForSave` | 单元测试 | JUnit5 | 需求 **2.3** 名称口径 | 主药/配伍含首尾空格；配伍结果为空白串 | `trimFieldsForSave` 后再校验 | 主药、配伍 trim 正确；空配伍结果变 `null` |
| JP-BZ-UT-012 | Service | 新增落库与子表 | 单元测试 | JUnit5+Mockito | 需求 **4.1** | Mock 主表 insert 回填 id；Mock 子表 insert | `insertBizDrugIncompatibilityStandard(合法体)` | `kb_sync_status=0`；子表 insert 被调用 |
| JP-BZ-UT-013 | Service | 修改—已同步拒绝 | 单元测试 | JUnit5+Mockito | 需求 **2.2**、**4.3** | 库中 `kb=1` | `updateBizDrugIncompatibilityStandard` | 业务失败；不写主表 update |
| JP-BZ-UT-014 | Service | 修改—未同步替换子表 | 单元测试 | JUnit5+Mockito | 技术方案一致性 | `kb=0`；Mock update/delete/insert | `update` 成功路径 | 先 `deleteByStandardId` 再批量 insert；`kb` 不被客户端篡改 |
| JP-BZ-UT-015 | Service | 修改—主键为空 | 单元测试 | JUnit5+Mockito | 异常流程 | `id=null` | `update` | 抛错（主键不能为空） |
| JP-BZ-UT-016 | Service | 同步成功（占位） | 单元测试 | JUnit5+Mockito | 需求 **4.5** | `kb=0`；Mock update | `syncKb(id)` | `kb=1`；`kb_doc_id` 含 `LOCAL-{id}` 或非空 |
| JP-BZ-UT-017 | Service | 同步—已同步拒绝 | 单元测试 | JUnit5+Mockito | 需求 **4.5** 互斥 | `kb=1` | `syncKb` | 抛错；不更新为成功态 |
| JP-BZ-UT-018 | Service | 移出成功 | 单元测试 | JUnit5+Mockito | 需求 **4.6** | `kb=1`；Mock update | `removeKb(id)` | `kb=0`；`kb_doc_id` 清空 |
| JP-BZ-UT-019 | Service | 移出—未同步拒绝 | 单元测试 | JUnit5+Mockito | 矩阵互斥 | `kb=0` | `removeKb` | 抛错 |
| JP-BZ-UT-020 | Service | 删除—已同步拒绝 | 单元测试 | JUnit5+Mockito | 需求 **4.4** | `kb=1` | `deleteBizDrugIncompatibilityStandardByIds` | 抛错；不执行主表 delete |
| JP-BZ-UT-021 | Service | 删除—批量含已同步整单失败 | 单元测试 | JUnit5+Mockito | 需求 **4.4** 批量 | ids 含未同步+已同步 | `delete...ByIds` | 抛错；主表 delete 不调用 |
| JP-BZ-UT-022 | Service | 删除—未同步成功 | 单元测试 | JUnit5+Mockito | 需求 **4.4** | 全部 `kb=0` | `delete...ByIds` | 主表 delete 调用（子表级联由库保证） |
| JP-BZ-UT-023 | Service | 详情带子表顺序 | 单元测试 | JUnit5+Mockito | 需求 **4.7** | Mock 主表+多行子表 | `selectBizDrugIncompatibilityStandardById` | `companions` 条数与顺序与 Mapper 一致 |
| JP-BZ-UT-024 | Mapper/XML（可选） | 列表 keyword 仅主药 | 单元测试 | MyBatis 集成或手工 SQL | 需求 **4.2**、技术方案 **3.1** | 造数：主药不含「甲」、配伍名含「甲」 | `list` 带 `keyword=甲` | 该条**不**命中（默认仅主药模糊） |

---

## 二、接口测试用例

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| JP-BZ-API-001 | 列表 | 默认分页与排序 | 接口测试 | Postman/curl/MockMvc | 需求 **4.2** | 已登录；有多条 | `GET /drugIncompatibility/standard/list?pageNum=1&pageSize=10` | `rows`/`total` 正常；默认创建时间新在前（与技术方案一致） |
| JP-BZ-API-002 | 列表 | 主药名称模糊 | 接口测试 | Postman | 需求 **4.2** | 已登录 | `keyword=` 仅命中 `main_drug_name` 子串 | 返回含预期行 |
| JP-BZ-API-003 | 列表 | 关键字不匹配「仅配伍含词」 | 接口测试 | Postman | 需求 **4.2**、**1.2** | 主药不含「测」、配伍名含「测」 | `keyword=测` | **不**出现该行 |
| JP-BZ-API-004 | 列表 | 展示同步状态与配伍条数 | 接口测试 | Postman | 需求 **4.2** | 已登录；有 0/1 状态数据 | `GET .../list` | 行内含 `kbSyncStatus`、`companionCount`（字段名与前端一致即可） |
| JP-BZ-API-005 | 新增 | 合法提交（含多配伍） | 接口测试 | Postman/MockMvc | 正常主流程 **4.1** | 已登录；`add` 权限 | `POST /drugIncompatibility/standard` Body：`mainDrugName`、`compatResult` 可空、`companions`≥1 | `code` 成功；库中 `kb_sync_status=0`；子表条数一致 |
| JP-BZ-API-006 | 新增 | 主药非法 | 接口测试 | Postman | 需求 **4.1** | 已登录 | 主药空或 21 字或缺主药 | 失败；无新主表行 |
| JP-BZ-API-006a | 新增 | 主药名称可重复 | 接口测试 | Postman | 需求 **4.1** | 已存在 `mainDrugName=主药A` | `POST` 再提交同名（配伍不同） | 成功；两条主药名相同记录并存 |
| JP-BZ-API-007 | 新增 | 配伍条数/重复/同主药 | 接口测试 | Postman | 需求 **2.3**、**4.1** | 已登录 | `companions` 空数组；或两配伍同名；或配伍=主药 | 失败；可读 `msg` |
| JP-BZ-API-008 | 新增 | 配伍结果超长 | 接口测试 | Postman | 需求 **4.1** | 已登录 | `compatResult` 长度 51 | 失败 |
| JP-BZ-API-009 | 详情 | 有效 id | 接口测试 | Postman | 需求 **4.7** | 已登录 | `GET /drugIncompatibility/standard/{id}` | 含主药、有序 `companions`、配伍结果、同步状态 |
| JP-BZ-API-010 | 详情 | 无效 id | 接口测试 | Postman | 异常 | 已登录 | 不存在 id | 404 或业务失败；与项目约定一致 |
| JP-BZ-API-011 | 修改 | 未同步成功 | 接口测试 | Postman | 需求 **4.3** | 已登录；目标 `kb=0` | `PUT /drugIncompatibility/standard` Body 含 `id`、主药/配伍/结果 | 成功；仍为 `kb=0`；详情与提交一致 |
| JP-BZ-API-012 | 修改 | 已同步拒绝 | 接口测试 | Postman | 需求 **4.3**、矩阵 | 已登录；目标 `kb=1` | `PUT ...` 改任意业务字段 | 失败；数据不变 |
| JP-BZ-API-013 | 删除 | 未同步成功 | 接口测试 | Postman | 需求 **4.4** | 已登录；`kb=0` | `DELETE /drugIncompatibility/standard/{id}` | 成功；记录消失（GET 无或失败） |
| JP-BZ-API-014 | 删除 | 已同步拒绝 | 接口测试 | Postman | 需求 **4.4** | 已登录；`kb=1` | `DELETE ...` | 失败；提示先移出 |
| JP-BZ-API-015 | 删除 | 批量 ids | 接口测试 | Postman | 技术方案 **3.5** | 已登录；多条 `kb=0` | `DELETE .../1,2,3` | 全部删除成功（与若依逗号分隔惯例一致） |
| JP-BZ-API-016 | 删除 | 批量含已同步整单失败 | 接口测试 | Postman | 需求 **4.4** 批量 | ids 含未同步+已同步 | `DELETE .../id1,id2` | 失败；均未删或符合先校验实现；`msg` 可读 |
| JP-BZ-API-017 | 同步知识库 | 未同步成功 | 接口测试 | Postman | 需求 **4.5** | 已登录；`kb=0` | `POST /drugIncompatibility/standard/syncKb/{id}` | `kb=1`；`kb_doc_id` 非空 |
| JP-BZ-API-018 | 同步知识库 | 已同步重复调用 | 接口测试 | Postman | 矩阵、**4.5** 互斥 | 已登录；`kb=1` | `POST .../syncKb/{id}` | 失败；仍为已同步 |
| JP-BZ-API-019 | 同步知识库 | 失败不翻转 | 接口测试 | Postman+Mock 对端 | 需求 **4.5** | **待 KB 对接**：对端失败 | `POST .../syncKb/{id}` | 仍为未同步；`msg` 可读可重试（占位阶段可跳过） |
| JP-BZ-API-020 | 移出知识库 | 已同步成功 | 接口测试 | Postman | 需求 **4.6** | 已登录；`kb=1` | `POST /drugIncompatibility/standard/removeKb/{id}` | `kb=0`；`kb_doc_id` 空 |
| JP-BZ-API-021 | 移出知识库 | 未同步调用 | 接口测试 | Postman | 矩阵 | 已登录；`kb=0` | `POST .../removeKb/{id}` | 失败 |
| JP-BZ-API-022 | 移出知识库 | 失败保持已同步 | 接口测试 | Postman | 需求 **4.6** | **待 KB 对接**：对端失败 | `POST .../removeKb/{id}` | 仍为已同步（占位阶段可跳过） |
| JP-BZ-API-023 | 关键路径 | 移出→改→再同步 | 接口测试 | Postman | 需求 **3.3** | 一条 `kb=1` | `removeKb`→`PUT`→`syncKb` | 最终 `kb=1`；业务与最后一次 PUT 一致 |
| JP-BZ-API-024 | 鉴权 | 未登录 | 接口测试 | curl | 安全 | 无 Token/Cookie | `GET .../list` | 401 或重定向登录 |
| JP-BZ-API-025 | 权限 | 无对应按钮权限 | 接口测试 | Postman | 需求 **第 7 章** | 仅 `list` 无 `add` | `POST /drugIncompatibility/standard` | 无权限失败 |
| JP-BZ-API-026 | 错误提示 | 校验与业务失败可读 | 接口测试 | Postman | 需求 **第 6 章** | 已登录 | 触发校验失败、已同步删除等 | `msg` 区分输入不合法与状态不允许 |

---

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

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

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| JP-BZ-UI-001 | 列表 | 默认分页与排序 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.2** | 已登录；数据>默认页大小 | 进入药物禁配标准列表（路由占位） | 每页条数与项目一致；创建时间新在前 |
| JP-BZ-UI-002 | 列表 | 主药关键字 | UI 测试 | Playwright+Chrome/Chromium | 主药模糊 | 存在主药含关键词记录 | 输入关键字→查询 | 表格含该行 |
| JP-BZ-UI-003 | 列表 | 关键字不匹配「仅配伍含词」 | UI 测试 | Playwright+Chrome/Chromium | 与 **4.2** 默认维度 | 造数：主药不含「测」、配伍含「测」 | 关键字「测」→查询 | **不**出现该行 |
| JP-BZ-UI-004 | 列表 | 同步状态与配伍条数/摘要 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.2** | 多条数据 | 查看列表列 | 可见未同步/已同步；可见配伍条数或等价摘要（若 UI 实现） |
| JP-BZ-UI-005 | 列表 | 操作矩阵 | UI 测试 | Playwright+Chrome/Chromium | 需求 **第 5 章** | 未同步、已同步各一条 | 逐行查看按钮 | 未同步：查/改/删/同步，无移出；已同步：查/移出，无改/删/同步 |
| JP-BZ-UI-006 | 新增 | 完整合法主流程 | UI 集成 | Playwright+Chrome/Chromium | 需求 **4.1**、**3.2** | 有新增权限 | 新增→主药+至少一条配伍+可选配伍结果→提交 | 成功提示；列表新行**未同步** |
| JP-BZ-UI-007 | 新增 | 配伍行增减与边界 | UI 测试 | Playwright+Chrome/Chromium | 需求 **2.3**、**4.1** | 打开新增 | 用 `+`/`-` 增减行；试删至 0 条；两配伍同名；配伍=主药；配伍结果超长 | 对应校验失败；不能 0 条保存 |
| JP-BZ-UI-008 | 详情 | 字段与同步状态 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.7** | 已有一条 | 点详情/查看 | 展示主药、有序配伍列表、配伍结果、同步状态 |
| JP-BZ-UI-009 | 详情 | 已同步只读 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.7** | 已同步记录 | 打开详情 | 无保存式编辑或编辑入口不可用 |
| JP-BZ-UI-010 | 详情 | 未同步可进编辑 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.7** | 未同步记录 | 详情或列表进编辑 | 可改并保存；列表/详情刷新后所见即所得 |
| JP-BZ-UI-011 | 修改 | 未同步保存成功 | UI 集成 | Playwright+Chrome/Chromium | 需求 **4.3** | 未同步样本 | 改主药或配伍顺序/内容→保存 | 成功；仍为**未同步** |
| JP-BZ-UI-012 | 修改 | 已同步不可改 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.3**、矩阵 | 已同步样本 | 尝试编辑 | 无编辑或保存被拒绝 |
| JP-BZ-UI-013 | 知识库 | 同步成功 | UI 集成 | Playwright+Chrome/Chromium | 需求 **4.5** | 未同步记录 | 点「同步知识库」→确认（若有） | 状态变**已同步** |
| JP-BZ-UI-014 | 知识库 | 已同步无「同步」入口 | UI 测试 | Playwright+Chrome/Chromium | 矩阵 | 已同步行 | 查看操作区 | 「同步」隐藏或禁用 |
| JP-BZ-UI-015 | 知识库 | 同步失败提示与可重试 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.5**、**第 6 章** | 未同步；KB 失败环境或 Mock | 点同步 | 明确失败文案；未同步；可再试（占位阶段可跳过） |
| JP-BZ-UI-016 | 知识库 | 移出成功 | UI 集成 | Playwright+Chrome/Chromium | 需求 **4.6** | 已同步记录 | 点「移出知识库」→确认 | 变**未同步**；可再编辑 |
| JP-BZ-UI-017 | 知识库 | 未同步无「移出」 | UI 测试 | Playwright+Chrome/Chromium | 矩阵 | 未同步行 | 查看操作区 | 「移出」隐藏或禁用 |
| JP-BZ-UI-018 | 知识库 | 移出失败提示 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.6** | 已同步；KB 移出失败（对接后） | 点移出 | 仍为已同步；可读错误（占位阶段可跳过） |
| JP-BZ-UI-019 | 删除 | 未同步删除 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.4** | 未同步记录 | 删除→二次确认确定 | 记录从列表消失 |
| JP-BZ-UI-020 | 删除 | 已同步不可删 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.4** | 已同步记录 | 尝试删除 | 无删除或失败并提示先移出 |
| JP-BZ-UI-021 | 删除 | 取消确认不删 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.4** 交互 | 未同步 | 删除→取消 | 记录仍在 |
| JP-BZ-UI-022 | 批量删除 | 含已同步整单失败 | UI 测试 | Playwright+Chrome/Chromium | 需求 **4.4** | 前端若支持多选删除 | 勾选未同步+已同步→删除 | 整单失败提示；数据未被部分误删 |
| JP-BZ-UI-023 | 端到端 | 状态机全链路 | UI 集成 | Playwright+Chrome/Chromium | 需求 **3.2**、**3.3** | 可新建 | 新增(未同步)→同步(已同步)→详情只读→移出(未同步)→编辑保存→再同步(已同步)→移出→删除 | 每步与矩阵一致；最终删除成功 |
| JP-BZ-UI-024 | 权限 | 只读/无写权限账号 | UI 测试 | Playwright+Chrome/Chromium | 需求 **第 7 章** | 无 `add`/`edit` 等 | 登录进入模块 | 无新增/编辑/删除或按钮不可见；若有 `list` 可查 |
| JP-BZ-UI-025 | 回归 | 无发布/下架入口 | UI 测试 | Playwright+Chrome/Chromium | 需求 **第 1 章** 边界 | 任意状态 | 扫页面按钮与路由 | **无**「发布」「下架」类操作 |

---

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

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

---

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

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

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

test('JP-BZ-UI-023 摘要：新增→同步→移出→改→再同步→移出→删', async ({ page }) => {
  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();
  // 进入「药物禁配标准」菜单（文案与路由以实际为准）
  await page.getByText('药物禁配', { exact: false }).click();
  // 新增：填写主药、配伍行（+ 添加行）、配伍结果；提交
  // 列表：同步知识库 → 详情断言已同步 → 移出 → 编辑保存 → 再同步 → 移出 → 删除
  // await expect(page.getByRole('row', { name: /未同步|已同步/ })).toBeVisible();
});
```

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