# 入场隔离登记 — 技术方案 > 依据:同目录 `入场隔离登记功能需求.md`。主表 + **分圈明细子表**;保存时校验 **R1~R5**;检疫证文件走若依上传;**逻辑删除**。 --- ## 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`;存 `cert_file_url`、`cert_file_path`(`/profile/...`) | **分层**:`Controller` → `Service`(`EntryIsolationValidation`、`EarTagCodec`、`ReleaseDateCalculator`、`EntryIsolationReferenceChecker`)→ `Mapper`/XML → `domain`。 **代码包(建议)**:`com.ruoyi.web.modules.trading` **初始化脚本**:`sql/biz_entry_isolation.sql` **业务摘要** | 场景 | 行为 | | --- | --- | | 新增/修改 | 校验供应商、圈位存在且未删;解析耳标;校验 **R1~R5**;计算 `expected_release_date`;主表 + 子表**同事务**保存 | | 修改分圈 | 先删该登记下全部子表行,再批量插入新分圈明细 | | 列表/详情 | 主表 `del_flag=0`;详情带子表 `penAllocList` | | 列表筛选 | 进场日期区间、检疫证号模糊、`supplier_id` 精确 | | 删除 | `del_flag=2`;下游引用时拒绝 | **预计解除日期**:`expected_release_date = DATE(entry_time) + isolation_days`(自然日相加,与功能需求默认一致)。 --- ## 2. 数据库设计 ### 2.1 表 `biz_entry_isolation`(入场隔离登记主表) | 字段 | 类型 | 非空 | 默认值 | 说明 | | --- | --- | --- | --- | --- | | `id` | `bigint(20)` | Y | 自增 | 主键 | | `entry_time` | `datetime` | Y | — | 进场时间 | | `supplier_id` | `bigint(20)` | Y | — | 关联 `biz_supplier.id` | | `cert_file_url` | `varchar(512)` | N | NULL | 检疫证访问 URL | | `cert_file_path` | `varchar(512)` | N | NULL | 检疫证存储路径 | | `cert_no` | `varchar(50)` | Y | — | 检疫证号 | | `quantity` | `int(11)` | Y | — | 总数量(头),正整数 | | `ear_tags` | `text` | Y | — | 主表耳标,逗号分隔入库 | | `isolation_days` | `int(11)` | Y | — | 隔离天数,1~3650 | | `expected_release_date` | `date` | Y | — | 预计解除日期(服务端计算) | | `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_entry_time (entry_time)`;`KEY idx_cert_no (cert_no)`;`KEY idx_supplier (supplier_id)`;`KEY idx_del_flag (del_flag)`。 ### 2.2 表 `biz_entry_isolation_alloc`(分圈明细) | 字段 | 类型 | 非空 | 默认值 | 说明 | | --- | --- | --- | --- | --- | | `id` | `bigint(20)` | Y | 自增 | 主键 | | `entry_id` | `bigint(20)` | Y | — | 关联主表 `id` | | `pen_id` | `bigint(20)` | Y | — | 关联 `biz_isolation_pen.id` | | `quantity` | `int(11)` | Y | — | 本圈数量(头) | | `ear_tags` | `text` | Y | — | 本圈耳标,逗号分隔 | | `sort_order` | `int(11)` | Y | `0` | 展示排序 | **索引**:`PRIMARY KEY (id)`;`KEY idx_entry_id (entry_id)`;`UNIQUE KEY uk_entry_pen (entry_id, pen_id)`(同一登记内圈位不可重复)。 > **说明**:`pen_id` 冗余存储便于关联;列表/详情通过 JOIN 带出 `pen_code` 展示。 ### 2.3 DDL(MySQL 5.7) ```sql CREATE TABLE `biz_entry_isolation` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', `entry_time` datetime NOT NULL COMMENT '进场时间', `supplier_id` bigint(20) NOT NULL COMMENT '供应商ID', `cert_file_url` varchar(512) DEFAULT NULL COMMENT '检疫证URL', `cert_file_path` varchar(512) DEFAULT NULL COMMENT '检疫证路径', `cert_no` varchar(50) NOT NULL COMMENT '检疫证号', `quantity` int(11) NOT NULL COMMENT '总数量头', `ear_tags` text NOT NULL COMMENT '耳标号逗号分隔', `isolation_days` int(11) NOT NULL COMMENT '隔离天数', `expected_release_date` date NOT 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_entry_time` (`entry_time`), KEY `idx_cert_no` (`cert_no`), KEY `idx_supplier` (`supplier_id`), KEY `idx_del_flag` (`del_flag`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='入场隔离登记'; CREATE TABLE `biz_entry_isolation_alloc` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', `entry_id` bigint(20) NOT NULL COMMENT '登记主表ID', `pen_id` bigint(20) NOT NULL COMMENT '圈位ID', `quantity` int(11) NOT NULL COMMENT '本圈数量头', `ear_tags` text NOT NULL COMMENT '本圈耳标逗号分隔', `sort_order` int(11) NOT NULL DEFAULT '0' COMMENT '排序', PRIMARY KEY (`id`), KEY `idx_entry_id` (`entry_id`), UNIQUE KEY `uk_entry_pen` (`entry_id`,`pen_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='入场隔离分圈明细'; ``` ### 2.4 关联与枚举 | 关联 | 说明 | | --- | --- | | `supplier_id` | `biz_supplier`,`del_flag=0` 方可选用 | | `pen_id` | `biz_isolation_pen`,`del_flag=0` 方可选用 | **删除标记 `del_flag`(主表)** | 值 | 含义 | | --- | --- | | 0 | 正常 | | 2 | 已逻辑删除 | ### 2.5 耳标存储约定(`EarTagCodec`) - 入库:列表 → 英文逗号拼接 `ear_tags`;去空白、去重、过滤空段。 - 出库:详情/列表分圈 → `earTagList`(`string[]`);主表同时返回 `earTags`(原文)与 `earTagList`。 - 单段长度 **1~32**;主表耳标个数 = `quantity`(**R1**)。 ### 2.6 一致性校验(`EntryIsolationValidation`) | 规则 | 实现要点 | | --- | --- | | R1 | `quantity == earTagList.size()` | | R2 | `sum(alloc.quantity) == quantity` | | R3 | 分圈耳标并集 = 主表耳标集合 | | R4 | 分圈耳标无交集重复 | | R5 | 每条 `alloc.quantity == alloc.earTagList.size()` | | 其他 | 分圈 ≥1 条;`pen_id` 不重复;供应商/圈位有效;检疫证后缀与 30MB(上传阶段校验) | --- ## 3. 接口设计 **统一响应**:`AjaxResult` / `TableDataInfo`(`code`、`msg`、`data` / `rows`、`total`)。 **权限标识**:`tradeMarket:entryIsolation:list|query|add|edit|remove` **Base Path**:`/tradeMarket/entryIsolation` | # | 说明 | Method | URI | 权限 | | --- | --- | --- | --- | --- | | 3.1 | 分页列表 | GET | `/tradeMarket/entryIsolation/list` | `list` | | 3.2 | 详情 | GET | `/tradeMarket/entryIsolation/{id}` | `query` | | 3.3 | 新增 | POST | `/tradeMarket/entryIsolation` | `add` | | 3.4 | 修改 | PUT | `/tradeMarket/entryIsolation` | `edit` | | 3.5 | 删除 | DELETE | `/tradeMarket/entryIsolation/{ids}` | `remove` | ### 3.1 列表 **Query(驼峰)** | 参数 | 类型 | 必填 | 说明 | | --- | --- | --- | --- | | `pageNum` / `pageSize` | int | N | 默认 `1` / `20` | | `entryDateStart` | string | N | `yyyy-MM-dd`,进场日期 ≥ | | `entryDateEnd` | string | N | `yyyy-MM-dd`,进场日期 ≤ | | `certNo` | string | N | 检疫证号**模糊** `LIKE %certNo%` | | `supplierId` | long | N | 供应商 **精确** | **条件**:`del_flag='0'`;日期对 `DATE(entry_time)` 过滤。 **排序**:`entry_time DESC, id DESC`。 **行字段(驼峰)**:`id`、`entryTime`、`supplierId`、`supplierName`、`certNo`、`quantity`、`isolationDays`、`expectedReleaseDate`、`penAllocSummary`(可选,如「3 个圈位」)+ 审计字段。 ### 3.2 详情 `data` 主表全字段 + `supplierName` + `earTagList` + `penAllocList`: | `penAllocList[]` | 类型 | 说明 | | --- | --- | --- | | `id` | long | 子表主键(修改时前端可不带,以全量覆盖为准) | | `penId` | long | 圈位 ID | | `penCode` | string | 圈位编号 | | `quantity` | int | 本圈数量 | | `earTagList` | string[] | 本圈耳标 | | `earTags` | string | 逗号拼接(展示用) | ### 3.3 新增 / 修改 Body(驼峰) | 字段 | 新增 | 修改 | 说明 | | --- | --- | --- | --- | | `id` | — | Y | 主键 | | `entryTime` | Y | Y | `yyyy-MM-dd HH:mm:ss` | | `supplierId` | Y | Y | 有效供应商 | | `certFileUrl` / `certFilePath` | N | N | 检疫证;有路径时校验后缀 | | `certNo` | Y | Y | 1~50 字,trim | | `quantity` | Y | Y | 正整数 | | `earTagList` | Y | Y | 耳标数组(优先);或与 `earTags` 二选一,服务端统一解析 | | `earTags` | N | N | 逗号分隔字符串(兼容) | | `isolationDays` | Y | Y | 1~3650 | | `penAllocList` | Y | Y | 至少 1 条,见下表 | | `remark` | N | N | ≤500 字 | **`penAllocList[]`** | 字段 | 必填 | 说明 | | --- | --- | --- | | `penId` | Y | 圈位 ID,同登记内不可重复 | | `earTagList` | Y | 本圈耳标,≥1;须属于主表耳标集合 | > `quantity` 由服务端按 `earTagList.size()` 写入子表,可不传或传了也以服务端为准(**R5**)。 **服务端流程** 1. `EarTagCodec` 规范化主表耳标 → 写 `ear_tags`。 2. 校验 **R1~R5** 及外键。 3. `ReleaseDateCalculator` 写 `expected_release_date`。 4. 插入/更新主表;**删除**原 `entry_id` 子表行后 **批量插入** 新分圈。 ### 3.4 删除 - `DELETE /tradeMarket/entryIsolation/{ids}`,单条传一个 id。 - `EntryIsolationReferenceChecker.hasReference(id)` 为 true 时失败。 - 主表 `del_flag='2'`;子表可保留(查询带主表 `del_flag`)或同步逻辑删除(**默认保留子表**,仅主表删)。 ### 3.5 校验与错误(摘要) | 项 | 规则 | | --- | --- | | 供应商 | 存在且 `biz_supplier.del_flag=0` | | 圈位 | 每条 `pen_id` 存在且 `biz_isolation_pen.del_flag=0` | | 检疫证文件 | 后缀 `pdf,md,doc,docx,xls,xlsx,ppt,pptx`;路径 `/profile` 前缀 | | 修改/删除 | 主表存在且 `del_flag=0` | | 失败文案 | 与功能需求 **§8** 对齐,如「总数量须与耳标个数一致」 | ### 3.6 本期不提供 - 检疫证 OCR 识别接口。 - 分圈明细独立 CRUD(仅随主表全量保存)。 --- ## 4. 菜单与权限(示例) | 类型 | 名称 | 权限标识 | | --- | --- | --- | | 菜单 | 入场隔离登记 | `tradeMarket:entryIsolation:list` | | 按钮 | 查询 | `tradeMarket:entryIsolation:query` | | 按钮 | 新增 | `tradeMarket:entryIsolation:add` | | 按钮 | 修改 | `tradeMarket:entryIsolation:edit` | | 按钮 | 删除 | `tradeMarket:entryIsolation:remove` | 组件路径:`tradeMarket/entryIsolation/index`(「交易市场平台」目录下)。 --- ## 5. 交付清单 - [ ] `sql/biz_entry_isolation.sql`、菜单权限 SQL - [ ] Domain / Mapper / Service / Controller(`trading` 包) - [ ] `EntryIsolationValidation`、`EarTagCodec`、`ReleaseDateCalculator` - [ ] 单元测试:R1~R5、日期计算、供应商/圈位外键、逻辑删除 --- ## 6. 修订记录 | 版本 | 说明 | | --- | --- | | 1.0 | 初稿:主表+分圈子表、R1~R5、接口 CRUD;对齐功能需求与 RuoYi/MySQL 5.7 |