Przeglądaj źródła

对接牦牛资产档案

wwh 1 miesiąc temu
rodzic
commit
74fa8453ac

+ 36 - 4
doc/产业数据模型及服务/牦牛存栏数据填报/牦牛存栏数据填报功能需求.md

@@ -6,7 +6,7 @@
6 6
 | --- | --- |
7 7
 | 模块名称 | 牦牛存栏数据填报 |
8 8
 | 目标 | 按**乡镇 × 所属年份**填报、导入、维护县域牦牛存栏结构统计数据,支撑养殖产业看板、监管报表及历史年度对比 |
9
-| 关联系统 | 业务后台(增删改查、Excel 导入);组织机构 `sys_dept`(乡镇字典) |
9
+| 关联系统 | 业务后台(增删改查、Excel 导入/导出);组织机构 `sys_dept`(乡镇字典) |
10 10
 | 修订依据 | 业务模板 `2026年.xlsx`(农业农村和科技水利局-业务办-畜群结构);用户填报诉求 |
11 11
 
12 12
 本文档仅描述**功能需求与业务规则**,不涉及库表字段命名、接口路径、技术栈选型及实现细节(见同目录技术方案)。
@@ -27,7 +27,7 @@
27 27
 | --- | --- |
28 28
 | 可选范围 | **当前自然年**起向前共 **6 个自然年**(含当年),即「当年、前 1 年 … 前 5 年」 |
29 29
 | 示例 | 若统计日为 2026 年,可选:2026、2025、2024、2023、2022、2021 |
30
-| 展示 | 列表、表单、导入时均以**四位年份**展示与存储 |
30
+| 展示 | 列表、表单、导入、导出时均以**四位年份**展示与存储 |
31 31
 
32 32
 ### 2.3 所属乡镇
33 33
 
@@ -83,12 +83,12 @@
83 83
 | 删除 | 逻辑删除;见 **§5.4** |
84 84
 | 详情 | 查看全部字段;见 **§5.5** |
85 85
 | Excel 导入 | 按模板批量导入**指定填报年份**下所有乡镇数据;见 **§5.6** |
86
+| Excel 导出 | 按**指定所属年份**导出巴青县**全部乡镇**存栏数据;见 **§5.7** |
86 87
 | 乡镇下拉 | 从 `sys_dept` 动态加载合法乡镇;见 **§5.2** |
87 88
 
88 89
 **本期不实现:**
89 90
 
90 91
 - 按村居维度的多行填报、审批流、版本对比、自动从生物资产台账汇总  
91
-- 导出 Excel(若需要可单独立项)  
92 92
 - 所属村居下钻维护(字段预留)
93 93
 
94 94
 ---
@@ -100,6 +100,7 @@ flowchart TD
100 100
     A[选择所属年份] --> B{录入方式}
101 101
     B -->|手工| C[选择乡镇并填写指标]
102 102
     B -->|导入| D[上传 Excel 模板]
103
+    B -->|导出| X[下载全县乡镇 Excel]
103 104
     C --> E[保存]
104 105
     D --> F[解析并匹配乡镇]
105 106
     F --> G{同年同乡镇是否存在}
@@ -108,6 +109,7 @@ flowchart TD
108 109
     E --> J[列表查询/详情]
109 110
     H --> J
110 111
     I --> J
112
+    X --> J
111 113
 ```
112 114
 
113 115
 ---
@@ -220,6 +222,23 @@ flowchart TD
220 222
 
221 223
 ---
222 224
 
225
+### 5.7 Excel 导出
226
+
227
+| 项 | 要求 |
228
+| --- | --- |
229
+| 入口 | 列表页「按年份导出」 |
230
+| 导出年份 | 用户**必须选择**筛选条件中的「所属年份」(范围 **§2.2**);未选择时前端提示 |
231
+| 导出范围 | **巴青县全部乡镇**(**§2.3** 合法乡镇,按 `order_num` 排序),**不受**「所属乡镇」筛选限制 |
232
+| 模板 | 与导入共用:`baqing-admin/src/main/resources/templates/yak_herd_inventory_import_template.xlsx`(业务源文件 `2026年.xlsx` 第一个 Sheet) |
233
+| 填充规则 | 表头标题中的年份替换为所选年份;每个乡镇一行;有填报数据的乡镇填入全部指标;未填报乡镇仅保留乡镇名称,指标为空 |
234
+| 合计行 | 数据区末行输出「合计」,对**已填报乡镇**的数值列求和 |
235
+| 文件格式 | `.xlsx`;文件名示例:`2026年牦牛存栏数据.xlsx` |
236
+| 与导入关系 | 导出文件列结构与导入模板一致,可再次导入(同年将 upsert) |
237
+
238
+**与「下载模板」区别**:「下载模板」为空白模板;「按年份导出」为按库内数据填充后的全县报表。
239
+
240
+---
241
+
223 242
 ## 6. 权限矩阵
224 243
 
225 244
 | 操作 | 建议权限 |
@@ -230,6 +249,7 @@ flowchart TD
230 249
 | 修改 | `dataModel:yakHerdInventory:edit` |
231 250
 | 删除 | `dataModel:yakHerdInventory:remove` |
232 251
 | 导入 | `dataModel:yakHerdInventory:import` |
252
+| 导出 | `dataModel:yakHerdInventory:export` |
233 253
 
234 254
 数据权限:县级管理员可见全县;乡镇账号仅可见本乡镇数据(若启用部门数据权限,按 `town_dept_id` 与登录用户 `dept_id` 过滤)。
235 255
 
@@ -239,6 +259,7 @@ flowchart TD
239 259
 
240 260
 - 必填项未填、年份/乡镇不合法、同年同乡镇重复  
241 261
 - 导入:文件格式错误、乡镇无法匹配、数字非整数、行为空  
262
+- 导出:未选年份、年份不在可选范围  
242 263
 - 删除成功/失败、导入部分失败(展示明细)  
243 264
 
244 265
 采用项目统一 `AjaxResult` 文案风格,**不约定** HTTP 状态码细节。
@@ -252,7 +273,7 @@ flowchart TD
252 273
 | 性能 | 列表分页;导入单次 ≤ 50 行(10 乡镇 × 多年份分批) |
253 274
 | 审计 | 记录创建人、创建时间、更新人、更新时间 |
254 275
 | 口径 | 母牛分龄边界若有县级红头文件,以业务口径覆盖本文档示例说明 |
255
-| 扩展 | 所属村居(`village_dept_id`)下钻、导出、与「养殖产业数据」看板联动 |
276
+| 扩展 | 所属村居(`village_dept_id`)下钻、与「养殖产业数据」看板联动 |
256 277
 
257 278
 ---
258 279
 
@@ -265,5 +286,16 @@ flowchart TD
265 286
 | 乡镇 sys_dept | §2.3 |
266 287
 | 增删改查 | §5.1~5.5 |
267 288
 | Excel 导入 | §5.6 |
289
+| Excel 导出 | §5.7 |
268 290
 | 列表/详情字段 | §5.1 列表 7 列;§5.5 详情全字段 |
269 291
 | 预留村居 `village_dept_id` | §2.4、§5.2、§5.5 |
292
+
293
+---
294
+
295
+## 10. 修订记录
296
+
297
+| 版本 | 说明 |
298
+| --- | --- |
299
+| 1.0 | 初稿 |
300
+| 1.1 | 增加 `village_dept_id` 预留字段;列表列「乡镇」「存栏数」 |
301
+| 1.2 | 增加 **§5.7 按年份导出** 全县乡镇 Excel;权限 `export` |

+ 58 - 31
doc/产业数据模型及服务/牦牛存栏数据填报/牦牛存栏数据填报技术方案.md

@@ -14,9 +14,11 @@
14 14
 | **Excel** | Apache POI 或项目已有 EasyExcel(与若依导入规范一致) |
15 15
 | **组织** | `sys_dept` 乡镇字典;`SysDeptMapper` 查询二级节点 |
16 16
 
17
-**分层**:`BizYakHerdInventoryController` → `IBizYakHerdInventoryService` / `BizYakHerdInventoryImportService` → `BizYakHerdInventoryMapper` → `domain`
17
+**分层**:`BizYakHerdInventoryController` → `IBizYakHerdInventoryService` → `BizYakHerdInventoryMapper` → `domain`;Excel 由 `YakHerdInventoryExcelParser`(导入解析)、`YakHerdInventoryExcelExporter`(按年导出)承担
18 18
 
19
-**代码位置(建议)**:`baqing-admin` → `com.ruoyi.web.modules.industryservice`(与产业数据模型同包)。
19
+**代码位置**:`baqing-admin` → `com.ruoyi.web.modules.industryservice`(与产业数据模型同包)。
20
+
21
+**模板文件**:`baqing-admin/src/main/resources/templates/yak_herd_inventory_import_template.xlsx`(导入、导出、下载模板共用)。
20 22
 
21 23
 ---
22 24
 
@@ -50,10 +52,10 @@
50 52
 
51 53
 **索引与约束**
52 54
 
53
-- `UNIQUE uk_year_town (stat_year, town_dept_id, del_flag)` — 仅对有效记录唯一;逻辑删除后 `del_flag=2` 可再插(或采用 `uk_year_town (stat_year, town_dept_id)` + 应用层校验,推荐后者并配合查询 `del_flag=0` 唯一校验)。  
54
-- **推荐**:`UNIQUE uk_year_town_active (stat_year, town_dept_id)` 且删除时物理保留单行、仅改 `del_flag`;新增前校验无 `del_flag=0` 重复。  
55
+- 应用层校验:同年同乡镇仅一条 `del_flag='0'` 有效记录(`countActiveByYearAndTownExcludeId`)。  
55 56
 - `KEY idx_stat_year (stat_year)`  
56
-- `KEY idx_town_dept_id (town_dept_id)`
57
+- `KEY idx_town_dept_id (town_dept_id)`  
58
+- `KEY idx_del_flag (del_flag)`
57 59
 
58 60
 **DDL 文件**:`sql/biz_yak_herd_inventory.sql`
59 61
 
@@ -98,6 +100,7 @@ for (int y = current; y >= current - 5; y--) { ... }
98 100
 | 3.7 | 年份下拉 | GET | `/yearOptions` | `list` |
99 101
 | 3.8 | 导入 | POST | `/importData` | `import` |
100 102
 | 3.9 | 下载导入模板 | GET | `/importTemplate` | `import` |
103
+| 3.10 | 按年份导出 | GET | `/export` | `export` |
101 104
 
102 105
 #### 3.1.1 列表 Query
103 106
 
@@ -164,25 +167,46 @@ for (int y = current; y >= current - 5; y--) { ... }
164 167
 | 数据行 | `headerRow + 1` 起至「乡镇」列为空 |
165 168
 | 列映射(默认,实施对照模板微调) | |
166 169
 
167
-| 列号 | 表头关键字 | 实体字段 |
170
+| 列号(0-based) | Excel 列 | 实体字段 |
171
+| --- | --- | --- |
172
+| 1 | B | 乡镇(按 `dept_name` 解析 `townDeptId`) |
173
+| 2 | C | `villageCount` |
174
+| 3 | D | `herdsmanHouseholdCount` |
175
+| 4 | E | `yakTotal`(存栏数) |
176
+| 5 | F | 与 E 同值(模板镜像列,导出时同步写入) |
177
+| 6 | G | `bullTotal` |
178
+| 9 | J | `breedingBullCount` |
179
+| 10 | K | `bullCastratedCount` |
180
+| 11 | L | `cowTotal` |
181
+| 12 | M | `cowUnder1Count` |
182
+| 13 | N | `cow13Count` |
183
+| 14 | O | `cow312Count` |
184
+| 15 | P | `cowOver12Count` |
185
+| 16 | Q | `remark` |
186
+
187
+> 数据区起始行:`headerRow + 3`(表头行由「序号」「乡镇」自动识别)。模板存在合并单元格,列号以代码常量为准。
188
+
189
+**导入事务**:逐乡镇独立处理,单乡镇失败不影响其余。
190
+
191
+#### 3.10 按年份导出
192
+
193
+**请求**:`GET /export?statYear=2026`
194
+
195
+| 参数 | 必填 | 说明 |
168 196
 | --- | --- | --- |
169
-| B | 乡镇 | `townDeptId`(按名称解析) |
170
-| C | 村居数 | `villageCount` |
171
-| D | 牧户数 | `herdsmanHouseholdCount` |
172
-| E | 总合计 / 牦牛 | `yakTotal` |
173
-| F | 公牛-合计 | `bullTotal` |
174
-| G | 种公牛 | `breedingBullCount` |
175
-| H | 公牛/阉畜 | `bullCastratedCount` |
176
-| I | 母牛-合计 | `cowTotal` |
177
-| J | 1岁以下 | `cowUnder1Count` |
178
-| K | 1-3岁 | `cow13Count` |
179
-| L | 3-12岁 | `cow312Count` |
180
-| M | 12岁以上 | `cowOver12Count` |
181
-| N | 备注 | `remark` |
182
-
183
-> 模板存在合并单元格时,以**数据区实际列字母**在代码中常量配置;导入单测使用 `2026年.xlsx` 快照校验 10 个乡镇行。
184
-
185
-**导入事务**:逐乡镇 `REQUIRES_NEW` 或整批单事务;推荐**逐条独立事务**,单乡镇失败不影响其余(与牧场同步模式一致)。
197
+| `statYear` | Y | 导出年份,须在 `yearOptions` 范围内 |
198
+
199
+**响应**:`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` 文件流;文件名 `{statYear}年牦牛存栏数据.xlsx`。
200
+
201
+**实现**:`BizYakHerdInventoryServiceImpl.exportByYear` → `YakHerdInventoryExcelExporter.export`:
202
+
203
+1. 加载 classpath 模板 `YakHerdInventoryExcelExporter.TEMPLATE_CLASSPATH`  
204
+2. 替换表头中的四位年份为 `statYear`  
205
+3. 查询 `stat_year = statYear` 且 `del_flag='0'` 的全部记录,按 `town_dept_id` 建 Map  
206
+4. 遍历 `TownDeptSupport.listReportTowns()`,逐乡镇写行;无数据则仅写乡镇名  
207
+5. 末行写「合计」,对已填报乡镇数值列求和  
208
+
209
+**与 3.9 区别**:`/importTemplate` 返回空白模板;`/export` 返回填充后的全县报表。
186 210
 
187 211
 ---
188 212
 
@@ -196,7 +220,8 @@ for (int y = current; y >= current - 5; y--) { ... }
196 220
 | **详情** | 抽屉/对话框只读展示全部字段 |
197 221
 | **表单** | 抽屉或对话框;年份/乡镇下拉;数字 `el-input-number` `:min="0"` `:precision="0"` |
198 222
 | **导入** | 先选「填报年份」→ 上传 → 展示导入结果摘要 |
199
-| **模板下载** | 可提供静态模板副本 `doc/.../templates/2026年-牦牛存栏导入模板.xlsx`(从业务文件复制) |
223
+| **模板下载** | `GET /importTemplate`;空白模板 |
224
+| **按年份导出** | 列表先选「所属年份」→ `GET /export`;`download('dataModel/yakHerdInventory/export', { statYear })` |
200 225
 
201 226
 ---
202 227
 
@@ -210,8 +235,9 @@ for (int y = current; y >= current - 5; y--) { ... }
210 235
 | 按钮 | 修改 | `dataModel:yakHerdInventory:edit` |
211 236
 | 按钮 | 删除 | `dataModel:yakHerdInventory:remove` |
212 237
 | 按钮 | 导入 | `dataModel:yakHerdInventory:import` |
238
+| 按钮 | 导出 | `dataModel:yakHerdInventory:export` |
213 239
 
214
-挂载目录:**产业数据模型及服务**。
240
+挂载目录:**产业数据模型及服务**。菜单 SQL 见 `sql/biz_sys_menu_yak_herd_inventory.sql`。
215 241
 
216 242
 ---
217 243
 
@@ -223,12 +249,12 @@ for (int y = current; y >= current - 5; y--) { ... }
223 249
 
224 250
 ## 7. 交付清单
225 251
 
226
-- [ ] `sql/biz_yak_herd_inventory.sql`  
227
-- [ ] `BizYakHerdInventory` Domain / Mapper / XML / Service / Controller  
228
-- [ ] `BizYakHerdInventoryImportService` + 列映射配置  
229
-- [ ] `TownDeptSupport` 乡镇字典  
230
-- [ ] 前端列表/表单/导入  
231
-- [ ] 单元测试 + 导入集成测试(`2026年.xlsx`)  
252
+- [x] `sql/biz_yak_herd_inventory.sql`  
253
+- [x] `BizYakHerdInventory` Domain / Mapper / XML / Service / Controller  
254
+- [x] `YakHerdInventoryExcelParser` + `YakHerdInventoryExcelExporter`  
255
+- [x] `TownDeptSupport` 乡镇字典  
256
+- [x] 前端列表/表单/导入/导出  
257
+- [x] 单元测试 + 导入/导出集成测试(`yak_herd_inventory_2026.xlsx`)  
232 258
 
233 259
 ---
234 260
 
@@ -238,3 +264,4 @@ for (int y = current; y >= current - 5; y--) { ... }
238 264
 | --- | --- |
239 265
 | 1.0 | 初稿:表结构、CRUD、导入、sys_dept 乡镇、年份前推 5 年 |
240 266
 | 1.1 | 增加预留字段 `village_dept_id`(非必填) |
267
+| 1.2 | 实现按年份导出全县乡镇 Excel;列映射与代码对齐;交付清单勾选 |

+ 14 - 2
doc/产业数据模型及服务/牦牛存栏数据填报/牦牛存栏数据填报测试用例.md

@@ -30,6 +30,8 @@
30 30
 | ZCZX-MYCLTB-UT-015 | 数据权限 | 乡镇账号过滤 | 单元测试 | JUnit5+Mockito | §6 | 用户 dept=杂色镇 | `selectList` | 仅本乡镇 |
31 31
 | ZCZX-MYCLTB-UT-016 | 预留字段 | village_dept_id 可空 | 单元测试 | JUnit5 | §2.4 | 无 | `insert` 不传 `villageDeptId` | 成功 |
32 32
 | ZCZX-MYCLTB-UT-017 | 预留字段 | village_dept_id 父子校验 | 单元测试 | JUnit5+Mockito | §2.4 | 乡镇 A、村居 B 非子节点 | `insert` 带错误 `villageDeptId` | 失败(若启用校验)或成功仅存 id |
33
+| ZCZX-MYCLTB-UT-018 | 导出 | 按年填充模板 | 单元测试 | JUnit5 | §5.7 | 模板+1 乡镇样本 | `YakHerdInventoryExcelExporter.export` | 返回 xlsx;解析含对应存栏数 |
34
+| ZCZX-MYCLTB-UT-019 | 导出 | 全县乡镇行数 | 单元测试 | JUnit5 | §5.7 | 10 乡镇字典 | `export(2026, towns, map)` | 解析数据行 + 合计行 |
33 35
 
34 36
 ---
35 37
 
@@ -57,6 +59,11 @@
57 59
 | ZCZX-MYCLTB-API-018 | 权限 | 无 add | 接口测试 | Postman | §6 | 仅 list 角色 | `POST /` | 403 |
58 60
 | ZCZX-MYCLTB-API-019 | 权限 | 无 import | 接口测试 | Postman | §6 | 无 import | `POST /importData` | 403 |
59 61
 | ZCZX-MYCLTB-API-020 | 端到端 | 导入-查-详情 | 接口测试 | Postman | §4 | 空库 | 导入 2026→list→detail | 数据一致 |
62
+| ZCZX-MYCLTB-API-021 | 导出 | 按年份导出 | 接口测试 | Postman/MockMvc | §5.7 | 有 2026 数据 | `GET /export?statYear=2026` | `200`;`Content-Type` 为 xlsx |
63
+| ZCZX-MYCLTB-API-022 | 导出 | 缺 statYear | 接口测试 | Postman | §5.7 | 已登录 | `GET /export` 无参 | 失败 |
64
+| ZCZX-MYCLTB-API-023 | 导出 | 非法年份 | 接口测试 | Postman | §5.7 | 已登录 | `statYear=1999` | 失败 |
65
+| ZCZX-MYCLTB-API-024 | 权限 | 无 export | 接口测试 | Postman | §6 | 无 export | `GET /export?statYear=2026` | 403 |
66
+| ZCZX-MYCLTB-API-025 | 端到端 | 导出-再导入 | 接口测试 | Postman | §5.7 | 有 2026 全量 | 导出→改一行→导入 | upsert 成功 |
60 67
 
61 68
 ---
62 69
 
@@ -79,6 +86,9 @@
79 86
 | ZCZX-MYCLTB-UI-011 | 导入 | 未选年份 | UI 测试 | Playwright | §5.6 | 有 import 权限 | 直接上传 | 前端拦截或后端失败提示 |
80 87
 | ZCZX-MYCLTB-UI-012 | 权限 | 无导入按钮 | UI 测试 | Playwright | §6 | 无 import | 进入页面 | 无导入入口 |
81 88
 | ZCZX-MYCLTB-UI-013 | 端到端 | 导入后列表 | UI 集成 | Playwright | §4 | 空库 | 导入→筛选 2026 | 10 行乡镇数据 |
89
+| ZCZX-MYCLTB-UI-014 | 导出 | 按年份导出 | UI 测试 | Playwright | §5.7 | 有 export 权限;已选年份 | 点「按年份导出」 | 下载 `{年}年牦牛存栏数据.xlsx` |
90
+| ZCZX-MYCLTB-UI-015 | 导出 | 未选年份 | UI 测试 | Playwright | §5.7 | 有 export 权限 | 未选年份点导出 | 提示先选择所属年份 |
91
+| ZCZX-MYCLTB-UI-016 | 权限 | 无导出按钮 | UI 测试 | Playwright | §6 | 无 export | 进入页面 | 无「按年份导出」入口 |
82 92
 
83 93
 ---
84 94
 
@@ -92,7 +102,8 @@
92 102
 | §5.2~5.4 CRUD | 必填、唯一、删除 | UT-003~006;API-006~011 |
93 103
 | §5.5 详情 | 全字段 | API-005;UI-009 |
94 104
 | §5.6 导入 | 2026年.xlsx | UT-007~012;API-014~017;UI-010~011 |
95
-| §6 权限 | 按钮级 | API-018~019;UI-012 |
105
+| §5.7 导出 | 按年全县乡镇 | UT-018~019;API-021~025;UI-014~015 |
106
+| §6 权限 | 按钮级 | API-018~019、024;UI-012、016 |
96 107
 
97 108
 ---
98 109
 
@@ -101,4 +112,5 @@
101 112
 | 版本 | 说明 |
102 113
 | --- | --- |
103 114
 | 1.0 | 初版:单元 15、接口 20、UI 13;编号前缀 **ZCZX-MYCLTB** |
104
-| 1.1 | 补充 `village_dept_id` 预留字段用例 UT-016~017;单元用例共 17 条 |
115
+| 1.1 | 补充 `village_dept_id` 预留字段用例 UT-016~017 |
116
+| 1.2 | 补充按年份导出用例 UT-018~019、API-021~025、UI-014~016 |