# 草场使用记录 — 技术方案

> 依据：同目录 `草场使用记录功能需求.md`。使用记录 **CRUD** + 主数据关联校验 + **使用天数服务端计算** + **逻辑删除**。

---

## 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`（草场管理）、`biz_pasture`（牧场管理 / 养殖主体） |

**分层**：`Controller` → `Service`（`GrasslandUsageValidation`、天数计算、主数据校验）→ `Mapper`/XML（列表 **JOIN** 主数据取名称）→ `domain`。

**代码位置（建议）**：`baqing-admin` → `com.ruoyi.web.modules.industryservice`（`BizGrasslandUsageRecord`、Mapper、Service、Controller）。

**业务摘要**

| 场景 | 行为 |
| --- | --- |
| 新增/修改 | 校验草场、主体 `del_flag='0'`；`usage_area_mu ≤ grassland.area_mu`；重算 `actual_use_days`；`del_flag='0'` |
| 列表/详情 | 仅 `del_flag='0'`；JOIN 展示 `grasslandName`、`pastureName`；排序 `create_time DESC, id DESC` |
| 删除 | **逻辑删除**：`del_flag` 置 `'2'` |
| 使用天数 | 服务端：`actual_use_days = DATEDIFF(end, start) + 1`（含起止日）；忽略客户端传入天数 |

---

## 2. 数据库设计

### 2.1 表 `biz_grassland_usage_record`（草场使用记录）

| 字段 | 类型 | 非空 | 默认值 | 说明 |
| --- | --- | --- | --- | --- |
| `id` | `bigint(20)` | Y | 自增 | 主键 |
| `grassland_id` | `bigint(20)` | Y | — | 关联 `biz_grassland.id` |
| `pasture_id` | `bigint(20)` | Y | — | 关联 `biz_pasture.id`（养殖主体） |
| `usage_area_mu` | `decimal(12,2)` | Y | — | 使用面积（**亩**），> 0 |
| `use_start_date` | `date` | Y | — | 开始使用日期 |
| `use_end_date` | `date` | Y | — | 结束使用日期；≥ 开始日 |
| `actual_use_days` | `int(11)` | Y | — | 实际使用天数（**服务端计算**入库） |
| `grazing_head_count` | `int(11)` | Y | — | 放牧数量（**头**），≥ 1 |
| `grazing_method` | `tinyint(4)` | Y | — | 放牧方式 **§2.2** |
| `del_flag` | `char(1)` | Y | `'0'` | `0` 存在 `2` 删除 |
| `create_by` / `create_time` / `update_by` / `update_time` | 若依惯例 | — | — | 审计；列表「预约日期」筛 `create_time` |
| `remark` | `varchar(500)` | N | NULL | 备注（可选扩展） |

**索引**：`PRIMARY KEY (id)`；`KEY idx_grassland_id (grassland_id)`；`KEY idx_pasture_id (pasture_id)`；`KEY idx_create_time (create_time)`；`KEY idx_del_flag (del_flag)`；`KEY idx_use_start (use_start_date)`。

**关联约定**：逻辑外键，不在库层强制 `FOREIGN KEY`（与项目其他业务表一致）；保存前 Service 校验主数据存在且 `del_flag='0'`。

**DDL（MySQL 5.7）**

```sql
CREATE TABLE `biz_grassland_usage_record` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `grassland_id` bigint(20) NOT NULL COMMENT '草场ID biz_grassland',
  `pasture_id` bigint(20) NOT NULL COMMENT '养殖主体ID biz_pasture',
  `usage_area_mu` decimal(12,2) NOT NULL COMMENT '使用面积(亩)',
  `use_start_date` date NOT NULL COMMENT '开始使用日期',
  `use_end_date` date NOT NULL COMMENT '结束使用日期',
  `actual_use_days` int(11) NOT NULL COMMENT '实际使用天数',
  `grazing_head_count` int(11) NOT NULL COMMENT '放牧数量(头)',
  `grazing_method` tinyint(4) NOT NULL COMMENT '放牧方式1轮牧2自由3季节',
  `del_flag` char(1) NOT NULL DEFAULT '0' COMMENT '0存在 2删除',
  `create_by` varchar(64) DEFAULT '' COMMENT '创建者',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(64) DEFAULT '' COMMENT '更新者',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`),
  KEY `idx_grassland_id` (`grassland_id`),
  KEY `idx_pasture_id` (`pasture_id`),
  KEY `idx_create_time` (`create_time`),
  KEY `idx_del_flag` (`del_flag`),
  KEY `idx_use_start` (`use_start_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='草场使用记录';
```

### 2.2 枚举常量

| 字段 | 值 | 含义 |
| --- | --- | --- |
| `grazing_method` | `1` | 轮牧 |
| | `2` | 自由放牧 |
| | `3` | 季节性放牧 |
| `del_flag` | `0` | 正常 |
| | `2` | 已逻辑删除 |

### 2.3 列表查询 JOIN（示意）

列表、详情需展示名称时关联主表（仅有效主数据用于**新增**校验；历史记录列表仍展示当时关联 id 对应名称，主数据已删时名称可为空或仍显示冗余策略由实现约定）：

```sql
-- 列表片段
FROM biz_grassland_usage_record r
LEFT JOIN biz_grassland g ON r.grassland_id = g.id
LEFT JOIN biz_pasture p ON r.pasture_id = p.id
WHERE r.del_flag = '0'
```

---

## 3. 接口设计

**统一响应**：`AjaxResult`（`code` / `msg` / `data`）或分页 `TableDataInfo`（`rows`、`total`）。

**权限标识**：`dataModel:grasslandUsage:list|query|add|edit|remove|export`

**Base Path**：`/dataModel/grasslandUsage`

| # | 说明 | Method | URI | 权限 | 要点 |
| --- | --- | --- | --- | --- | --- |
| 3.1 | 分页列表 | GET | `/dataModel/grasslandUsage/list` | `dataModel:grasslandUsage:list` | Query **3.1.1**；JOIN 名称；`create_time DESC, id DESC` |
| 3.2 | 详情 | GET | `/dataModel/grasslandUsage/{id}` | `dataModel:grasslandUsage:query` | 含 `grasslandId`、`pastureId` 及名称展示字段 |
| 3.3 | 新增 | POST | `/dataModel/grasslandUsage` | `dataModel:grasslandUsage:add` | Body **3.3.1**；服务端算天数 |
| 3.4 | 修改 | PUT | `/dataModel/grasslandUsage` | `dataModel:grasslandUsage:edit` | Body 含 `id`；忽略客户端 `actualUseDays` |
| 3.5 | 删除 | DELETE | `/dataModel/grasslandUsage/{ids}` | `dataModel:grasslandUsage:remove` | 逻辑删除；`ids` 逗号分隔批量 |

**主数据下拉（复用，不重复建设）**

| 用途 | 建议接口 | 说明 |
| --- | --- | --- |
| 草场选择 | `GET /dataModel/grassland/list` | `keyword` 模糊名称；仅 `del_flag=0`；前端取 `id`、`grasslandName`、`areaMu` |
| 养殖主体选择 | `GET /dataModel/pasture/list` | `keyword` 模糊 `pastureName`；仅有效主体 |

若需轻量选项列表，可增加 `GET .../optionList`（返回 `id`+`name`），本期非必须。

#### 3.1.1 列表 Query

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `pageNum` / `pageSize` | int | N | 默认 `1` / `20` |
| `keyword` | string | N | 关联**草场名称**模糊（`g.grassland_name`）；空不过滤 |
| `startDate` | string | N | 创建日期起 `yyyy-MM-dd`（界面「预约日期」起） |
| `endDate` | string | N | 创建日期止 `yyyy-MM-dd`；`date(create_time)` 闭区间 |

**日期区间 Service 校验**：若 `startDate`、`endDate` 均非空且 `startDate > endDate`，抛业务异常。

#### 3.3.1 新增 / 修改 Body（驼峰）

| 字段 | 必填 | 说明 |
| --- | --- | --- |
| `id` | 修改 Y | 主键 |
| `grasslandId` | Y | 草场主键 |
| `pastureId` | Y | 养殖主体主键 |
| `usageAreaMu` | Y | 正数（亩）；≤ 该草场 `area_mu` |
| `useStartDate` | Y | `yyyy-MM-dd` |
| `useEndDate` | Y | `yyyy-MM-dd`；≥ `useStartDate` |
| `actualUseDays` | N | **服务端覆盖**；客户端传值忽略 |
| `grazingHeadCount` | Y | 正整数（头） |
| `grazingMethod` | Y | `1`～`3` |
| `remark` | N | 备注 |

**列表/详情响应扩展字段（非表字段）**：`grasslandName`、`pastureName`（JOIN 或二次查询填充）。

### 3.4 使用天数计算（服务端）

```text
actual_use_days = (use_end_date - use_start_date) 的自然日差 + 1
```

- Java 实现：`GrasslandUsageValidation.resolveActualUseDays(start, end)`。  
- 保存前统一调用，写入 `actual_use_days`。

### 3.5 Mapper 约定

- 业务表 CRUD 均带 `r.del_flag = '0'`（列表、详情、更新）。  
- 逻辑删除：`update ... set del_flag='2', update_by=?, update_time=now() where del_flag='0' and id in (...)`。  
- 列表按 **§3.1.1** 过滤；`keyword` 作用在 `biz_grassland.grassland_name`。

### 3.6 服务端校验（摘要）

| 项 | 规则 |
| --- | --- |
| 草场 / 主体 | `grasslandId`、`pastureId` 非空；对应记录存在且 `del_flag='0'` |
| 使用面积 | > 0；≤ 所选草场 `area_mu` |
| 使用日期 | 非空；`useStartDate ≤ useEndDate` |
| 实际使用天数 | 由 **§3.4** 计算后入库 |
| 放牧数量 | ≥ 1 整数 |
| 放牧方式 | ∈ {1, 2, 3} |
| 列表创建日期区间 | `startDate ≤ endDate`（均填时） |

校验类建议：`GrasslandUsageRules`、`GrasslandUsageValidation`（`validateRowForSave`、`validateDateRangeForList`）。

---

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

| 类型 | 名称 | 权限标识 |
| --- | --- | --- |
| 菜单 | 草场使用记录 | `dataModel:grasslandUsage:list` |
| 按钮 | 查询 | `dataModel:grasslandUsage:query` |
| 按钮 | 新增 | `dataModel:grasslandUsage:add` |
| 按钮 | 修改 | `dataModel:grasslandUsage:edit` |
| 按钮 | 删除 | `dataModel:grasslandUsage:remove` |
| 按钮 | 导出 | `dataModel:grasslandUsage:export` |

组件路径建议：`dataModel/grasslandUsage/index`（挂载在「产业数据模型及体系」下）。

---

## 5. 交付清单

- [ ] `sql/biz_grassland_usage_record.sql`、菜单与按钮权限 SQL  
- [ ] `BizGrasslandUsageRecord` Domain / Mapper（含 JOIN 列表）/ Service / Controller  
- [ ] `GrasslandUsageRules`、`GrasslandUsageValidation`（主数据、面积上限、天数计算）  
- [ ] 逻辑删除  
- [ ] 单元测试 / 接口测试（MockMvc）  

---

## 6. 修订记录

| 版本 | 说明 |
| --- | --- |
| 1.0 | 初稿：对齐功能需求；`biz_grassland_usage_record`；关联 `biz_grassland`/`biz_pasture`；天数服务端计算；逻辑删除 |
