西藏巴青项目

养殖产业数据技术方案.md 15KB

养殖产业数据 — 技术方案

依据:同目录 养殖产业数据功能需求.md。本期为只读看板:全页指标由既有台账聚合计算,无业务台账 CRUD、无第三方同步接口。


1. 技术架构

说明
后端 RuoYi v3.9.2springboot2 分支):JDK 8、Spring Boot 2.x、Spring MVC、MyBatis、Druid
数据库 MySQL 5.7.39,InnoDB,utf8mb4
前端 若依 Vue2;见 养殖产业数据前端技术方案.md
时区 Asia/Shanghai(自然年、统计日、预警时间范围)

分层ControllerIBreedingIndustryDashboardService(口径编排、数据权限)→ BreedingIndustryStatsMapper(聚合 SQL)→ 既有 biz_* 表。

代码位置(建议)baqing-admincom.ruoyi.web.modules.industryserviceBreedingIndustryDashboardControllerBreedingIndustryStatsMapper、VO)。

场景 行为
看板加载/刷新 一次请求返回四块数据;statDate 取服务器当前日期
数据权限 Service 注入用户可见 pastureId 列表;牦牛、预警按 §2.2 过滤;草场全县台账不按牧场过滤
月末存栏趋势 应用层按 1~当前月循环,每月按 §2.2 月末存栏判定 及需求 §2.10 计数( asset_status=1 简单过滤)
公/母牛 仅统计当前存栏asset_status=1),与趋势图口径分离
性能 数据量大时启用 §2.3 可选快照表 + 短 TTL 缓存;本期可先实时聚合;月末 12 点目标 ≤3s(1 万级档案,评审可调)

依赖模块(只读)

模块
biz_pasture 牧场管理
biz_grassland 草场管理
biz_yak_asset 牦牛资产档案
biz_yak_disease_warning 牦牛疾病预警

2. 数据库设计

2.1 设计原则

本模块不新增业务台账表;指标均通过对上表 SELECT 聚合 得到,口径以功能需求 §2 为准。

2.2 数据源与字段映射

biz_pasture(牧场数量)

指标 SQL 条件要点
牧场数量 del_flag='0' AND biz_status=1 AND id IN (可见牧场ID集合)

biz_grassland(草场数量、面积、可用/使用中、退化占比)

指标 SQL 条件要点
草场数量 del_flag='0'
草场面积 SUM(area_mu)area_mu IS NOT NULL
可用草场 del_flag='0' AND degradation_level IN (1,2)(1未退化 2轻度)
使用中草场 del_flag='0' AND (utilization_type IN (2,3) OR (utilization_type=1 AND 统计日 NOT BETWEEN grazing_ban_start_date AND grazing_ban_end_date))
退化分组 GROUP BY degradation_level(1~5)

枚举(与草场管理一致)

字段 含义
degradation_level 1~5 未退化、轻度、中度、重度、极重度
utilization_type 1/2/3 禁牧、轮牧、全年放牧

biz_yak_asset(存栏、出栏、性别、年龄、月趋势)

指标 SQL 条件要点
当前存栏 del_flag='0' AND pasture_id IN (...) AND pasture_id IS NOT NULL AND asset_status=1
年出栏 同上牧场范围 AND asset_status=4 AND status_change_date 在当年且 ≤ 统计日 AND status_change_date IS NOT NULL
公牛/母牛 在存栏集合上 gender='公' / gender='母'
未标注性别 存栏总量 − 公牛 − 母牛
年龄分档 在存栏集合上按 age_months(或 birth_date 计算月龄)分 0~6、7~12、≥13
月末存栏 对每条档案用 §2.2 月末存栏判定(见下),非仅 asset_status=1

asset_status1 正常 2 死淘 3 丢失 4 出栏。

月末时点计入存栏(单头判定,记月末日为 M

计入 ⇔ del_flag='0'
      AND pasture_id IN (可见牧场)
      AND pasture_id IS NOT NULL
      AND NOT (asset_status IN (2,3,4) AND status_change_date IS NOT NULL AND status_change_date <= M)
      AND asset_status 可识别(1 或 2/3/4 且 change_date > M)

当前月:M 替换为 统计日

biz_yak_disease_warning(预警牦牛数量)

-- 口径示意:当年 1/1 0:00 ~ 统计日 23:59:59,去重 yak_no,牦牛须在纳入集合内
SELECT COUNT(DISTINCT w.yak_no)
FROM biz_yak_disease_warning w
INNER JOIN biz_yak_asset y ON y.yak_no = w.yak_no AND y.del_flag = '0'
WHERE w.alert_time >= ? AND w.alert_time <= ?
  AND y.pasture_id IN (...)
  AND y.pasture_id IS NOT NULL;

2.3 可选表 biz_breeding_dashboard_cache(性能优化)

数据量较大或刷新慢时启用;非本期必建

字段 类型 说明
id bigint(20) 主键
scope_key varchar(64) 权限范围键,如 alluser:{userId}
stat_date date 统计日
payload_json mediumtext 看板完整 JSON(与 §3.2 data 结构一致)
expire_time datetime 过期时间
create_time datetime 写入时间

索引UNIQUE uk_scope_stat (scope_key, stat_date)

刷新时:先查未过期缓存命中则直接返回;否则实时计算后 UPSERT。TTL 建议 5~15 分钟(可配置)。

2.4 索引建议(既有表)

若统计偏慢,确认已存在(sql/biz_yak_asset.sql 等已含大部分):

建议索引
biz_yak_asset (pasture_id, del_flag, asset_status)status_change_date
biz_yak_disease_warning (alert_time)(yak_no, alert_time)
biz_grassland (del_flag, degradation_level)(del_flag, utilization_type)
biz_pasture (del_flag, biz_status)

2.5 可选缓存表 DDL

CREATE TABLE IF NOT EXISTS `biz_breeding_dashboard_cache` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `scope_key` varchar(64) NOT NULL COMMENT '权限范围键',
  `stat_date` date NOT NULL COMMENT '统计日',
  `payload_json` mediumtext NOT NULL COMMENT '看板JSON',
  `expire_time` datetime NOT NULL COMMENT '过期时间',
  `create_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_scope_stat` (`scope_key`,`stat_date`),
  KEY `idx_expire_time` (`expire_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='养殖产业看板缓存(可选)';

2.6 数据权限实现

与「牦牛疾病预警」列表一致:聚合前解析可见牧场 ID 集合 allowedPastureIds,传入 BreedingIndustryStatsMapperIN (...) 条件(牦牛、预警、牧场数量);草场统计不加牧场条件。

建议类com.ruoyi.web.modules.industryservice.support.BreedingIndustryPastureScope(或复用后续统一的 IndustryPastureScopeHelper)。

步骤 说明
1 查询 biz_pasturedel_flag='0' AND biz_status=1,得到候选 ID 列表
2 按当前登录用户应用项目数据权限(部门/角色自定义范围,与预警 allowedPastureIds 同源);县级全量角色得到全部候选 ID
3 将结果写入统计上下文 BreedingIndustryStatsContext.allowedPastureIds

三种语义(实现须区分)

allowedPastureIds 牦牛 / 预警 / 牧场数 草场
null(未限制,全量管理员) 候选列表 = 步骤 1 全部正常牧场 ID 全县 del_flag=0
非空列表 pasture_id IN (...)pasture_id IS NOT NULL 不变
空列表 [](有权限框架但无可见牧场) 牦牛类、预警、牧场数 均为 0 仍全县统计

Controller 示例

List<Long> allowed = pastureScope.resolveAllowedPastureIds();
return success(dashboardService.loadDashboard(allowed, forceRefresh));

Mapper 约定:与 BizYakDiseaseWarningMapper.xml 相同写法——仅当 allowedPastureIds != null && size > 0 时追加 pasture_id in (...);全量管理员传完整 ID 列表(勿传 null 跳过牦牛过滤,以免误统计 pasture_id 为空的档案)。

预警biz_yak_disease_warningbiz_yak_assetyak_no 关联后,再限制 y.pasture_id IN (...)(见 §2.2 SQL)。


3. 接口设计

统一响应AjaxResultcode / msg / data)。

权限dataModel:breedingIndustry:query(查看与刷新共用)。

Base Path/dataModel/breedingIndustry

本期仅 1 个业务接口;不提供 POST/PUT/DELETE 维护接口。

# 说明 Method URI 权限
3.1 看板全量数据 GET /dashboard query

前端进入页与点击「刷新」均调用 3.1

3.1.1 Query

参数 类型 必填 说明
forceRefresh boolean N 默认 falsetrue 时跳过 §2.3 缓存

3.1.2 响应 data 结构

节点 类型 说明
statDate string 统计日 yyyy-MM-dd
statYear int 当前自然年,如 2026
overview object 产业总览 §3.1.3
inventoryChange object 牦牛存栏变动 §3.1.4
ageStructure array 年龄结构 §3.1.5
grassland object 草场类型占比 §3.1.6

3.1.3 overview

字段 类型 说明
inventoryTotal int 牦牛存栏总量(头)
annualOutbound int 牦牛年出栏量(头)
warningYakCount int 预警牦牛数量(头)
pastureCount int 牧场数量(个)
grasslandCount int 草场数量(个)
grasslandAreaMu decimal 草场面积(亩)

3.1.4 inventoryChange

字段 类型 说明
bullCount int 公牛数量(头)
cowCount int 母牛数量(头)
unknownGenderCount int 未标注性别(头);为 0 时前端可不展示
monthlyTrend array 按月存栏,仅含 1~当前月

monthlyTrend[] 元素:

字段 类型 说明
month int 1~12
inventoryCount int 该月还原存栏总量(头)

3.1.5 ageStructure[]

字段 类型 说明
bandCode string M1_6 / M7_12 / M13_PLUS
bandLabel string 展示名:1-6月龄
count int 头数
ratio decimal 占比 0~1 或前端转 %(约定返回 0~100 数值,保留 1 位小数,如 37.5 表示 37.5%)

三档固定返回(count 为 0 也返回)。

3.1.6 grassland

字段 类型 说明
availableCount int 可用草场(个)
inUseCount int 使用中草场(个)
degradationStats array 退化等级统计

degradationStats[] 元素:

字段 类型 说明
degradationLevel int 1~5
degradationLabel string 未退化、轻度…
count int 个数
ratio decimal 占比 %(同 ageStructure

五档固定返回;count 之和应等于 overview.grasslandCount

3.1.7 响应示例(节选)

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "statDate": "2026-05-20",
    "statYear": 2026,
    "overview": {
      "inventoryTotal": 120,
      "annualOutbound": 8,
      "warningYakCount": 5,
      "pastureCount": 12,
      "grasslandCount": 80,
      "grasslandAreaMu": 12500.50
    },
    "inventoryChange": {
      "bullCount": 45,
      "cowCount": 60,
      "unknownGenderCount": 15,
      "monthlyTrend": [
        { "month": 1, "inventoryCount": 110 },
        { "month": 5, "inventoryCount": 120 }
      ]
    },
    "ageStructure": [
      { "bandCode": "M1_6", "bandLabel": "1-6月龄", "count": 30, "ratio": 25.0 }
    ],
    "grassland": {
      "availableCount": 50,
      "inUseCount": 40,
      "degradationStats": [
        { "degradationLevel": 1, "degradationLabel": "未退化", "count": 30, "ratio": 37.5 }
      ]
    }
  }
}

3.1.8 异常与校验

场景 处理
无查询权限 403 或 AjaxResult.error 无权限
聚合异常 AjaxResult.error("加载失败,请稍后重试")data 为空
可见牧场为空 牦牛类指标均为 0;草场类仍按全县统计
刷新中重复点击 前端防重;后端可选短锁(与资产同步类似)

4. 实现要点(摘要)

说明
月龄 COALESCE(age_months, TIMESTAMPDIFF(MONTH, birth_date, statDate))birth_date 空则 0
性别 入库值 trim 后精确匹配 /;第三方若写「公牛」「母牛」等,Service 层归一后再统计
公母 vs 趋势 bullCount/cowCount/inventoryTotal/ageStructure§2.3asset_status=1);monthlyTrend§2.10 月末判定
一致性 monthlyTrend 最后一月 inventoryCount 应等于 overview.inventoryTotal(同统计日);实现后 UT 断言
比率 ratio = count * 100.0 / NULLIF(total, 0),分母为 0 时 ratio=0
月末性能 优先 1 条 SQL + CASE WHEN 按月末分组,或 Java 循环 12 次;超过 3s 启用 §2.3 缓存
日志 看板接口可 @Log(title="养殖产业数据", businessType=OTHER) 或仅记录慢查询

前端:见同目录 养殖产业数据前端技术方案.md


5. 菜单与权限(示例)

类型 名称 权限标识
目录/菜单 养殖产业数据 dataModel:breedingIndustry:query
按钮 刷新(与查看共用) dataModel:breedingIndustry:query

组件路径:livestockIndustry/breedingIndustry/index(挂载「产业数据模型」或同级目录,与菜单配置一致)。

sys_menu 示例(menu_id 按环境递增)

-- 父菜单 parent_id 替换为「产业数据模型」实际 ID
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark)
VALUES ('养殖产业数据', {parentId}, 10, 'breedingIndustry', 'livestockIndustry/breedingIndustry/index', 1, 0, 'C', '0', '0', 'dataModel:breedingIndustry:query', 'chart', 'admin', sysdate(), '养殖产业看板');

本期 add/edit/remove/sync 按钮权限。


6. 需求追溯

功能需求 技术落点
§5.2 产业总览 overview + §2.2 SQL
§5.3 存栏变动 inventoryChange + §2.2 月末判定
§5.4 年龄结构 ageStructure
§5.5 草场占比 grassland
§5.1 刷新 3.1 forceRefresh
§2.9 数据权限 §2.6 BreedingIndustryPastureScope
§2.10 月末趋势 §2.2 月末判定 + monthlyTrend
§2.5 预警 §2.2 warning SQL