# 牦牛疾病预警 — 测试用例

> 依据：`牦牛疾病预警功能需求.md`、`牦牛疾病预警技术方案.md`  
> **后台接口 Base Path（示例）**：`/dataModel/yakDiseaseWarning`；**移动端**：`/app/healthAlert`；若含 `context-path` 或网关前缀须补齐。鉴权与若依一致（Cookie / Token）。

**通用前置（无特殊说明）**：具备本模块菜单与按钮权限的账号已登录；已执行 `sql/biz_yak_disease_warning.sql`；库中已有同步或造数样本。

**预警类型 `warning_type`（技术方案 §2.2）**：`1` 体温异常、`2` 运动量异常、`9` 其他。

**样本约定（示例，评审可替换为环境真实 ID）**

| 代号 | 说明 | 要点 |
| --- | --- | --- |
| **P1** | 牧场甲，`pasture_id=101` | 用户 **UA** 数据权限可见 |
| **P2** | 牧场乙，`pasture_id=202` | 仅用户 **UB** 可见；UA **不可见** |
| **AW1** | 近期预警 | `yak_no=YAK-W-001`，`pasture_id=P1`，`alert_time` 为 3 日内；`alert_message` 含「体温异常」 |
| **AW2** | 超窗预警 | 同 P1，`alert_time` 为 4 个月前 |
| **AW3** | 越权样本 | 归属 P2；UA 列表/详情应不可见 |
| **AW-TP1** | 同步键 | `external_alert_id=TP-ALERT-001`；首次同步 insert，二次改 `alert_message` 为 update |

**界面（UI）测试**：**Playwright** + **Chromium**（`channel: 'chrome'` 使用本机 **Google Chrome**）。菜单组件路径以 `dataModel/yakDiseaseWarning/index` 为准。

---

## 一、单元测试用例

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| ZCZX-MYNBJY-UT-001 | 日期校验 | 开始晚于结束 | 单元测试 | JUnit5 | 功能需求 §5.1 | 无 | `validateDateRange(start=2026-05-10,end=2026-05-01)` | 抛 `ServiceException`；msg 含「开始日期不能晚于结束日期」 |
| ZCZX-MYNBJY-UT-002 | 日期校验 | 仅开始日期 | 单元测试 | JUnit5 | 结束缺省今天 | 固定「今天」=2026-05-20 | `buildCriteria(start=2026-05-01,end=null)` | SQL 条件：`alert_time>=2026-05-01 00:00:00` 且 `<=2026-05-20 23:59:59` |
| ZCZX-MYNBJY-UT-003 | 日期校验 | 仅结束日期 | 单元测试 | JUnit5 | §5.1 | 无 | `buildCriteria(start=null,end=2026-04-30)` | `alert_time<=2026-04-30 23:59:59` |
| ZCZX-MYNBJY-UT-004 | 日期校验 | 起止均为空 | 单元测试 | JUnit5 | 不过滤日期 | 无 | `buildCriteria` 双空 | 生成条件不含 `alert_time` 上下界 |
| ZCZX-MYNBJY-UT-005 | 枚举 | warning_type 合法 | 单元测试 | JUnit5 | §2.2 | 无 | 入参 1、2、9 | 通过；写入 DTO 不报错 |
| ZCZX-MYNBJY-UT-006 | 枚举 | warning_type 非法 | 单元测试 | JUnit5 | 枚举边界 | 无 | 入参 0、3、null（若业务禁止） | 映射失败或落 null（与实现一致并固定） |
| ZCZX-MYNBJY-UT-007 | 文案拼接 | 组装 alert_message | 单元测试 | JUnit5 | 技术方案 §4 | 无 | `measured=39.8℃`、`reference=正常体温`、`type=1` | 生成文案含实测值、参考范围、体温异常语义 |
| ZCZX-MYNBJY-UT-008 | 同步合并 | external_id 新增 | 单元测试 | JUnit5+Mockito | 幂等 | DB 无 TP-ALERT-001 | `upsert(dto)` | `insert`；`insertCount+1` |
| ZCZX-MYNBJY-UT-009 | 同步合并 | external_id 更新 | 单元测试 | JUnit5+Mockito | 去重 | 已有 TP-ALERT-001 | 改 `alert_message` 再 `upsert` | `update`；列表仅 1 行；文案为新值 |
| ZCZX-MYNBJY-UT-010 | 同步安全 | 失败不清库 | 单元测试 | JUnit5+Mockito | §5.3 | 已有 AW1 | Mock 第三方抛异常；`sync` | 已有记录条数与内容不变 |
| ZCZX-MYNBJY-UT-011 | 同步安全 | 单条失败不抹成功条 | 单元测试 | JUnit5+Mockito | 部分成功 | 批次 2 条第 2 条非法 | `syncAll` | 第 1 条落库；`failCount>=1` |
| ZCZX-MYNBJY-UT-012 | 并发 | 同步防重入 | 单元测试 | JUnit5 | 技术方案 §1 | 无 | 连续 `tryLock` 两次 | 第二次失败或跳过；返回「正在同步，请稍候」 |
| ZCZX-MYNBJY-UT-013 | 牧场映射 | 映射成功 | 单元测试 | JUnit5+Mockito | §4 | `biz_pasture` 有编码 | 同步带第三方牧场码 | `pasture_id` 非空；`pasture_name` 与主数据一致 |
| ZCZX-MYNBJY-UT-014 | 牧场映射 | 映射失败仅名称 | 单元测试 | JUnit5+Mockito | §4 | 无匹配主数据 | 未知牧场码+名称 | `pasture_id` 空；`pasture_name` 为第三方名 |
| ZCZX-MYNBJY-UT-015 | Service | 列表牧场权限 | 单元测试 | JUnit5+Mockito | §5.1.4 | UA 仅可见 P1 | `selectList(UA)` | 结果仅 `pasture_id=101`；不含 AW3 |
| ZCZX-MYNBJY-UT-016 | Service | 详情不存在 | 单元测试 | JUnit5+Mockito | 异常 | id 不存在 | `selectById(99999)` | null 或 `ServiceException`「预警记录不存在」 |
| ZCZX-MYNBJY-UT-017 | Service | 详情越权牧场 | 单元测试 | JUnit5+Mockito | §3.2.1 | AW3 属 P2 | UA `selectDetail(AW3.id)` | `ServiceException`「无权查看该预警」 |
| ZCZX-MYNBJY-UT-018 | Mapper | 列表排序 | 单元测试 | JUnit5 | §3.1.1 | 审阅 XML | `selectList` | `ORDER BY alert_time DESC, id DESC` |
| ZCZX-MYNBJY-UT-019 | Mapper | 牧场精确筛选 | 单元测试 | JUnit5 | §3.1.1 | 无 | 审阅 XML | `pastureId` 非空时 `pasture_id = #{pastureId}` |
| ZCZX-MYNBJY-UT-020 | 关联 | yakAssetId 解析 | 单元测试 | JUnit5+Mockito | §3.2.1 可选 | `biz_yak_asset` 有 YAK-W-001 | `selectDetail(AW1)` | `data.yakAssetId` 非空 |

---

## 二、接口测试用例

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| ZCZX-MYNBJY-API-001 | 列表 | 默认分页排序 | 接口测试 | Postman/curl | §5.1 | UA 登录；有 AW1、AW2 | `GET /dataModel/yakDiseaseWarning/list` | `code=200`；`rows`≤20；首条 `alertTime` 最新 |
| ZCZX-MYNBJY-API-002 | 列表 | 日期范围命中 | 接口测试 | Postman | 含 AW1 不含 AW2 | 已登录 | `startDate`、`endDate` 覆盖 AW1 日历日 | 含 AW1；不含 AW2 |
| ZCZX-MYNBJY-API-003 | 列表 | 仅开始日期 | 接口测试 | Postman | §5.1 | 有 AW2 超窗 | 仅传 `startDate`（AW2 之后） | 含 AW2；至今天止 |
| ZCZX-MYNBJY-API-004 | 列表 | 仅结束日期 | 接口测试 | Postman | §5.1 | 有 AW1、AW2 | 仅传 `endDate` 早于 AW1 | 不含 AW1；含更早记录 |
| ZCZX-MYNBJY-API-005 | 列表 | 日期颠倒 | 接口测试 | Postman | 异常 | 已登录 | `startDate=2026-05-10&endDate=2026-05-01` | 失败；msg 含「开始日期不能晚于结束日期」 |
| ZCZX-MYNBJY-API-006 | 列表 | 牧场精确筛选 | 接口测试 | Postman | §5.1 | 有 P1、P2 数据 | `pastureId=101` | 仅 P1 记录 |
| ZCZX-MYNBJY-API-007 | 列表 | 组合筛选 | 接口测试 | Postman | 且关系 | 已登录 | `startDate`+`endDate`+`pastureId` | 同时满足 |
| ZCZX-MYNBJY-API-008 | 列表 | 分页参数 | 接口测试 | Postman | 分页 | 数据>20 | `pageNum=2&pageSize=10` | 第 2 页；条数≤10 |
| ZCZX-MYNBJY-API-009 | 列表 | 数据权限隔离 | 接口测试 | Postman | §5.1.4 | UA 登录；存在 AW3 | `GET list` 不传牧场 | 无 AW3 |
| ZCZX-MYNBJY-API-010 | 列表 | 无数据空列表 | 接口测试 | Postman | 空态 | 新环境无预警 | `GET list` | `rows=[]`；`total=0`；不报错 |
| ZCZX-MYNBJY-API-011 | 详情 | 有效 id 全字段 | 接口测试 | Postman | §3.2.1 | AW1 含扩展字段 | `GET /dataModel/yakDiseaseWarning/{id}` | `data` 含 `yakNo`、`alertTime`、`pastureName`、`alertMessage`、`warningType`、`measuredValue` 等 |
| ZCZX-MYNBJY-API-012 | 详情 | 无效 id | 接口测试 | Postman | 异常 | 已登录 | `GET /{id}` id=999999 | 失败；msg「预警记录不存在」或 `data` 空 |
| ZCZX-MYNBJY-API-013 | 详情 | 越权牧场 | 接口测试 | Postman | §3.2.1 | UA；AW3 属 P2 | `GET /{AW3.id}` | 失败；msg「无权查看该预警」 |
| ZCZX-MYNBJY-API-014 | 详情 | yakAssetId 可选 | 接口测试 | Postman | 跳转档案 | 档案有 YAK-W-001 | `GET detail` | `yakAssetId` 非空（若已实现） |
| ZCZX-MYNBJY-API-015 | 同步 | 成功摘要 | 接口测试 | Postman/Mock | §3.3.1 | Mock 返回 1 新+1 更 | `POST .../sync` | `insertCount`/`updateCount`/`syncTime` 正确；`failCount=0` |
| ZCZX-MYNBJY-API-016 | 同步 | 幂等更新 | 接口测试 | Postman/Mock | AW-TP1 | 已同步 TP-ALERT-001 | 再次同步改文案 | `updateCount>=1`；`insertCount=0`；列表 1 条 |
| ZCZX-MYNBJY-API-017 | 同步 | 第三方超时 | 接口测试 | Postman/Mock | §5.3 | Mock 超时 | `POST .../sync` | 失败；msg 提示不可用/超时；库内 AW1 仍在 |
| ZCZX-MYNBJY-API-018 | 同步 | 鉴权失败 | 接口测试 | Postman/Mock | 第三方 | Mock 401 | `POST .../sync` | 失败；不误删数据 |
| ZCZX-MYNBJY-API-019 | 同步 | 处理中拒重复 | 接口测试 | Postman | 防重 | 同步耗时 Mock | 连续两次 `POST sync` | 第二次 msg「正在同步，请稍候」或 HTTP 业务失败 |
| ZCZX-MYNBJY-API-020 | 同步 | 更新后列表可见 | 接口测试 | Postman | 端到端 | Mock 新预警 | `POST sync`→`GET list` | 列表出现新 `yakNo`/`alertMessage` |
| ZCZX-MYNBJY-API-021 | 范围约束 | 无维护接口 | 接口测试 | Postman | 本期无 CRUD | 已登录 | `POST/PUT/DELETE` 预警实体路径（非 sync） | 404/405 或无权限 |
| ZCZX-MYNBJY-API-022 | 鉴权 | 未登录后台 | 接口测试 | curl | 安全 | 无 Token | `GET .../list` | 401 或统一未登录 |
| ZCZX-MYNBJY-API-023 | 权限 | 无 list | 接口测试 | Postman | §6 | 无 list 角色 | `GET list` | 403 或业务无权限 |
| ZCZX-MYNBJY-API-024 | 权限 | 无 query | 接口测试 | Postman | §6 | 仅有 list | `GET/{id}` | 无权限失败 |
| ZCZX-MYNBJY-API-025 | 权限 | 无 sync | 接口测试 | Postman | §6 | 有 list+query 无 sync | `POST sync` | 403 或业务无权限 |
| ZCZX-MYNBJY-API-026 | 移动端 | 列表默认 | 接口测试 | Postman | §3.5 M1 | 牧民 Token；有 AW1 | `GET /app/healthAlert/list` | `code=200`；含 `yakNo`、`alertTime`、`alertMessage`；时间倒序 |
| ZCZX-MYNBJY-API-027 | 移动端 | 数据权限 | 接口测试 | Postman | 与后台一致 | UA 移动端；有 AW3 | `GET /app/healthAlert/list` | 不含 AW3 |
| ZCZX-MYNBJY-API-028 | 移动端 | 未登录 | 接口测试 | curl | 安全 | 无 Token | `GET /app/healthAlert/list` | 401 |
| ZCZX-MYNBJY-API-029 | 移动端 | 详情可选 | 接口测试 | Postman | M2 可选 | 已实现 M2 | `GET /app/healthAlert/{id}` | 与后台详情字段一致或精简子集 |
| ZCZX-MYNBJY-API-030 | 牧场下拉 | 复用牧场列表 | 接口测试 | Postman | §3.4 | 已登录 | `GET /dataModel/pasture/list?pageSize=500` | 下拉选项与筛选 `pastureId` 一致 |
| ZCZX-MYNBJY-API-031 | 端到端 | 同步-查-详情 | 接口测试 | Postman | §4 | sync+query 权限；Mock | `POST sync`→`GET list`→`GET/{id}` | 各步成功；详情与列表核心字段一致 |
| ZCZX-MYNBJY-API-032 | 响应 | 失败可读 | 接口测试 | Postman | §7 | 已登录 | 触发日期颠倒、不存在 id、越权、同步失败 | `code`、`msg` 可区分场景 |

---

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

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

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| ZCZX-MYNBJY-UI-001 | 列表 | 菜单与标题 | UI 测试 | Playwright+Chrome | 入口 | UA 有菜单权限 | 登录→进入「牦牛疾病预警」 | 页面标题/面包屑正确；见列表区 |
| ZCZX-MYNBJY-UI-002 | 列表 | 表头列完整 | UI 测试 | Playwright+Chrome | §5.1 | 有 AW1 | 查看表头 | 含预警时间、牦牛编号、所属牧场、预警信息、操作 |
| ZCZX-MYNBJY-UI-003 | 列表 | 默认排序 | UI 测试 | Playwright+Chrome | 时间倒序 | 有多条不同时间 | 进入页不筛选 | 最新 `alertTime` 在上 |
| ZCZX-MYNBJY-UI-004 | 列表 | 预警日期筛选 | UI 测试 | Playwright+Chrome | §5.1 | 有 AW1、AW2 | 选范围仅含 AW1→搜索 | 仅 AW1；无 AW2 |
| ZCZX-MYNBJY-UI-005 | 列表 | 所属牧场筛选 | UI 测试 | Playwright+Chrome | 精确 | 有 P1、P2 数据 | 下拉选牧场甲→搜索 | 仅牧场甲记录 |
| ZCZX-MYNBJY-UI-006 | 列表 | 日期颠倒提示 | UI 测试 | Playwright+Chrome | §7 | 已登录 | 开始日晚于结束日→搜索 | 前端校验或接口错误提示；不刷新为错误数据 |
| ZCZX-MYNBJY-UI-007 | 列表 | 重置 | UI 测试 | Playwright+Chrome | §5.2 | 已筛选 | 搜索→点「重置」 | 日期、牧场清空；恢复默认列表 |
| ZCZX-MYNBJY-UI-008 | 列表 | 无数据空态 | UI 测试 | Playwright+Chrome | §5.1.4 | 筛选无结果 | 输入不存在组合→搜索 | 「未找到符合条件的预警」或等价空态 |
| ZCZX-MYNBJY-UI-009 | 列表 | 预警信息长文案 | UI 测试 | Playwright+Chrome | §5.1 | AW1 长 `alertMessage` | 查看列表行 | 换行或 Tooltip 可看全文 |
| ZCZX-MYNBJY-UI-010 | 列表 | 无增删改 | UI 测试 | Playwright+Chrome | §3 | 已登录 | 查看工具栏与操作列 | 无「新增」「编辑」「删除」；操作仅「查看」 |
| ZCZX-MYNBJY-UI-011 | 同步 | 成功提示 | UI 测试 | Playwright+Chrome | §5.3 | 有 sync 权限；第三方可用 | 点「同步」等待完成 | 成功 Message 含新增/更新摘要；列表可刷新 |
| ZCZX-MYNBJY-UI-012 | 同步 | 执行中防重复 | UI 测试 | Playwright+Chrome | 防重 | 有 sync 权限 | 同步中快速再次点击 | 按钮禁用或二次无效 |
| ZCZX-MYNBJY-UI-013 | 同步 | 失败提示 | UI 测试 | Playwright+Chrome | §7 | Mock/断网第三方 | 点「同步」 | 失败提示；列表原数据仍在 |
| ZCZX-MYNBJY-UI-014 | 同步 | 无 sync 权限 | UI 测试 | Playwright+Chrome | §6 | 仅 list+query 账号 | 进入列表 | 无「同步」或点击被拒绝 |
| ZCZX-MYNBJY-UI-015 | 详情 | 入口与只读 | UI 测试 | Playwright+Chrome | §5.4 | 有 query 权限 | 列表点「查看」 | 打开详情/抽屉；无「保存」「确定」编辑主按钮 |
| ZCZX-MYNBJY-UI-016 | 详情 | 核心字段展示 | UI 测试 | Playwright+Chrome | §5.4 | AW1 | 查看详情表单 | 牦牛编号、预警时间、所属牧场、预警信息与接口一致 |
| ZCZX-MYNBJY-UI-017 | 详情 | 扩展字段展示 | UI 测试 | Playwright+Chrome | §5.4 | 样本含扩展字段 | 查看详情 | 展示预警类型、实测值、参考范围、设备号、采集时间等（有则显示） |
| ZCZX-MYNBJY-UI-018 | 详情 | 跳转档案可选 | UI 测试 | Playwright+Chrome | §5.4 | 已实现跳转；档案存在 | 点击牦牛编号链接 | 打开牦牛资产档案详情；不存在则提示 |
| ZCZX-MYNBJY-UI-019 | 详情 | 返回列表 | UI 测试 | Playwright+Chrome | 交互 | 在详情页 | 点返回/关闭 | 回到列表且筛选条件保留 |
| ZCZX-MYNBJY-UI-020 | 权限 | 无菜单 | UI 测试 | Playwright+Chrome | §6 | 无本模块角色 | 登录浏览菜单 | 无「牦牛疾病预警」入口 |
| ZCZX-MYNBJY-UI-021 | 权限 | 无 query 详情 | UI 测试 | Playwright+Chrome | §6 | 仅 list（若可配） | 直接访问详情路由 | 403 或拦截 |
| ZCZX-MYNBJY-UI-022 | 端到端 | 同步-搜-详情 | UI 集成 | Playwright+Chrome | §4 | sync+query | 同步→按日期搜索→查看详情 | 全流程无报错；字段一致 |
| ZCZX-MYNBJY-UI-023 | 移动端 | 健康预警列表 | UI 测试 | Playwright+Chrome | §5.5 | H5/模拟器可访问；牧民登录 | 打开「健康预警」页 | 列表含编号、预警时间、说明；时间倒序 |
| ZCZX-MYNBJY-UI-024 | 移动端 | 头像占位 | UI 测试 | Playwright+Chrome | §5.5 | 无 `yakAvatarUrl` 样本 | 查看条目 | 显示默认占位图 |
| ZCZX-MYNBJY-UI-025 | 移动端 | 空态 | UI 测试 | Playwright+Chrome | §5.5 | 账号无预警 | 进入健康预警 | 「暂无健康预警」类文案 |

---

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

- 安装：`npm i -D @playwright/test`；`npx playwright install chromium`；本机 Chrome：`npx playwright install chrome`。  
- 配置：`playwright.config.ts` 中 `use: { channel: 'chrome', headless: false, locale: 'zh-CN', baseURL: 'http://localhost:80' }`；`storageState` 复用 UA 登录态。  
- 后台路由：`await page.goto('/dataModel/yakDiseaseWarning')`（以菜单 `component` 为准）。  
- 日期：`page.locator('.el-date-editor').first()` 填开始/结束；或 `getByPlaceholder('选择开始日期')`。  
- 牧场：`page.locator('.el-select').filter({ hasText: '所属牧场' })` 选「牧场甲」。  
- 搜索/重置：`page.getByRole('button', { name: '搜索' })` / `{ name: '重置' }`。  
- 同步：`page.getByRole('button', { name: '同步' })`；断言 `page.locator('.el-message--success')` 含「同步」或「新增」「更新」。  
- 详情：`page.getByRole('button', { name: '查看' }).first().click()`；断言 `page.getByText('预警信息')` 可见且无 `page.getByRole('button', { name: '保存' })`。  
- 移动端 H5：独立 `baseURL` 或 `page.goto` 健康预警路径；登录态单独 `storageState`。

---

## 五、覆盖对照

| 类别 | 单元 UT | 接口 API | 界面 UI |
| --- | --- | --- | --- |
| 正常流程 | UT-002～004、008～009、013～015、018～020 | API-001～004、006～008、011、015～016、020、026、031 | UI-001～005、011、015～019、022～023 |
| 异常流程 | UT-001、006、010～011、016～017 | API-005、012～013、017～019、022、028、032 | UI-006、008、013、020～021 |
| 业务约束 | UT-005～007、012、019 | API-006～009、021、027、030 | UI-004～005、007、009～010、014 |
| 同步与第三方 | UT-008～012 | API-015～020 | UI-011～013、022 |
| 数据权限 | UT-015、017 | API-009、013、027 | UI-020～021 |
| 只读无 CRUD | — | API-021 | UI-010、015 |
| 移动端 | — | API-026～029 | UI-023～025 |

---

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

| 需求章节 | 要点 | 主要对应用例 |
| --- | --- | --- |
| §5.1 列表 | 日期、牧场、分页、排序、权限 | API-001～010；UI-002～009 |
| §5.2 工具栏 | 搜索、重置、同步 | UI-007、011～013；API-031 |
| §5.3 同步 | 幂等、失败不清库、防重、摘要 | UT-008～012；API-015～020 |
| §5.4 详情 | 只读、全字段、跳转档案 | API-011～014；UI-015～019 |
| §5.5 移动端 | 列表字段、倒序、空态 | API-026～028；UI-023～025 |
| §5.6 协作 | 牧场下拉、统计口径 | API-030；UT-013～014 |
| §6～§7 权限与提示 | 分权、可读错误 | API-022～025、032；UI-006、013～014、020～021 |
| §3 范围外 | 无手工 CRUD | API-021；UI-010 |

---

## 七、修订记录

| 版本 | 说明 |
| --- | --- |
| 1.0 | 初版：单元 20、接口 32、UI 25；对齐功能/技术方案；Playwright+Chrome；编号前缀 **ZCZX-MYNBJY** |
