# 隔离巡查登记 — 技术方案 > 依据:同目录 `隔离巡查登记功能需求.md`。单表台账 **CRUD** + **逻辑删除**;保存时校验 **P1~P3**;圈位/耳标选项来自 **入场隔离登记** 在栏数据;巡查照片走若依 `/common/upload`。 --- ## 1. 技术架构 | 项 | 说明 | | --- | --- | | **后端** | RuoYi **v3.9.2**(**springboot2** 分支):JDK 8、Spring Boot 2.x、Spring MVC、MyBatis、Druid | | **数据库** | MySQL **5.7.39**,InnoDB,`utf8mb4` | | **前端** | 若依 Vue2;字段 **小驼峰**(页面见前端技术方案,本文不展开) | | **文件** | 复用 `/common/upload`;存 `photo_url`、`photo_path`(`/profile/...`) | **分层**:`Controller` → `Service`(`PatrolValidation`、`EarTagCodec` 复用入场模块)→ `Mapper`/XML → `domain`。 **代码包(建议)**:`com.ruoyi.web.modules.trading`(与入场隔离、隔离圈位同包) **初始化脚本**:`sql/biz_isolation_patrol.sql` **业务摘要** | 场景 | 行为 | | --- | --- | | 新增/修改 | 校验圈位在栏;解析耳标;校验 **P1~P3**;校验巡查时间不晚于当前;写入单表 | | 列表/详情 | `del_flag=0`;`patrol_time DESC`;圈位编号模糊、巡查日期区间筛选 | | 删除 | `del_flag=2` | | 表单下拉 | `penOptions`、`penEarTags` 按在栏规则查询(见 **§2.4**) | **在栏判定(本期)**:入场登记 `del_flag=0` 且 `expected_release_date >= CURDATE()`;分圈明细关联圈位。无独立出圈表时以此代理「未解除隔离」;后续若有 `release_status` 字段,仅改查询条件。 --- ## 2. 数据库设计 ### 2.1 表 `biz_isolation_patrol`(隔离巡查记录) | 字段 | 类型 | 非空 | 默认值 | 说明 | | --- | --- | --- | --- | --- | | `id` | `bigint(20)` | Y | 自增 | 主键 | | `patrol_time` | `datetime` | Y | — | 巡查时间 | | `pen_id` | `bigint(20)` | Y | — | 关联 `biz_isolation_pen.id` | | `pen_code` | `char(2)` | Y | — | 圈位编号冗余,便于列表模糊筛选 | | `sick_dead_qty` | `int(11)` | Y | `0` | 病死病害数量(头),≥0 | | `content_desc` | `varchar(200)` | Y | — | 巡查内容描述 | | `handle_desc` | `varchar(200)` | Y | — | 处理情况 | | `ear_tags` | `text` | N | NULL | 耳标逗号分隔;`sick_dead_qty=0` 时为 NULL | | `photo_url` | `varchar(512)` | N | NULL | 巡查照片访问 URL | | `photo_path` | `varchar(512)` | N | NULL | 巡查照片存储路径 | | `del_flag` | `char(1)` | Y | `'0'` | `0` 存在 `2` 删除 | | `create_by` / `create_time` / `update_by` / `update_time` | 若依惯例 | — | — | 审计 | | `remark` | `varchar(500)` | N | NULL | 备注 | **索引**:`PRIMARY KEY (id)`;`KEY idx_patrol_time (patrol_time)`;`KEY idx_pen_id (pen_id)`;`KEY idx_pen_code (pen_code)`;`KEY idx_del_flag (del_flag)`。 ### 2.2 DDL(MySQL 5.7) ```sql CREATE TABLE `biz_isolation_patrol` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', `patrol_time` datetime NOT NULL COMMENT '巡查时间', `pen_id` bigint(20) NOT NULL COMMENT '圈位ID', `pen_code` char(2) NOT NULL COMMENT '圈位编号', `sick_dead_qty` int(11) NOT NULL DEFAULT '0' COMMENT '病死病害数量头', `content_desc` varchar(200) NOT NULL COMMENT '巡查内容描述', `handle_desc` varchar(200) NOT NULL COMMENT '处理情况', `ear_tags` text COMMENT '耳标逗号分隔', `photo_url` varchar(512) DEFAULT NULL COMMENT '巡查照片URL', `photo_path` varchar(512) DEFAULT NULL COMMENT '巡查照片路径', `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_patrol_time` (`patrol_time`), KEY `idx_pen_id` (`pen_id`), KEY `idx_pen_code` (`pen_code`), KEY `idx_del_flag` (`del_flag`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='隔离巡查登记'; ``` ### 2.3 关联与枚举 | 关联 | 说明 | | --- | --- | | `pen_id` | `biz_isolation_pen`,`del_flag=0` 且须在在栏集合内(**§2.4**) | | 入场分圈 | `biz_entry_isolation` + `biz_entry_isolation_alloc` 提供在栏耳标 | **删除标记 `del_flag`** | 值 | 含义 | | --- | --- | | 0 | 正常 | | 2 | 已逻辑删除 | ### 2.4 在栏圈位与耳标(查询约定) **在栏圈位**(`penOptions`): ```sql SELECT DISTINCT p.id AS pen_id, p.pen_code FROM biz_isolation_pen p INNER JOIN biz_entry_isolation_alloc a ON a.pen_id = p.id INNER JOIN biz_entry_isolation e ON e.id = a.entry_id WHERE p.del_flag = '0' AND e.del_flag = '0' AND e.expected_release_date >= CURDATE() ORDER BY p.pen_code ASC; ``` **指定圈位在栏耳标**(`penEarTags`,`penId` 必填): - 取满足上式且 `a.pen_id = #{penId}` 的所有 `a.ear_tags`; - `EarTagCodec.parse` 合并去重后返回 `string[]`(单段 1~32 字)。 无在栏圈位时 `penOptions` 返回空数组,前端提示「暂无在栏圈位…」。 ### 2.5 耳标入库(复用 `EarTagCodec`) - `sick_dead_qty = 0`:`ear_tags` 置 `NULL`,`earTagList` 忽略。 - `sick_dead_qty > 0`:列表 → 英文逗号写 `ear_tags`;去空白、去重。 - 出库:详情/列表返回 `earTags`(原文)+ `earTagList`(数组)。 ### 2.6 一致性校验(`PatrolValidation`) | 规则 | 实现要点 | | --- | --- | | P1 | `sick_dead_qty == 0` → `earTagList` 为空 | | P2 | `sick_dead_qty > 0` → `earTagList.size() == sick_dead_qty` | | P3 | 每个耳标 ∈ 当前 `penId` 在栏集合;登记内不重复 | | 其他 | `pen_id` 在栏;`patrol_time` ≤ `now`;`content_desc`/`handle_desc` 1~200 字;`sick_dead_qty` 非负整数;照片后缀 `jpg,jpeg,png`(上传阶段) | --- ## 3. 接口设计 **统一响应**:`AjaxResult` / `TableDataInfo`(`code`、`msg`、`data` / `rows`、`total`)。 **权限标识**:`tradeMarket:isolationPatrol:list|query|add|edit|remove` **Base Path**:`/tradeMarket/isolationPatrol` | # | 说明 | Method | URI | 权限 | | --- | --- | --- | --- | --- | | 3.1 | 分页列表 | GET | `/tradeMarket/isolationPatrol/list` | `list` | | 3.2 | 详情 | GET | `/tradeMarket/isolationPatrol/{id}` | `query` | | 3.3 | 新增 | POST | `/tradeMarket/isolationPatrol` | `add` | | 3.4 | 修改 | PUT | `/tradeMarket/isolationPatrol` | `edit` | | 3.5 | 删除 | DELETE | `/tradeMarket/isolationPatrol/{ids}` | `remove` | | 3.6 | 在栏圈位下拉 | GET | `/tradeMarket/isolationPatrol/penOptions` | `list` 或 `query` | | 3.7 | 圈位在栏耳标 | GET | `/tradeMarket/isolationPatrol/penEarTags` | `list` 或 `query` | ### 3.1 列表 **Query(驼峰)** | 参数 | 类型 | 必填 | 说明 | | --- | --- | --- | --- | | `pageNum` / `pageSize` | int | N | 默认 `1` / `20` | | `patrolDateStart` | string | N | `yyyy-MM-dd`,`DATE(patrol_time) >=` | | `patrolDateEnd` | string | N | `yyyy-MM-dd`,`DATE(patrol_time) <=` | | `penCode` | string | N | 圈位编号**模糊** `LIKE %penCode%` | **条件**:`del_flag='0'`;起止日期均填时要求 `patrolDateStart <= patrolDateEnd`,否则 400。 **排序**:`patrol_time DESC, id DESC`。 **行字段(驼峰)**:`id`、`patrolTime`、`penId`、`penCode`、`sickDeadQty`、`contentDesc`、`handleDesc`、`earTags`、`earTagList`(可选,列表可仅 `earTags` 拼接展示)、`photoUrl` + 审计字段。 ### 3.2 详情 `data` 全字段 + `earTagList`;`photoUrl` 可访问。 ### 3.3 新增 / 修改 Body(驼峰) | 字段 | 新增 | 修改 | 说明 | | --- | --- | --- | --- | | `id` | — | Y | 主键 | | `patrolTime` | Y | Y | `yyyy-MM-dd HH:mm:ss`,≤ 当前时间 | | `penId` | Y | Y | 在栏圈位 ID;服务端写 `pen_code` | | `sickDeadQty` | Y | Y | 非负整数,默认 0 | | `contentDesc` | Y | Y | 1~200 字 | | `handleDesc` | Y | Y | 1~200 字 | | `earTagList` | 条件 | 条件 | `sickDeadQty>0` 必填;或与 `earTags` 二选一 | | `earTags` | N | N | 逗号分隔(兼容) | | `photoUrl` / `photoPath` | N | N | 巡查照片;传空串可清空 | | `remark` | N | N | ≤500 字 | **服务端流程** 1. 校验 `penId` 在栏,读取 `pen_code` 写入。 2. `EarTagCodec` + **P1~P3**。 3. 插入/更新 `biz_isolation_patrol`。 ### 3.4 删除 - `DELETE /tradeMarket/isolationPatrol/{ids}`,单条传一个 id。 - 记录须 `del_flag=0`,否则「记录不存在或已删除」。 - 更新 `del_flag='2'`(**本期不校验**下游引用)。 ### 3.5 在栏圈位 `penOptions` **响应 `data`**:`[{ penId, penCode }]`,按 `penCode` 升序。 ### 3.6 圈位在栏耳标 `penEarTags` **Query**:`penId`(long,必填)。 **响应 `data`**:`string[]`;`penId` 非在栏或无效时 `[]` 或 400「圈位无效或已无在栏牲畜」。 ### 3.7 校验与错误(摘要) | 项 | 规则 | | --- | --- | | 圈位 | `pen_id` 在 **§2.4** 集合内 | | 耳标 | **P1~P3**;失败文案对齐功能需求 **§8** | | 巡查时间 | 非空且 `patrol_time <= now()` | | 照片 | 后缀 `jpg,jpeg,png`;路径 `/profile` 前缀;≤10MB(上传接口校验) | | 失败示例 | 「病死病害数量与已勾选耳标号数量不一致」「耳标 xxx 不在当前圈位或已出圈」 | ### 3.8 本期不提供 - 巡查记录导入/导出、审批流接口。 - 与出圈、无害化处理自动联动接口。 --- ## 4. 菜单与权限(示例) | 类型 | 名称 | 权限标识 | | --- | --- | --- | | 菜单 | 隔离巡查登记 | `tradeMarket:isolationPatrol:list` | | 按钮 | 查询 | `tradeMarket:isolationPatrol:query` | | 按钮 | 新增 | `tradeMarket:isolationPatrol:add` | | 按钮 | 修改 | `tradeMarket:isolationPatrol:edit` | | 按钮 | 删除 | `tradeMarket:isolationPatrol:remove` | 组件路径:`tradeMarket/isolationPatrol/index`(「交易市场平台」目录下)。 --- ## 5. 交付清单 - [ ] `sql/biz_isolation_patrol.sql`、菜单权限 SQL - [ ] `BizIsolationPatrol` Domain / Mapper / Service / Controller - [ ] `PatrolValidation`;复用 `EarTagCodec` - [ ] `penOptions`、`penEarTags` 查询 Mapper - [ ] 单元测试:P1~P3、在栏圈位/耳标、逻辑删除、巡查时间校验 --- ## 6. 修订记录 | 版本 | 说明 | | --- | --- | | 1.0 | 初版:单表 `biz_isolation_patrol`、CRUD + 在栏下拉接口、P1~P3;对齐功能需求与 RuoYi/MySQL 5.7 |