西藏巴青项目

牦牛出栏数据填报技术方案.md 13KB

牦牛出栏数据填报 — 技术方案

依据:同目录 牦牛出栏数据填报功能需求.md;模板 E:\luhao\工作交接\巴青项目\农牧数据\农业农村和科技水利局-业务办\牲畜数据.xlsx6 个 Sheet(2026 年度出栏及自食统计表)。


1. 技术架构

说明
后端 RuoYi v3.9.2(springboot2):JDK 8、Spring Boot 2.x、Spring MVC、MyBatis、Druid
数据库 MySQL 5.7.39,InnoDB,utf8mb4
前端 若依 Vue2 + Element UI
Excel Apache POI(与「牦牛存栏数据填报」一致)
组织 sys_dept 乡镇字典;复用 TownDeptSupport

分层BizYakOutboundReportControllerIBizYakOutboundReportServiceBizYakOutboundReportMapperdomain;Excel 由 YakOutboundReportExcelParser(导入解析)、YakOutboundReportExcelExporter(按年月导出)承担。

代码位置baqing-admincom.ruoyi.web.modules.industryservice(与产业数据模型同包)。

模板文件baqing-admin/src/main/resources/templates/yak_outbound_report_import_template.xlsx(导入、导出、下载模板共用)。

参考实现BizYakHerdInventory*YakHerdInventoryMonthSupportTownDeptSupport(存栏填报模块,年月模式一致)。


2. 数据库设计

2.1 表 biz_yak_outbound_report(牦牛出栏数据填报)

字段 类型 非空 说明
id bigint(20) Y 主键
stat_year int(11) Y 所属年份(自然年)
stat_month int(11) Y 所属月份(1~12)
town_dept_id bigint(20) Y 所属乡镇,sys_dept.dept_id
town_name varchar(64) Y 乡镇名称冗余
farmer_household_count int(11) N 农牧户户数
farmer_population_count int(11) N 农牧户人数
yak_outbound_count int(11) N 牦牛出栏数(头)
cattle_outbound_count int(11) N 黄牛/奶牛/犏牛出栏数(头)
sheep_outbound_count int(11) N 绵羊出栏数(只)
goat_outbound_count int(11) N 山羊出栏数(只)
horse_mule_outbound_count int(11) N 马骡出栏数(匹)
donkey_outbound_count int(11) N 驴出栏数(头)
self_consumption_cattle_count int(11) N 自食牛头数(头)
self_consumption_sheep_count int(11) N 自食绵羊只数(只)
self_consumption_goat_count int(11) N 自食山羊只数(只)
create_by / create_time 审计
update_by / update_time 审计
remark varchar(500) N 备注

说明:本表不使用逻辑删除,无 del_flag;删除为物理 DELETE

索引与约束

  • UNIQUE uk_year_month_town (stat_year, stat_month, town_dept_id) — 年月+乡镇唯一,支撑导入 upsert。
  • KEY idx_stat_year_month (stat_year, stat_month)
  • KEY idx_town_dept_id (town_dept_id)

DDL 文件sql/biz_yak_outbound_report.sql(新库,已含 stat_month)。
已有库升级(v1.1 按年表 uk_year_town):增加 stat_month 列(历史行默认 12),调整唯一键为 uk_year_month_town(实施阶段提供迁移 SQL,模式同存栏模块)。

2.2 乡镇字典(复用)

SELECT dept_id, dept_name, order_num
FROM sys_dept
WHERE parent_id = 100
  AND order_num > 12
  AND del_flag = '0'
  AND status = '0'
ORDER BY order_num;

封装:TownDeptSupport.listReportTowns()requireReportTown(deptId)

乡镇别名(导入):模板「扎色镇」→ sys_dept「杂色镇」,在 YakOutboundReportExcelParser.resolveTownDeptIdTownDeptSupport 扩展 normalizeTownName

2.3 可选年份

与存栏模块一致:复用 YakHerdInventoryYearSupport.resolveYearOptions(),返回当前年起向前 5 年共 6 个年份。

2.4 可选月份

复用 YakHerdInventoryMonthSupport.resolveMonthOptions() 返回 1~12validateStatPeriod(year, month) 校验年份在 §2.3 范围内、月份合法且非未来月。出栏校验类 YakOutboundReportValidation 亦调用该 Support。


3. 接口设计

Base Path/dataModel/yakOutboundReport

权限前缀dataModel:yakOutboundReport

# 说明 Method URI 权限
3.1 分页列表 GET /list list
3.2 详情 GET /{id} query
3.3 新增 POST / add
3.4 修改 PUT / edit
3.5 删除 DELETE /{ids} remove
3.6 乡镇下拉 GET /townOptions list
3.7 年份下拉 GET /yearOptions list
3.8 月份下拉 GET /monthOptions list
3.9 导入 POST /importData import
3.10 下载导入模板 GET /importTemplate import
3.11 按年月导出 GET /export export

3.1.1 列表 Query

参数 类型 说明
pageNum / pageSize int 默认 1 / 20
statYear int 所属年份,精确
statMonth int 所属月份(1~12),精确
townDeptId long 所属乡镇 dept_id,精确

列表 VO

列标题 字段
所属年份 statYear
所属月份 statMonth
所属乡镇 townName
农牧户户数 farmerHouseholdCount
农牧户人数 farmerPopulationCount
牦牛出栏数 yakOutboundCount
自食情况(牛) selfConsumptionCattleCount

3.2.1 详情

返回需求 §5.5 指定字段(驼峰):

statYearstatMonthtownNamefarmerHouseholdCountfarmerPopulationCountyakOutboundCountselfConsumptionCattleCount

可选附带 idtownDeptId 供前端跳转修改。

3.3.1 新增/修改 Body

字段 必填 校验
statYear Y yearOptions 范围内
statMonth Y 1~12;非未来月
townDeptId Y 须在乡镇字典内
farmerHouseholdCount 等数量字段 N >= 0 整数
id 修改必填

唯一性(手工新增)countByYearMonthAndTownExcludeId > 0 则拒绝,提示「该年该月该乡镇已填报」。导入走 upsert。

3.5.1 删除

DELETE FROM biz_yak_outbound_report WHERE id IN (...),物理删除。

3.9 导入

请求multipart/form-data

参数 必填 说明
file Y .xlsx
statYear Y 填报年份
statMonth Y 填报月份(1~12)

UpsertBizYakOutboundReportServiceImpl.upsertImportRow):

  1. selectByYearMonthAndTown(statYear, statMonth, townDeptId)
  2. 无记录 → insert;有记录 → update 全字段覆盖

同一文件对同一填报年月多次导入:各乡镇行均为更新,不产生重复有效记录。

响应 datainsertCountupdateCountfailCountfailMessageswarnMessages(自食大于出栏等)

3.9.1 Excel 第 6 Sheet 解析约定

约定
Sheet workbook.getSheetAt(5)(第 6 个 Sheet,0-based 索引 5)
表头识别 扫描含「乡镇」且同行含「户数」「牦牛」的行;数据自 headerRow + 1
终止行 乡镇为空,或乡镇名为「合计」
列映射(0-based)
列号 实体字段
1 乡镇 → townDeptId / townName
2 farmerHouseholdCount
3 farmerPopulationCount
4 yakOutboundCount
5 cattleOutboundCount
6 sheepOutboundCount
7 goatOutboundCount
8 horseMuleOutboundCount
9 donkeyOutboundCount
10 跳过(出栏小计)
11 selfConsumptionCattleCount
12 selfConsumptionSheepCount
13 selfConsumptionGoatCount
14 跳过(自食小计)
15 remark

模板存在合并单元格,部分行 POI 读列可能错位;解析器宜按表头行固定列号读取,并对空单元格按 0 或 null 处理(与存栏导入一致)。

导入事务:逐乡镇独立处理,单乡镇失败不影响其余。

3.11 按年月导出

请求GET /export?statYear=2026&statMonth=7

参数 必填 说明
statYear Y 导出年份,须在 yearOptions 范围内
statMonth Y 导出月份(1~12);须通过 validateStatPeriod

响应application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 文件流;文件名 {statYear}年{statMonth}月牦牛出栏数据.xlsx

实现BizYakOutboundReportServiceImpl.exportByYearMonthYakOutboundReportExcelExporter.export

  1. 加载 classpath 模板 YakOutboundReportExcelExporter.TEMPLATE_CLASSPATH
  2. 解析出栏 Sheet(YakOutboundReportExcelParser.resolveSheet
  3. 替换表头中的四位年份为 statYear(含标题行与「年牲畜出栏数」表头区块;年份与「年」之间允许空格)
  4. 将 Sheet 名称更新为 {年份后两位}年出栏(如 2024 → 24年出栏
  5. 查询 stat_year = statYear AND stat_month = statMonth 的全部记录,按 town_dept_id 建 Map
  6. 遍历 TownDeptSupport.listReportTowns(),逐乡镇写行;无数据则仅写乡镇名
  7. 末行写「合计」,对已填报乡镇数值列求和;K/O 列写入出栏/自食小计

与 3.10 区别/importTemplate 返回空白模板;/export 返回填充后的全县报表。


4. 前端设计(要点)

说明
路径 ruoyi-ui/src/views/dataModel/yakOutboundReport/index.vue
API ruoyi-ui/src/api/dataModel/yakOutboundReport.js
列表 年份、月份、乡镇筛选;进入页面不默认年月;列:所属年份、所属月份、所属乡镇、农牧户户数、农牧户人数、牦牛出栏数、自食情况(牛)
详情 对话框只读,展示 §5.5 七字段(含月份)
表单 新增/修改展示全部数量字段;年份/月份/乡镇下拉;新增默认当前年月el-input-number :min="0" :precision="0"
导入 先选「填报年份」「填报月份」(默认当前年月)→ 上传 → 展示导入结果摘要
模板下载 GET /importTemplate;空白模板
导出 弹窗选年月(默认当前年月)→ GET /export?statYear=&statMonth=

5. 菜单与权限(示例)

类型 名称 权限标识
菜单 牦牛出栏数据填报 dataModel:yakOutboundReport:list
按钮 查询 dataModel:yakOutboundReport:query
按钮 新增 dataModel:yakOutboundReport:add
按钮 修改 dataModel:yakOutboundReport:edit
按钮 删除 dataModel:yakOutboundReport:remove
按钮 导入 dataModel:yakOutboundReport:import
按钮 导出 dataModel:yakOutboundReport:export

挂载目录:产业数据模型及服务。菜单 SQL:sql/biz_sys_menu_yak_outbound_report.sql(实施阶段)。


6. 与存栏填报模块的关系

模块 模板 Sheet 业务含义
牦牛存栏数据填报 biz_yak_herd_inventory 牲畜数据.xlsx 第 1 Sheet(畜群结构) 月末存栏结构
牦牛出栏数据填报 biz_yak_outbound_report 牲畜数据.xlsx 第 6 Sheet 月度出栏及自食

两模块共用 TownDeptSupport、年份范围规则、YakHerdInventoryMonthSupport 年月校验、导入 upsert 模式;本期不实现跨表校验(如出栏数不大于存栏数)。

6.1 与大屏 / 养殖看板(暂不改)

产业总览中「牦牛出栏数」「农牧户户数」等字段当前仍通过 HomeScreenStatsMapper.sumOutboundReport*ByStatYear 仅按 stat_year 汇总,不按月份过滤。同一年存在多个月份填报时可能重复累加;后续单独设计看板口径,本模块按年月维护明细。


7. 交付清单

  • sql/biz_yak_outbound_report.sql(含 stat_month
  • BizYakOutboundReport Domain / Mapper / XML / Service / Controller
  • YakOutboundReportExcelParser + YakOutboundReportExcelExporter + 导入模板 xlsx(第 6 Sheet)
  • 复用 TownDeptSupportYakHerdInventoryYearSupportYakHerdInventoryMonthSupport
  • 前端列表/表单/导入/导出(年月 UI,对齐存栏模块)
  • 单元测试 + 接口测试
  • sql/biz_sys_menu_yak_outbound_report.sql

8. 修订记录

版本 说明
1.0 初稿:表结构、CRUD、第 6 Sheet 导入、物理删除、乡镇字典
1.1 实现按年份导出全县乡镇 Excel;YakOutboundReportExcelExporter
1.2 按年月填报:stat_monthuk_year_month_town/monthOptions;导入/导出/CRUD 支持月份;复用 YakHerdInventoryMonthSupport