# 养殖产业数据 — 测试用例

> 依据：`养殖产业数据功能需求.md`、`养殖产业数据技术方案.md`、`养殖产业数据前端技术方案.md`  
> **接口 Base Path**：`/dataModel/breedingIndustry`（含网关/context-path 时须补齐前缀）。鉴权：若依 Cookie / `Authorization` Token。

**通用前置**：已执行 `sql/biz_pasture.sql`、`sql/biz_grassland.sql`、`sql/biz_yak_asset.sql`、`sql/biz_yak_disease_warning.sql`、`sql/biz_yak_herd_inventory.sql`、`sql/biz_yak_outbound_report.sql`；测试账号具备 `dataModel:breedingIndustry:query` 及菜单「养殖产业数据」。

**UI 工具**：**Playwright** + **Chromium**，`channel: 'chrome'`（本机 **Google Chrome**）。组件路径：`livestockIndustry/breedingIndustry/index`。

---

## 测试数据样本（可替换为环境真实 ID）

| 代号 | 说明 |
| --- | --- |
| **UA** | 仅可见牧场 **P1**（`id=101`） |
| **UB** | 仅可见 **P2**（`id=202`） |
| **ADMIN** | 县级全量，可见 P1+P2 |
| **U0** | 有模块权限但可见牧场 **[]** |
| **P-停** | `biz_status=2` 停业牧场，UA 不可见或全量不计入牧场数 |
| **Y-N** | P1、`asset_status=1`、`del_flag=0` |
| **Y-死/丢/栏** | `status` 2/3/4，不计当前存栏 |
| **Y-U** | `gender` 空，计入总量不计公母 |
| **Y-O** | `status=4`，`status_change_date` 在当年且 ≤ 统计日 |
| **Y-O0** | `status=4`，`status_change_date` 空 |
| **Y-P0** | `pasture_id` 空 |
| **Y-H** | `status=4`，`change_date` 在 3 月；2 月末趋势仍计入 |
| **Y-L** | `status=4`，`change_date` = 2 月最后一天；2 月末 **不计** |
| **W-in** | 当年 1/1 0:00 ≤ `alert_time` ≤ 统计日 23:59:59 |
| **W-out** | `alert_time` 为上年或统计日次日 |
| **G** | 草场 `del_flag=0`；退化 1～5、禁牧/轮牧/全年各造 1 条 |

| **HERD-L** | `biz_yak_herd_inventory` 最近填报年月全县 bull/cow/yak 合计 |
| **HERD-M** | 某历史月填报记录，用于滚动 12 月趋势 |

**联调统计日**：固定 **2026-05-20**（`monthlyTrend` 为所选年 1～N；每项含 `statYear`、`month`、`bullCount`、`cowCount`）。

---

## 覆盖说明

| 类别 | 覆盖要点 |
| --- | --- |
| **正常流程** | 菜单进入自动加载、刷新全页、四块指标与图表展示（§4.2、§5.1～§5.5） |
| **异常流程** | 未登录、无权限、聚合失败、空库、接口不存在维护能力（§3、§7.2、技术§3.1.8） |
| **业务约束** | §2.5～§2.11 口径；§2.9 权限三态；§3 本期不实现项（无 CRUD/筛选/导出） |

---

## 一、单元测试用例

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| ZCZX-YZCYSJ-UT-001 | 数据权限 | 全量可见牧场 ID 列表 | 单元测试 | JUnit5 | §2.6、§2.9 | Mock ADMIN 数据范围 | 调用 `BreedingIndustryPastureScope.resolve()` | 返回全部 `biz_status=1` 且未删牧场 ID；非 null 非空 |
| ZCZX-YZCYSJ-UT-002 | 数据权限 | 可见牧场为空列表 | 单元测试 | JUnit5 | §2.6 空 `[]` | Mock U0 | `loadDashboard([])` | 填报三项仍返回；预警/图表=0；草场仍统计 |
| ZCZX-YZCYSJ-UT-003 | 数据权限 | 部分牧场 IN 过滤 | 单元测试 | JUnit5 | §2.9 | 造数 P1、P2 | `allowed=[101]` 统计牦牛 | 仅 P1 档案计入 |
| ZCZX-YZCYSJ-UT-004 | 存栏 | 仅正常状态 | 单元测试 | JUnit5 | §2.3 | 含 status 1/2/3/4 | `countInventory` | 仅 `asset_status=1` |
| ZCZX-YZCYSJ-UT-005 | 存栏 | 逻辑删除排除 | 单元测试 | JUnit5 | §2.3 | `del_flag=2` 档案 | 统计 | 不计入 |
| ZCZX-YZCYSJ-UT-006 | 存栏 | pasture_id 空排除 | 单元测试 | JUnit5 | §2.9 | Y-P0 | 统计 | 不计入任何牦牛类指标 |
| ZCZX-YZCYSJ-UT-007 | 出栏 | 当年且变更日非空 | 单元测试 | JUnit5 | §2.4 | Y-O、Y-O0 | `countAnnualOutbound` | 仅 Y-O；Y-O0 为 0 |
| ZCZX-YZCYSJ-UT-008 | 出栏 | 周期上界统计日 | 单元测试 | JUnit5 | §2.4 | `change_date=统计日` | 统计 | 计入；次日不计 |
| ZCZX-YZCYSJ-UT-009 | 预警 | 去重 yak_no | 单元测试 | JUnit5 | §2.5 | 同牛 2 条 W-in | `countWarning` | 计 1 |
| ZCZX-YZCYSJ-UT-010 | 预警 | 周期下界 1/1 0:00 | 单元测试 | JUnit5 | §2.5 | `alert_time=当年1/1 00:00:00` | 统计 | 计入 |
| ZCZX-YZCYSJ-UT-011 | 预警 | 周期外排除 | 单元测试 | JUnit5 | §2.5 | W-out | 统计 | 0 |
| ZCZX-YZCYSJ-UT-012 | 预警 | 须关联纳入档案 | 单元测试 | JUnit5 | §2.9 | 预警牛仅 P2、范围 P1 | `countWarning` | 0 |
| ZCZX-YZCYSJ-UT-013 | 存栏变动 | 公母来自所选年最大月 | 单元测试 | JUnit5 | §5.3.1、§2.11 | HERD-L | `loadDashboard` | `bullCount`/`cowCount` 与所选年最大月一致；`unknownGenderCount=yak−公−母` |
| ZCZX-YZCYSJ-UT-028 | 产业总览 | 填报三项所选年最大月 | 单元测试 | JUnit5 | §5.2、§2.11 | Mock 填报合计 | `loadDashboard` | `inventoryTotal`/`annualOutbound`/`pastureCount`/`grasslandAreaMu` 与大屏所选年口径一致 |
| ZCZX-YZCYSJ-UT-014 | 性别 | trim 后匹配 | 单元测试 | JUnit5 | 技术§4 | `gender=' 母 '` | `normalizeGender` | 计母牛（档案类，预警用） |
| ZCZX-YZCYSJ-UT-015 | 存栏结构 | 三类编码 | 单元测试 | JUnit5 | §5.4 | Mock 填报结构 | `buildYakInventoryStructure` | `categoryCode` 为 BULL/COW/INSURED |
| ZCZX-YZCYSJ-UT-016 | 存栏结构 | 占比与零分母 | 单元测试 | JUnit5 | §5.4 | 合计 0 | `buildYakInventoryStructure` | 三类 count=0、ratio=0 仍返回 |
| ZCZX-YZCYSJ-UT-017 | 存栏结构 | 细分明细 | 单元测试 | JUnit5 | §5.4 | 有细分数据 | details | 含种公牛/阉畜/幼畜/成畜/保险 |
| ZCZX-YZCYSJ-UT-018 | 存栏趋势 | 所选年 1～N 长度 | 单元测试 | JUnit5 | §5.3.2、§2.11 | 最大填报月=7 | `buildBullCowMonthlyTrendFromHerdReport` | 长度=**7**；含 statYear+month |
| ZCZX-YZCYSJ-UT-019 | 存栏趋势 | 缺月补零 | 单元测试 | JUnit5 | §2.11 | 仅 HERD-M 有数 | 趋势某月 | `bullCount`/`cowCount` 无填报为 **0** |
| ZCZX-YZCYSJ-UT-020 | 存栏趋势 | 最大月与 header 一致 | 单元测试 | JUnit5 | §5.3.1 | HERD-L | 趋势最后一点 vs header | 公/母分别相等 |
| ZCZX-YZCYSJ-UT-021 | 存栏趋势 | 无填报为空 | 单元测试 | JUnit5 | §2.11 | 所选年无存栏 | 趋势 | 空数组 |
| ZCZX-YZCYSJ-UT-022 | 存栏趋势 | 不依赖档案月末 | 单元测试 | JUnit5 | §2.11 | 档案与填报不一致 | 趋势 | 以填报表为准 |
| ZCZX-YZCYSJ-UT-023 | 牧场 | 原口径暂保留 | 单元测试 | JUnit5 | §2.6 | P-停、注销 | `countPastureInScope` 仍调用 | 结果**不**写入 `pastureCount` |
| ZCZX-YZCYSJ-UT-024 | 草场 | 面积空不参与求和 | 单元测试 | JUnit5 | §2.6 | 一条 `area_mu` null | `sumArea` | 不计该条 |
| ZCZX-YZCYSJ-UT-025 | 草场 | 可用=未退化+轻度 | 单元测试 | JUnit5 | §2.7 | 退化 1、2、3 各 1 | `availableCount` | 2 |
| ZCZX-YZCYSJ-UT-026 | 草场 | 禁牧闭区间 | 单元测试 | JUnit5 | §2.7 | 禁牧期内/外；统计日在期内 | `inUseCount` | 仅期外+轮牧/全年计入 |
| ZCZX-YZCYSJ-UT-027 | 草场 | 退化五档合计 | 单元测试 | JUnit5 | §2.8、§5.5 | G 全集 | `degradationStats` | sum(count)=`grasslandCount` |
| ZCZX-YZCYSJ-UT-028 | 缓存 | 命中未过期 | 单元测试 | JUnit5 | §2.3 | 有 cache 且未过期 | `load(false)` | 不调用聚合；返回缓存 JSON |
| ZCZX-YZCYSJ-UT-029 | 缓存 | forceRefresh 跳过 | 单元测试 | JUnit5 | §3.1.1 | 有缓存 | `load(true)` | 强制实时计算并 UPSERT |

---

## 二、接口测试用例

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| ZCZX-YZCYSJ-API-001 | 看板 | 首次加载全量结构 | 接口测试 | Postman/curl | §5.1 正常流程 | UA 登录；造数完整 | `GET /dataModel/breedingIndustry/dashboard` | `code=200`；`data` 含 `statDate`、`statYear`、`overview`、`inventoryChange`、`yakInventoryStructure`、`grassland` |
| ZCZX-YZCYSJ-API-002 | 看板 | 默认当前年 | 接口测试 | Postman | §2.11 | 已登录；不传 `statYear` | `GET .../dashboard` | `statYear`=服务器当前公历年 |
| ZCZX-YZCYSJ-API-003 | 产业总览 | 六项指标非负 | 接口测试 | Postman | §5.2 | 已知样本 | 读 `overview` 六字段 | 与 UT 手工值一致；`grasslandAreaMu`≥0 |
| ZCZX-YZCYSJ-API-004 | 存栏变动 | 公母与未标注 | 接口测试 | Postman | §5.3.1 | HERD-L | 读 `inventoryChange` | 公/母与所选年最大月一致；`unknownGenderCount=yak−公−母` |
| ZCZX-YZCYSJ-API-005 | 存栏变动 | 所选年 1～N 趋势 | 接口测试 | Postman | §5.3.2 | 最大填报月=5 | `monthlyTrend` | 长度=**5**；含 `statYear`/`month`/`bullCount`/`cowCount`；末项 month=5 |
| ZCZX-YZCYSJ-API-006 | 存栏变动 | 最大月与 header | 接口测试 | Postman | §5.3.1、§2.11 | HERD-L | 比 `monthlyTrend` 最后一项与 header | 公/母分别相等 |
| ZCZX-YZCYSJ-API-007 | 存栏结构 | 固定三类 | 接口测试 | Postman | §5.4 | 已登录 | `yakInventoryStructure` | 长度=**3**；`categoryCode` 为 BULL/COW/INSURED；含 details |
| ZCZX-YZCYSJ-API-008 | 存栏结构 | 占比之和容忍 | 接口测试 | Postman | §5.4 | 存栏>0 | sum(大类 ratio) | 99.9～100.1（四舍五入容忍） |
| ZCZX-YZCYSJ-API-009 | 草场占比 | 可用/使用中 | 接口测试 | Postman | §5.5.1 | 已登录 | `grassland.availableCount/inUseCount` | 与 UT-025～026 一致 |
| ZCZX-YZCYSJ-API-010 | 草场占比 | 退化五档与合计 | 接口测试 | Postman | §5.5.2 | 已登录 | `degradationStats` | 长度=5；sum(count)=`grasslandCount` |
| ZCZX-YZCYSJ-API-011 | 刷新 | forceRefresh + 指定年 | 接口测试 | Postman | §5.1 | 已登录 | `GET .../dashboard?statYear=2025&forceRefresh=true` | `code=200`；`statYear` 回显 2025 |
| ZCZX-YZCYSJ-API-012 | 刷新 | 同日再次加载一致性 | 接口测试 | Postman | 技术§4 一致性 | 后台数据未变 | 连续两次 `GET`（第二次 `forceRefresh=true`） | 指标一致（除非源数据变） |
| ZCZX-YZCYSJ-API-013 | 数据权限 | UA 隔离 P2 | 接口测试 | Postman | §2.9 | P2 牦牛+W2；UA 登录 | `GET /dashboard` | 不含 P2 牦牛；`warningYakCount` 不含 W2 |
| ZCZX-YZCYSJ-API-014 | 数据权限 | UB 仅 P2 | 接口测试 | Postman | §2.9 | UB 登录 | `GET /dashboard` | 仅 P2 牦牛；`pastureCount` 含 P2 不含 P1 |
| ZCZX-YZCYSJ-API-015 | 数据权限 | ADMIN 全量 | 接口测试 | Postman | §2.9 | ADMIN 登录 | `GET /dashboard` | P1+P2 牦牛合计 |
| ZCZX-YZCYSJ-API-016 | 数据权限 | U0 牦牛为零草场仍有 | 接口测试 | Postman | §2.6、§3.1.8 | U0 登录；有 G | `GET /dashboard` | 牦牛类=0；`grasslandCount`>0 |
| ZCZX-YZCYSJ-API-017 | 鉴权 | 未登录 | 接口测试 | curl | §7.2 异常 | 无 Token | `GET /dashboard` | 401 或统一未登录 |
| ZCZX-YZCYSJ-API-018 | 权限 | 无 query 权限 | 接口测试 | Postman | §7.1 | 角色无 `breedingIndustry:query` | `GET /dashboard` | 403 或 `msg` 无权限 |
| ZCZX-YZCYSJ-API-019 | 异常 | 聚合失败 | 接口测试 | Postman/Mock | §3.1.8 | Mock Mapper 抛错 | `GET /dashboard` | 失败；`data` 空；`msg` 含「加载失败」类文案 |
| ZCZX-YZCYSJ-API-020 | 异常 | 空库零值 | 接口测试 | Postman | §5.1 空态 | 新库无业务数据 | `GET /dashboard` | `code=200`；数值 0；`yakInventoryStructure` 长 **3**、`degradationStats` 长 5 |
| ZCZX-YZCYSJ-API-021 | 范围 | 无维护/同步接口 | 接口测试 | Postman | §3 本期不实现 | 已登录 | `POST/PUT/DELETE /dataModel/breedingIndustry/**` | 404/405 或无权限 |
| ZCZX-YZCYSJ-API-022 | 范围 | 无筛选参数 | 接口测试 | Postman | §3、§8 | 已登录 | 传 `pastureId`/`year` 等非法 Query | 忽略或 400；口径仍按 §2 默认 |
| ZCZX-YZCYSJ-API-023 | 出栏 | 无变更日出栏 | 接口测试 | Postman | §2.4 | 仅 Y-O0 | `annualOutbound` | 0 |
| ZCZX-YZCYSJ-API-024 | 预警 | 年内去重口径 | 接口测试 | Postman | §2.5 | W-in 双条同 yak_no | 对比 SQL `COUNT DISTINCT` | 与 `warningYakCount` 一致 |
| ZCZX-YZCYSJ-API-025 | 预警 | 周期外不计 | 接口测试 | Postman | §2.5 | 仅 W-out | `warningYakCount` | 0 |
| ZCZX-YZCYSJ-API-026 | 台账 | 已删档案不计 | 接口测试 | Postman | §2.3 | 牦牛 `del_flag=2` | `GET /dashboard` | `inventoryTotal` 不含该头 |

---

## 三、界面测试用例（Playwright + Google Chrome）

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| ZCZX-YZCYSJ-UI-001 | 正常流程 | 菜单进入自动加载 | UI | Playwright+Chrome | §4.2、§5.1 | UA 有菜单；后端正常 | 登录→点击「养殖产业数据」 | 出现四块标题；`v-loading` 结束后六项卡片有数；无报错弹窗 |
| ZCZX-YZCYSJ-UI-002 | 产业总览 | 六项单位与只读 | UI | Playwright+Chrome | §5.2 | 有数据 | 检查首屏卡片 | 名称含存栏/出栏/预警/农牧户/草场/面积；单位头/户/个/亩；卡片不可点击跳转 |
| ZCZX-YZCYSJ-UI-003 | 存栏变动 | 公母文案 | UI | Playwright+Chrome | §5.3.1 | 有 HERD-L | 查看存栏区文案 | 显示公牛、母牛数量；**无**「未标注性别」 |
| ZCZX-YZCYSJ-UI-004 | 存栏变动 | 双折线 12 月 | UI | Playwright+Chrome | §5.3.2、前端§4 | 5 月环境 | 检查 ECharts 折线 | **12** 个刻度；横轴仅「N月」；悬停显「YYYY年N月」 |
| ZCZX-YZCYSJ-UI-005 | 存栏变动 | 最近月与 header | UI | Playwright+Chrome | §5.3.1 | 有 HERD-L | 比对折线最后一点与上方公/母数 | 分别一致 |
| ZCZX-YZCYSJ-UI-006 | 存栏结构 | 双层饼图 | UI | Playwright+Chrome | §5.4 | 有数据 | 查看存栏结构图例/标签 | 含公牛/母牛/保险及细分；显示 % |
| ZCZX-YZCYSJ-UI-007 | 草场占比 | 可用/使用中与退化 | UI | Playwright+Chrome | §5.5 | 有 G | 查看草场区 | 展示可用、使用中个数；退化列表/图五档；列表占比与图一致 |
| ZCZX-YZCYSJ-UI-009 | 刷新 | 成功更新全页 | UI | Playwright+Chrome | §5.1 | 已打开页 | 点击「刷新」 | 按钮 loading/disabled；完成后四块数据更新；可选轻提示「数据已更新」 |
| ZCZX-YZCYSJ-UI-010 | 刷新 | 防重复点击 | UI | Playwright+Chrome | §5.1、前端§4 | 已打开页 | 刷新未完成时连点 2 次 | 仅 1 次有效请求或第二次按钮不可用 |
| ZCZX-YZCYSJ-UI-011 | 异常 | 加载失败提示 | UI | Playwright+Chrome | §5.1、§7.2 | Mock `/dashboard` 返回 500 | 进入或点刷新 | 提示「加载失败，请稍后重试」类；页面不展示上一用户/会话缓存假数据 |
| ZCZX-YZCYSJ-UI-012 | 空态 | 无牦牛有草场 | UI | Playwright+Chrome | §5.1 | U0 或仅删光牦牛 | 打开页 | 牦牛相关为 0 或「—」；草场区仍有数或 0 空态不崩溃 |
| ZCZX-YZCYSJ-UI-013 | 权限 | 无菜单不可进 | UI | Playwright+Chrome | §7.1 | 无 `breedingIndustry:query` | 直接访问 `#/livestockIndustry/breedingIndustry/index` | 403/无权限页/重定向；不见四块看板 |
| ZCZX-YZCYSJ-UI-014 | 范围 | 无维护按钮 | UI | Playwright+Chrome | §3、§5.1 | UA 有 query | 扫描工具栏与页面 | 无新增/编辑/删除/同步/导出；仅有刷新（及返回） |
| ZCZX-YZCYSJ-UI-015 | 布局 | 纵向四块可滚动 | UI | Playwright+Chrome | §5.1 | 小屏或缩窗口 | 滚动页面 | 四块标题均可达；统计日/年可选展示 |
| ZCZX-YZCYSJ-UI-016 | 权限 | UB 预警隔离 | UI | Playwright+Chrome | §2.9 | UB 登录；P1、P2 均有预警 | 打开看板记 `warningYakCount`；换 UA 对比 | UB 与 UA 预警数不同；填报类指标（存栏/出栏/农牧户）全县一致 |

---

## 四、需求追溯矩阵

| 功能需求 | 单元 | 接口 | 界面 |
| --- | --- | --- | --- |
| §2.1 只读看板 | — | API-021 | UI-014 |
| §2.2 统计日/趋势月份 | UT-018～022 | API-002、005 | UI-004 |
| §2.3 存栏认定（档案） | UT-004～006 | API-003、026 | — |
| §2.11 填报存栏趋势 | UT-013、018～022 | API-004～006 | UI-003～005 |
| §2.4 年出栏 | UT-007～008 | API-023 | API-003 |
| §2.5 预警 | UT-009～012 | API-024～025 | API-003 |
| §2.6 牧场草场总览 | UT-023～024 | API-003、010 | UI-002、008 |
| §2.7 可用/使用中 | UT-025～026 | API-009 | UI-008 |
| §2.8 退化占比 | UT-027 | API-010 | UI-008 |
| §2.9 数据权限 | UT-001～003 | API-013～016 | UI-016 |
| §2.10 月末趋势（档案） | — | — | — |
| §5.1 加载/刷新/空态 | UT-028～029 | API-001、011～012、019～020 | UI-001、009～012 |
| §5.2 产业总览 | UT-028 | API-003 | UI-002 |
| §5.3 存栏变动 | UT-013、018～022 | API-004～006 | UI-003～005 |
| §5.4 牦牛存栏结构 | UT-015～017 | API-007～008 | UI-006 |
| §5.5 草场占比 | UT-025～027 | API-009～010 | UI-007 |
| §7 权限提示 | — | API-017～018 | UI-013 |
| §3 本期不实现 | — | API-021～022 | UI-014 |

---

## 五、Playwright 执行说明（摘要）

```javascript
// playwright.config 片段
use: { channel: 'chrome', locale: 'zh-CN', baseURL: process.env.RUOYI_BASE_URL }
```

1. `beforeEach`：UI 用例先 `page.goto('/login')` 登录 UA/UB/U0/ADMIN。  
2. 菜单路径以环境为准：`page.getByText('养殖产业数据').click()` 或路由 `#/livestockIndustry/breedingIndustry/index`。  
3. 接口 Mock：UI-011 可用 `page.route('**/dataModel/breedingIndustry/dashboard', route => route.fulfill({ status: 500 }))`。  
4. 图表断言：对 ECharts 容器检查 DOM 或拦截 XHR 校验 `monthlyTrend` 长度 **=12**。

**用例统计**：单元 **29**、接口 **26**、界面 **15**，合计 **70** 条。
