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

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

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

**口径摘要**

| 域 | 规则 |
| --- | --- |
| 统计年 **Y** | 切换后全页一致；默认当前自然年 |
| 统计日 | `Y`=当前年→今日；历史年→`Y-12-31` |
| 按月图表 | 固定 12 月；缺月、当前年未来月=**0** |
| 牦牛/牧场/草场 | 复用养殖产业口径；大屏**全县**正常牧场；`pasture_id` 为空不参与牦牛统计 |
| 流通三项 | 供应商/承销商/交易市场**实时快照**，**不随 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**。

---

## 测试数据样本

| 代号 | 说明 |
| --- | --- |
| **YAK-INV** | 可见牧场内 `asset_status=1` 存栏已知；含公/母/未标注性别 |
| **YAK-OUT-Y** | **Y** 年出栏仅 3、8 月各 1 头（`status_change_date` 非空） |
| **YAK-EMPTY** | **Y** 时点无存栏（或全县无有效档案） |
| **YAK-HIST** | 历史年 **2024** 末还原存栏与 2026 不同 |
| **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-005 | 存栏趋势 | 按月 12 条补零 | 单元测试 | JUnit5 | §2.3、SY-04 | Mock 仅 3、8 月有公/母还原值 | `buildInventoryMonthlyTrend(2026, statDate)` | `length=12`；缺月 `bullCount=cowCount=0` |
| DP-SY-UT-006 | 存栏趋势 | 未来月为 0 | 单元测试 | JUnit5 | §2.3 | 当前年 statDate=5 月 | 检查 `month=6..12` | 均为 0 |
| DP-SY-UT-007 | 出栏按月 | 缺月补零 | 单元测试 | JUnit5 | SY-06 | `YAK-OUT-Y` | `buildOutboundMonthly(2026)` | 12 条；非 3/8 月 `outboundCount=0` |
| DP-SY-UT-008 | 年龄结构 | 三档占比之和 | 单元测试 | JUnit5 | SY-05 | `YAK-INV` 三档头数 30/30/60 | `buildAgeStructure` | 三档固定返回；`sum(ratio)≈100`（有存栏时） |
| DP-SY-UT-009 | 年龄结构 | 无存栏 | 单元测试 | JUnit5 | §7.1 | `YAK-EMPTY` | `buildAgeStructure` | 三档 `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 | 检查 HomeScreenService | 月末存栏调用 `BreedingIndustryCalculator` | 无重复实现月末判定 |
| DP-SY-UT-023 | 约束 | 只读无写 | 单元测试 | JUnit5 | §3.2、SY-14 | 检查 Controller/Service | 无 insert/update/delete | 仅 `dashboard`、`standards` 查询 |
| DP-SY-UT-024 | 约束 | 无标准详情接口 | 单元测试 | JUnit5 | §5.9 | 检查 Controller 映射 | 无 `GET .../standards/{id}` | 仅列表分页 |
| DP-SY-UT-025 | 缓存 | forceRefresh | 单元测试 | JUnit5 | 技术§2.3 | 启用缓存未过期 | `loadDashboard(2026,true)` | 跳过缓存重新聚合 |

---

## 二、接口测试用例

| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| DP-SY-API-001 | 看板 | 满数据正常 | 接口测试 | Postman/curl | SY-01 正常流程 | 已登录；2026 各域有样本 | `GET .../dashboard?statYear=2026` | `code` 成功；含 `industryOverview`～`trainingTrend` 全节点；`statDate` 为今日 |
| DP-SY-API-002 | 看板 | 产业总览八项 | 接口测试 | Postman | SY-03 | `YAK-INV`+`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 | `YAK-HIST` | `statYear=2024` | `statDate=2024-12-31`；存栏/出栏按 2024 口径 |
| DP-SY-API-005 | 看板 | 存栏趋势 12 月双系列 | 接口测试 | Postman | SY-04 | 2026 仅部分月有还原值 | `GET dashboard` | `monthlyTrend.length=12`；含 `bullCount`、`cowCount`；未来月为 0 |
| DP-SY-API-006 | 看板 | 年龄结构占比 | 接口测试 | Postman | SY-05 | `YAK-INV` | `GET dashboard` | `ageStructure.length=3`；有存栏时 `sum(ratio)≈100` |
| DP-SY-API-007 | 看板 | 出栏按月 | 接口测试 | Postman | SY-06 | `YAK-OUT-Y` | `GET dashboard` | `outboundMonthly.length=12`；3、8 月=1，其余 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 | `YAK-EMPTY` | `GET dashboard` | `inventoryTotal=0`；`ageStructure` 全 0 |
| 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 | 2026 原 `YAK-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` |

---

## 三、界面测试用例（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 | 图表 | 存栏公/母双折线 12 月 | UI 测试 | Playwright+Chrome | SY-04 | 2026 缺月样本 | 查看存栏变动图+悬停 | 12 刻度；公/母两图例；缺月/未来月为 0 |
| DP-SY-UI-007 | 图表 | 年龄结构饼图 | UI 测试 | Playwright+Chrome | SY-05 | `YAK-INV` | 查看年龄结构 | 三档标签；占比之和 100%（有存栏时） |
| DP-SY-UI-008 | 图表 | 出栏柱状 12 月 | UI 测试 | Playwright+Chrome | SY-06 | `YAK-OUT-Y` | 查看出栏统计+悬停 | 12 柱；仅 3、8 月有值 |
| 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` | 查看培训报名趋势 | 活动数、报名人数两系列；12 月 |
| DP-SY-UI-015 | 空态 | 无出栏 | UI 测试 | Playwright+Chrome | §7.1 | 选无出栏年 | 切换该年 | 出栏图空态或全 0；年出栏卡片为 0 |
| DP-SY-UI-016 | 空态 | 无存栏趋势 | UI 测试 | Playwright+Chrome | §7.1 | `YAK-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 | 出栏按月 | UT-007；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.0 | 初版：单元 25、接口 37、UI 29；覆盖 SY-01～14 与正常/异常/约束；Playwright+Chrome；Base `/bigScreen/home` |
