Explorar o código

对接牦牛资产档案

wwh hai 1 mes
pai
achega
1d94926393

+ 1 - 1
doc/产业数据模型及服务/牦牛疾病预警/牦牛疾病预警技术方案.md

@@ -238,7 +238,7 @@ WHERE alert_time >= ? AND alert_time < ?
238 238
 | 文案 | 集中预警由分析器生成 `alert_message`;体温/运动量类仍可按实测值拼接 |
239 239
 | 失败策略 | 单条源记录失败记 `failCount`;第三方整体不可用抛异常且**不清库** |
240 240
 | Stub | `thirdparty/stub-quarantine-records.json` 等(`third-party.farming.stub-enabled=true`) |
241
-| 与资产同步 | `POST /sync` **仅**健康源 + 分析预警;资产需单独调用 `/dataModel/yakAsset/sync` |
241
+| 与资产同步 | 疾病预警 `POST /dataModel/yakDiseaseWarning/sync` **仅**健康源+分析;生物资产 `POST /dataModel/yakAsset/sync` 为五段全量;IoT 称重/定位/环境为独立定时任务(见牦牛资产技术方案 **§3.9**) |
242 242
 
243 243
 ---
244 244
 

+ 24 - 11
doc/产业数据模型及服务/牧业金融生物资产管理/牦牛资产档案管理功能需求.md

@@ -127,22 +127,34 @@ flowchart TD
127 127
 
128 128
 ---
129 129
 
130
-### 5.3 第三方同步
130
+### 5.3 第三方同步(手动 — 生物资产全量)
131 131
 
132 132
 | 项 | 要求 |
133 133
 | --- | --- |
134
-| 触发 | 用户点击「同步」;定时任务是否启用另定 |
134
+| 触发 | 用户点击列表「**同步**」;**不**包含 IoT 物联数据 |
135 135
 | 权限 | 仅同步权限角色可执行 |
136
-| 数据源 | 第三方生产管理系统开放接口 `GET /open-api/v1/farming/entry-filings`(入栏建档列表,字段见草稿 `20260611111807_455_398.jpg`) |
136
+| 范围 | **五段顺序**全量:养殖场 → 圈舍 → 生产计划 → 批次 → **入栏建档(牦牛档案)** |
137
+| 入栏接口 | `GET /open-api/v1/farming/entry-filings`(字段见草稿 `20260611111807_455_398.jpg`) |
137 138
 | 鉴权 | 与牧场管理同源:`third-party.farming` 配置 `base-url`、`app-key`、`app-secret` |
138
-| 合并键 | 优先以第三方 **cattleId**(`external_id`)upsert;辅以 **耳标编号 earTagNumber**(无则 cattleNo)作为 `yak_no` |
139
-| 范围 | 分页拉取(`pageNum`/`pageSize`≤200)直至取完 |
140
-| 反馈 | 结束提示成功/失败及摘要(新增/更新/失败条数) |
141
-| 牧场映射 | `farmId` 关联 `biz_pasture.external_id`;失败则冗余 `farmName`,展示「未关联」 |
142
-| 失败 | 第三方超时、鉴权失败等须明确提示,**不得**误删或清空已有档案 |
143
-| 子数据 | **本期列表接口**同步主表 + 圈舍/批次关联;生理/生长/繁殖/饲喂等子表待详情接口对接后增量覆盖(未返回的子表不清空) |
139
+| 合并键 | 优先 **cattleId**(`external_id`);辅以 **earTagNumber**(无则 cattleNo)作为 `yak_no` |
140
+| 分页 | `pageNum`/`pageSize`≤200 直至取完 |
141
+| 反馈 | 提示成功/失败;摘要含各段及入栏建档新增/更新/失败头数(前端兼容顶层 `insertCount` 等) |
142
+| 牧场映射 | `farmId`  `biz_pasture.external_id`;失败则冗余 `farmName` |
143
+| 失败 | 第三方不可用须明确提示,**不得**误删已有档案 |
144
+| 子数据 | 入栏列表同步主表;生理/生长等子表待详情接口对接后增量覆盖 |
144 145
 
145
-同步成功后,列表与详情展示最近一次成功结果;列表宜可展示「数据截至:最近同步时间」类提示(**§8**)。
146
+### 5.3.1 IoT 数据定时同步(后台)
147
+
148
+| 项 | 要求 |
149
+| --- | --- |
150
+| 触发 | **定时任务**每日凌晨 **1:00**;**无**列表页按钮 |
151
+| 数据 | 牛只称重、牦牛定位项圈、环境监测 |
152
+| 接口 | `/open-api/v1/iot/weighings`、`/collar-locations`、`/env-monitoring` |
153
+| 全量条件 | 对应本地表 `biz_yak_weighing_record` / `biz_yak_collar_location` / `biz_env_monitoring_record` **无任何行**(`count(*)=0`,含逻辑删除行) |
154
+| 增量条件 | 表内有数据时,按事件时间与配置水印增量拉取 |
155
+| 初始化 | 执行 `sql/biz_sys_job_iot_data_incremental_sync.sql` 并在「系统管理-定时任务」启用 |
156
+
157
+同步成功后,列表与详情展示最近一次**入栏建档**手动同步结果;IoT 数据供详情/预警等模块读取,宜标注数据来源与采集时间(**§8**)。
146 158
 
147 159
 ---
148 160
 
@@ -327,6 +339,7 @@ flowchart TD
327 339
 | 生理与曲线图关系 | §5.5(3)区分当前值与近一月曲线 |
328 340
 | 列表缺分页、重置 | §5.1、§5.2 |
329 341
 | 入栏建档 OpenAPI 对接 | §5.3 明确 `entry-filings` 接口、字段映射与 `external_id` 合并键 |
342
+| 全量/IoT 分离 | §5.3 手动五段全量;§5.3.1 IoT 定时 1:00、全量/增量策略 |
330 343
 
331 344
 ---
332 345
 
@@ -336,7 +349,7 @@ flowchart TD
336 349
 | --- | --- |
337 350
 | 列表筛选 | §5.1 |
338 351
 | 资产状态 | §2.2 |
339
-| 同步 | §5.3 |
352
+| 同步 | §5.3、§5.3.1 |
340 353
 | 详情八块 | §5.5 |
341 354
 | 生理曲线 | §5.5(3) |
342 355
 | 生长列表/曲线/预测 | §5.5(4) |

+ 55 - 15
doc/产业数据模型及服务/牧业金融生物资产管理/牦牛资产档案管理技术方案.md

@@ -14,15 +14,16 @@
14 14
 | **第三方** | HTTP/RPC 客户端(`RestTemplate` / `OkHttp` 等,实现期选型);配置项:地址、鉴权、超时 |
15 15
 | **关联主数据** | `biz_pasture`(牧场,可选 `pasture_id` 映射) |
16 16
 
17
-**分层**:`Controller` → `YakAssetSyncService`(拉取、合并入库)→ `IYakAssetService`(查询)→ `Mapper`/XML → `domain`
17
+**分层**:`Controller` → `LivestockFinanceMasterSyncService`(全量编排)→ 各 `*SyncService` / `*SyncTxService` → `Mapper`/XML → `domain`;IoT 数据由 `IotDataIncrementalSyncTask` 定时触发,**不**经列表页「同步」按钮
18 18
 
19
-**代码位置**:`baqing-admin` → `com.ruoyi.web.modules.industryservice`(`BizYakAsset`、子表 Mapper、`ThirdPartyYakClientImpl`)
19
+**代码位置**:`baqing-admin` → `com.ruoyi.web.modules.industryservice`。
20 20
 
21 21
 **业务摘要**
22 22
 
23 23
 | 场景 | 行为 |
24 24
 | --- | --- |
25
-| 同步 | 按 `yak_no` 合并主表;子表按「先删后插」或「按批次版本」覆盖(**默认:每头牦牛同步时删除其旧子表再批量插入**) |
25
+| 同步(手动) | `POST /dataModel/yakAsset/sync` 触发**五段**基础全量:养殖场→圈舍→生产计划→批次→入栏建档;**不含** IoT |
26
+| IoT 同步(定时) | 每日 **凌晨 1:00** Quartz 任务;称重/定位项圈/环境监测;见 **§3.9** |
26 27
 | 列表/详情 | `del_flag='0'`;排序 `status_change_date DESC, id DESC`(或与 `last_sync_time` 统一) |
27 28
 | 月龄 | 列表/详情可展示库字段;同步时可写入,亦可用 `birth_date` 现场计算 |
28 29
 | 删除 | 第三方下架策略**默认不物理删除**:仅更新状态或 `del_flag`(若第三方提供「离场」状态则映射为已出售/死亡等);全量对账删除须评审 |
@@ -150,9 +151,19 @@
150 151
 
151 152
 ### 2.6 子表 `biz_yak_pen_rel` / `biz_yak_batch_rel`
152 153
 
153
-圈舍:`pen_name`;批次:`batch_no`;均关联 `yak_asset_id`。详情接口冗余返回 `yakNo`
154
+圈舍、批次信息已合并入主表字段 `pen_location`、`batch_no`;上述关联子表已废弃删除
154 155
 
155
-### 2.7 枚举 `asset_status`
156
+### 2.7 IoT 落库表(定时同步,非手动「同步」按钮)
157
+
158
+| 表名 | 第三方接口 | 唯一键 | DDL |
159
+| --- | --- | --- | --- |
160
+| `biz_yak_weighing_record` | `GET /open-api/v1/iot/weighings` | `external_id` | `sql/biz_yak_weighing_record.sql` |
161
+| `biz_yak_collar_location` | `GET /open-api/v1/iot/collar-locations` | `(yak_no, report_time)` | `sql/biz_yak_collar_location.sql` |
162
+| `biz_env_monitoring_record` | `GET /open-api/v1/iot/env-monitoring` | `external_id` | `sql/biz_env_monitoring_record.sql` |
163
+
164
+关联解析:称重/项圈可关联 `biz_yak_asset`(优先 `cattleId`→`external_id`,其次耳标/`cattleNo`→`yak_no`);环境/称重可关联 `biz_pasture`、`biz_farm_enclosure`。
165
+
166
+### 2.8 枚举 `asset_status`
156 167
 
157 168
 | 值 | 含义 |
158 169
 | --- | --- |
@@ -221,7 +232,7 @@ CREATE TABLE `biz_yak_asset` (
221 232
 | 3.3 | 按编号查详情 | GET | `/dataModel/yakAsset/byNo/{yakNo}` | `query` | 系谱跳转父/母 |
222 233
 | 3.4 | 生理曲线 | GET | `/dataModel/yakAsset/{id}/physioChart` | `query` | Query `days` 默认 30 |
223 234
 | 3.5 | 生长曲线 | GET | `/dataModel/yakAsset/{id}/growthChart` | `query` | 实测+预测分 `dataKind` |
224
-| 3.6 | 第三方同步 | POST | `/dataModel/yakAsset/sync` | `sync` | 返回 **3.6.1** 摘要 |
235
+| 3.6 | 第三方全量同步 | POST | `/dataModel/yakAsset/sync` | `sync` | 返回 **3.6.1**(五段统计;顶层 `insertCount` 兼容旧前端,取自入栏建档) |
225 236
 
226 237
 本期**不提供** `POST/PUT/DELETE` 档案维护接口。
227 238
 
@@ -261,19 +272,21 @@ CREATE TABLE `biz_yak_asset` (
261 272
 
262 273
 返回:`{ actual: [...], forecast: [...] }`,按 `data_kind` 分组。
263 274
 
264
-#### 3.6.1 同步响应(`data`)
275
+#### 3.6.1 全量同步响应(`data`)
276
+
277
+编排类:`LivestockFinanceMasterSyncService.syncAll()`。
265 278
 
266 279
 | 字段 | 说明 |
267 280
 | --- | --- |
268
-| `insertCount` | 新增头数 |
269
-| `updateCount` | 更新头数 |
270
-| `failCount` | 失败头数 |
271
-| `failMessages` | 失败摘要列表(可选) |
272
-| `syncTime` | 本次同步完成时间 |
281
+| `pasture` / `enclosure` / `plan` / `batch` / `yakAsset` | 各步骤 `{ insertCount, updateCount, failCount, failMessages?, syncTime }` |
282
+| `insertCount` / `updateCount` / `failCount` | **兼容旧前端**:取值自 `yakAsset` 步骤(入栏建档头数) |
283
+| `syncTime` | 本次全量同步开始时间 |
284
+
285
+**同步顺序**:`biz_pasture` → `biz_farm_enclosure` → `biz_farming_plan` → `biz_farming_batch` → `biz_yak_asset`。
273 286
 
274
-同步过程建议 `@Log` + `biz_yak_sync_log`(可选表:操作人、起止时间、结果 JSON)
287
+IoT 三类数据**不在此接口**内执行,见 **§3.9**
275 288
 
276
-### 3.7 第三方对接(实现要点)
289
+### 3.7 第三方对接 — 入栏建档(实现要点)
277 290
 
278 291
 | 项 | 说明 |
279 292
 | --- | --- |
@@ -284,7 +297,7 @@ CREATE TABLE `biz_yak_asset` (
284 297
 | 牧场 | `farmId` → `biz_pasture.external_id`;失败则仅写 `farmName` |
285 298
 | 事务 | 每头牦牛独立事务(`REQUIRES_NEW`);子表仅当 bundle 携带对应列表时覆盖 |
286 299
 | Stub | `thirdparty/stub-yak-entries.json`(OpenAPI 记录样例) |
287
-| 并发 | `synchronized` 防重复点击 |
300
+| 并发 | 全量编排 `syncLock`;各子步骤 `AtomicBoolean` 防重入 |
288 301
 
289 302
 ### 3.8 查询约定
290 303
 
@@ -292,6 +305,30 @@ CREATE TABLE `biz_yak_asset` (
292 305
 - 列表排序:`status_change_date DESC, id DESC`。  
293 306
 - 子表列表:`collect_time` 或业务字段倒序。
294 307
 
308
+### 3.9 IoT 数据定时同步
309
+
310
+| 项 | 说明 |
311
+| --- | --- |
312
+| **触发** | Quartz:`iotDataIncrementalSyncTask.syncIncremental` |
313
+| **Cron** | `0 0 1 * * ?`(每天凌晨 **1:00**) |
314
+| **初始化 SQL** | `sql/biz_sys_job_iot_data_incremental_sync.sql`(`sys_job` + `sys_config` 水印) |
315
+| **编排** | `IotDataIncrementalSyncService` → `YakWeighingSyncService` / `YakCollarLocationSyncService` / `EnvMonitoringSyncService` |
316
+| **配置** | 同源 `third-party.farming`(`base-url`、`app-key`、`app-secret`、`mode=http\|stub`) |
317
+| **水印** | `sys_config.third_party.iot.last_sync_time`;增量时作为 `updatedAfter`;任务成功后更新 |
318
+
319
+**全量 / 增量策略(按表独立)**
320
+
321
+| 本地表行数 `count(*)` | 模式 | HTTP 查询参数 |
322
+| --- | --- | --- |
323
+| **= 0**(表已清空) | **全量** | 不传 `startTime` / `endTime` / `updatedAfter`,分页拉取全部 |
324
+| **> 0** | **增量** | `startTime` = 最大事件时间 − 5 分钟;`endTime` = 当前;`updatedAfter` = 配置水印 |
325
+
326
+行数统计**不按** `del_flag` 过滤;仅 `TRUNCATE`/`DELETE` 清空整表后才会对该表走全量。
327
+
328
+**Stub 样例**:`thirdparty/stub-iot-weighings.json`、`stub-iot-collar-locations.json`、`stub-iot-env-monitoring.json`。
329
+
330
+**日志**:任务开始/结束写入应用日志;结束行输出 `IotDataIncrementalSyncResultVo` JSON。
331
+
295 332
 ---
296 333
 
297 334
 ## 4. 菜单与权限(示例)
@@ -309,6 +346,8 @@ CREATE TABLE `biz_yak_asset` (
309 346
 ## 5. 交付清单
310 347
 
311 348
 - [ ] `sql/biz_yak_asset.sql`(主表 + 4 子表 + 可选同步日志表)  
349
+- [ ] `LivestockFinanceMasterSyncService`、IoT `IotDataIncrementalSyncTask`  
350
+- [ ] `sql/biz_yak_weighing_record.sql` 等 IoT 三表 + `sql/biz_sys_job_iot_data_incremental_sync.sql`  
312 351
 - [ ] `ThirdPartyYakClient`、`YakAssetSyncService`  
313 352
 - [ ] `BizYakAsset` Domain / Mapper / `IYakAssetService` / `BizYakAssetController`  
314 353
 - [ ] 单元测试(同步合并、状态映射、曲线查询)  
@@ -322,3 +361,4 @@ CREATE TABLE `biz_yak_asset` (
322 361
 | --- | --- |
323 362
 | 1.0 | 初稿:主表+四子表;只读查询+同步接口;生长实测/预测分 `data_kind`;对接可配置 |
324 363
 | 1.1 | 对齐入栏建档 OpenAPI:`OpenYakEntryDto`、`external_id`、分页拉取、字段映射 **§2.1.1** |
364
+| 1.2 | 全量同步扩展为五段编排;IoT 三表独立定时同步 **§2.7、§3.9**;全量/增量按表 `count(*)` 判断 |

+ 18 - 3
doc/产业数据模型及服务/牧业金融生物资产管理/牦牛资产档案管理测试用例.md

@@ -31,7 +31,9 @@
31 31
 | ZCZX-MYNZDA-UT-013 | Service | 详情组装 | 单元测试 | JUnit5+Mockito | §3.2.1 | Mock 主表+六子表 | `selectDetailById` | 返回 `asset`+六数组;含 `penRecords`/`batchRecords` |
32 32
 | ZCZX-MYNZDA-UT-014 | Service | 已删档案不可查 | 单元测试 | JUnit5+Mockito | del_flag | `del_flag=2` | `selectById` / `selectDetail` | null 或 `ServiceException` |
33 33
 | ZCZX-MYNZDA-UT-015 | Service | byNo 查询 | 单元测试 | JUnit5+Mockito | §3.3 | 有效 yak_no | `selectDetailByYakNo` | 与按 id 详情结构一致 |
34
-| ZCZX-MYNZDA-UT-016 | 并发 | 同步防重入 | 单元测试 | JUnit5 | §3.7 | 无 | 连续两次 `tryLock` 同步 | 第二次拒绝或排队;不重复写库 |
34
+| ZCZX-MYNZDA-UT-016 | 并发 | 全量同步防重入 | 单元测试 | JUnit5 | §3.7 | 无 | `LivestockFinanceMasterSyncService` 连续触发 | 第二次阻塞或子步骤拒绝 |
35
+| ZCZX-MYNZDA-UT-016A | IoT 同步 | 空表全量窗口 | 单元测试 | JUnit5 | §3.9 | `countRecords=0` | `IotSyncSupport.resolveSyncQuery` | `fullSync=true`;无时间参数 |
36
+| ZCZX-MYNZDA-UT-016B | IoT 同步 | 有数据增量窗口 | 单元测试 | JUnit5 | §3.9 | `countRecords>0` | `resolveSyncQuery` | 含 start/end/updatedAfter |
35 37
 | ZCZX-MYNZDA-UT-017 | Mapper | 列表过滤排序 | 单元测试 | JUnit5 | §3.8 | 无 | 检查 `BizYakAssetMapper.xml` | `del_flag='0'`;`status_change_date desc, id desc`;`keyword` 模糊 `yak_no` |
36 38
 | ZCZX-MYNZDA-UT-018 | Mapper | 子表按资产删除 | 单元测试 | JUnit5 | 同步删插 | 无 | 检查子表 Mapper `deleteByYakAssetId` | 同步前按 `yak_asset_id` 删除 |
37 39
 
@@ -72,6 +74,17 @@
72 74
 
73 75
 ---
74 76
 
77
+## 二(补)、IoT 定时同步用例
78
+
79
+| 用例编号 | 测试模块 | 测试项 | 测试类型 | 测试工具 | 测试目的 | 前置条件 | 测试步骤 | 预期结果 |
80
+| --- | --- | --- | --- | --- | --- | --- | --- | --- |
81
+| ZCZX-MYNZDA-JOB-001 | IoT 定时 | 任务注册 | 运维/SQL | SQL+管理端 | §5.3.1 | 执行 `biz_sys_job_iot_data_incremental_sync.sql` | 查 `sys_job` job_id=102 | cron=`0 0 1 * * ?`;目标=`iotDataIncrementalSyncTask.syncIncremental`;status=正常 |
82
+| ZCZX-MYNZDA-JOB-002 | IoT 定时 | 增量执行 | 定时/手动 | 管理端「执行一次」 | §3.9 | 三表均有数据;第三方可用 | 触发任务 | 日志含 `模式=增量`;对应表 upsert |
83
+| ZCZX-MYNZDA-JOB-003 | IoT 定时 | 清表全量 | 定时/手动 | SQL+管理端 | §3.9 | `TRUNCATE` 某 IoT 表 | 触发任务 | 该表日志 `模式=全量`;数据回补;不传 updatedAfter |
84
+| ZCZX-MYNZDA-JOB-004 | IoT 定时 | 水印更新 | 配置 | SQL | §3.9 | 任务成功 | 查 `sys_config.third_party.iot.last_sync_time` | 值为最近成功时间 |
85
+
86
+---
87
+
75 88
 ## 三、界面测试用例(UI)
76 89
 
77 90
 **测试工具**:**Playwright** + **Chromium**(`channel: 'chrome'` 使用本机 **Google Chrome**)。
@@ -130,7 +143,7 @@
130 143
 | 正常流程 | UT-003、005~006、009~013、015 | API-001~004、009~011、013~017、020~021、027 | UI-001~006、008、012~025、028 |
131 144
 | 异常流程 | UT-002、007~008、014 | API-005、010、012、018~019、023~026、028 | UI-010、017、020、026、029~030 |
132 145
 | 业务约束 | UT-001~002、011~012、016~018 | API-003、005~006、015、021~022 | UI-003、007、009、022~023 |
133
-| 同步与第三方 | UT-005~010、016 | API-017~021 | UI-008~011、027~028 |
146
+| 同步与第三方 | UT-005~010、016、016A~016B | API-017~021 | UI-008~011、027~028;JOB-001~004 |
134 147
 | 详情六块/曲线 | UT-011~013 | API-009、013~016 | UI-012~026 |
135 148
 | 权限 | — | API-023~026 | UI-011、029~030 |
136 149
 | 只读无 CRUD | — | API-022 | UI-007、012 |
@@ -143,7 +156,8 @@
143 156
 | --- | --- | --- |
144 157
 | §5.1 列表 | 编号模糊、状态精确、分页、列 | API-001~008;UI-001~006 |
145 158
 | §5.2 工具栏 | 搜索、重置、同步防重 | UI-004、008~009;API-027 |
146
-| §5.3 同步 | 合并键、子表、失败不清库、摘要 | UT-005~010、016;API-017~021;UI-008~011 |
159
+| §5.3 同步 | 手动五段全量、合并键、失败不清库 | UT-005~010、016;API-017~021;UI-008~011 |
160
+| §5.3.1 IoT | 定时 1:00、全量/增量 | UT-016A~016B;JOB-001~004 |
147 161
 | §5.4 详情 | 只读、摘要、系谱跳转 | API-009~012;UI-012~017 |
148 162
 | §5.5 六块 | 生理曲线、生长切换/预测、繁殖、饲喂 | UT-011~013;API-013~016;UI-018~026 |
149 163
 | §6~§7 | 权限、错误提示 | API-023~028;UI-010、011、017、029~030 |
@@ -156,3 +170,4 @@
156 170
 | 版本 | 说明 |
157 171
 | --- | --- |
158 172
 | 1.0 | 初版:单元 18、接口 28、UI 30;对齐功能/技术方案;Playwright+Chrome;编号前缀 **ZCZX-MYNZDA** |
173
+| 1.1 | 补充 IoT 定时同步用例 JOB-001~004;全量/增量策略 UT-016A~016B |

+ 3 - 1
doc/产业数据模型及服务/牧业金融生物资产管理/牧业金融生物资产管理前端技术方案.md

@@ -30,7 +30,9 @@
30 30
 | `getYakAssetByNo` | GET | `/byNo/{yakNo}` 系谱跳转 |
31 31
 | `getYakPhysioChart` | GET | `/{id}/physioChart?days=30` |
32 32
 | `getYakGrowthChart` | GET | `/{id}/growthChart?metric=weight` |
33
-| `syncYakAsset` | POST | `/sync` |
33
+| `syncYakAsset` | POST | `/sync`(五段基础全量;**不含** IoT) |
34
+
35
+IoT 称重/定位/环境由后端 **Quartz 定时任务**同步,前端无对应按钮与 API。
34 36
 
35 37
 本期**无**档案增删改接口。
36 38