Kaynağa Gözat

对接牦牛资产档案

wwh 1 ay önce
ebeveyn
işleme
042c7421b0
14 değiştirilmiş dosya ile 288 ekleme ve 6 silme
  1. 18 0
      baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/service/FarmEnclosureSyncService.java
  2. 23 0
      baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/service/FarmEnclosureSyncTxService.java
  3. 10 0
      baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/service/FarmingBatchSyncService.java
  4. 14 0
      baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/service/FarmingBatchSyncTxService.java
  5. 10 0
      baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/service/FarmingPlanSyncService.java
  6. 21 0
      baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/service/FarmingPlanSyncTxService.java
  7. 41 2
      baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/service/LivestockFinanceMasterSyncService.java
  8. 38 0
      baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/service/YakAssetBundleSyncTxService.java
  9. 25 1
      baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/service/YakAssetSyncService.java
  10. 19 1
      baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/support/FarmingDataSyncExecutor.java
  11. 3 1
      baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/support/FarmingSyncRules.java
  12. 21 0
      baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/support/FarmingSyncSupport.java
  13. 16 0
      baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/support/ThirdPartyFarmingHttpSupport.java
  14. 29 1
      baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/support/ThirdPartyFarmingPageFetcher.java

+ 18 - 0
baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/service/FarmEnclosureSyncService.java

@@ -9,9 +9,22 @@ import com.ruoyi.web.modules.industryservice.support.FarmingDataSyncExecutor;
9 9
 import com.ruoyi.web.modules.industryservice.support.FarmingSyncRules;
10 10
 import com.ruoyi.web.modules.industryservice.support.ThirdPartyFarmEnclosureClient;
11 11
 
12
+/**
13
+ * 第三方<strong>养殖圈舍</strong>同步编排。
14
+ * <p>
15
+ * 接口:{@code GET /open-api/v1/farming/enclosures} → 落库 {@code biz_farm_enclosure}。
16
+ * 通常由 {@link LivestockFinanceMasterSyncService} 在养殖场同步之后调用;
17
+ * 也可在全量同步流程中单独作为第二步执行。
18
+ * </p>
19
+ * <p>
20
+ * 每条圈舍记录通过 {@link FarmEnclosureSyncTxService} 以 {@code REQUIRES_NEW} 独立事务写入,
21
+ * 单条失败计入 {@link YakAssetSyncResultVo#getFailCount()},不影响同批其他圈舍。
22
+ * </p>
23
+ */
12 24
 @Service
13 25
 public class FarmEnclosureSyncService
14 26
 {
27
+    /** 防并发重入:同一时刻只允许一个圈舍同步任务 */
15 28
     private final AtomicBoolean syncing = new AtomicBoolean(false);
16 29
 
17 30
     @Autowired
@@ -20,6 +33,11 @@ public class FarmEnclosureSyncService
20 33
     @Autowired
21 34
     private FarmEnclosureSyncTxService farmEnclosureSyncTxService;
22 35
 
36
+    /**
37
+     * 从第三方拉取全量圈舍列表并 upsert 到本地。
38
+     *
39
+     * @return 新增/更新/失败条数及失败明细(key 为第三方 id)
40
+     */
23 41
     public YakAssetSyncResultVo syncFromThirdParty()
24 42
     {
25 43
         if (!syncing.compareAndSet(false, true))

+ 23 - 0
baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/service/FarmEnclosureSyncTxService.java

@@ -15,6 +15,20 @@ import com.ruoyi.web.modules.industryservice.mapper.BizPastureMapper;
15 15
 import com.ruoyi.web.modules.industryservice.support.FarmingSyncRules;
16 16
 import com.ruoyi.web.modules.industryservice.support.FarmingSyncSupport;
17 17
 
18
+/**
19
+ * 单条养殖圈舍同步事务服务。
20
+ * <p>
21
+ * 将 {@link OpenFarmEnclosureDto}(OpenFarmEnclosureVo)映射为 {@link BizFarmEnclosure},
22
+ * 按 {@code external_id} upsert 到 {@code biz_farm_enclosure}。
23
+ * 每条记录独立事务(REQUIRES_NEW),避免批量同步中单条失败拖垮整批。
24
+ * </p>
25
+ *
26
+ * <h3>关联解析</h3>
27
+ * <ul>
28
+ *   <li>{@code farmId} → {@code farm_external_id},并尝试解析本地 {@code pasture_id}</li>
29
+ *   <li>若养殖场尚未同步,{@code pasture_id} 暂为 null,后续全量同步会先补全养殖场</li>
30
+ * </ul>
31
+ */
18 32
 @Service
19 33
 public class FarmEnclosureSyncTxService
20 34
 {
@@ -24,6 +38,13 @@ public class FarmEnclosureSyncTxService
24 38
     @Autowired
25 39
     private BizPastureMapper pastureMapper;
26 40
 
41
+    /**
42
+     * 持久化一条第三方圈舍记录。
43
+     *
44
+     * @param dto      OpenFarmEnclosureVo 单条数据
45
+     * @param syncTime 本次同步批次时间,写入 last_sync_time
46
+     * @return {@code true}=新增,{@code false}=更新
47
+     */
27 48
     @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
28 49
     public boolean persistEnclosure(OpenFarmEnclosureDto dto, Date syncTime)
29 50
     {
@@ -38,6 +59,7 @@ public class FarmEnclosureSyncTxService
38 59
         BizFarmEnclosure row = new BizFarmEnclosure();
39 60
         row.setExternalId(dto.getId());
40 61
         row.setFarmExternalId(dto.getFarmId());
62
+        // 依赖 biz_pasture.external_id = farmId,需先同步养殖场
41 63
         row.setPastureId(FarmingSyncSupport.resolvePastureId(pastureMapper, dto.getFarmId()));
42 64
         row.setFarmName(StringUtils.trim(dto.getFarmName()));
43 65
         row.setEnclosureName(dto.getEnclosureName().trim());
@@ -45,6 +67,7 @@ public class FarmEnclosureSyncTxService
45 67
         row.setQrCode(StringUtils.trim(dto.getQrCode()));
46 68
         row.setSupervisor(StringUtils.trim(dto.getSupervisor()));
47 69
         row.setIntroduction(StringUtils.trim(dto.getIntroduction()));
70
+        // enclosureStatus: 0使用中 1空闲 2维护中
48 71
         row.setEnclosureStatus(StringUtils.trim(dto.getEnclosureStatus()));
49 72
         row.setAreaType(StringUtils.trim(dto.getAreaType()));
50 73
         row.setCattleQuantity(dto.getCattleQuantity());

+ 10 - 0
baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/service/FarmingBatchSyncService.java

@@ -9,6 +9,13 @@ import com.ruoyi.web.modules.industryservice.support.FarmingDataSyncExecutor;
9 9
 import com.ruoyi.web.modules.industryservice.support.FarmingSyncRules;
10 10
 import com.ruoyi.web.modules.industryservice.support.ThirdPartyFarmingBatchClient;
11 11
 
12
+/**
13
+ * 第三方<strong>养殖批次</strong>同步编排。
14
+ * <p>
15
+ * 接口:{@code GET /open-api/v1/farming/batches} → 落库 {@code biz_farming_batch}。
16
+ * 在全量同步中位于生产计划之后、入栏建档之前执行。
17
+ * </p>
18
+ */
12 19
 @Service
13 20
 public class FarmingBatchSyncService
14 21
 {
@@ -20,6 +27,9 @@ public class FarmingBatchSyncService
20 27
     @Autowired
21 28
     private FarmingBatchSyncTxService farmingBatchSyncTxService;
22 29
 
30
+    /**
31
+     * 从第三方拉取全量养殖批次并 upsert 到本地。
32
+     */
23 33
     public YakAssetSyncResultVo syncFromThirdParty()
24 34
     {
25 35
         if (!syncing.compareAndSet(false, true))

+ 14 - 0
baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/service/FarmingBatchSyncTxService.java

@@ -16,6 +16,13 @@ import com.ruoyi.web.modules.industryservice.mapper.BizPastureMapper;
16 16
 import com.ruoyi.web.modules.industryservice.support.FarmingSyncRules;
17 17
 import com.ruoyi.web.modules.industryservice.support.FarmingSyncSupport;
18 18
 
19
+/**
20
+ * 单条养殖批次同步事务服务。
21
+ * <p>
22
+ * 将 {@link OpenFarmingBatchDto} 映射为 {@link BizFarmingBatch},按 {@code external_id} upsert。
23
+ * 批次编号 {@code batchNo} 与入栏建档接口的 {@code entryCycle} 对应,供牦牛资产关联查询。
24
+ * </p>
25
+ */
19 26
 @Service
20 27
 public class FarmingBatchSyncTxService
21 28
 {
@@ -28,6 +35,13 @@ public class FarmingBatchSyncTxService
28 35
     @Autowired
29 36
     private BizFarmEnclosureMapper farmEnclosureMapper;
30 37
 
38
+    /**
39
+     * 持久化一条第三方养殖批次。
40
+     *
41
+     * @param dto      开放接口单条批次
42
+     * @param syncTime 本次同步批次时间
43
+     * @return {@code true}=新增,{@code false}=更新
44
+     */
31 45
     @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
32 46
     public boolean persistBatch(OpenFarmingBatchDto dto, Date syncTime)
33 47
     {

+ 10 - 0
baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/service/FarmingPlanSyncService.java

@@ -9,6 +9,13 @@ import com.ruoyi.web.modules.industryservice.support.FarmingDataSyncExecutor;
9 9
 import com.ruoyi.web.modules.industryservice.support.FarmingSyncRules;
10 10
 import com.ruoyi.web.modules.industryservice.support.ThirdPartyFarmingPlanClient;
11 11
 
12
+/**
13
+ * 第三方<strong>养殖生产计划</strong>同步编排。
14
+ * <p>
15
+ * 接口:{@code GET /open-api/v1/farming/plans} → 落库 {@code biz_farming_plan}。
16
+ * 在全量同步中位于圈舍同步之后、批次同步之前执行。
17
+ * </p>
18
+ */
12 19
 @Service
13 20
 public class FarmingPlanSyncService
14 21
 {
@@ -20,6 +27,9 @@ public class FarmingPlanSyncService
20 27
     @Autowired
21 28
     private FarmingPlanSyncTxService farmingPlanSyncTxService;
22 29
 
30
+    /**
31
+     * 从第三方拉取全量生产计划并 upsert 到本地。
32
+     */
23 33
     public YakAssetSyncResultVo syncFromThirdParty()
24 34
     {
25 35
         if (!syncing.compareAndSet(false, true))

+ 21 - 0
baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/service/FarmingPlanSyncTxService.java

@@ -16,6 +16,20 @@ import com.ruoyi.web.modules.industryservice.mapper.BizPastureMapper;
16 16
 import com.ruoyi.web.modules.industryservice.support.FarmingSyncRules;
17 17
 import com.ruoyi.web.modules.industryservice.support.FarmingSyncSupport;
18 18
 
19
+/**
20
+ * 单条养殖生产计划同步事务服务。
21
+ * <p>
22
+ * 将 {@link OpenFarmingPlanDto}(OpenFarmingPlanVo)映射为 {@link BizFarmingPlan},
23
+ * 按 {@code external_id} upsert。
24
+ * </p>
25
+ *
26
+ * <h3>关联解析</h3>
27
+ * <ul>
28
+ *   <li>{@code farmId} → {@code pasture_id}</li>
29
+ *   <li>{@code farmEnclosureId} → {@code enclosure_id}(需圈舍已同步)</li>
30
+ *   <li>{@code planStatus}:0草稿 1已下发 2执行中 3已完成 4已取消(以第三方字典为准)</li>
31
+ * </ul>
32
+ */
19 33
 @Service
20 34
 public class FarmingPlanSyncTxService
21 35
 {
@@ -28,6 +42,13 @@ public class FarmingPlanSyncTxService
28 42
     @Autowired
29 43
     private BizFarmEnclosureMapper farmEnclosureMapper;
30 44
 
45
+    /**
46
+     * 持久化一条第三方生产计划。
47
+     *
48
+     * @param dto      OpenFarmingPlanVo 单条数据
49
+     * @param syncTime 本次同步批次时间
50
+     * @return {@code true}=新增,{@code false}=更新
51
+     */
31 52
     @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
32 53
     public boolean persistPlan(OpenFarmingPlanDto dto, Date syncTime)
33 54
     {

+ 41 - 2
baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/service/LivestockFinanceMasterSyncService.java

@@ -5,14 +5,43 @@ import org.springframework.beans.factory.annotation.Autowired;
5 5
 import org.springframework.stereotype.Service;
6 6
 import com.ruoyi.common.exception.ServiceException;
7 7
 import com.ruoyi.web.modules.industryservice.domain.vo.LivestockFinanceSyncResultVo;
8
-import com.ruoyi.web.modules.industryservice.support.FarmingSyncRules;
9 8
 
10 9
 /**
11
- * 牧业金融生物资产同步编排:养殖场 → 圈舍 → 生产计划 → 批次 → 入栏建档。
10
+ * 牧业金融生物资产<strong>全量同步编排</strong>服务。
11
+ * <p>
12
+ * 由 {@code POST /dataModel/yakAsset/sync} 触发,按第三方数据依赖关系<strong>严格顺序</strong>执行五段同步,
13
+ * 确保入栏建档(牦牛资产)落库前,养殖场、圈舍、生产计划、批次等基础台账已就绪。
14
+ * </p>
15
+ *
16
+ * <h3>同步顺序与落库表</h3>
17
+ * <ol>
18
+ *   <li>养殖场 → {@code biz_pasture}({@link PastureSyncService})</li>
19
+ *   <li>养殖圈舍 → {@code biz_farm_enclosure}({@link FarmEnclosureSyncService})</li>
20
+ *   <li>养殖生产计划 → {@code biz_farming_plan}({@link FarmingPlanSyncService})</li>
21
+ *   <li>养殖批次 → {@code biz_farming_batch}({@link FarmingBatchSyncService})</li>
22
+ *   <li>入栏建档 → {@code biz_yak_asset}({@link YakAssetSyncService})</li>
23
+ * </ol>
24
+ *
25
+ * <h3>并发控制</h3>
26
+ * <p>
27
+ * 本类持有全局 {@link #syncLock},同一时刻仅允许一个全量同步任务运行。
28
+ * 各子步骤另有独立的防重入标志(如 {@link PastureSyncService} 的 {@code AtomicBoolean}),
29
+ * 避免全量同步进行中用户单独触发某一步导致交叉写入。
30
+ * </p>
31
+ *
32
+ * <h3>失败策略</h3>
33
+ * <p>
34
+ * 任一步骤 HTTP 拉取失败(第三方不可用、鉴权失败等)会抛出 {@link ServiceException} 并<strong>中断后续步骤</strong>。
35
+ * 单条记录持久化失败由各子服务的「单条独立事务 + 失败计数」处理,不拖垮同批其他记录。
36
+ * </p>
37
+ *
38
+ * @see LivestockFinanceSyncResultVo
39
+ * @see com.ruoyi.web.modules.industryservice.service.impl.BizYakAssetServiceImpl#syncFromThirdParty()
12 40
  */
13 41
 @Service
14 42
 public class LivestockFinanceMasterSyncService
15 43
 {
44
+    /** 全量同步互斥锁,防止重复点击或并发请求 */
16 45
     private final Object syncLock = new Object();
17 46
 
18 47
     @Autowired
@@ -30,6 +59,11 @@ public class LivestockFinanceMasterSyncService
30 59
     @Autowired
31 60
     private YakAssetSyncService yakAssetSyncService;
32 61
 
62
+    /**
63
+     * 执行牧业金融生物资产全量同步(五段顺序执行)。
64
+     *
65
+     * @return 各步骤的新增/更新/失败统计;顶层 {@code insertCount} 等字段兼容旧前端,取自入栏建档步骤
66
+     */
33 67
     public LivestockFinanceSyncResultVo syncAll()
34 68
     {
35 69
         synchronized (syncLock)
@@ -38,10 +72,15 @@ public class LivestockFinanceMasterSyncService
38 72
             Date syncTime = new Date();
39 73
             try
40 74
             {
75
+                // 1. 养殖场:后续圈舍/计划/批次/牦牛均依赖 farmId → pasture_id 解析
41 76
                 result.setPasture(pastureSyncService.syncFromThirdParty());
77
+                // 2. 圈舍:计划与批次可关联 enclosure_id
42 78
                 result.setEnclosure(farmEnclosureSyncService.syncFromThirdParty());
79
+                // 3. 生产计划:批次可关联 plan_external_id
43 80
                 result.setPlan(farmingPlanSyncService.syncFromThirdParty());
81
+                // 4. 批次:入栏建档 entryCycle/batchId 与批次台账对应
44 82
                 result.setBatch(farmingBatchSyncService.syncFromThirdParty());
83
+                // 5. 入栏建档(牦牛生物资产主表)
45 84
                 result.setYakAsset(yakAssetSyncService.syncFromThirdParty());
46 85
             }
47 86
             catch (ServiceException e)

+ 38 - 0
baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/service/YakAssetBundleSyncTxService.java

@@ -25,6 +25,28 @@ import com.ruoyi.web.modules.industryservice.mapper.BizYakReproductionRecordMapp
25 25
 import com.ruoyi.web.modules.industryservice.support.YakAssetRules;
26 26
 import com.ruoyi.web.modules.industryservice.support.YakAssetValidation;
27 27
 
28
+/**
29
+ * 单头牦牛资产同步事务服务。
30
+ * <p>
31
+ * 接收 {@link YakAssetBundleDto}(含主表 {@link BizYakAsset} 及可选子表列表),
32
+ * 按 {@code external_id}(第三方 cattleId)优先、其次 {@code yak_no} 进行 upsert。
33
+ * 每头牦牛独立事务(REQUIRES_NEW),单头失败不影响其他头。
34
+ * </p>
35
+ *
36
+ * <h3>主表合并策略</h3>
37
+ * <ul>
38
+ *   <li>先查 external_id,再查 yak_no</li>
39
+ *   <li>存在则 update,不存在则 insert</li>
40
+ *   <li>自动解析 pasture_id:farmId → biz_pasture.external_id,或牧场名称精确匹配</li>
41
+ * </ul>
42
+ *
43
+ * <h3>子表覆盖策略</h3>
44
+ * <p>
45
+ * bundle 中某子表列表为 {@code null} 表示<strong>本次不同步</strong>该子表(保留本地数据);
46
+ * 非 null 时先 delete 再 batchInsert(全量覆盖该子表)。
47
+ * 当前入栏建档 OpenAPI 仅填充主表字段,子表列表默认为 null。
48
+ * </p>
49
+ */
28 50
 @Service
29 51
 public class YakAssetBundleSyncTxService
30 52
 {
@@ -46,6 +68,13 @@ public class YakAssetBundleSyncTxService
46 68
     @Autowired
47 69
     private BizYakFeedingRecordMapper feedingRecordMapper;
48 70
 
71
+    /**
72
+     * 持久化一头牦牛的同步数据包。
73
+     *
74
+     * @param bundle   含主表及可选子表
75
+     * @param syncTime 本次同步批次时间,写入 last_sync_time
76
+     * @return {@code true}=新增,{@code false}=更新
77
+     */
49 78
     @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
50 79
     public boolean persistBundle(YakAssetBundleDto bundle, Date syncTime)
51 80
     {
@@ -94,6 +123,9 @@ public class YakAssetBundleSyncTxService
94 123
         return inserted;
95 124
     }
96 125
 
126
+    /**
127
+     * 解析本地已存在档案:优先 external_id(cattleId),其次 yak_no。
128
+     */
97 129
     private BizYakAsset resolveExisting(BizYakAsset incoming)
98 130
     {
99 131
         if (incoming.getExternalId() != null)
@@ -107,6 +139,9 @@ public class YakAssetBundleSyncTxService
107 139
         return bizYakAssetMapper.selectBizYakAssetByYakNo(incoming.getYakNo());
108 140
     }
109 141
 
142
+    /**
143
+     * 关联养殖场:优先 thirdPartyPastureCode(farmId),其次 pastureName 精确匹配。
144
+     */
110 145
     private void resolvePasture(BizYakAsset incoming, YakAssetBundleDto bundle)
111 146
     {
112 147
         BizPasture pasture = null;
@@ -137,6 +172,9 @@ public class YakAssetBundleSyncTxService
137 172
         }
138 173
     }
139 174
 
175
+    /**
176
+     * 子表「按需覆盖」:列表为 null 跳过;非 null 则 delete + batchInsert。
177
+     */
140 178
     private void replaceChildRows(Long yakAssetId, YakAssetBundleDto bundle)
141 179
     {
142 180
         if (bundle.getPhysioSeries() != null)

+ 25 - 1
baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/service/YakAssetSyncService.java

@@ -12,11 +12,29 @@ import com.ruoyi.web.modules.industryservice.support.ThirdPartyYakClient;
12 12
 import com.ruoyi.web.modules.industryservice.support.YakAssetStatusMapper;
13 13
 
14 14
 /**
15
- * 第三方档案同步编排(防并发重入;单头独立事务)。
15
+ * 第三方<strong>入栏建档(牦牛生物资产)</strong>同步编排。
16
+ * <p>
17
+ * 接口:{@code GET /open-api/v1/farming/entry-filings} → 落库 {@code biz_yak_asset} 及可选子表。
18
+ * 在全量同步中由 {@link LivestockFinanceMasterSyncService} 作为最后一步调用;
19
+ * 也可单独运行(前提是养殖场等基础数据已同步,否则 pasture_id 可能无法解析)。
20
+ * </p>
21
+ *
22
+ * <h3>处理流程</h3>
23
+ * <ol>
24
+ *   <li>{@link ThirdPartyYakClient#fetchAll()} 分页拉取并映射为 {@link YakAssetBundleDto}</li>
25
+ *   <li>逐头调用 {@link YakAssetBundleSyncTxService#persistBundle}(单头独立事务)</li>
26
+ *   <li>汇总 insert/update/fail 计数</li>
27
+ * </ol>
28
+ *
29
+ * <h3>并发与容错</h3>
30
+ * <p>
31
+ * {@link #syncLock} 防止同一服务实例并发同步;单头失败仅增加 failCount,已成功落库的头不回滚。
32
+ * </p>
16 33
  */
17 34
 @Service
18 35
 public class YakAssetSyncService
19 36
 {
37
+    /** 入栏建档同步互斥锁 */
20 38
     private final Object syncLock = new Object();
21 39
 
22 40
     @Autowired
@@ -25,6 +43,11 @@ public class YakAssetSyncService
25 43
     @Autowired
26 44
     private YakAssetBundleSyncTxService bundleSyncTxService;
27 45
 
46
+    /**
47
+     * 从第三方拉取全量入栏建档列表并逐头 upsert。
48
+     *
49
+     * @return 新增/更新/失败头数;失败明细 key 为 yakNo
50
+     */
28 51
     public YakAssetSyncResultVo syncFromThirdParty()
29 52
     {
30 53
         synchronized (syncLock)
@@ -75,6 +98,7 @@ public class YakAssetSyncService
75 98
         }
76 99
     }
77 100
 
101
+    /** 第三方状态码 → 本地 asset_status(供测试或扩展使用) */
78 102
     public static int mapThirdPartyStatus(String code)
79 103
     {
80 104
         return YakAssetStatusMapper.mapToLocalStatus(code);

+ 19 - 1
baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/support/FarmingDataSyncExecutor.java

@@ -9,7 +9,17 @@ import com.ruoyi.common.utils.StringUtils;
9 9
 import com.ruoyi.web.modules.industryservice.domain.vo.YakAssetSyncResultVo;
10 10
 
11 11
 /**
12
- * 第三方养殖基础数据批量同步执行器。
12
+ * 第三方养殖基础数据(圈舍/计划/批次等)批量同步执行器。
13
+ * <p>
14
+ * 封装「逐条 persist + 统计 insert/update/fail」的通用循环,
15
+ * 供 {@link com.ruoyi.web.modules.industryservice.service.FarmEnclosureSyncService} 等编排类复用。
16
+ * </p>
17
+ *
18
+ * <h3>容错</h3>
19
+ * <p>
20
+ * 单条 {@code persistFn} 抛异常时捕获并记入 {@link YakAssetSyncResultVo#getFailMessages()},
21
+ * 继续处理后续记录;与 {@code REQUIRES_NEW} 事务配合,已成功落库的行不回滚。
22
+ * </p>
13 23
  */
14 24
 public final class FarmingDataSyncExecutor
15 25
 {
@@ -17,6 +27,14 @@ public final class FarmingDataSyncExecutor
17 27
     {
18 28
     }
19 29
 
30
+    /**
31
+     * 批量执行同步持久化。
32
+     *
33
+     * @param rows      第三方拉取的全量列表
34
+     * @param persistFn 单条持久化函数,返回 true=新增 false=更新
35
+     * @param keyFn     失败明细中的标识(通常为 external_id)
36
+     * @return 汇总结果
37
+     */
20 38
     public static <T> YakAssetSyncResultVo execute(List<T> rows, BiFunction<T, Date, Boolean> persistFn,
21 39
             Function<T, String> keyFn)
22 40
     {

+ 3 - 1
baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/support/FarmingSyncRules.java

@@ -1,12 +1,14 @@
1 1
 package com.ruoyi.web.modules.industryservice.support;
2 2
 
3 3
 /**
4
- * 养殖基础数据同步规则。
4
+ * 养殖基础数据同步规则与提示文案
5 5
  */
6 6
 public final class FarmingSyncRules
7 7
 {
8
+    /** 并发同步进行中时的用户提示 */
8 9
     public static final String MSG_SYNC_IN_PROGRESS = "正在同步,请稍候";
9 10
 
11
+    /** 第三方 id 为空时的校验提示 */
10 12
     public static final String MSG_EXTERNAL_ID_REQUIRED = "第三方编号不能为空";
11 13
 
12 14
     private FarmingSyncRules()

+ 21 - 0
baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/support/FarmingSyncSupport.java

@@ -12,11 +12,16 @@ import com.ruoyi.web.modules.industryservice.mapper.BizPastureMapper;
12 12
 
13 13
 /**
14 14
  * 养殖基础数据同步共用工具。
15
+ * <p>
16
+ * 提供第三方时间解析、本地关联 ID 解析等,供各 {@code *SyncTxService} 使用。
17
+ * </p>
15 18
  */
16 19
 public final class FarmingSyncSupport
17 20
 {
21
+    /** 写入各 biz_* 表的 data_source 字段 */
18 22
     public static final String DATA_SOURCE_THIRD_PARTY = "third_party";
19 23
 
24
+    /** 同步操作人标识,写入 create_by / update_by */
20 25
     public static final String SYNC_BY = "sync";
21 26
 
22 27
     private static final SimpleDateFormat THIRD_PARTY_TIME = createThirdPartyTimeFormat();
@@ -27,6 +32,12 @@ public final class FarmingSyncSupport
27 32
     {
28 33
     }
29 34
 
35
+    /**
36
+     * 解析第三方时间字符串,支持 {@code yyyy-MM-dd HH:mm:ss} 与 {@code yyyy-MM-dd}。
37
+     *
38
+     * @param value    原始字符串
39
+     * @param fallback 解析失败或为空时的默认值
40
+     */
30 41
     public static Date parseThirdPartyTime(String value, Date fallback)
31 42
     {
32 43
         if (StringUtils.isEmpty(value))
@@ -54,6 +65,11 @@ public final class FarmingSyncSupport
54 65
         }
55 66
     }
56 67
 
68
+    /**
69
+     * 根据第三方 farmId 解析本地养殖场主键。
70
+     *
71
+     * @return {@code biz_pasture.id},未同步则 null
72
+     */
57 73
     public static Long resolvePastureId(BizPastureMapper pastureMapper, Long farmExternalId)
58 74
     {
59 75
         if (farmExternalId == null || pastureMapper == null)
@@ -64,6 +80,11 @@ public final class FarmingSyncSupport
64 80
         return pasture != null ? pasture.getId() : null;
65 81
     }
66 82
 
83
+    /**
84
+     * 根据第三方圈舍 id 解析本地圈舍主键。
85
+     *
86
+     * @return {@code biz_farm_enclosure.id},未同步则 null
87
+     */
67 88
     public static Long resolveEnclosureId(BizFarmEnclosureMapper enclosureMapper, Long enclosureExternalId)
68 89
     {
69 90
         if (enclosureExternalId == null || enclosureMapper == null)

+ 16 - 0
baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/support/ThirdPartyFarmingHttpSupport.java

@@ -18,6 +18,10 @@ import com.ruoyi.common.exception.ServiceException;
18 18
 
19 19
 /**
20 20
  * 第三方生产管理系统 HTTP 调用共用工具。
21
+ * <p>
22
+ * 封装 RestTemplate 创建、OpenAPI 鉴权头(X-App-Key / X-App-Secret)、URL 拼接及 JSON 反序列化配置。
23
+ * 所有 {@code ThirdParty*ClientImpl} 均依赖本类,配置来源 {@link ThirdPartyFarmingProperties}。
24
+ * </p>
21 25
  */
22 26
 public final class ThirdPartyFarmingHttpSupport
23 27
 {
@@ -25,6 +29,7 @@ public final class ThirdPartyFarmingHttpSupport
25 29
     {
26 30
     }
27 31
 
32
+    /** 校验 base-url 已配置,否则抛出业务异常 */
28 33
     public static void requireBaseUrl(ThirdPartyFarmingProperties properties, String hint)
29 34
     {
30 35
         if (!StringUtils.hasText(properties.getBaseUrl()))
@@ -33,6 +38,7 @@ public final class ThirdPartyFarmingHttpSupport
33 38
         }
34 39
     }
35 40
 
41
+    /** 按配置创建 RestTemplate,含连接/读取超时 */
36 42
     public static RestTemplate createRestTemplate(ThirdPartyFarmingProperties properties)
37 43
     {
38 44
         SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
@@ -41,6 +47,7 @@ public final class ThirdPartyFarmingHttpSupport
41 47
         return new RestTemplate(factory);
42 48
     }
43 49
 
50
+    /** 构建开放接口鉴权请求头 */
44 51
     public static HttpHeaders buildOpenApiHeaders(ThirdPartyFarmingProperties properties)
45 52
     {
46 53
         HttpHeaders headers = new HttpHeaders();
@@ -56,6 +63,7 @@ public final class ThirdPartyFarmingHttpSupport
56 63
         return headers;
57 64
     }
58 65
 
66
+    /** baseUrl + path,baseUrl 末尾斜杠自动去除 */
59 67
     public static String resolveUrl(ThirdPartyFarmingProperties properties, String path)
60 68
     {
61 69
         return trimTrailingSlash(properties.getBaseUrl()) + path;
@@ -71,6 +79,13 @@ public final class ThirdPartyFarmingHttpSupport
71 79
         return url;
72 80
     }
73 81
 
82
+    /**
83
+     * 创建 JSON 反序列化器。
84
+     * <p>
85
+     * 注册 {@link FlexibleLongDeserializer}:第三方部分 int64 字段以字符串返回(如 {@code "2061327199252615170"}),
86
+     * 需兼容解析为 {@link Long}。
87
+     * </p>
88
+     */
74 89
     public static ObjectMapper createObjectMapper()
75 90
     {
76 91
         ObjectMapper mapper = new ObjectMapper();
@@ -85,6 +100,7 @@ public final class ThirdPartyFarmingHttpSupport
85 100
         return mapper;
86 101
     }
87 102
 
103
+    /** 兼容 JSON 数字与字符串形式的 Long 字段 */
88 104
     private static final class FlexibleLongDeserializer extends JsonDeserializer<Long>
89 105
     {
90 106
         @Override

+ 29 - 1
baqing-admin/src/main/java/com/ruoyi/web/modules/industryservice/support/ThirdPartyFarmingPageFetcher.java

@@ -23,7 +23,23 @@ import com.ruoyi.web.modules.industryservice.domain.dto.OpenApiPageResult;
23 23
 import com.ruoyi.web.modules.industryservice.domain.dto.OpenApiResponse;
24 24
 
25 25
 /**
26
- * 第三方养殖开放接口分页拉取共用逻辑。
26
+ * 第三方养殖开放接口<strong>分页拉取</strong>共用逻辑。
27
+ * <p>
28
+ * 开放接口统一返回 {@link OpenApiResponse}{@code <}{@link OpenApiPageResult}{@code <T>>},
29
+ * 本类负责循环 {@code pageNum=1..N} 直至 records 为空或已达 total。
30
+ * </p>
31
+ *
32
+ * <h3>分页终止条件(满足任一即停止)</h3>
33
+ * <ul>
34
+ *   <li>当前页 records 为空</li>
35
+ *   <li>当前页条数 &lt; pageSize</li>
36
+ *   <li>已收集条数 &gt;= total</li>
37
+ * </ul>
38
+ *
39
+ * <h3>Stub 模式</h3>
40
+ * <p>
41
+ * {@link #fetchFromStub} 从 classpath JSON 读取全量列表,供 {@code third-party.farming.mode=stub} 联调。
42
+ * </p>
27 43
  */
28 44
 public final class ThirdPartyFarmingPageFetcher
29 45
 {
@@ -31,6 +47,13 @@ public final class ThirdPartyFarmingPageFetcher
31 47
     {
32 48
     }
33 49
 
50
+    /**
51
+     * HTTP 模式:分页拉取直至全量。
52
+     *
53
+     * @param listPath   开放接口路径,如 {@code /open-api/v1/farming/enclosures}
54
+     * @param responseType Jackson 泛型类型引用
55
+     * @param apiLabel   异常消息中的接口中文名
56
+     */
34 57
     public static <T> List<T> fetchAllPages(ThirdPartyFarmingProperties properties, ObjectMapper objectMapper,
35 58
             String listPath, TypeReference<OpenApiResponse<OpenApiPageResult<T>>> responseType, String apiLabel)
36 59
     {
@@ -39,6 +62,7 @@ public final class ThirdPartyFarmingPageFetcher
39 62
         HttpHeaders headers = ThirdPartyFarmingHttpSupport.buildOpenApiHeaders(properties);
40 63
         List<T> all = new ArrayList<>();
41 64
         int pageNum = 1;
65
+        // 接口上限 200,配置超出时截断
42 66
         int pageSize = Math.min(Math.max(properties.getPageSize(), 1), 200);
43 67
         Long total = null;
44 68
         while (true)
@@ -56,6 +80,7 @@ public final class ThirdPartyFarmingPageFetcher
56 80
                     throw new ServiceException(apiLabel + "接口返回异常");
57 81
                 }
58 82
                 OpenApiResponse<OpenApiPageResult<T>> body = objectMapper.readValue(response.getBody(), responseType);
83
+                // code=0 表示业务成功;401 鉴权失败;500 服务端错误
59 84
                 if (body == null || body.getCode() == null || body.getCode() != 0)
60 85
                 {
61 86
                     String msg = body != null && StringUtils.hasText(body.getMessage()) ? body.getMessage() : "业务失败";
@@ -97,6 +122,9 @@ public final class ThirdPartyFarmingPageFetcher
97 122
         return all;
98 123
     }
99 124
 
125
+    /**
126
+     * Stub 模式:读取 classpath 样例 JSON 数组。
127
+     */
100 128
     public static <T> List<T> fetchFromStub(ObjectMapper objectMapper, String stubResource,
101 129
             TypeReference<List<T>> listType, String apiLabel)
102 130
     {