# 草畜平衡计算 — 技术方案

> 依据：同目录 `草畜平衡计算功能需求.md`。本期为**无状态测算服务**：接收参数、服务端按公式计算并返回结果；**不落库**测算记录。

---

## 1. 技术架构

| 项 | 说明 |
| --- | --- |
| **后端** | RuoYi **v3.9.2**（**springboot2** 分支）：JDK 8、Spring Boot 2.x、Spring MVC、MyBatis、Druid |
| **数据库** | MySQL **5.7.39**，InnoDB，`utf8mb4` |
| **前端** | 若依 Vue2（本方案不展开） |
| **依赖主数据** | `biz_grassland`（草场管理） |

**分层**：`Controller` → `GrassLivestockBalanceService`（`GrassLivestockBalanceValidation` 校验 + `GrassLivestockBalanceCalculator` 纯计算）→ 可选读 `BizGrasslandMapper` 校验草场有效。

**代码位置（建议）**：`baqing-admin` → `com.ruoyi.web.modules.industryservice`（`GrassLivestockBalanceController`、`support/GrassLivestockBalance*`）。

**业务摘要**

| 场景 | 行为 |
| --- | --- |
| 草场下拉/带出 | **复用**草场管理接口或本模块只读查询；`del_flag='0'` |
| 计算 | `POST` 入参 → 校验 → 计算 11 项指标 → 返回 DTO；**不写库** |
| 清空 | **前端**重置表单；无后端接口 |
| 默认值 | 羊单位年需草量、折算系数可从 `sys_config` 读取（可选） |

---

## 2. 数据库设计

### 2.1 本期：无新增业务表

功能需求 **§3** 明确本期不保存测算历史；计算过程**无持久化**，仅依赖已有草场台账。

| 依赖表 | 用途 |
| --- | --- |
| `biz_grassland` | 草场下拉、选定后带出 `area_mu`、`grass_yield_per_mu`、`utilization_rate`、`grassland_name` |

### 2.2 关联表字段（`biz_grassland` 摘录）

| 字段 | 类型 | 测算用途 |
| --- | --- | --- |
| `id` | `bigint(20)` | 入参 `grasslandId` |
| `grassland_name` | `varchar(32)` | 展示 |
| `area_mu` | `decimal(12,2)` | 草场面积（亩） |
| `grass_yield_per_mu` | `decimal(12,2)` | 亩产草量（kg/亩） |
| `utilization_rate` | `decimal(5,4)` | 利用率 0～1 |
| `del_flag` | `char(1)` | 仅 `0` 可选 |

### 2.3 系统参数（可选，非必须建表）

| 配置键（示例） | 说明 | 默认值（示例） |
| --- | --- | --- |
| `grass.balance.sheepUnitAnnualForageKg` | 羊单位年需草量 kg/SU | 按县级规范配置 |
| `grass.balance.sheepUnitConvertCoeff` | 羊单位折算系数 SU/头 | 如牦牛折算系数 |

存于若依 `sys_config`，由 `GET .../defaults` 下发给前端初始填充。

### 2.4 后续扩展（本期不建）

若需测算留痕，可增表 `biz_grass_livestock_balance_record`（输入快照 + 结果 JSON + 操作人/时间）；本期不实现。

---

## 3. 接口设计

**统一响应**：`AjaxResult`（`code` / `msg` / `data`）。

**权限标识**：`dataModel:grassLivestockBalance:query`（进入模块、计算、读默认值）

**Base Path**：`/dataModel/grassLivestockBalance`

| # | 说明 | Method | URI | 权限 | 要点 |
| --- | --- | --- | --- | --- | --- |
| 3.1 | 执行计算 | POST | `/dataModel/grassLivestockBalance/calculate` | `query` | Body **3.1.1**；返回 **3.1.2** |
| 3.2 | 羊单位默认参数 | GET | `/dataModel/grassLivestockBalance/defaults` | `query` | 读 `sys_config`，可选 |
| 3.3 | 草场台账（复用） | GET | `/dataModel/grassland/list` | 草场 `list` 权限 | 下拉；`keyword` 模糊名称 |
| 3.4 | 草场参数带出（复用） | GET | `/dataModel/grassland/{id}` | 草场 `query` 权限 | 选定草场后取面积、产草、利用率 |

本期**不提供**测算记录的增删改查接口；**清空**为前端行为。

#### 3.1.1 计算请求 Body

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `grasslandId` | long | Y | 关联 `biz_grassland.id`，须 `del_flag='0'` |
| `areaMu` | decimal | Y | 草场面积（亩），> 0 |
| `grassYieldPerMu` | decimal | Y | 亩产草量（kg/亩），≥ 0 |
| `utilizationRate` | decimal | Y | 利用率 0～1（前端传 % 时先换算） |
| `sheepUnitAnnualForageKg` | decimal | Y | 羊单位年需草量（kg/SU），> 0 |
| `sheepUnitConvertCoeff` | decimal | Y | 折算系数（SU/头），> 0 |
| `breedingHeadCount` | int | Y | 养殖数量（头），≥ 0 |

> 面积、产草、利用率允许与台账不一致（用户调整后测算）；`grasslandId` 用于校验草场有效及回显名称。

#### 3.1.2 计算响应 `data`

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `grasslandId` | long | 回显 |
| `grasslandName` | string | 来自台账 |
| `availableForageKg` | decimal | 可利用饲草量（kg） |
| `theoreticalCarryingSu` | decimal | 理论载畜量（SU） |
| `actualCarryingSu` | decimal | 实际载畜量（SU） |
| `balanceIndex` | decimal | 草畜平衡指数 |
| `overloadRate` | decimal | 超载率（%，已×100） |
| `balanceLevel` | int | 等级码 **§3.2** |
| `balanceLevelName` | string | 等级中文 |
| `overloadSu` | decimal | 超载羊单位（SU）；未超载可为负 |
| `annualForageDemandKg` | decimal | 全年需草量（kg） |
| `forageGapKg` | decimal | 饲草缺口（kg） |
| `theoreticalSuitableHeads` | decimal | 理论适宜放牧（头） |
| `actualOverloadHeads` | int | 实际超载数量（头），未超载为 0 |
| `warning` | string | 边界提示（如理论载畜量为 0），可选 |

数值：**金额类指标保留 2 位小数**；`actualOverloadHeads` 为整数；`overloadRate` 保留 2 位小数。

#### 3.2 等级码 `balance_level`

| 值 | `balanceLevelName` | 超载率 R（比较前保留 4 位小数） |
| --- | --- | --- |
| `1` | 未超载 | R ≤ 0 |
| `2` | 基本平衡 | 0 < R ≤ 0.15 |
| `3` | 超载 | 0.15 < R ≤ 0.50 |
| `4` | 严重超载 | R > 0.50 |

其中 `R = balanceIndex - 1`（与需求「超载率 = (指数−1)×100%」一致，判定用未乘 100 的比率）。

#### 3.3 服务端计算公式（与需求 §5.3 一致）

```
availableForageKg      = areaMu × grassYieldPerMu × utilizationRate
theoreticalCarryingSu  = availableForageKg / sheepUnitAnnualForageKg
actualCarryingSu       = breedingHeadCount × sheepUnitConvertCoeff
balanceIndex           = actualCarryingSu / theoreticalCarryingSu   // theoretical=0 见 §3.4
overloadRate           = (balanceIndex - 1) × 100
overloadSu             = actualCarryingSu - theoreticalCarryingSu
annualForageDemandKg   = actualCarryingSu × sheepUnitAnnualForageKg
forageGapKg            = annualForageDemandKg - availableForageKg
theoreticalSuitableHeads = theoreticalCarryingSu / sheepUnitConvertCoeff
actualOverloadHeads    = max(0, breedingHeadCount - floor(theoreticalSuitableHeads))  // 头数取整策略见 §3.4
balanceLevel           = resolveLevel(overloadRate/100)
```

#### 3.4 边界实现约定

| 情形 | 实现 |
| --- | --- |
| `sheepUnitAnnualForageKg` 或 `sheepUnitConvertCoeff` ≤ 0 | 校验失败，`ServiceException` |
| `theoreticalCarryingSu = 0` 且 `breedingHeadCount > 0` | `balanceIndex`、`overloadRate` 置 `null`；`balanceLevel=4`；`warning="无饲草供给"` |
| `theoreticalCarryingSu = 0` 且 `breedingHeadCount = 0` | `balanceIndex=0`；等级 **未超载** |
| `breedingHeadCount = 0` | 实际载畜量、全年需草量为 0；超载率为 −100%；等级 **未超载** |
| `theoreticalSuitableHeads` 取整 | 默认 **四舍五入保留 2 位小数**参与比较；`actualOverloadHeads` 用 `breedingHeadCount - theoreticalSuitableHeads` 向上取整为整数且 `<0` 则 0 |
| 超载羊单位/饲草缺口为负 | **原值返回**（保留 2 位小数），前端可标注「余量」 |

#### 3.5 校验（`GrassLivestockBalanceValidation`）

与需求 **§5.1** 一致；`grasslandId` 须 `selectBizGrasslandById` 存在且 `del_flag='0'`。

---

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

| 类型 | 名称 | 权限标识 |
| --- | --- | --- |
| 菜单 | 草畜平衡计算 | `dataModel:grassLivestockBalance:query` |
| 按钮 | 计算 | `dataModel:grassLivestockBalance:query` |

组件路径：`dataModel/grassLivestockBalance/index`（产业数据体系目录下）。

草场下拉另需：`dataModel:grassland:list`、`dataModel:grassland:query`。

---

## 5. 交付清单

- [ ] `GrassLivestockBalanceValidation`、`GrassLivestockBalanceCalculator`、`GrassLivestockBalanceRules`  
- [ ] `GrassLivestockBalanceController`、`IGrassLivestockBalanceService`  
- [ ] 单元测试：公式、等级区间、边界（理论载畜量 0、存栏 0）  
- [ ] MockMvc：`POST /calculate` 成功/校验失败  
- [ ] （可选）`sys_config` 初始化 SQL 与 `GET /defaults`  

---

## 6. 修订记录

| 版本 | 说明 |
| --- | --- |
| 1.0 | 初稿：无新表；POST 计算 + 复用草场接口；等级码 1～4；公式与功能需求 §5.3 对齐 |
