# 共同富裕成果年度报告 — 技术方案

> 依据：同目录 `共同富裕成果年度报告功能需求.md`；数据源为《共同富裕成果技术方案》表 `biz_common_prosperity_achievement`。本模块**只读统计**，无独立业务表。

---

## 1. 技术架构

| 项 | 说明 |
| --- | --- |
| **后端** | RuoYi **v3.9.2**（**springboot2** 分支）：JDK 8、Spring Boot 2.x、Spring MVC、MyBatis、Druid |
| **数据库** | MySQL **5.7.39**，InnoDB，`utf8mb4` |
| **前端** | 若依 Vue2 + **ECharts**（或项目已有图表封装）；单接口驱动整页 |
| **代码包** | `com.ruoyi.web.modules.commonprosperity`（与成果台账同包，建议 `achievement` 子包旁增 `report` 或合入 `AchievementReportController`） |

**分层**：`Controller`（权限、`statYear` 校验、`AjaxResult`）→ `ReportService`（按年查询、缺月补 0、年度汇总）→ 复用 `BizCommonProsperityAchievementMapper`（或只读 SQL）。

**实现要点**

- **不新建业务表**；按 `stat_year` 查询成果表，内存组装 **12 个月序列** + **年度总览**。
- **缺月按 0**（功能需求 §2.2、§2.4）；**全年无记录**时 `hasData=false`，不返回「全 0 汇总」误导前端。
- 前端**一次请求**加载总览与全部图表（功能需求 §8 性能建议）。

---

## 2. 数据库设计

### 2.1 本模块表结构

**无新增表。** 年度报告数据全部来自既有表：

| 表名 | 用途 |
| --- | --- |
| `biz_common_prosperity_achievement` | 月度成果台账；按 `stat_period`（`YYYY-MM`）取数 |

字段、DDL、索引以《共同富裕成果技术方案》**§2** 为准，此处不重复。

### 2.2 查询与索引

| 项 | 说明 |
| --- | --- |
| **按年筛选** | `stat_period LIKE CONCAT(#{statYear}, '-%')` 或 `LEFT(stat_period, 4) = #{statYear}` |
| **排序** | `ORDER BY stat_period ASC`（组装 1～12 月） |
| **索引** | 沿用 `idx_stat_period` / `uk_stat_period`；单年最多 12 行，无额外索引要求 |
| **可选年份列表** | `SELECT DISTINCT LEFT(stat_period, 4) AS stat_year FROM biz_common_prosperity_achievement ORDER BY stat_year DESC` |

### 2.3 汇总计算（服务端，与大屏共同富裕一致）

| 类型 | 字段 | 算法 |
| --- | --- | --- |
| 总览 `summary` | 全部 12 项指标 | 取该年台账 **最大统计月** 的单月值 |
| 分月 `monthly` | 同上字段 | 所选年 **1～最大月 N**；区间内缺月补 **0**；无数据返回空列表 |

**`hasData` 判定**：该年存在 ≥1 条有效台账为 `true`；为 `false` 时 `summary=null`，`monthly=[]`。

---

## 3. 接口设计

**统一响应**：`AjaxResult` → `data` 为 **3.1** 结构（驼峰 JSON）。

**权限标识（示例）**：`commonProsperity:achievementReport:query`（仅查询；与成果台账 CRUD 权限分离）

**Base Path（示例）**：`/commonProsperity/achievementReport`

| # | 说明 | Method | URI | 权限 | 要点 |
| --- | --- | --- | --- | --- | --- |
| 3.1 | 年度报告数据 | GET | `/commonProsperity/achievementReport` | `commonProsperity:achievementReport:query` | Query：`statYear`（必填，4 位年） |
| 3.2 | 可选年份列表 | GET | `/commonProsperity/achievementReport/years` | 同上 | 无参；返回台账中出现过的年份，供年份选择器 |

> 若希望接口更少，可将 **3.2** 的 `availableYears` 并入 **3.1** 的 `data.availableYears`。

### 3.1.1 Query 参数（年度报告）

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `statYear` | int / string | Y | 统计年份，如 `2026`；非法年份返回 400 业务错误 |

### 3.1.2 响应 `data` 结构

```json
{
  "statYear": 2026,
  "hasData": true,
  "summary": {
    "collectiveEconomyIncome": 120.50,
    "employmentDrivenCount": 360,
    "newJobPositions": 48,
    "envProjectCount": 12,
    "newGreenArea": 85.20,
    "culturalHeritageCount": 24,
    "ethnicIntegrationProjectCount": 6,
    "ethnicUnityActivityCount": 18,
    "digitalPlatformUsageRate": 0.7250,
    "regionalPublicBrandCount": 9,
    "brandPremiumRate": 0.1500
  },
  "monthly": [
    {
      "month": 1,
      "statPeriod": "2026-01",
      "collectiveEconomyIncome": 10.00,
      "employmentDrivenCount": 30,
      "newJobPositions": 4,
      "envProjectCount": 1,
      "newGreenArea": 7.10,
      "culturalHeritageCount": 2,
      "ethnicIntegrationProjectCount": 1,
      "ethnicUnityActivityCount": 2,
      "digitalPlatformUsageRate": 0.80,
      "regionalPublicBrandCount": 1,
      "brandPremiumRate": 0.12
    }
  ]
}
```

| 字段 | 说明 |
| --- | --- |
| `statYear` | 请求年份回显 |
| `hasData` | 该年是否存在 ≥1 条成果记录 |
| `summary` | 年度总览；`hasData=false` 时为 **`null`**（不推荐返回全 0） |
| `monthly` | **固定长度 12**；`month` 为 1～12；`statPeriod` 有台账则为 `YYYY-MM`，缺月为 **`null`** 或省略（指标值均为 **0**） |
| `monthly[].*` 指标 | 与成果台账字段一致（驼峰）；缺月各项为 **0** |

**图表与字段映射（前端）**

| 图表区块 | 取用字段 |
| --- | --- |
| 集体经济收入（横向柱） | `monthly[].collectiveEconomyIncome` |
| 就业带动（双折线） | `employmentDrivenCount`、`newJobPositions` |
| 环境治理（柱） | `envProjectCount` |
| 绿化建设（折线） | `newGreenArea` |
| 文化传承（柱） | `culturalHeritageCount` |
| 民族团结（堆叠柱） | `ethnicIntegrationProjectCount`、`ethnicUnityActivityCount` |

### 3.2 响应 `data`（可选年份）

```json
{
  "years": [2026, 2025, 2024]
}
```

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

```
1. 校验 statYear（四位数字，合理范围如 1900–2100）
2. List<Achievement> rows = mapper.selectByStatYear(statYear)
3. hasData = rows.notEmpty()
4. Map<Integer, Achievement> byMonth = 按 stat_period 月份 1-12 映射
5. for m in 1..12:
     monthly[m] = byMonth.get(m) 或 全指标 0
6. if hasData:
     summary = 按 §2.3 对 monthly 序列做 SUM / AVG(比率,÷12)
   else:
     summary = null
7. return AjaxResult.success(data)
```

**异常**：`statYear` 缺失/非法 → `code`/`msg` 明确；Mapper 异常走统一异常处理。

---

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

| 类型 | 名称 | 权限标识 | 组件路径（示例） |
| --- | --- | --- | --- |
| 菜单 | 共同富裕成果年度报告 | `commonProsperity:achievementReport:query` | `commonProsperity/achievementReport/index` |

前端 API 建议：`ruoyi-ui/src/api/commonProsperity/achievementReport.js` → `getAchievementAnnualReport(statYear)`。

---

## 5. 交付清单

- [ ] `AchievementReportController` + `IAchievementReportService`（或合入现有 Service）
- [ ] Mapper 方法：`selectByStatYear`、`selectDistinctYears`（只读）
- [ ] 单元测试：全年无数据、仅 3 月有数据（其余月为 0）、比率年均、加总正确
- [ ] 前端：年份选择 + ECharts 六图 + `hasData` 空态；i18n 复用 `commonProsperity.achievement`
- [ ] 菜单权限 SQL（`commonProsperity:achievementReport:query`）
- [ ] 依赖成果表 DDL 已执行（`biz_common_prosperity_achievement`）

---

## 6. 修订记录

| 版本 | 说明 |
| --- | --- |
| 1.0 | 初稿：无新表；单接口年度报告 + 可选年份列表；缺月补 0；`hasData` 控制空态 |
| 1.1 | 与大屏对齐：总览取最大月单月；`monthly` 为 1～最大月 |
