# 牦牛资产档案管理 — 技术方案 > 依据:同目录 `牦牛资产档案管理功能需求.md`。档案 **列表/详情只读** + **第三方同步写入**;本期无手工增删改接口。 --- ## 1. 技术架构 | 项 | 说明 | | --- | --- | | **后端** | RuoYi **v3.9.2**(**springboot2** 分支):JDK 8、Spring Boot 2.x、Spring MVC、MyBatis、Druid | | **数据库** | MySQL **5.7.39**,InnoDB,`utf8mb4` | | **前端** | 若依 Vue2(本方案不展开) | | **第三方** | HTTP/RPC 客户端(`RestTemplate` / `OkHttp` 等,实现期选型);配置项:地址、鉴权、超时 | | **关联主数据** | `biz_pasture`(牧场,可选 `pasture_id` 映射) | **分层**:`Controller` → `YakAssetSyncService`(拉取、合并入库)→ `IYakAssetService`(查询)→ `Mapper`/XML → `domain`。 **代码位置**:`baqing-admin` → `com.ruoyi.web.modules.industryservice`(`BizYakAsset`、子表 Mapper、`ThirdPartyYakClientImpl`)。 **业务摘要** | 场景 | 行为 | | --- | --- | | 同步 | 按 `yak_no` 合并主表;子表按「先删后插」或「按批次版本」覆盖(**默认:每头牦牛同步时删除其旧子表再批量插入**) | | 列表/详情 | `del_flag='0'`;排序 `status_change_date DESC, id DESC`(或与 `last_sync_time` 统一) | | 月龄 | 列表/详情可展示库字段;同步时可写入,亦可用 `birth_date` 现场计算 | | 删除 | 第三方下架策略**默认不物理删除**:仅更新状态或 `del_flag`(若第三方提供「离场」状态则映射为已出售/死亡等);全量对账删除须评审 | --- ## 2. 数据库设计 ### 2.1 主表 `biz_yak_asset`(牦牛资产档案) | 字段 | 类型 | 非空 | 说明 | | --- | --- | --- | --- | | `id` | `bigint(20)` | Y | 主键 | | `external_id` | `bigint(20)` | N | 第三方牛只 ID(`OpenYakEntryDto.cattleId`),**唯一** | | `yak_no` | `varchar(64)` | Y | 牦牛编号(优先 `earTagNumber`),**唯一** | | `pasture_id` | `bigint(20)` | N | 关联 `biz_pasture.id`(映射成功时) | | `pasture_name` | `varchar(128)` | N | 所属牧场名称(同步冗余展示) | | `batch_no` | `varchar(64)` | N | 批次编号 | | `gender` | `varchar(16)` | N | 性别 | | `birth_date` | `date` | N | 出生日期 | | `age_months` | `int(11)` | N | 月龄(月) | | `entry_date` | `date` | N | 入栏日期 | | `entry_weight_kg` | `decimal(10,2)` | N | 入栏体重 kg | | `source` | `varchar(64)` | N | 来源 | | `breeding_method` | `varchar(64)` | N | 养殖方式 | | `asset_status` | `tinyint(4)` | Y | 资产状态 **§2.6** | | `status_change_date` | `date` | N | 变更日期 | | `status_change_reason` | `varchar(256)` | N | 状态变更原因 | | `pen_location` | `varchar(128)` | N | 圈舍位置 | | `expected_out_date` | `date` | N | 预计出栏日期 | | `supplement_plan` | `varchar(512)` | N | 补饲方案 | | `realtime_temp` | `decimal(6,2)` | N | 实时体温 ℃ | | `realtime_steps` | `int(11)` | N | 实时运动量 | | `env_temp` | `decimal(6,2)` | N | 环境温度 ℃(详情当前值) | | `location` | `varchar(256)` | N | 位置 | | `physio_collect_time` | `datetime` | N | 生理快照采集时间 | | `father_yak_no` | `varchar(64)` | N | 父亲编号 | | `mother_yak_no` | `varchar(64)` | N | 母亲编号 | | `last_sync_time` | `datetime` | N | 最近同步时间 | | `del_flag` | `char(1)` | Y | `0` 存在 `2` 删除 | | `create_time` / `update_time` | datetime | — | 审计 | **索引**:`UNIQUE uk_external_id (external_id)`;`UNIQUE uk_yak_no (yak_no)`;`KEY idx_asset_status`;`KEY idx_pasture_id`;`KEY idx_status_change_date`;`KEY idx_last_sync_time`;`KEY idx_del_flag`。 #### 2.1.1 第三方入栏建档字段映射(`OpenYakEntryDto` → `biz_yak_asset`) | OpenAPI 字段 | 库字段 | 说明 | | --- | --- | --- | | `cattleId` | `external_id` | 同步 upsert 主键 | | `earTagNumber` / `cattleNo` | `yak_no` | 优先耳标 | | `farmId` | `pasture_id` | 经 `biz_pasture.external_id` 解析 | | `farmName` | `pasture_name` | 冗余展示 | | `entryCycle` / `batchId` | `batch_no` | 优先 `entryCycle` | | `farmEnclosureName` | `pen_location` | 并写入 `biz_yak_pen_rel` | | `farmLocation` | `location` | | | `cattleSex` | `gender` | 归一公/母 | | `cattleSource` | `source` | | | `fatherNumber` | `father_yak_no` | | | `motherNumber` | `mother_yak_no` | | | `weight` | `entry_weight_kg` | 当前体重 | | `entryTime` | `entry_date` | | | `birthDate` | `birth_date` | 月龄由同步时计算写入 `age_months` | | `remark` | `status_change_reason` | | | `id` / `cattlePicture` / `registrant` / `cattleVariety` | — | 本期不落库 | 列表接口未返回的字段(`asset_status`、实时体温/运动量等)保持库内原值或默认「正常」。 ### 2.2 子表 `biz_yak_physio_series`(生理时序,曲线图) | 字段 | 类型 | 说明 | | --- | --- | --- | | `id` | `bigint(20)` | 主键 | | `yak_asset_id` | `bigint(20)` | 关联主表 | | `collect_time` | `datetime` | 采集时间 | | `body_temp` | `decimal(6,2)` | 体温 ℃ | | `steps` | `int(11)` | 运动量 | | `env_temp` | `decimal(6,2)` | 环境温度 ℃ | **索引**:`KEY idx_yak_collect (yak_asset_id, collect_time)`。 ### 2.3 子表 `biz_yak_growth_record`(生长性状) | 字段 | 类型 | 说明 | | --- | --- | --- | | `id` | `bigint(20)` | 主键 | | `yak_asset_id` | `bigint(20)` | 关联主表 | | `day_age` | `int(11)` | 日龄(天) | | `weight_kg` | `decimal(10,2)` | 体重 | | `height_cm` | `decimal(10,2)` | 体高 | | `chest_cm` | `decimal(10,2)` | 胸围 | | `length_cm` | `decimal(10,2)` | 体长 | | `collect_time` | `datetime` | 采集时间 | | `data_kind` | `tinyint(4)` | `1` 实测 `2` 预测 | **索引**:`KEY idx_yak_growth (yak_asset_id, collect_time)`。 ### 2.4 子表 `biz_yak_reproduction_record`(繁殖性能) | 字段 | 类型 | 说明 | | --- | --- | --- | | `id` | `bigint(20)` | 主键 | | `yak_asset_id` | `bigint(20)` | 关联主表 | | `day_age` | `int(11)` | 日龄 | | `parity` | `int(11)` | 胎次 | | `calving_interval_days` | `int(11)` | 产犊间隔(天) | | `calf_count` | `int(11)` | 产犊数量(头) | | `calf_survival_rate` | `decimal(6,4)` | 犊牛成活率 0~1 | | `calf_birth_weight_kg` | `decimal(10,2)` | 犊牛出生体重 | | `dystocia_flag` | `char(1)` | `0` 否 `1` 是 | **索引**:`KEY idx_yak_repro (yak_asset_id, parity)`。 ### 2.5 子表 `biz_yak_feeding_record`(饲喂数据) | 字段 | 类型 | 说明 | | --- | --- | --- | | `id` | `bigint(20)` | 主键 | | `yak_asset_id` | `bigint(20)` | 关联主表 | | `start_day_age` | `int(11)` | 开始日龄 | | `end_day_age` | `int(11)` | 结束日龄 | | `daily_supplement_kg` | `decimal(10,2)` | 日补饲量 kg | | `feed_type` | `varchar(64)` | 饲料类型 | **索引**:`KEY idx_yak_feeding (yak_asset_id)`。 ### 2.6 子表 `biz_yak_pen_rel` / `biz_yak_batch_rel` 圈舍:`pen_name`;批次:`batch_no`;均关联 `yak_asset_id`。详情接口冗余返回 `yakNo`。 ### 2.7 枚举 `asset_status` | 值 | 含义 | | --- | --- | | `1` | 正常 | | `2` | 死淘 | | `3` | 丢失 | | `4` | 出栏 | 第三方旧码(如在养/死亡/已出售等)由 `YakAssetStatusMapper` 归一;存量库执行 `sql/biz_yak_asset_migrate_v2.sql`。 `data_kind`(生长):`1` 实测,`2` 预测。 ### 2.8 DDL(MySQL 5.7,节选) ```sql CREATE TABLE `biz_yak_asset` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `yak_no` varchar(64) NOT NULL COMMENT '牦牛编号', `pasture_id` bigint(20) DEFAULT NULL, `pasture_name` varchar(128) DEFAULT NULL, `batch_no` varchar(64) DEFAULT NULL, `gender` varchar(16) DEFAULT NULL, `birth_date` date DEFAULT NULL, `age_months` int(11) DEFAULT NULL, `entry_date` date DEFAULT NULL, `entry_weight_kg` decimal(10,2) DEFAULT NULL, `source` varchar(64) DEFAULT NULL, `breeding_method` varchar(64) DEFAULT NULL, `asset_status` tinyint(4) NOT NULL DEFAULT '1', `status_change_date` date DEFAULT NULL, `realtime_temp` decimal(6,2) DEFAULT NULL, `realtime_steps` int(11) DEFAULT NULL, `env_temp` decimal(6,2) DEFAULT NULL, `location` varchar(256) DEFAULT NULL, `physio_collect_time` datetime DEFAULT NULL, `father_yak_no` varchar(64) DEFAULT NULL, `mother_yak_no` varchar(64) DEFAULT NULL, `last_sync_time` datetime DEFAULT NULL, `del_flag` char(1) NOT NULL DEFAULT '0', `create_time` datetime DEFAULT NULL, `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_yak_no` (`yak_no`), KEY `idx_asset_status` (`asset_status`), KEY `idx_status_change_date` (`status_change_date`), KEY `idx_del_flag` (`del_flag`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='牦牛资产档案'; -- 子表及迁移见 sql/biz_yak_asset.sql、sql/biz_yak_asset_migrate_v2.sql ``` --- ## 3. 接口设计 **统一响应**:`AjaxResult` / `TableDataInfo`。 **权限**:`dataModel:yakAsset:list|query|sync` **Base Path**:`/dataModel/yakAsset` | # | 说明 | Method | URI | 权限 | 要点 | | --- | --- | --- | --- | --- | --- | | 3.1 | 分页列表 | GET | `/dataModel/yakAsset/list` | `list` | Query **3.1.1** | | 3.2 | 档案详情 | GET | `/dataModel/yakAsset/{id}` | `query` | 主表 + 六类子表列表 | | 3.3 | 按编号查详情 | GET | `/dataModel/yakAsset/byNo/{yakNo}` | `query` | 系谱跳转父/母 | | 3.4 | 生理曲线 | GET | `/dataModel/yakAsset/{id}/physioChart` | `query` | Query `days` 默认 30 | | 3.5 | 生长曲线 | GET | `/dataModel/yakAsset/{id}/growthChart` | `query` | 实测+预测分 `dataKind` | | 3.6 | 第三方同步 | POST | `/dataModel/yakAsset/sync` | `sync` | 返回 **3.6.1** 摘要 | 本期**不提供** `POST/PUT/DELETE` 档案维护接口。 #### 3.1.1 列表 Query | 参数 | 类型 | 必填 | 说明 | | --- | --- | --- | --- | | `pageNum` / `pageSize` | int | N | 默认 `1` / `20` | | `keyword` | string | N | `yak_no` **模糊** | | `assetStatus` | int | N | `1`~`4` **精确** | #### 3.2.1 详情响应结构(`data`) | 节点 | 内容 | | --- | --- | | `asset` | 主表字段(驼峰,含 `yakNo`、`pastureName`、`assetStatus` 等) | | `physioSeries` | 生理时序数组(近一月可由独立接口或详情内截断) | | `growthRecords` | 生长列表(含 `dataKind`) | | `reproductionRecords` | 繁殖列表 | | `feedingRecords` | 饲喂列表 | | `penRecords` | 圈舍列表 | | `batchRecords` | 批次列表 | #### 3.4.1 生理曲线 Query | 参数 | 说明 | | --- | --- | | `days` | 默认 `30`;按 `collect_time` 倒序取最近 N 天 | 返回序列:`[{ collectTime, bodyTemp, steps, envTemp }, ...]`,供 ECharts 多折线。 #### 3.5.1 生长曲线 Query | 参数 | 说明 | | --- | --- | | `metric` | 可选 `weight`/`height`/`chest`/`length`,默认 `weight` | 返回:`{ actual: [...], forecast: [...] }`,按 `data_kind` 分组。 #### 3.6.1 同步响应(`data`) | 字段 | 说明 | | --- | --- | | `insertCount` | 新增头数 | | `updateCount` | 更新头数 | | `failCount` | 失败头数 | | `failMessages` | 失败摘要列表(可选) | | `syncTime` | 本次同步完成时间 | 同步过程建议 `@Log` + `biz_yak_sync_log`(可选表:操作人、起止时间、结果 JSON)。 ### 3.7 第三方对接(实现要点) | 项 | 说明 | | --- | --- | | 配置 | `application.yml`:`third-party.farming`(与牧场管理同源) | | 接口 | `GET /open-api/v1/farming/entry-filings`;分页 `OpenApiPageResult` | | 客户端 | `ThirdPartyYakClientImpl` → `YakEntryOpenApiMapper.toBundles()` → `YakAssetBundleSyncTxService` | | 映射 | 字段对照 **§2.1.1**;`YakAssetStatusMapper` 供后续含状态字段的接口 | | 牧场 | `farmId` → `biz_pasture.external_id`;失败则仅写 `farmName` | | 事务 | 每头牦牛独立事务(`REQUIRES_NEW`);子表仅当 bundle 携带对应列表时覆盖 | | Stub | `thirdparty/stub-yak-entries.json`(OpenAPI 记录样例) | | 并发 | `synchronized` 防重复点击 | ### 3.8 查询约定 - 列表、详情:`del_flag = '0'`。 - 列表排序:`status_change_date DESC, id DESC`。 - 子表列表:`collect_time` 或业务字段倒序。 --- ## 4. 菜单与权限(示例) | 类型 | 名称 | 权限标识 | | --- | --- | --- | | 菜单 | 牦牛资产档案 | `dataModel:yakAsset:list` | | 按钮 | 查询 | `dataModel:yakAsset:query` | | 按钮 | 同步 | `dataModel:yakAsset:sync` | 组件路径:`livestockFinance/yakAsset/index`(挂载「牧业金融 · 生物资产」目录下)。 --- ## 5. 交付清单 - [ ] `sql/biz_yak_asset.sql`(主表 + 4 子表 + 可选同步日志表) - [ ] `ThirdPartyYakClient`、`YakAssetSyncService` - [ ] `BizYakAsset` Domain / Mapper / `IYakAssetService` / `BizYakAssetController` - [ ] 单元测试(同步合并、状态映射、曲线查询) - [ ] MockMvc:list、detail、sync(Mock 第三方) --- ## 6. 修订记录 | 版本 | 说明 | | --- | --- | | 1.0 | 初稿:主表+四子表;只读查询+同步接口;生长实测/预测分 `data_kind`;对接可配置 | | 1.1 | 对齐入栏建档 OpenAPI:`OpenYakEntryDto`、`external_id`、分页拉取、字段映射 **§2.1.1** |