# 大屏 — 首页统计 — 测试用例

> 依据：`大屏首页功能需求.md`（v1.1）、`大屏首页技术方案.md`（v1.1）  
> **接口 Base Path**：`/bigScreen/home`（含 `context-path`、网关前缀须补齐）。鉴权：若依 Cookie / `Authorization` Token；权限 `bigScreen:home:query`。

**通用前置（无特殊说明）**：各业务 `sql/biz_*.sql` 已执行；测试账号具备大屏首页查询权限；时区 `Asia/Shanghai`。

**口径摘要**

| 域 | 规则 |
| --- | --- |
| 统计年 **Y** | 切换后全页一致；默认当前自然年 |
| 统计日 | `Y`=当前年→今日；历史年→`Y-12-31` |
| 按月图表（预约、培训） | 已改为**滚动 12 月**（与存栏/出栏一致）；含 `statYear`+`month`；**不**随 **Y** |
| 滚动 12 月（存栏变动、出栏统计） | 锚点=当前年月；含 `statYear`+`month`；**不**随 **Y**；缺月=**0** |
| 牦牛填报 | `biz_yak_herd_inventory` / `biz_yak_outbound_report`；产业总览存栏/出栏取所选年最大填报月 |
| 流通三项 | 供应商/承销商/交易市场**实时快照**，**不随 Y** |
| 预约 | `appoint_date` 在 **Y**；`status NOT IN (2,3)`；`provider_type` 1/3/2→兽医/专家/机构 |
| 补贴 | `grant_date` 在 **Y**；三类金额合计转**万元** |
| 农技 | 视频 `004008` 且 `publish_time` 在 **Y**；培训场次 `publish_time` 在 **Y** |
| 培训趋势 | `publish_status=1`；按 `training_time` 分月；`hasTrainingData=false` 走空态 |
| 养殖标准 | `publish_status=1` 且 `create_time` 日期在 **Y**；五 Tab 分表 |

**UI 测试**：**Playwright** + **Chromium**，`playwright.config` 中 `use: { channel: 'chrome' }` 调用本机 **Google Chrome**。

---

## 测试数据样本

| 代号 | 说明 |
| --- | --- |
| **HERD-R12** | 存栏填报：滚动窗口内仅 **2026-07** 有公 100/母 200；其余月无数据 |
| **OUT-R12** | 出栏填报：滚动窗口内仅 **2026-05** 出栏 88 头 |
| **HERD-EMPTY** | 无存栏填报 |
| **GRASS-MIX** | 草场五档退化已知；可用/使用中数量可手工核算 |
| **TRADE-SNAP** | 供应商 3、承销商 2、市场正常 1+停业 1（停业不计入） |
| **APT-Y** | **2026** 兽医 2、专家 1、机构 1；`appoint_date` 分月 |
| **APT-EXCL** | 同年 1 条 `status=3` 已取消、1 条 `status=2` 已拒绝 |
| **SUB-Y** | **2026** 三类补贴各 1 条，金额 10000/20000/30000 元 |
| **SUB-EMPTY** | 某年无发放记录 |
| **STD-PUB** | `biz_breeding_standard`：`publish_status=1`，`create_time` 在 **2026** |
| **STD-DRAFT** | 同年创建但 `publish_status=0` |
| **STD-OUT** | 已发布但 `create_time` 在 **2025** |
| **VIDEO-Y** | `biz_tech_resource`：`004008` 已发布，`publish_time` 在 **2026** ×2 |
| **TRAIN-PUB** | 培训已发布，`training_time` 在 **2026-03**，`actual_enrolled_count=20` |
| **TRAIN-EMPTY** | **2026** 无已发布培训（`training_time` 在 Y） |

---

## 一、单元测试用例

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| DP-SY-UT-001 | 参数校验 | statYear 合法 | 单元测试 | JUnit5 | 年份解析 | 无 | 传入 `2026`、`2000` | 解析为整数通过 |
| DP-SY-UT-002 | 参数校验 | statYear 非法 | 单元测试 | JUnit5 | 异常入参 | 无 | 空、`26`、`abcd`、`1899` | 抛 `ServiceException` 或等价失败 |
| DP-SY-UT-003 | 统计日 | 当前年 statDate | 单元测试 | JUnit5 | §2.2、SY-01 | Mock 当前日 2026-05-20 | `resolveStatDate(2026)` | 返回 `2026-05-20` |
| DP-SY-UT-004 | 统计日 | 历史年 statDate | 单元测试 | JUnit5 | §2.2 | 无 | `resolveStatDate(2024)` | 返回 `2024-12-31` |
| DP-SY-UT-005b | 存栏趋势 | 按最大月 1～N | 单元测试 | JUnit5 | §2.5 | Mock 仅 3、5 月有公/母 | `buildBullCowMonthlyTrendFromHerdReport` | `length=5`；1～2/4 补 0；3、5 有值 |
| DP-SY-UT-005b | 存栏趋势 | 窗口起止年月 | 单元测试 | JUnit5 | §2.3 | anchor=2026-07 | 检查首条 | `statYear=2025,month=8` |
| DP-SY-UT-006 | 存栏趋势 | 不随 statYear | 单元测试 | JUnit5 | 技术§3.1 | `loadDashboard(2025)` 与 `2026` | 比较 `monthlyTrend` | 两次 `monthlyTrend` 相同（Mock 填报一致时） |
| DP-SY-UT-007 | 出栏按月 | 按最大月 1～N | 单元测试 | JUnit5 | SY-06 | Mock 仅 3、8 月有出栏 | `buildOutboundMonthly` | `length=8`；1～2/4～7 补 0；3、8 有值 |
| DP-SY-UT-007b | 出栏按月 | statYear+month | 单元测试 | JUnit5 | §3.1.4 | 同上 | 检查含 `statYear`、`month` | 每条均含年月 |
| DP-SY-UT-008 | 年龄结构 | 两档占比之和 | 单元测试 | JUnit5 | SY-05 | 两档头数 10/40 | `buildCowAgeStructureFromHerdReport` | 两档固定返回；`sum(ratio)≈100` |
| DP-SY-UT-009 | 年龄结构 | 无数据 | 单元测试 | JUnit5 | §7.1 | `HERD-EMPTY` | `buildCowAgeStructureFromHerdReport(null)` | 两档 `count=0`、`ratio=0` |
| DP-SY-UT-010 | 流通快照 | 切年不变 | 单元测试 | JUnit5 | §2.6、SY-03 | 固定 `TRADE-SNAP` | `loadDashboard(2025)` 与 `2026` | `supplierCount`/`distributorCount`/`tradeMarketCount` 相同 |
| DP-SY-UT-011 | 预约 | 状态过滤 | 单元测试 | JUnit5 | 技术§2.4 | `APT-Y`+`APT-EXCL` | `countAppointments(2026)` | 不计入 status 2、3；三条有效计入 |
| DP-SY-UT-012 | 预约 | 三类映射 | 单元测试 | JUnit5 | §2.7 | provider_type 1/3/2 各 1 | `buildAppointmentStats` | 兽医/专家/机构年度合计各 1 |
| DP-SY-UT-013 | 补贴 | 万元换算 | 单元测试 | JUnit5 | SY-09 | 类型 1 合计 10000 元 | `toAmountWan` | `1.00` 万元（保留 2 位） |
| DP-SY-UT-014 | 补贴 | 无数据标记 | 单元测试 | JUnit5 | §7.1 | `SUB-EMPTY` | `buildSubsidy` | `hasSubsidyData=false`；`typeStats` 仍 3 项金额为 0 |
| DP-SY-UT-015 | 农技课堂 | 合计 | 单元测试 | JUnit5 | SY-11 | 视频 2、培训场次 1 | `buildAgriClassroom` | `trainingContentTotal=3`；`hasAgriData=true` |
| DP-SY-UT-016 | 农技课堂 | 双零空态 | 单元测试 | JUnit5 | §7.1 | 视频 0、培训 0 | `buildAgriClassroom` | `hasAgriData=false` |
| DP-SY-UT-017 | 培训趋势 | 按月活动与报名 | 单元测试 | JUnit5 | SY-12 | `TRAIN-PUB` 3 月 | `buildTrainingTrend(2026)` | 3 月 `activityCount=1`、`enrolledCount=20`；其余月 0 |
| DP-SY-UT-018 | 培训趋势 | 无培训空态 | 单元测试 | JUnit5 | §7.1 | `TRAIN-EMPTY` | `buildTrainingTrend` | `hasTrainingData=false` |
| DP-SY-UT-019 | 养殖标准 | Tab 路由表 | 单元测试 | JUnit5 | §2.9、SY-10 | `category=FEEDING` | `resolveStandardTable` | 指向 `biz_feeding_standard`；type 含 `001005` |
| DP-SY-UT-020 | 养殖标准 | 纳入条件 | 单元测试 | JUnit5 | SY-10 | `STD-PUB`+`STD-DRAFT`+`STD-OUT` | `filterStandards(2026)` | 仅 `STD-PUB` 通过 |
| DP-SY-UT-021 | 可选年份 | 并集含当前年 | 单元测试 | JUnit5 | §2.1 | Mock 出栏年 `[2025]`，其余空；当前 2026 | `buildAvailableYears` | 含 `2026`；降序 |
| DP-SY-UT-022 | 约束 | 滚动趋势不依赖 Calculator | 单元测试 | JUnit5 | 技术§2.6 | 检查 `buildRolling12Month*` | 填报表聚合 | 不调用 `BreedingIndustryCalculator` |
| DP-SY-UT-023 | 约束 | 只读无写 | 单元测试 | JUnit5 | §3.2、SY-14 | 检查 Controller/Service | 无 insert/update/delete | 仅 `dashboard`、`standards`、`townMapMarkers` 查询 |
| DP-SY-UT-024 | 约束 | 无标准详情接口 | 单元测试 | JUnit5 | §5.9 | 检查 Controller 映射 | 无 `GET .../standards/{id}` | 仅列表分页 |
| DP-SY-UT-025 | 缓存 | forceRefresh | 单元测试 | JUnit5 | 技术§2.3 | 启用缓存未过期 | `loadDashboard(2026,true)` | 跳过缓存重新聚合 |
| DP-SY-UT-026 | 地图标注 | 所选年最大填报月 | 单元测试 | JUnit5 | §5.12 | Mock 存栏 2026-12、出栏 2026-05 | `loadTownMapMarkers("2026")` | `herdStatYear/Month=2026/12`；`outboundStatYear/Month=2026/5`；含乡镇字段 |
| DP-SY-UT-027 | 产业总览 | 填报合计 | 单元测试 | JUnit5 | §5.2 | Mock 所选年最大填报月合计 | `loadDashboard(Y)` | `inventoryTotal`/`annualOutbound` 取 **Y** 年内最大月村级合计 |
| DP-SY-UT-028 | 产业总览 | 农牧户合计 | 单元测试 | JUnit5 | §5.2 | Mock `biz_farmer_household` 所选年最大月 | `loadDashboard(Y)` | `pastureCount` 为该年月全县村级 `household_count` 合计 |
| DP-SY-UT-029 | 产业总览 | 合作社等计数 | 单元测试 | JUnit5 | §5.2 | Mock 三表所选年最大月 | `loadDashboard(Y)` | `cooperativeCount`/`familyRanchCount`/`largeLivestockFarmerCount` 为该年月记录条数 |
| DP-SY-UT-030 | 产业总览 | 有畜户/草场面积 | 单元测试 | JUnit5 | §5.2 | Mock 有无畜户与农牧户面积 | `loadDashboard(Y)` | `withLivestockHouseholdCount`/`grasslandAreaMu` 取所选年最大月合计 |
| DP-SY-UT-031 | 人均收入明细 | 字段映射与未传年 | 单元测试 | JUnit5 | §2.5 | Mock `biz_disposable_income` | `listPerCapitaIncomes(2025)` / `null` | 字段完整；未传年不过滤 |
| DP-SY-UT-032 | 农牧户明细 | 条件过滤与未传年月 | 单元测试 | JUnit5 | §2.5 | Mock `biz_farmer_household` | `listFarmerHouseholds(...)` / `null,null` | 字段完整；未传年月不过滤 |
| DP-SY-UT-033 | 合作社明细 | 条件过滤与未传年月 | 单元测试 | JUnit5 | §2.5 | Mock `biz_cooperative_development` | `listCooperativeDevelopments(...)` | 字段完整；未传年月不过滤 |
| DP-SY-UT-034 | 家庭牧场明细 | 条件过滤与未传年月 | 单元测试 | JUnit5 | §2.5 | Mock `biz_family_ranch` | `listFamilyRanches(...)` | 字段完整；未传年月不过滤 |
| DP-SY-UT-035 | 养殖大户明细 | 条件过滤与未传年月 | 单元测试 | JUnit5 | §2.5 | Mock `biz_large_livestock_farmer` | `listLargeLivestockFarmers(...)` | 字段完整；未传年月不过滤 |
| DP-SY-UT-036 | 牦牛出栏明细 | 条件过滤与未传年月 | 单元测试 | JUnit5 | §2.5 | Mock `biz_yak_outbound_report` | `listYakOutboundReports(...)` | 字段完整；未传年月不过滤 |
| DP-SY-UT-037 | 牦牛存栏明细 | 条件过滤与未传年月 | 单元测试 | JUnit5 | §2.5 | Mock `biz_yak_herd_inventory` | `listYakHerdInventories(...)` | 字段完整；未传年月不过滤 |
| DP-SY-UT-038 | 有无畜户明细 | 条件过滤与未传年月 | 单元测试 | JUnit5 | §2.5 | Mock `biz_livestock_ownership_household` | `listLivestockOwnershipHouseholds(...)` | 字段完整；未传年月不过滤 |
| DP-SY-UT-039 | 存出栏情况明细 | 关联与跨年全量 | 单元测试 | JUnit5 | §2.5 | Mock `selectYakInventoryOutboundList` | `listYakInventoryOutbounds(...)` | 返回存栏/出栏；未传年月不过滤（传 null） |
| DP-SY-UT-040 | 养殖主体数量明细 | 关联计数与跨年全量 | 单元测试 | JUnit5 | §2.5 | Mock `selectBreedingEntityQuantityList` | `listBreedingEntityQuantities(...)` | 返回四类数量；未传年月不过滤 |
| DP-SY-UT-041 | 乡镇列表 | 全县口径 | 单元测试 | JUnit5 | §2.5 | Mock `toAllOptionList` | `listTownOptions()` | 返回乡镇选项 |
| DP-SY-UT-042 | 村树 | 全县口径 | 单元测试 | JUnit5 | §2.5 | Mock `buildAllVillageTree` | `listVillageTree()` | 返回县根节点 |

---

## 二、接口测试用例

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| DP-SY-API-001 | 看板 | 满数据正常 | 接口测试 | Postman/curl | SY-01 正常流程 | 已登录；2026 各域有样本 | `GET .../dashboard?statYear=2026` | `code` 成功；含 `industryOverview`～`trainingTrend` 全节点；`statDate` 为今日 |
| DP-SY-API-002 | 看板 | 产业总览八项 | 接口测试 | Postman | SY-03 | 填报+`GRASS-MIX`+`TRADE-SNAP` | `GET dashboard` | 八项数值与手工/SQL 一致；存栏/出栏/农牧户均取所选年最大填报月 |
| DP-SY-API-003 | 看板 | 主数据切年不变 | 接口测试 | Postman | §2.6 | `TRADE-SNAP` 固定 | `statYear=2025` 与 `2026` | `supplierCount` 等三项相同 |
| DP-SY-API-004 | 看板 | 历史年 statDate | 接口测试 | Postman | §2.2 | 已登录 | `statYear=2024` | `statDate=2024-12-31`；预约/补贴等按 2024；滚动 12 月趋势与 **Y** 无关 |
| DP-SY-API-005 | 看板 | 存栏趋势滚动 12 月 | 接口测试 | Postman | SY-04 | `HERD-R12` | `GET dashboard` | `monthlyTrend.length=12`；含 `statYear`、`month`、`bullCount`、`cowCount`；缺月补 0 |
| DP-SY-API-006 | 看板 | 年龄结构两档 | 接口测试 | Postman | SY-05 | 有存栏填报 | `GET dashboard` | `ageStructure.length=2`；有数据时 `sum(ratio)≈100` |
| DP-SY-API-007 | 看板 | 出栏按最大月 1～N | 接口测试 | Postman | SY-06 | 出栏填报有样本 | `GET dashboard` | `outboundMonthly.length`=所选年最大填报月；含 `statYear`、`month`；区间缺月为 0 |
| DP-SY-API-008 | 看板 | 草场退化五档 | 接口测试 | Postman | SY-07 | `GRASS-MIX` | `GET dashboard` | `degradationStats.length=5`；`sum(count)=grasslandCount` |
| DP-SY-API-009 | 看板 | 预约三类年度+堆叠 | 接口测试 | Postman | SY-08 | `APT-Y`+`APT-EXCL` | `GET dashboard` | 年度合计不含拒绝/取消；`monthlyStacked` 12 条 |
| DP-SY-API-010 | 看板 | 补贴万元 | 接口测试 | Postman | SY-09 | `SUB-Y` | `GET dashboard` | `typeStats` 3 项；金额分别为 1.00/2.00/3.00 万；`hasSubsidyData=true` |
| DP-SY-API-011 | 看板 | 农技课堂 | 接口测试 | Postman | SY-11 | `VIDEO-Y`+`TRAIN-PUB` | `GET dashboard` | `videoCourseCount=2`；`practicalTrainingCount=1`；`trainingContentTotal=3` |
| DP-SY-API-012 | 看板 | 培训趋势 | 接口测试 | Postman | SY-12 | `TRAIN-PUB` | `GET dashboard` | `hasTrainingData=true`；3 月 `activityCount=1`、`enrolledCount=20` |
| DP-SY-API-013 | 看板 | 无补贴空态 | 接口测试 | Postman | §7.1 | `SUB-EMPTY` 年 | `GET dashboard` | `hasSubsidyData=false`；三类 `amountWan=0` |
| DP-SY-API-014 | 看板 | 无农技空态 | 接口测试 | Postman | §7.1 | 无视频无培训发布 | `GET dashboard` | `hasAgriData=false` |
| DP-SY-API-015 | 看板 | 无培训趋势空态 | 接口测试 | Postman | §7.1 | `TRAIN-EMPTY` | `GET dashboard` | `hasTrainingData=false` |
| DP-SY-API-016 | 看板 | 无存栏填报 | 接口测试 | Postman | §7.1 | `HERD-EMPTY` | `GET dashboard` | `inventoryTotal=0`；`ageStructure` 全 0；`monthlyTrend` 仍 12 条 |
| DP-SY-API-017 | 看板 | 缺 statYear | 接口测试 | Postman | 异常 | 已登录 | 不传 `statYear` | 失败或默认当前年（与实现一致并文档统一） |
| DP-SY-API-018 | 看板 | 非法 statYear | 接口测试 | Postman | 异常 | 已登录 | `statYear=abc` | 失败；`msg` 参数错误 |
| DP-SY-API-019 | 看板 | statYear 回显 | 接口测试 | Postman | 一致性 | 已登录 | `statYear=2025` | `data.statYear=2025` |
| DP-SY-API-020 | 看板 | availableYears | 接口测试 | Postman | §2.1 | 多表有不同年份 | `GET dashboard` | 含各源年份+当前年；降序 |
| DP-SY-API-021 | 看板 | 切年不串 | 接口测试 | Postman | SY-02 | 2025、2026 数据不同 | 分别请求两年 | 出栏/预约/补贴等互不混入 |
| DP-SY-API-022 | 看板 | forceRefresh | 接口测试 | Postman | 技术§2.3 | 已启用缓存 | `?statYear=2026&forceRefresh=true` | 成功；与库最新一致 |
| DP-SY-API-023 | 标准列表 | 分页首页 | 接口测试 | Postman | SY-10 | 2026 `STD-PUB`≥11 条 | `GET .../standards?statYear=2026&category=BREEDING_MGMT&pageNum=1&pageSize=10` | `rows.length≤10`；`total` 正确 |
| DP-SY-API-024 | 标准列表 | 第二页 | 接口测试 | Postman | 分页 | 同上 | `pageNum=2` | 与第一页 `id` 不重复 |
| DP-SY-API-025 | 标准列表 | 五类 Tab | 接口测试 | Postman | §2.9 | 五表各 1 条符合条件 | 分别 `category=FEEDING` 等 | 仅对应表数据；`standardType` 在白名单内 |
| DP-SY-API-026 | 标准列表 | 未发布/跨年排除 | 接口测试 | Postman | 约束 | `STD-DRAFT`+`STD-OUT`+`STD-PUB` | `GET standards` 2026 | `total` 仅含 `STD-PUB` |
| DP-SY-API-027 | 标准列表 | 行内字段 | 接口测试 | Postman | §5.9 | `STD-PUB` 有封面正文 | `GET standards` | 含 `standardName`、`introduction`、`contentFileUrl`、`coverFileUrl`、`publishTime` |
| DP-SY-API-028 | 标准列表 | 缺 category | 接口测试 | Postman | 异常 | 已登录 | 不传 `category` | 失败 |
| DP-SY-API-029 | 标准列表 | 非法 category | 接口测试 | Postman | 异常 | 已登录 | `category=INVALID` | 失败或空列表（与实现一致） |
| DP-SY-API-030 | 范围 | 无详情接口 | 接口测试 | Postman | §5.9 | 已登录 | `GET .../home/standards/1` | 404/405 |
| DP-SY-API-031 | 范围 | 无维护接口 | 接口测试 | Postman | §3.2 | 已登录 | `POST/PUT/DELETE .../bigScreen/home/**` | 404/405 或无权限 |
| DP-SY-API-032 | 联动 | 补录填报后 | 接口测试 | Postman | SY-13 | 原 `HERD-EMPTY` | 后台新增存栏填报→`GET dashboard` | `inventoryTotal` 更新 |
| DP-SY-API-033 | 联动 | 发布标准后 | 接口测试 | Postman | SY-13 | 2026 原无标准 | 后台发布→`GET standards` | `total` 增加 |
| DP-SY-API-034 | 鉴权 | 未登录 | 接口测试 | curl | 安全 | 无 Token | `GET dashboard` | 401 或统一未登录 |
| DP-SY-API-035 | 权限 | 无大屏权限 | 接口测试 | Postman | 越权 | 无 `bigScreen:home:query` | `GET dashboard`、`GET standards` | 403 或无权限 |
| DP-SY-API-036 | 权限 | 有大屏无台账写 | 接口测试 | Postman | 只读 SY-14 | 仅有 query | `GET` 成功；`POST` 牦牛/标准维护接口 | 大屏可读；台账写权限独立 |
| DP-SY-API-037 | 一致性 | 切年双请求 | 接口测试 | Postman | 技术§3 | 已登录 | 模拟前端切年同时调 `dashboard`+`standards` | 两接口 `statYear` 一致；列表为新年 `pageNum=1` |
| DP-SY-API-038 | 地图标注 | 乡镇存栏出栏 | 接口测试 | Postman/curl | §5.12 | 乡镇填报表有样本 | `GET .../townMapMarkers?statYear=2026` | `code` 成功；含 `herdStatYear`/`herdStatMonth`、`outboundStatYear`/`outboundStatMonth`、`markers[]`；字段与 SQL 一致；无填报为 0 |
| DP-SY-API-040 | 人均收入明细 | 分页与字段 | 接口测试 | Postman/curl | §2.5 | 填报表有样本 | `GET .../perCapitaIncomes?statYear=2026&pageNum=1&pageSize=10` | `code` 成功；`rows[]` 含年份、可支配收入、增幅、增速、脱贫人口纯收入；按年倒序 |
| DP-SY-API-041 | 农牧户明细 | 分页与字段 | 接口测试 | Postman/curl | §2.5 | 填报表有样本 | `GET .../farmerHouseholds?statYear=2026&statMonth=3&pageNum=1&pageSize=10` | `code` 成功；`rows[]` 含年/月/乡镇/村/户数/草场承包面积等全部业务字段；按时间倒序；**草场面积明细复用本接口** |
| DP-SY-API-042 | 合作社明细 | 分页与字段 | 接口测试 | Postman/curl | §2.5 | 填报表有样本 | `GET .../cooperativeDevelopments?statYear=2026&statMonth=4` | `code` 成功；含 `cooperativeName` 等业务字段 |
| DP-SY-API-043 | 家庭牧场明细 | 分页与字段 | 接口测试 | Postman/curl | §2.5 | 填报表有样本 | `GET .../familyRanches?statYear=2026&statMonth=5` | `code` 成功；含 `enterpriseName` 等业务字段 |
| DP-SY-API-044 | 养殖大户明细 | 分页与字段 | 接口测试 | Postman/curl | §2.5 | 填报表有样本 | `GET .../largeLivestockFarmers?statYear=2026&statMonth=6` | `code` 成功；含 `enterpriseName` 等业务字段 |
| DP-SY-API-045 | 牦牛出栏明细 | 分页与字段 | 接口测试 | Postman/curl | §2.5 | 填报表有样本 | `GET .../yakOutboundReports?statYear=2026&statMonth=7` | `code` 成功；含出栏/自食等业务字段 |
| DP-SY-API-046 | 牦牛存栏明细 | 分页与字段 | 接口测试 | Postman/curl | §2.5 | 填报表有样本 | `GET .../yakHerdInventories?statYear=2026&statMonth=8` | `code` 成功；含 `yakTotal` 等业务字段 |
| DP-SY-API-047 | 有无畜户明细 | 分页与字段 | 接口测试 | Postman/curl | §2.5 | 填报表有样本 | `GET .../livestockOwnershipHouseholds?statYear=2026&statMonth=9` | `code` 成功；含有畜/无畜户数字段 |
| DP-SY-API-048 | 存出栏情况明细 | 分页与关联字段 | 接口测试 | Postman/curl | §2.5 | 存栏/出栏有样本 | `GET .../yakInventoryOutbounds?statYear=2026&statMonth=6` | `code` 成功；`rows[]` 含年/月/乡镇/村/`yakTotal`/`yakOutboundCount`；按时间倒序 |
| DP-SY-API-049 | 养殖主体数量明细 | 分页与四类计数 | 接口测试 | Postman/curl | §2.5 | 农牧户等有样本 | `GET .../breedingEntityQuantities?statYear=2026&statMonth=5` | `code` 成功；含 `farmerHouseholdCount`/`cooperativeCount`/`familyRanchCount`/`largeLivestockFarmerCount` |
| DP-SY-API-050 | 乡镇列表 | 全县选项 | 接口测试 | Postman/curl | §2.5 | 部门有填报乡镇 | `GET .../townOptions` | `data[]` 含 `deptId`/`deptName` |
| DP-SY-API-051 | 村树 | 县乡镇村 | 接口测试 | Postman/curl | §2.5 | 部门有村 | `GET .../villageTree` | `data[0]` 为县，含乡镇 children |

---

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

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| DP-SY-UI-001 | 页面 | 默认当前年加载 | UI 测试 | Playwright+Chrome | SY-01 | 已登录有权限；有当前年数据 | 打开「大屏首页」路由 | 年份为当前自然年；请求 `dashboard`+`standards`；各区块同年 |
| DP-SY-UI-002 | 筛选 | 切换年份整页刷新 | UI 测试 | Playwright+Chrome | SY-02 | 含 2025、2026 数据 | 年份 2025→2026 | 总览/图表/预约/补贴/培训等同步更新；无旧年残留 |
| DP-SY-UI-003 | 筛选 | 加载中防残留 | UI 测试 | Playwright+Chrome | §7.2 | 已登录 | 切年（可 `route.fulfill` 延迟 dashboard） | Loading/遮罩；完成后仅新年数据 |
| DP-SY-UI-004 | 产业总览 | 八项卡片与单位 | UI 测试 | Playwright+Chrome | §5.2、SY-03 | 2026 有完整样本 | 查看产业总览区 | 8 项名称/数值/单位（头、个、亩）正确；**无**预警牦牛卡片 |
| DP-SY-UI-005 | 产业总览 | 流通三项切年不变 | UI 测试 | Playwright+Chrome | §2.6 | `TRADE-SNAP` 固定 | 2025→2026 | 供应商/承销商/市场三数不变（未改主数据前提下） |
| DP-SY-UI-006 | 图表 | 存栏公/母双折线 | UI 测试 | Playwright+Chrome | SY-04 | `HERD-R12` | 查看存栏变动图+悬停 | 横轴 12 刻度**仅月份**；悬停显 `YYYY年M月`；公/母两图例 |
| DP-SY-UI-007 | 图表 | 年龄结构饼图 | UI 测试 | Playwright+Chrome | SY-05 | 有存栏填报 | 查看年龄结构 | 两档标签；占比之和 100%（有数据时） |
| DP-SY-UI-008 | 图表 | 出栏柱状 12 月 | UI 测试 | Playwright+Chrome | SY-06 | `OUT-R12` | 查看出栏统计+悬停 | 12 柱、**无趋势折线**；横轴仅月；悬停显年月+头数 |
| DP-SY-UI-009 | 图表 | 草场可用/使用中 | UI 测试 | Playwright+Chrome | SY-07 | `GRASS-MIX` | 查看草场区上部 | 两数字区分「可用」「使用中」；不与退化表简单相加为总量 |
| DP-SY-UI-010 | 图表 | 退化等级占比 | UI 测试 | Playwright+Chrome | SY-07 | 五档均有 | 查看退化表/环图 | 五档名称与 API 一致；占比为 % |
| DP-SY-UI-011 | 图表 | 预约堆叠柱 | UI 测试 | Playwright+Chrome | SY-08 | `APT-Y` | 查看预约区+悬停 | 左侧三类年度卡片；右侧滚动 12 月堆叠；横轴仅月；悬停显年月 |
| DP-SY-UI-012 | 图表 | 补贴三类柱图 | UI 测试 | Playwright+Chrome | SY-09 | `SUB-Y` | 查看惠农补贴 | 横轴三类；纵轴万元；与 API 一致 |
| DP-SY-UI-013 | 指标 | 农技课堂 | UI 测试 | Playwright+Chrome | SY-11 | `VIDEO-Y`+培训 | 查看农技课堂 | 视频数、培训场次、培训内容合计正确 |
| DP-SY-UI-014 | 图表 | 培训双曲线 | UI 测试 | Playwright+Chrome | SY-12 | `TRAIN-PUB` | 查看培训报名趋势+悬停 | 活动数、报名人数两系列；横轴仅月；悬停显年月 |
| DP-SY-UI-015 | 空态 | 无出栏 | UI 测试 | Playwright+Chrome | §7.1 | 选无出栏年 | 切换该年 | 出栏图空态或全 0；年出栏卡片为 0 |
| DP-SY-UI-016 | 空态 | 无存栏趋势 | UI 测试 | Playwright+Chrome | §7.1 | `HERD-EMPTY` | 打开页 | 趋势图空态或全 0 折线；年龄结构空态 |
| DP-SY-UI-017 | 空态 | 无预约 | UI 测试 | Playwright+Chrome | §7.1 | 无预约年 | 切换该年 | 预约区空态；三类年度卡片为 0 |
| DP-SY-UI-018 | 空态 | 无补贴 | UI 测试 | Playwright+Chrome | §7.1 | `SUB-EMPTY` 年 | 切换该年 | 补贴图空态或三类型 0 |
| DP-SY-UI-019 | 空态 | 无农技 | UI 测试 | Playwright+Chrome | §7.1 | 视频+培训均为 0 | 切换该年 | 农技课堂空态（非假数据） |
| DP-SY-UI-020 | 空态 | 无培训趋势 | UI 测试 | Playwright+Chrome | §7.1 | `TRAIN-EMPTY` | 切换该年 | 培训趋势空态；**不**用假曲线 |
| DP-SY-UI-021 | 标准 | Tab 切换 | UI 测试 | Playwright+Chrome | §5.9 | 五类各有数据 | 依次点 5 个 Tab | 每次请求 `standards` 且 `category` 对应；从第 1 页加载 |
| DP-SY-UI-022 | 标准 | 分页翻页 | UI 测试 | Playwright+Chrome | SY-10 | ≥11 条；pageSize=10 | 点下一页 | 仅 `standards` 请求；看板区不重复请求 |
| DP-SY-UI-023 | 标准 | 行内详情展开 | UI 测试 | Playwright+Chrome | §5.9 | 列表有数据 | 点行内「详情」 | 展开/浮层显示简介或正文；**不**跳转后台路由 |
| DP-SY-UI-024 | 只读 | 无维护按钮 | UI 测试 | Playwright+Chrome | SY-14、§3.2 | 已登录 | 扫描全页 | 无新增/编辑/删除/预约/补贴/发布类按钮；可有刷新 |
| DP-SY-UI-025 | 错误 | 接口失败重试 | UI 测试 | Playwright+Chrome | §7.2 | Mock `dashboard` 500 | 进入或切年 | 失败提示+重试；不长期显示上年数据 |
| DP-SY-UI-026 | 联动 | 后台补录后刷新 | UI 集成 | Playwright+Chrome | SY-13 | 2026 某区块原空 | 后台补数据→F5 或点刷新 | 对应区块出现新数据 |
| DP-SY-UI-027 | 权限 | 无菜单不可进 | UI 测试 | Playwright+Chrome | 安全 | 无大屏菜单账号 | 登录查侧栏/直链 | 无入口或 403 |
| DP-SY-UI-028 | 端到端 | 按年查阅全流程 | UI 集成 | Playwright+Chrome | §4、SY-01～14 | 2026 完整样本 | 登录→进首页→默认年→切年→悬停各图→切 Tab→翻页 | 十区块正确；无控制台报错 |
| DP-SY-UI-029 | 刷新 | 手动刷新 | UI 测试 | Playwright+Chrome | §7.3 | 已打开；后台改数据 | 点刷新按钮 | 重新请求 `dashboard`+当前 Tab `standards`；数据更新 |

---

## Playwright 实施提示

```javascript
// playwright.config.ts 片段
use: { channel: 'chrome', headless: false }

// 登录后进入首页（路由以实际为准）
await page.goto('/#/bigScreen/home/index');

// 切年：监听双请求
await Promise.all([
  page.waitForResponse('**/bigScreen/home/dashboard**'),
  page.waitForResponse('**/bigScreen/home/standards**'),
  page.getByRole('combobox', { name: /统计年份|年份/ }).selectOption('2026'),
]);

// 标准 Tab（按页面文案调整）
await page.getByRole('tab', { name: '牦牛饲喂' }).click();
await page.waitForResponse(r =>
  r.url().includes('/bigScreen/home/standards') &&
  r.url().includes('category=FEEDING')
);

// 行内详情
await page.getByRole('button', { name: '详情' }).first().click();
await expect(page.locator('.standard-detail-panel, [class*="detail"]')).toBeVisible();

// 空态（按页面文案调整）
await expect(page.getByText(/暂无|本年度暂无/)).toBeVisible();

// 接口失败（UI-025）
await page.route('**/bigScreen/home/dashboard**', route =>
  route.fulfill({ status: 500, body: '{}' })
);
```

---

## 覆盖对照

| 类别 | 单元 UT | 接口 API | 界面 UI |
| --- | --- | --- | --- |
| 正常流程 | UT-003～008、012～013、015～017、019 | API-001～012、023～025、032～033 | UI-001～014、021～023、028 |
| 异常流程 | UT-002、009、014、016、018 | API-013～018、028～029、034～035 | UI-015～020、025、027 |
| 业务约束 | UT-010～011、020～024 | API-003、009、026、030～031、036～037 | UI-004～005、024、026 |

---

## 功能需求追溯（SY / 章节）

| 追溯 | 要点 | 主要用例 |
| --- | --- | --- |
| SY-01 | 默认当前年 | UI-001；API-001 |
| SY-02 | 切年一致 | API-021、037；UI-002～003 |
| SY-03 | 产业总览八项 | UT-010；API-002～003；UI-004～005 |
| SY-04 | 存栏趋势滚动 12 月 | UT-005～006；API-005；UI-006 |
| SY-05 | 年龄结构两档 | UT-008～009；API-006；UI-007 |
| SY-06 | 出栏滚动 12 月 | UT-007、007b；API-007；UI-008 |
| SY-07 | 草场占比 | API-008；UI-009～010 |
| SY-08 | 预约统计 | UT-011～012；API-009；UI-011 |
| SY-09 | 补贴金额 | UT-013～014；API-010、013；UI-012 |
| SY-10 | 养殖标准 | UT-019～020；API-023～027；UI-021～023 |
| SY-11 | 农技课堂 | UT-015～016；API-011、014；UI-013、019 |
| SY-12 | 培训报名趋势 | UT-017～018；API-012、015；UI-014、020 |
| SY-13 | 后台刷新 | API-032～033；UI-026、029 |
| SY-14 | 只读 | UT-023～024；API-031、036；UI-024 |
| §2.2 | 统计日 | UT-003～004；API-004 |
| §2.6 | 主数据实时 | UT-010；API-003；UI-005 |
| §7.1 | 空态 | UT-009、014、016、018；API-013～016；UI-015～020 |
| §7.2 | 加载失败 | UI-003、025 |

---

## 修订记录

| 版本 | 说明 |
| --- | --- |
| 1.8 | 明细分页：年份未传不过滤（UT-031～038） |
| 1.7 | 新增大屏乡镇列表、村树接口（UT-041～042、API-050～051） |
| 1.6 | 新增养殖主体数量明细接口（UT-040、API-049） |
| 1.5 | 存出栏明细：年月未传不过滤，支持跨年全量翻页（UT-039） |
| 1.4 | 新增牦牛存出栏情况明细接口（UT-039、API-048） |
| 1.3 | 明确草场面积明细复用农牧户明细接口（API-041） |
| 1.2 | 年龄结构改为两档；用例 UT-008～009、API-006、UI-007 同步 |
| 1.1 | 对齐 v1.1 需求/技术方案：滚动 12 月、`statYear`+`month`、填报表样本、UI 横轴/Tooltip、四档年龄、地图最近填报年月 |
| 1.0 | 初版：单元 25、接口 37、UI 29；覆盖 SY-01～14 与正常/异常/约束；Playwright+Chrome；Base `/bigScreen/home` |
