# 大屏 — 疫病风险统计 — 技术方案

> 依据：同目录 `大屏疫病风险功能需求.md`；数据源为《实验室检测技术方案》《疫情上报技术方案》《检疫站管理技术方案》《风险因子设置技术方案》既有表。本模块为**大屏只读看板**，无业务台账 CRUD；**气象数据不由本模块提供**。

---

## 1. 技术架构

| 项 | 说明 |
| --- | --- |
| **后端** | RuoYi **v3.9.2**（**springboot2** 分支）：JDK 8、Spring Boot 2.x、Spring MVC、MyBatis、Druid |
| **数据库** | MySQL **5.7.39**，InnoDB，`utf8mb4` |
| **前端** | 大屏独立页面；**切换年份**拉取看板主数据，疫情列表**分页**单独请求；气象由前端调公开接口 |
| **代码包** | `com.ruoyi.web.modules.screen`（`EpidemicRiskScreenController` / `IEpidemicRiskScreenService` / `vo` / `support`） |

**分层**：`Controller` → `IEpidemicRiskScreenService`（口径编排、聚合）→ 扩展 Mapper / 复用既有 Service 只读查询。

| 场景 | 行为 |
| --- | --- |
| 进入 / 切换 `statYear` | 调用 **§3.1**（检测汇总、疫情统计、检疫态势、风险因子等） |
| 疫情列表翻页 | 仅调用 **§3.2** |
| 数据权限 | 全县数据，**不按**部门/用户过滤 |
| 汇总口径 | 与功能需求 **§2** 一致；**样本来源**按 **Y**；**实验室检测趋势**为滚动 12 月、缺月补 0；解除时效为按疫病类型**算术平均天数** |
| 检疫点 | **实时**主数据快照（与统计年份无关） |
| 性能 | 默认实时聚合；可选 **§2.3** 缓存表 |

**依赖表（只读）**

| 表 | 用途 |
| --- | --- |
| `biz_lab_detection` | 样本来源、年度/月度检测量与阳性、检出率 |
| `biz_epidemic_report` | 风险等级、类型、途径、列表、地区去重、解除时效 |
| `biz_quarantine_station` | 可用/停用检疫点数（`del_flag=0`） |
| `biz_risk_factor_weight` | 五项权重（单行 `id=1`） |

---

## 2. 数据库设计

### 2.1 本模块业务表

**无新增业务台账表。** DDL 以各业务模块 `sql/*.sql` 为准。

### 2.2 查询口径（按统计年份 `Y`）

#### 实验室检测 `biz_lab_detection`

| 项 | 条件 / 说明 |
| --- | --- |
| 按年（样本来源） | `selectByTestYear(Y)`；`test_date` 在 **Y** 年内 |
| 滚动 12 月（趋势区） | `selectByTestDateBetween(rangeStart, rangeEnd)`；锚点 = `HomeScreenSupport.currentYearMonth()`；窗口 `[anchor−11 月 1 日, rollingWindowEnd]` |
| 样本来源汇总 | 基于 **Y** 年记录 `GROUP BY sample_source`，`SUM(test_quantity)` |
| 趋势区合计 | 滚动窗口内 `SUM(test_quantity)`、`SUM(positive_count)` |
| 按月趋势 | 按 `YEAR(test_date)`+`MONTH(test_date)` 聚合；`EpidemicRiskScreenSupport.buildLabDetection(rows, anchor)` 补全 **12** 槽位，含 `statYear`、`month` |

**`sample_source`**：`1` 场地抽检 / `2` 疫情上报送检 / `3` 调运检疫 / `4` 日常检测。

#### 疫情上报 `biz_epidemic_report`

| 统计用途 | SQL 条件要点 |
| --- | --- |
| 按上报日（饼图、列表、类型、途径等） | `report_date` 在 **Y** 年内 |
| 防控中地区 | `report_date` 在 **Y** 年内 AND `release_date IS NULL` |
| 已解除地区、解除时效 | `release_date IS NOT NULL` AND `release_date` 在 **Y** 年内 |
| 未解除判定 | `release_date IS NULL` |
| 地区去重 | 对 `affected_location_track` **DISTINCT** 计数（应用层亦可） |

**枚举（入库值，展示名见功能需求 §2.6）**

| 字段 | 值 | 含义 |
| --- | --- | --- |
| `risk_level` | 1 / 2 / 3 | 高危 / 中危 / 低危 |
| `epidemic_type` | 1～4 | 病毒 / 细菌 / 寄生虫 / 其他 |
| `transmission_route` | 1～4 | 调运 / 本地扩散 / 环境 / 其他未知 |

**解除时效（平均天）**：对满足「已解除且解除日在 Y」的记录，`DATEDIFF(release_date, report_date)` 按 `epidemic_type` 分组后求 **AVG**（四类型固定返回，无记录时 `avgDays=null`）。

#### 检疫站 `biz_quarantine_station`

| 项 | 条件 |
| --- | --- |
| 范围 | `del_flag = '0'` |
| 可用检疫点 | `operate_status = 1`（正常） |
| 停用检疫点 | `operate_status IN (2, 3)`（停业、注销） |

> **不按** `statYear` 过滤；取当前库内快照。

#### 风险因子 `biz_risk_factor_weight`

| 项 | 条件 |
| --- | --- |
| 读取 | `SELECT * FROM biz_risk_factor_weight WHERE id = 1` |
| 无记录 | 返回 `hasRiskFactorConfig=false`，前端走空态 |

### 2.3 可选表 `biz_epidemic_risk_screen_cache`（性能优化）

**非本期必建。**

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `id` | `bigint(20)` | 主键 |
| `stat_year` | `int(11)` | 统计年份 |
| `payload_json` | `mediumtext` | **§3.1** 响应 JSON（不含分页列表） |
| `expire_time` | `datetime` | 过期时间 |
| `create_time` | `datetime` | 写入时间 |

**索引**：`UNIQUE uk_stat_year (stat_year)`。TTL 建议 **1～5 分钟**。

### 2.4 索引建议（既有表）

| 表 | 建议 |
| --- | --- |
| `biz_lab_detection` | 沿用 `idx_test_date`、`idx_sample_source` |
| `biz_epidemic_report` | 沿用 `idx_report_date`、`idx_release_date`、`idx_risk_level` |
| `biz_quarantine_station` | 沿用 `idx_publish` 等价 `operate_status` 索引（见检疫站 DDL） |

### 2.5 Mapper 扩展（建议）

| Mapper | 方法 | 说明 |
| --- | --- | --- |
| `BizLabDetectionMapper` | `selectByTestYear(int statYear)` | 当年全部检测行（聚合在 Service） |
| `BizLabDetectionMapper` | `selectDistinctTestYears()` | 可选年份 |
| `BizEpidemicReportMapper` | `selectByReportYear(int statYear)` | 上报日在 Y 年内 |
| `BizEpidemicReportMapper` | `selectRelievedByReleaseYear(int statYear)` | 已解除且解除日在 Y |
| `BizEpidemicReportMapper` | `selectActiveControlByReportYear(int statYear)` | 上报日在 Y 且未解除 |
| `BizEpidemicReportMapper` | `selectDistinctReportYears()` | 可选年份 |
| `BizQuarantineStationMapper` | `countByOperateStatus(int status)` | 按运营状态计数 |
| `BizRiskFactorWeightMapper` | `selectConfig()` | 读 `id=1`（可复用既有查询） |

---

## 3. 接口设计

**统一响应**：`AjaxResult`（`code` / `msg` / `data`）；疫情列表用 `TableDataInfo`（`rows` / `total`）。

**权限标识（示例）**：`bigScreen:epidemicRisk:query`

**Base Path**：`/bigScreen/epidemicRisk`

| # | 说明 | Method | URI | 权限 |
| --- | --- | --- | --- | --- |
| 3.1 | 看板主数据 | GET | `/bigScreen/epidemicRisk/dashboard` | `bigScreen:epidemicRisk:query` |
| 3.2 | 疫情上报分页列表 | GET | `/bigScreen/epidemicRisk/reports` | 同上 |

前端：切换 `statYear` → **3.1** + **3.2**（`pageNum=1`）；仅翻页 → **3.2**。

---

### 3.1 看板主数据

#### Query

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `statYear` | string | Y | 四位年份；非法 → 业务异常（复用 `AchievementReportValidation.parseStatYear` 或同等校验） |
| `forceRefresh` | boolean | N | 默认 `false`；`true` 跳过 **§2.3** 缓存 |

#### 响应 `data` 结构（节选）

| 字段 | 说明 |
| --- | --- |
| `statYear` | 回显 |
| `availableYears` | `int[]`，检测年与上报年并集 + 当前年，降序 |
| `hasLabData` | 该年是否存在 ≥1 条检测记录 |
| `hasEpidemicData` | 该年按上报日是否存在 ≥1 条疫情上报 |
| `labDetection` | 见 **§3.1.1** |
| `sampleSourceStats` | 见 **§3.1.2** |
| `riskLevelStats` | 风险等级饼图 **§3.1.3** |
| `epidemicSituation` | 检疫点 + 地区 **§3.1.4** |
| `epidemicTypeStats` | 疫病类型占比 |
| `transmissionRouteStats` | 传播途径数量与占比 |
| `relieveDurationByType` | 按类型平均解除天数 **§3.1.5** |
| `riskFactorWeights` | 五项权重 **§3.1.6** |

#### 3.1.1 `labDetection`

| 字段 | 说明 |
| --- | --- |
| `testQuantityTotal` | 滚动 12 月窗口内检测量合计 |
| `positiveCountTotal` | 滚动 12 月窗口内阳性合计 |
| `positiveRate` | 检出率 0～1；`testQuantityTotal=0` 时为 `null` 或 `0`（与产品约定） |
| `monthly` | 长度 **12**；**滚动最近 12 月**；每项含 `statYear`、`month`、`testQuantity`、`positiveCount`；缺月为 **0** |

#### 3.1.2 `sampleSourceStats[]`

| 字段 | 说明 |
| --- | --- |
| `sampleSource` | 1～4 |
| `sampleSourceName` | 中文名 |
| `testQuantity` | 份 |
| `ratio` | 占当年检测量合计，0～1 |

固定返回 **4** 项（无数据时 `testQuantity=0`、`ratio=0`）。

#### 3.1.3 `riskLevelStats[]`

| 字段 | 说明 |
| --- | --- |
| `riskLevel` | 1 / 2 / 3 |
| `riskLevelName` | 高危 / 中危 / 低危 |
| `count` | 条数 |
| `ratio` | 占当年按上报日纳入总数 |

固定 **3** 项。

#### 3.1.4 `epidemicSituation`

| 字段 | 说明 |
| --- | --- |
| `availableQuarantinePointCount` | 正常运营检疫站数 |
| `disabledQuarantinePointCount` | 停业+注销检疫站数 |
| `activeControlAreaCount` | 防控中地区数（涉疫点位去重） |
| `releasedAreaCount` | 已解除地区数（涉疫点位去重） |

#### 3.1.5 `relieveDurationByType[]`

| 字段 | 说明 |
| --- | --- |
| `epidemicType` | 1～4 |
| `epidemicTypeName` | 中文名 |
| `avgRelieveDays` | 平均解除天数；该类型无已解除记录时为 `null` |
| `recordCount` | 参与平均的条数（可选，便于前端展示） |

#### 3.1.6 `riskFactorWeights`

| 字段 | 说明 |
| --- | --- |
| `hasRiskFactorConfig` | 是否存在 `id=1` 配置 |
| `items` | 固定 5 项：`factorCode`、`factorName`、`weight`（decimal） |

`factorCode` 建议：`breedingDensity`、`trafficMobility`、`quarantineIntensity`、`meteorologicalSuitability`、`pastDiseaseLevel`。

---

### 3.2 疫情上报分页列表

#### Query

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `statYear` | string | Y | 同 **3.1** |
| `pageNum` | int | N | 默认 `1` |
| `pageSize` | int | N | 默认 `8` |

#### 响应

`TableDataInfo`：`rows` 为 **§3.2.1**，`total` 为当年按上报日纳入的总条数。

**不提供** `GET /{id}` 大屏详情接口。

#### 3.2.1 `rows[]`

| 字段 | 说明 |
| --- | --- |
| `id` | 主键 |
| `reportDate` | `yyyy-MM-dd` |
| `transmissionRoute` / `transmissionRouteName` | 编码 + 展示名 |
| `epidemicType` / `epidemicTypeName` | 编码 + 展示名 |
| `epidemicName` | 疫病名称 |
| `riskLevel` / `riskLevelName` | 编码 + 展示名 |
| `affectedLocationTrack` | 涉疫点位/轨迹 |
| `relieveStatus` | `0` 未解除 / `1` 已解除 |
| `releaseDate` | 已解除时有值 |

默认排序：`report_date DESC, create_time DESC, id DESC`。

---

### 3.3 服务端逻辑（摘要）

```
1. year = parseStatYear(statYear)
2. labRows = selectByTestYear(year)
3. reportRows = selectByReportYear(year)
4. relievedRows = selectRelievedByReleaseYear(year)
5. activeRows = selectActiveControlByReportYear(year)
6. 组装 labDetection、sampleSourceStats（固定4类）
7. 组装 riskLevelStats（固定3类）、epidemicTypeStats、transmissionRouteStats
8. epidemicSituation = 检疫站 count + 地区 distinct(activeRows/relievedRows)
9. relieveDurationByType = AVG(DATEDIFF) GROUP BY epidemic_type（固定4类）
10. riskFactorWeights = selectConfig id=1
11. availableYears = union(distinct test years, distinct report years, current year)
12. return AjaxResult.success(vo)
```

**枚举展示名**：`EpidemicRiskScreenSupport` 集中维护 code→name，与功能需求 §2.6 一致。

---

## 4. 菜单与权限（示例）

| 类型 | 名称 | 权限标识 |
| --- | --- | --- |
| 菜单 | 大屏疫病风险 | `bigScreen:epidemicRisk:query` |

SQL 示例：`sql/big_screen_epidemic_risk_perm.sql`（挂载「大屏」父菜单下）。

---

## 5. 交付清单

- [ ] `EpidemicRiskScreenController` + `IEpidemicRiskScreenService` + VO + `EpidemicRiskScreenSupport`
- [ ] `BizLabDetectionMapper` / `BizEpidemicReportMapper` 大屏统计 SQL
- [ ] 单元测试：缺月补 0、检出率、占比、地区去重、平均解除时效、无配置权重
- [ ] 接口测试：`/dashboard`、`/reports`
- [ ] 菜单权限 SQL
- [ ] 依赖四张业务表 DDL 已执行

---

## 6. 修订记录

| 版本 | 日期 | 说明 |
| --- | --- | --- |
| 1.0 | 2026-05-25 | 初稿：无新表；双接口；对齐功能需求 v1.1（解除时效为平均天数）；气象不在本模块 |
