# 畜牧监控 — 技术方案 > 依据:同目录 `畜牧监控功能需求.md`(v1.3)。监控点位 **CRUD** + **逻辑删除** + **第三方同步**(仅列表 upsert);**实时播放地址**按需调用第三方 play-url,**不落库**;列表**名称模糊**、**所属主体精确**筛选;主体下拉为养殖主体(正常)+ 交易市场(正常)合并选项。 --- ## 1. 技术架构 | 项 | 说明 | | --- | --- | | **后端** | RuoYi **v3.9.2**(**springboot2** 分支):JDK 8、Spring Boot 2.x、Spring MVC、MyBatis、Druid | | **数据库** | MySQL **5.7.39**,InnoDB,`utf8mb4` | | **前端** | 若依 Vue2;列表「查看实时监控」调用本系统 `playUrl` 接口,**LivePlayer** 播放 HLS/m3u8 | | **第三方** | 与牧场管理共用 `third-party.farming` 配置;开放接口鉴权 `X-App-Key` / `X-App-Secret` | **分层**:`Controller` → `Service`(`LivestockMonitorValidation`、主体有效性校验)→ `Mapper`/XML → `domain`;同步编排见 `LivestockMonitorSyncService`;实时地址见 `ThirdPartyCameraClient.fetchPlayUrl`。 **代码包**:`com.ruoyi.web.modules.video` **业务摘要** | 场景 | 行为 | | --- | --- | | 新增 | 校验字段;若填写主体则校验存在且正常;冗余 `owner_name`;`del_flag=0` | | 修改 | 全字段可改;可更换主体;更新 `owner_name` | | 列表/详情 | 仅 `del_flag=0`;排序 `create_time DESC, id DESC` | | 列表筛选 | `monitor_name` 模糊;`owner_type` + `owner_id` **精确**(成对) | | 删除 | `del_flag=2`;**不**校验下游引用 | | 第三方同步 | 拉取监控列表 upsert(按 `monitor_id`);**不**拉取、**不**持久化播放地址 | | 实时预览 | 按 `monitorId` 调第三方 play-url,返回 `videoUrl`(仅接口响应,不写表) | | 唯一性 | **不**校验名称重复;同步以 `monitor_id` 为 upsert 键 | --- ## 2. 数据库设计 ### 2.1 表 `biz_livestock_monitor`(畜牧监控点位) | 字段 | 类型 | 非空 | 说明 | | --- | --- | --- | --- | | `id` | `bigint(20)` | Y | 主键 | | `monitor_id` | `varchar(32)` | Y | 摄像头 ID(第三方 `cameraId`,同步 upsert 键) | | `monitor_name` | `varchar(32)` | Y | 监控名称(**1~10** 字) | | `owner_type` | `tinyint(4)` | N | 1 养殖主体 / 2 交易市场 | | `owner_id` | `bigint(20)` | N | 主体主键 | | `owner_name` | `varchar(64)` | N | 主体名称冗余 | | `enclosure_name` | `varchar(64)` | N | 圈舍 | | `install_location` | `varchar(32)` | N | 安装位置(**≤10** 字) | | `video_source` | `varchar(32)` | Y | 视频来源:`wvp` / `ys7` / `url` | | `del_flag` | `char(1)` | Y | `0` 存在 `2` 删除 | | 审计字段 | 若依惯例 | — | `create_by` / `create_time` / `update_by` / `update_time` | **索引**:`PRIMARY KEY (id)`;`KEY idx_monitor_id (monitor_id)`;`KEY idx_monitor_name`;`KEY idx_owner`;`KEY idx_create_time`;`KEY idx_del_flag`。 > 已移除历史字段 `ip_address`、`video_url`(v1.3 播放地址不落库)。DDL 与升级脚本见 `sql/biz_livestock_monitor.sql`。 ### 2.2 枚举 **所属主体类型 `owner_type`** | 值 | 含义 | 关联表 | | --- | --- | --- | | 1 | 养殖主体 | `biz_pasture`(`biz_status=1` 且 `del_flag=0`) | | 2 | 交易市场 | `biz_trade_market`(`operate_status=1` 且 `del_flag=0`) | **视频来源 `video_source`** | 值 | 含义 | | --- | --- | | wvp | WVP 平台 | | ys7 | 萤石云 | | url | 直链 URL | **删除标记 `del_flag`** | 值 | 含义 | | --- | --- | | 0 | 正常 | | 2 | 已逻辑删除 | ### 2.3 主体主数据校验(Service) 保存/修改时,**仅当** `owner_type` 与 `owner_id` **均非空**时校验: | `owner_type` | 校验要点 | | --- | --- | | 1 | `biz_pasture`:`id=#{ownerId}` AND `del_flag='0'` AND `biz_status=1` | | 2 | `biz_trade_market`:`id=#{ownerId}` AND `del_flag='0'` AND `operate_status=1` | 仅填其一则报「所属主体信息不完整」。两者皆空则主体字段置空。 --- ## 3. 接口设计 **统一响应**:`AjaxResult` / `TableDataInfo`(`code`、`msg`、`rows`/`data`)。 **权限标识**:`videoSurveillance:livestockMonitor:list|query|add|edit|remove|sync` **Base Path**:`/videoSurveillance/livestockMonitor` | # | 说明 | Method | URI | 权限 | | --- | --- | --- | --- | --- | | 3.1 | 分页列表 | GET | `/list` | `list` | | 3.2 | 详情 | GET | `/{id}` | `query` | | 3.3 | 新增 | POST | `/` | `add` | | 3.4 | 修改 | PUT | `/` | `edit` | | 3.5 | 删除 | DELETE | `/{ids}` | `remove` | | 3.6 | 所属主体选项 | GET | `/ownerOptions` | `list` | | 3.7 | 第三方同步 | POST | `/sync` | `sync` | | 3.8 | 实时播放地址 | GET | `/playUrl?monitorId=` | `query` | ### 3.1 列表 `GET /list` **Query**:`pageNum`、`pageSize`、`monitorName`(模糊)、`ownerType`+`ownerId`(成对精确)。 **列表行(驼峰)**:`id`、`monitorId`、`monitorName`、`ownerType`、`ownerTypeName`、`ownerId`、`ownerName`、`enclosureName`、`installLocation`、`videoSource`、`videoSourceName`、`createTime`。 ### 3.2 详情 `GET /{id}` 返回上述字段及审计字段;仅 `del_flag=0`。 ### 3.3 新增 / 3.4 修改 **Body(驼峰)** | 字段 | 必填 | 说明 | | --- | --- | --- | | `id` | 修改必填 | 主键 | | `monitorId` | Y | 摄像头 ID,≤32 字符 | | `monitorName` | Y | 1~10 字 | | `ownerType` / `ownerId` | N | 成对可选 | | `enclosureName` | N | ≤64 字符 | | `installLocation` | N | ≤10 字符 | | `videoSource` | Y | `wvp` / `ys7` / `url` | ### 3.5 删除 `DELETE /{ids}` 逻辑删除,`del_flag='2'`。 ### 3.6 所属主体选项 `GET /ownerOptions` **Query**:`keyword`(可选,模糊)。 **响应 `data[]`**:`ownerType`、`ownerTypeName`、`ownerId`、`ownerName`。 ### 3.7 第三方同步 `POST /sync` **说明**:从第三方生产管理系统**仅**拉取监控点位列表并 upsert。编排类 `LivestockMonitorSyncService`;返回 `YakAssetSyncResultVo`(与牧场同步结构一致)。 **响应 `data`** | 字段 | 说明 | | --- | --- | | `insertCount` | 新增条数 | | `updateCount` | 更新条数 | | `failCount` | 失败条数(单条落库失败) | | `failMessages` | 失败明细,键名为 `{cameraId}: …` | | `syncTime` | 同步完成时间 | **并发**:同步进行中再次调用返回「正在同步,请稍候」。 **更新时保留本地字段**:`owner_type`、`owner_id`、`install_location`(不同步覆盖)。 ### 3.8 实时播放地址 `GET /playUrl` **Query**:`monitorId`(必填,对应 `biz_livestock_monitor.monitor_id` / 第三方 `cameraId`)。 **流程**: 1. 校验本地存在且 `del_flag=0` 的监控记录; 2. 调用第三方 `GET /open-api/v1/iot/cameras/{cameraId}/play-url`; 3. 返回 `LivestockMonitorPlayUrlVo`,**不写入数据库**。 **响应 `data`(驼峰)** | 字段 | 说明 | | --- | --- | | `monitorId` | 摄像头 ID | | `monitorName` | 本地监控名称 | | `videoSource` | 第三方返回或本地冗余 | | `videoSourceName` | 展示用中文 | | `videoUrl` | 可播放地址(通常 HLS/m3u8) | **失败**:监控不存在、第三方未启用、play-url 为空或业务码非 0 时返回可读 `msg`。 ### 3.9 服务端校验(`LivestockMonitorValidation`) | 项 | 规则 | | --- | --- | | 摄像头 ID | 非空;≤32 字符 | | 监控名称 | trim 后 1~10 字 | | 视频来源 | 非空;∈ {wvp, ys7, url} | | 圈舍 | 可选;≤64 字符 | | 安装位置 | 可选;≤10 字符 | | 主体 | 成对可选;填则 §2.3 校验 | ### 3.10 第三方开放接口(Apifox) 与牧场、牦牛档案等同源,配置前缀 **`third-party.farming`**: | 配置项 | 说明 | | --- | --- | | `enabled` | 是否允许调用第三方(同步与 play-url 共用) | | `mode` | `stub`(classpath 样例)/ `http`(远程) | | `base-url` | 第三方根地址 | | `app-key` / `app-secret` | 请求头鉴权 | | `page-size` | 列表分页大小(上限 200) | **查询监控数据(同步用)** - `GET /open-api/v1/iot/cameras` - Query:`pageNum`、`pageSize`;可选 `farmId`、`enclosureId`、`updatedAfter` - 响应:`OpenApiResponse>`,`code=0` 成功 - 单条 `records[]` 字段映射: | 第三方字段 | 本地字段 | | --- | --- | | `cameraId` | `monitor_id` | | `deviceName` | `monitor_name`(超长截断至 10 字) | | `farmName` | `owner_name`(仅文本,不绑定 owner_type/id) | | `enclosureName` | `enclosure_name` | | `videoSource` | `video_source` | **获取监控视频播放地址(实时预览用)** - `GET /open-api/v1/iot/cameras/{cameraId}/play-url` - 响应 `data`:`cameraId`、`videoSource`、`videoUrl` - 本系统经 `GET /playUrl` 转发,**不**落库 **实现类** | 类 | 职责 | | --- | --- | | `ThirdPartyCameraClient` / `ThirdPartyCameraClientImpl` | HTTP / stub:`fetchAllCameras`、`fetchPlayUrl` | | `LivestockMonitorSyncService` | 同步编排、防重入(仅列表 upsert) | | `LivestockMonitorSyncTxService` | 单条 upsert(`REQUIRES_NEW`) | | `BizLivestockMonitorServiceImpl.getPlayUrlByMonitorId` | 本地校验 + 调 play-url | | `ThirdPartyFarmingHttpSupport` | 共用 RestTemplate、鉴权头(`industryservice` 包) | **Stub 样例** - `thirdparty/stub-cameras.json` — 监控列表(同步) - `thirdparty/stub-camera-play-urls.json` — 播放地址(实时预览) --- ## 4. 菜单与权限(示例) | 类型 | 名称 | 权限标识 | | --- | --- | --- | | 目录 | 视频监控系统 | — | | 菜单 | 畜牧监控 | `videoSurveillance:livestockMonitor:list` | | 按钮 | 查询 | `videoSurveillance:livestockMonitor:query`(含详情、playUrl) | | 按钮 | 新增 | `videoSurveillance:livestockMonitor:add` | | 按钮 | 修改 | `videoSurveillance:livestockMonitor:edit` | | 按钮 | 删除 | `videoSurveillance:livestockMonitor:remove` | | 按钮 | 同步 | `videoSurveillance:livestockMonitor:sync` | 组件路径:`videoSurveillance/livestockMonitor/index` --- ## 5. 模块关联 | 模块 | 关系 | | --- | --- | | 牧场管理 | `owner_type=1`;第三方同步养殖场列表同源配置 | | 交易市场管理 | `owner_type=2` | | 第三方生产管理系统 | 监控列表 + 播放地址开放接口 | --- ## 6. 交付清单 - [x] `sql/biz_livestock_monitor.sql` - [x] `BizLivestockMonitor`、`BizLivestockMonitorMapper`/XML、`BizLivestockMonitorServiceImpl`、`BizLivestockMonitorController` - [x] `LivestockMonitorValidation`、`LivestockMonitorSyncService`、`ThirdPartyCameraClientImpl` - [x] `GET /playUrl` 实时播放地址 - [x] 前端 `livestockMonitor/index.vue`(LivePlayer + `getLivestockMonitorPlayUrl`)、`livestockMonitor.js`、同步按钮 - [x] 单元测试 / MockMvc(含 `/sync`、`/playUrl`) --- ## 7. 修订记录 | 版本 | 说明 | | --- | --- | | 1.0 | 初稿:CRUD + `ownerOptions`;含 `ip_address` | | 1.1 | 表结构:`monitor_id`、`enclosure_name`、`video_source`;移除 `ip_address`;主体/安装位置可选 | | 1.2 | 第三方同步:`POST /sync`;对接 `/iot/cameras` 与 play-url 两阶段落库 `video_url` | | 1.3 | **移除 `video_url` 字段**;同步仅 upsert 列表;**新增 `GET /playUrl`** 按需拉取播放地址;前端 LivePlayer 实时预览 |