Răsfoiți Sursa

Update 优化离线数据上传

Yue 3 zile în urmă
părinte
comite
614dceb9a1

+ 1 - 1
UI/CF.APP/chicken_farm/lib/apis/breeding/_query.dart

@@ -3,7 +3,7 @@ import 'dart:math';
 import 'package:chicken_farm/modes/breeding/batch.dart';
 import 'package:chicken_farm/modes/breeding/family.dart';
 import 'package:chicken_farm/modes/breeding/wing_tag_num.dart';
-import 'package:chicken_farm/modes/page/page_model.dart';
+import 'package:chicken_farm/modes/api/page_model.dart';
 
 class BreedQueryApi {
   static final BreedQueryApi _instance = BreedQueryApi._internal();

+ 146 - 112
UI/CF.APP/chicken_farm/lib/apis/breeding/_submit.dart

@@ -1,6 +1,8 @@
 import 'package:chicken_farm/core/api/api_option.dart';
 import 'package:chicken_farm/core/api/api_service.dart';
 import 'package:chicken_farm/core/config/app_config.dart';
+import 'package:chicken_farm/core/config/breed_config.dart';
+import 'package:chicken_farm/core/db/table_config.dart';
 import 'package:chicken_farm/core/services/breeding_data_service.dart';
 import 'package:chicken_farm/modes/api/result_model.dart';
 
@@ -10,142 +12,178 @@ class BreedSubmitApi {
   factory BreedSubmitApi() => _instance;
 
   BreedSubmitApi._internal();
+  final BreedingDataService _breedingDataService = BreedingDataService();
+
+  final String bindChickenUrl = '/app/breeding/bind/';
+  final String cageChangeUrl = '/app/breeding/cageChange/';
+  final String weightUrl = '/app/breeding/weight/';
+  final String cullUrl = '/app/breeding/cull/';
 
   Future<ResultModel> bindChicken(dynamic data) async {
     try {
-      if (AppConfig.isOffline) {
-        return await bindChickenOffline(data);
-      } else {
-        return await ApiService().postWithOfflineSupport(
-          '/app/breeding/create/',
-          data: data,
+      List<String> ids = data['rfids'];
+      // 保存到本地数据库
+      List<Map<String, dynamic>> list = ids.map<Map<String, dynamic>>((id) {
+        return {
+          'rfid': id,
+          'batch_num': data['batchNum'],
+          'family_id': data['familyId'],
+          'date': data['date'],
+          'is_export': 0,
+        };
+      }).toList();
+      ResultModel result = ResultModel.offline();
+      if (!AppConfig.isOffline) {
+        result = await ApiService().post(
+          bindChickenUrl,
+          data: list,
           apiOption: ApiOption.noAlert(),
         );
       }
+      if (result.isOffline == true || result.isNetError == true) {
+        return await _breedingDataService.batchInsert(
+          TableConfig.chicken,
+          list,
+        );
+      } else {
+        return result;
+      }
     } catch (e) {
       return ResultModel(success: false, message: e.toString(), data: null);
     }
   }
 
-  Future<ResultModel> bindChickenOffline(dynamic data) async {
-    // 保存到本地数据库
-    List<Map<String, dynamic>> list = data.map<Map<String, dynamic>>((item) {
-      return {
-        'rfid': item['rfid'],
-        'batch_num': item['batchNum'],
-        'family_id': item['familyId'],
-        'date': item['date'],
-        'is_export': 0,
-      };
-    }).toList();
-    if (await BreedingDataService().batchInsertChicken(list)) {
-      return ResultModel.success('数据保存成功');
-    } else {
-      return ResultModel.fail('数据保存失败');
-    }
-  }
-
   Future<ResultModel> cageChange(dynamic data) async {
     try {
-      if (AppConfig.isOffline) {
-        return await cageChangeOffline(data);
+      final ids = data['rfids'] as List<String>;
+      List<Map<String, dynamic>> list = ids.map<Map<String, dynamic>>((id) {
+        return {
+          'rfid': id,
+          'target_cage': data['targetCage'],
+          'date': data['date'],
+          'is_export': 0,
+        };
+      }).toList();
+      ResultModel result = ResultModel.offline();
+      if (!AppConfig.isOffline) {
+        result = await ApiService().post(
+          cageChangeUrl,
+          data: list,
+          apiOption: ApiOption.noAlert(),
+        );
+      }
+      if (result.isOffline == true || result.isNetError == true) {
+        return await _breedingDataService.batchInsert(
+          TableConfig.cageChange,
+          list,
+        );
+      } else {
+        return result;
       }
-      return await ApiService().postWithOfflineSupport(
-        '/app/breeding/cageChange/',
-        data: data,
-        apiOption: ApiOption.noAlert(),
-      );
     } catch (e) {
       return ResultModel(success: false, message: e.toString(), data: null);
     }
   }
 
-  Future<ResultModel> cageChangeOffline(dynamic data) async {
-    // 保存到本地数据库
-    final ids = data['rfids'] as List<String>;
-    List<Map<String, dynamic>> list = ids.map<Map<String, dynamic>>((id) {
-      return {
-        'rfid': id,
-        'target_cage': data['targetCage'],
+  Future<ResultModel> weight(dynamic data) async {
+    try {
+      Map<String, dynamic> weightData = {
+        'rfid': data['rfid'],
+        'weight': data['weight'],
         'date': data['date'],
         'is_export': 0,
       };
-    }).toList();
-    if (await BreedingDataService().batchInsertCageChange(list)) {
-      return ResultModel.success('数据保存成功');
-    } else {
-      return ResultModel.fail('数据保存失败');
-    }
-  }
-
-  Future<ResultModel> weight(dynamic data) async {
-    try {
-      if (AppConfig.isOffline) {
-        return await weightOffline(data);
+      ResultModel result = ResultModel.offline();
+      if (!AppConfig.isOffline) {
+        result = await ApiService().post(
+          weightUrl,
+          data: weightData,
+          apiOption: ApiOption.noAlert(),
+        );
+      }
+      if (result.isOffline == true || result.isNetError == true) {
+        return await _breedingDataService.insert(
+          TableConfig.weight,
+          weightData,
+        );
+      } else {
+        return result;
       }
-      return await ApiService().postWithOfflineSupport(
-        '/app/breeding/weight/',
-        data: data,
-        apiOption: ApiOption.noAlert(),
-      );
     } catch (e) {
       return ResultModel(success: false, message: e.toString(), data: null);
     }
   }
 
-  Future<ResultModel> weightOffline(dynamic data) async {
-    // 保存到本地数据库
-    Map<String, dynamic> weightData = {
-      'rfid': data['rfid'],
-      'weight': data['weight'],
-      'date': data['date'],
-      'is_export': 0,
-    };
-    if (await BreedingDataService().insertWeight(weightData)) {
-      return ResultModel.success('数据保存成功');
-    } else {
-      return ResultModel.fail('数据保存失败');
-    }
-  }
-
   Future<ResultModel> cull(dynamic data) async {
     try {
-      if (AppConfig.isOffline) {
-        return await cullOffline(data);
+      List<Map<String, dynamic>> list = [
+        {
+          'rfid': data['rfid'],
+          'cull_reason': data['cullReason'],
+          'disposal_method': data['disposalMethod'],
+          'date': data['date'],
+          'is_export': 0,
+        },
+      ];
+      ResultModel result = ResultModel.offline();
+      if (!AppConfig.isOffline) {
+        result = await ApiService().post(
+          cullUrl,
+          data: list,
+          apiOption: ApiOption.noAlert(),
+        );
+      }
+      if (result.isOffline == true || result.isNetError == true) {
+        return await _breedingDataService.batchInsert(TableConfig.cull, list);
+      } else {
+        return result;
       }
-      return await ApiService().postWithOfflineSupport(
-        '/app/breeding/cull/',
-        data: data,
-        apiOption: ApiOption.noAlert(),
-      );
     } catch (e) {
       return ResultModel(success: false, message: e.toString(), data: null);
     }
   }
 
-  Future<ResultModel> cullOffline(dynamic data) async {
-    // 保存到本地数据库
-    Map<String, dynamic> cullData = {
-      'rfid': data['rfid'],
-      'cull_reason': data['cullReason'],
-      'disposal_method': data['disposalMethod'],
-      'date': data['date'],
-      'is_export': 0,
-    };
-    if (await BreedingDataService().insertCull(cullData)) {
-      return ResultModel.success('数据保存成功');
-    } else {
-      return ResultModel.fail('数据保存失败');
+  Future<ResultModel> batchCull(dynamic data) async {
+    try {
+      List<String> ids = data['rfids'];
+      List<Map<String, dynamic>> list = ids.map<Map<String, dynamic>>((id) {
+        return {
+          'rfid': id,
+          'cull_reason': data['cullReason'],
+          'disposal_method': data['disposalMethod'],
+          'date': data['date'],
+          'is_export': 0,
+        };
+      }).toList();
+      ResultModel result = ResultModel.offline();
+      if (!AppConfig.isOffline) {
+        result = await ApiService().post(
+          cullUrl,
+          data: list,
+          apiOption: ApiOption.noAlert(),
+        );
+      }
+      if (result.isOffline == true || result.isNetError == true) {
+        return await _breedingDataService.batchInsert(TableConfig.cull, list);
+      } else {
+        return result;
+      }
+    } catch (e) {
+      return ResultModel(success: false, message: e.toString(), data: null);
     }
   }
 
-  Future<ResultModel> batchCull(dynamic data) async {
+  Future<ResultModel> upload(String type, Map<String, dynamic> data) async {
     try {
       if (AppConfig.isOffline) {
-        return await batchCullOffline(data);
+        return ResultModel.failOffline("脱机模式不支持调用upload接口");
+      }
+      String url = getUrl(type);
+      if (url.isEmpty) {
+        return ResultModel.fail("上传接口不存在");
       }
-      return await ApiService().postWithOfflineSupport(
-        '/app/breeding/batchCull/',
+      return await ApiService().post(
+        url,
         data: data,
         apiOption: ApiOption.noAlert(),
       );
@@ -153,24 +191,20 @@ class BreedSubmitApi {
       return ResultModel(success: false, message: e.toString(), data: null);
     }
   }
+}
 
-  Future<ResultModel> batchCullOffline(dynamic data) async {
-    List<String> ids = data['rfids'];
-    List<Map<String, dynamic>> batchCullData = ids.map<Map<String, dynamic>>((
-      id,
-    ) {
-      return {
-        'rfid': id,
-        'cull_reason': data['cullReason'],
-        'disposal_method': data['disposalMethod'],
-        'date': data['date'],
-        'is_export': 0,
-      };
-    }).toList();
-    if (await BreedingDataService().batchInsertCull(batchCullData)) {
-      return ResultModel.success('数据保存成功');
-    } else {
-      return ResultModel.fail('数据保存失败');
-    }
+String getUrl(String type) {
+  final prefix = '/app/breeding/';
+  switch (type) {
+    case BreedConfig.chicken:
+      return '${prefix}bindChicken/';
+    case BreedConfig.cageChange:
+      return '${prefix}cageChange/';
+    case BreedConfig.weight:
+      return '${prefix}weight/';
+    case BreedConfig.cull:
+      return '${prefix}cull/';
+    default:
+      return '';
   }
 }

+ 13 - 100
UI/CF.APP/chicken_farm/lib/core/api/api_service.dart

@@ -8,8 +8,6 @@ import 'package:dio/dio.dart';
 import 'package:go_router/go_router.dart';
 import 'api_client.dart';
 import '../errors/error_handler.dart';
-import 'package:chicken_farm/core/services/offline_storage_service.dart';
-import 'package:uuid/uuid.dart';
 
 class ApiService {
   final Dio _dio = ApiClient.instance;
@@ -54,6 +52,9 @@ class ApiService {
       );
       return _handleResponse(response, option);
     } catch (e) {
+      if (_isNetworkError(e)) {
+        return ResultModel.isNetError('网络连接错误');
+      }
       throw ErrorHandler.handleError(e);
     }
   }
@@ -104,95 +105,6 @@ class ApiService {
     }
   }
 
-  // 新增支持离线操作的POST方法
-  Future<ResultModel> postWithOfflineSupport(
-    String path, {
-    dynamic data,
-    Map<String, dynamic>? queryParameters,
-    ApiOption? apiOption,
-    Options? options,
-    CancelToken? cancelToken,
-  }) async {
-    // 先检查是否有待上传的数据,如果有则直接保存到离线队列
-    final pendingOps = await OfflineStorageService().getPendingOperations();
-    if (pendingOps.isNotEmpty) {
-      return (await _saveOfflineOperation(path, 'POST', data)).convert();
-    }
-
-    try {
-      return await post(
-        path,
-        data: data,
-        queryParameters: queryParameters,
-        apiOption: apiOption,
-        options: options,
-        cancelToken: cancelToken,
-      );
-    } catch (e) {
-      // 如果是网络错误,保存到离线队列
-      logger.e('请求错误: $e');
-      if (_isNetworkError(e)) {
-        return (await _saveOfflineOperation(path, 'POST', data)).convert();
-      }
-      return ResultModel.fail('请求出错');
-    }
-  }
-
-  // 新增支持离线操作的PUT方法
-  Future<ResultModel> putWithOfflineSupport(
-    String path, {
-    dynamic data,
-    Map<String, dynamic>? queryParameters,
-    ApiOption? apiOption,
-    Options? options,
-    CancelToken? cancelToken,
-  }) async {
-    // 先检查是否有待上传的数据,如果有则直接保存到离线队列
-    final pendingOps = await OfflineStorageService().getPendingOperations();
-    if (pendingOps.isNotEmpty) {
-      return (await _saveOfflineOperation(path, 'PUT', data)).convert();
-    }
-
-    // 没有待上传数据,尝试直接发送请求
-    try {
-      return await put(
-        path,
-        data: data,
-        queryParameters: queryParameters,
-        apiOption: apiOption,
-        options: options,
-        cancelToken: cancelToken,
-      );
-    } catch (e) {
-      // 如果是网络错误,保存到离线队列
-      logger.e('请求错误: $e');
-      if (_isNetworkError(e)) {
-        return (await _saveOfflineOperation(path, 'PUT', data)).convert();
-      }
-      return ResultModel.fail('请求出错');
-    }
-  }
-
-  /// 创建并保存离线操作的通用方法
-  Future<ApiOfflineModel> _saveOfflineOperation(
-    String endpoint,
-    String method,
-    dynamic data,
-  ) async {
-    final offlineOp = OfflineOperation(
-      id: const Uuid().v4(),
-      endpoint: endpoint,
-      method: method,
-      data: data ?? {},
-      timestamp: DateTime.now(),
-    );
-
-    final storageService = OfflineStorageService();
-    await storageService.saveOperation(offlineOp);
-    logger.d('离线保存: ${offlineOp.id}');
-    return ApiOfflineModel();
-  }
-
   bool _isNetworkError(dynamic error) {
     // 根据实际的错误类型判断是否为网络错误
     final errorString = error.toString().toLowerCase();
@@ -232,7 +144,7 @@ class ApiService {
           NavigationService.navigatorKey.currentContext?.goNamed(
             AppRouteNames.login,
           );
-          return ResultModel.fail(msg ?? '请先登录!');
+          return ResultModel.isAuthError("请先登录");
         } else {
           final msg1 = code == 403 ? "没有权限!" : data['msg'] ?? "操作失败";
           if (apiOption.alert) {
@@ -254,15 +166,16 @@ class ApiService {
         return ResultModel.fail(msg2);
       }
     } catch (e) {
-      logger.e("数据解析失败:$e");
-      final msg = "数据解析失败";
-      if (apiOption.alert) {
-        ToastUtil.errorAlert(msg);
+      if (_isNetworkError(e)) {
+        return ResultModel.isNetError('网络连接错误');
+      } else {
+        logger.e("数据解析失败:$e");
+        final msg = "数据解析失败";
+        if (apiOption.alert) {
+          ToastUtil.errorAlert(msg);
+        }
+        return ResultModel.fail(msg);
       }
-      // else {
-      //   throw Exception(msg);
-      // }
-      return ResultModel.fail(msg);
     }
   }
 }

+ 2 - 2
UI/CF.APP/chicken_farm/lib/core/config/app_config.dart

@@ -1,5 +1,5 @@
-// import 'package:chicken_farm/core/config/env/dev_config.dart';
-import 'package:chicken_farm/core/config/env/dev_offline_config.dart';
+import 'package:chicken_farm/core/config/env/dev_config.dart';
+// import 'package:chicken_farm/core/config/env/dev_offline_config.dart';
 // import 'package:chicken_farm/core/config/env/prod_config.dart';
 import 'package:chicken_farm/core/utils/storage.dart';
 

+ 11 - 0
UI/CF.APP/chicken_farm/lib/core/db/sqlite_manager.dart

@@ -253,6 +253,17 @@ class SqliteManager {
     }
   }
 
+  Future<int> deleteById(String table, int id) async {
+    try {
+      return _executeWithAutoClose((db) async {
+        return await db.delete(table, where: "id = ?", whereArgs: [id]);
+      });
+    } on Exception catch (e, s) {
+      logger.e("根据ID删除数据失败:表[$table] 错误: $e\n堆栈跟踪: $s");
+      return -1;
+    }
+  }
+
   /// 查询符合条件的数据条数
   Future<int> queryCount(
     String table, {

+ 65 - 100
UI/CF.APP/chicken_farm/lib/core/services/breeding_data_service.dart

@@ -1,3 +1,4 @@
+import 'package:chicken_farm/core/config/app_config.dart';
 import 'package:chicken_farm/core/config/breed_config.dart';
 import 'package:chicken_farm/core/db/sqlite_manager.dart';
 import 'package:chicken_farm/core/db/table_config.dart';
@@ -16,136 +17,100 @@ class BreedingDataService {
 
   final SqliteManager _sqliteManager = SqliteManager();
 
-  Future<bool> insertChicken(Map<String, dynamic> data) async {
+  Future<ResultModel> insert(String table, Map<String, dynamic> data) async {
     try {
-      return (await _sqliteManager.insert(
-            table: TableConfig.chicken,
-            data: data,
-          )) >
-          0;
+      await _sqliteManager.insert(table: table, data: data);
+      return ResultModel.success(AppConfig.isOffline ? "数据提交成功" : "离线数据保存成功");
     } catch (e) {
-      logger.e('插入个体出错:$e');
-      return false;
+      logger.e('插入数据出错[$table]:$e');
+      return ResultModel.fail("数据提交失败");
     }
   }
 
-  Future<bool> batchInsertChicken(List<Map<String, dynamic>> dataList) async {
+  Future<ResultModel> batchInsert(
+    String table,
+    List<Map<String, dynamic>> dataList,
+  ) async {
     try {
-      await _sqliteManager.batchInsert(
-        table: TableConfig.chicken,
-        dataList: dataList,
-      );
-      return true;
+      await _sqliteManager.batchInsert(table: table, dataList: dataList);
+      return ResultModel.success(AppConfig.isOffline ? "数据提交成功" : "离线数据保存成功");
     } catch (e) {
-      logger.e('批量插入个体出错:$e');
-      return false;
+      logger.e('批量插入数据出错[$table]:$e');
+      return ResultModel.fail("数据提交失败");
     }
   }
 
-  Future<bool> insertCageChange(Map<String, dynamic> data) async {
+  Future<List<Map<String, dynamic>>> query(
+    String table, {
+    String? where = 'is_export = 0',
+    List<dynamic>? whereArgs,
+  }) async {
     try {
-      return await _sqliteManager.insert(
-            table: TableConfig.cageChange,
-            data: data,
-          ) >
-          0;
+      final list = _sqliteManager.query(
+        table: table,
+        where: where,
+        whereArgs: whereArgs,
+      );
+      return list;
     } catch (e) {
-      logger.e('插入换笼记录出错:$e');
-      return false;
+      logger.e('查询数据出错[$table]:$e');
+      return [];
     }
   }
 
-  Future<bool> batchInsertCageChange(
-    List<Map<String, dynamic>> dataList,
-  ) async {
-    try {
-      await _sqliteManager.batchInsert(
-        table: TableConfig.cageChange,
-        dataList: dataList,
-      );
-      return true;
-    } catch (e) {
-      logger.e('批量插入换笼记录出错:$e');
-      return false;
-    }
+  Future<int> queryCount(String tableName) async {
+    final count = await _sqliteManager.queryCount(
+      tableName,
+      where: "is_export = 0",
+    );
+    return count;
   }
 
-  Future<bool> insertWeight(Map<String, dynamic> data) async {
-    try {
-      return await _sqliteManager.insert(
-            table: TableConfig.weight,
-            data: data,
-          ) >
-          0;
-    } catch (e) {
-      logger.e('插入称重记录出错:$e');
-      return false;
-    }
+  Future<int> queryTotalCount() async {
+    final chickenCount = await queryCount(TableConfig.chicken);
+    final cageChangeCount = await queryCount(TableConfig.cageChange);
+    final weightCount = await queryCount(TableConfig.weight);
+    final cullCount = await queryCount(TableConfig.cull);
+
+    return chickenCount + cageChangeCount + weightCount + cullCount;
   }
 
-  Future<bool> insertCull(Map<String, dynamic> data) async {
+  Future<ResultModel> deleteById(String table, int id) async {
     try {
-      return await _sqliteManager.insert(table: TableConfig.cull, data: data) >
-          0;
+      int r = await _sqliteManager.deleteById(table, id);
+      if (r > 0) {
+        return ResultModel.success("删除成功");
+      } else {
+        return ResultModel.fail("删除失败");
+      }
     } catch (e) {
-      logger.e('插入淘汰记录出错:$e');
-      return false;
+      logger.e('根据ID删除数据出错[$table]:$e');
+      return ResultModel.fail("删除失败");
     }
   }
 
-  Future<bool> batchInsertCull(List<Map<String, dynamic>> dataList) async {
+  Future<ResultModel> delete(
+    String table,
+    String where,
+    List<dynamic> whereArgs,
+  ) async {
     try {
-      await _sqliteManager.batchInsert(
-        table: TableConfig.cull,
-        dataList: dataList,
+      int r = await _sqliteManager.delete(
+        table: table,
+        where: where,
+        whereArgs: whereArgs,
       );
-      return true;
+      if (r > 0) {
+        return ResultModel.success("删除成功");
+      } else {
+        return ResultModel.fail("删除失败");
+      }
     } catch (e) {
-      logger.e('批量插入淘汰记录出错:$e');
-      return false;
+      logger.e('删除数据出错[$table]:$e');
+      return ResultModel.fail("删除失败");
     }
   }
 
-  Future<List<Map<String, dynamic>>> queryAllChickenList() async {
-    final list = _sqliteManager.query(
-      table: TableConfig.chicken,
-      where: 'is_export = 0',
-    );
-    return list;
-  }
-
-  Future<List<Map<String, dynamic>>> queryAllCageChangeList() async {
-    final list = _sqliteManager.query(
-      table: TableConfig.cageChange,
-      where: 'is_export = 0',
-    );
-    return list;
-  }
-
-  Future<List<Map<String, dynamic>>> queryAllWeightList() async {
-    final list = _sqliteManager.query(
-      table: TableConfig.weight,
-      where: 'is_export = 0',
-    );
-    return list;
-  }
-
-  Future<List<Map<String, dynamic>>> queryAllCullList() async {
-    final list = _sqliteManager.query(
-      table: TableConfig.cull,
-      where: 'is_export = 0',
-    );
-    return list;
-  }
-
-  Future<int> queryCount(String tableName) async {
-    final count = await _sqliteManager.queryCount(
-      tableName,
-      where: "is_export = 0",
-    );
-    return count;
-  }
-
   Future<ResultModel> exportData(String type) async {
     String tableName = "";
     String templateKey = "";

+ 126 - 114
UI/CF.APP/chicken_farm/lib/core/services/upload_service.dart

@@ -1,15 +1,18 @@
-import 'package:chicken_farm/core/api/api_option.dart';
-import 'package:chicken_farm/core/api/api_service.dart';
-import 'package:chicken_farm/core/services/offline_storage_service.dart';
+import 'dart:async';
+
+import 'package:chicken_farm/core/config/breed_config.dart';
+import 'package:chicken_farm/core/db/table_config.dart';
+import 'package:chicken_farm/core/services/breeding_data_service.dart';
 import 'package:chicken_farm/core/utils/logger.dart';
 import 'package:chicken_farm/core/utils/service_checker.dart';
-import 'package:dio/dio.dart';
+import 'package:chicken_farm/apis/breeding/_submit.dart';
+import 'package:chicken_farm/modes/api/result_model.dart';
 import 'package:flutter_riverpod/flutter_riverpod.dart';
 
 typedef UploadProgressCallback =
     void Function(int uploaded, int total, String status);
 typedef UploadCompleteCallback = void Function();
-typedef UploadErrorCallback = void Function(String error);
+typedef UploadErrorCallback = void Function(String error, {bool isAuthError});
 
 class UploadService {
   static final UploadService _instance = UploadService._internal();
@@ -20,9 +23,9 @@ class UploadService {
 
   UploadService._internal();
 
-  final ApiService _apiService = ApiService();
   final container = ProviderContainer();
-  final OfflineStorageService _storageService = OfflineStorageService();
+  final BreedingDataService _bdService = BreedingDataService();
+  final BreedSubmitApi _breedSubmitApi = BreedSubmitApi();
   bool _isUploading = false;
   bool isHideUpload = false;
   bool isAuthErr = false;
@@ -57,7 +60,6 @@ class UploadService {
     }
 
     isAuthErr = false;
-
     // 在上传之前检查网络是否恢复
     final isConnected = await ServiceChecker().checkService();
     if (!isConnected) {
@@ -65,112 +67,117 @@ class UploadService {
       _onError?.call('网络未连接');
       return;
     }
-
     // 设置上传状态为正在进行
     _isUploading = true;
     uploadedCount = 0;
-
     try {
-      // 获取待处理操作
-      final pendingOperations = await _storageService.getPendingOperations();
-      totalOperations = pendingOperations.length;
+      final chickenCount = await _bdService.queryCount(TableConfig.chicken);
+      final cageCount = await _bdService.queryCount(TableConfig.cageChange);
+      final weightCount = await _bdService.queryCount(TableConfig.weight);
+      final cullCount = await _bdService.queryCount(TableConfig.cull);
+      // 获取所有待处理操作的总数
+      totalOperations = chickenCount + cageCount + weightCount + cullCount;
       // 如果没有待处理的操作,直接完成
-      if (pendingOperations.isEmpty) {
+      if (totalOperations == 0) {
         logger.i('没有待上传的数据');
         _onComplete?.call();
         return;
       }
 
-      logger.i('开始上传 ${pendingOperations.length} 个离线操作');
-
-      // 按时间戳排序,确保按顺序处理
-      pendingOperations.sort((a, b) => a.timestamp.compareTo(b.timestamp));
-
+      logger.i('开始上传 $totalOperations 个离线操作');
       _onProgress?.call(uploadedCount, totalOperations, '上传中...');
 
-      for (final operation in pendingOperations) {
-        // 检查网络连接状态,如果断开则暂停上传
-        final isStillConnected = await ServiceChecker().checkService();
-        if (!isStillConnected) {
-          logger.i('网络连接中断,暂停上传操作');
-          _onError?.call('网络连接中断');
-          return;
-        }
-
-        try {
-          bool success = false;
-
-          switch (operation.method) {
-            case 'POST':
-              // await _apiService.post(
-              //   operation.endpoint,
-              //   data: operation.data,
-              //   apiOption: ApiOption.noAlertNoLoading(),
-              // );
-              // 模拟上传
-              await Future.delayed(Duration(seconds: 10));
-              success = true;
-              break;
-            case 'PUT':
-              await _apiService.put(
-                operation.endpoint,
-                data: operation.data,
-                apiOption: ApiOption.noAlertNoLoading(),
-              );
-              success = true;
-              break;
-            case 'DELETE':
-              await _apiService.delete(
-                operation.endpoint,
-                data: operation.data,
-                apiOption: ApiOption.noAlertNoLoading(),
-              );
-              success = true;
-              break;
-          }
-
-          if (success) {
-            await _storageService.removeOperation(operation.id);
-            uploadedCount++;
-            logger.i('操作 ${operation.id} 上传成功');
-            _onProgress?.call(uploadedCount, totalOperations, '正在上传...');
-          }
-        } catch (e) {
-          logger.e('上传操作 ${operation.id} 失败: $e');
-          // 检查是否为鉴权失败
-          if (_isAuthError(e)) {
-            logger.i('遇到鉴权失败错误,停止上传');
-            _onError?.call('鉴权失败,请重新登录');
-            // 鉴权失败时导航到登录页面
-            // if (context != null) {
-            //   // 添加mounted检查以确保context仍然有效
-            //   if (context.mounted) {
-            //     context.goNamed(AppRouteNames.login);
-            //   }
-            // }
-            isAuthErr = true;
-            return;
-          }
-          // 如果是网络错误,停止后续上传尝试
-          if (_isNetworkError(e)) {
-            logger.i('遇到网络错误,暂停上传');
-            _onError?.call('网络连接失败');
-            return;
-          }
-          // 对于其他错误,继续尝试上传下一个操作
-        }
+      bool flag = true;
+      // 上传个体绑定数据
+      if (flag && chickenCount > 0) {
+        flag = await _uploadTypeData(BreedConfig.chicken, TableConfig.chicken);
+      }
+      // 上传换笼记录数据
+      if (flag && cageCount > 0) {
+        flag = await _uploadTypeData(
+          BreedConfig.cageChange,
+          TableConfig.cageChange,
+        );
+      }
+      // 上传称重记录数据
+      if (flag && weightCount > 0) {
+        flag = await _uploadTypeData(BreedConfig.weight, TableConfig.weight);
+      }
+      // 上传淘汰记录数据
+      if (flag && cullCount > 0) {
+        flag = await _uploadTypeData(BreedConfig.cull, TableConfig.cull);
+      }
+      if (flag) {
+        // 上传完成
+        _onComplete?.call();
       }
-
-      // 上传完成
-      _onComplete?.call();
     } finally {
       // 上传完成后重置状态
       _isUploading = false;
-      logger.i('上传流程结束');
+      logger.i('上传结束');
     }
   }
 
-  // 为了保持向后兼容性,保留原来的方法
+  /// 上传指定类型的数据
+  Future<bool> _uploadTypeData(String breedConfigType, String tableName) async {
+    // 查询待上传数据
+    final dataList = await _bdService.query(tableName);
+    if (dataList.isEmpty) {
+      logger.i('没有待上传的 $breedConfigType 类型数据');
+      return true;
+    }
+
+    for (final data in dataList) {
+      // 检查网络连接状态,如果断开则暂停上传
+      final isStillConnected = await ServiceChecker().checkService();
+      if (!isStillConnected) {
+        logger.i('网络连接中断,暂停上传操作');
+        _onError?.call('网络连接中断');
+        return false;
+      }
+      int count = 0;
+      // final result = await _breedSubmitApi.upload(breedConfigType, data);
+      ResultModel result = ResultModel.fail("");
+      // 模拟5秒延迟
+      await Future.delayed(Duration(seconds: 2), () {
+        result = ResultModel.success("");
+      });
+      if (result.success) {
+        // 上传成功,更新导出状态
+        await _bdService.delete(tableName, 'id = ?', [data['id']]);
+        uploadedCount++;
+        count++;
+        logger.i('$breedConfigType 类型数据 id:${data['id']} 上传成功');
+        _onProgress?.call(
+          uploadedCount,
+          totalOperations,
+          count == dataList.length
+              ? '${BreedConfig.getName(breedConfigType)}已上传完成!}'
+              : '上传中...',
+        );
+      } else if (result.isNetError == true) {
+        logger.e(
+          '$breedConfigType 类型数据 id:${data['id']} 上传失败: ${result.message}',
+        );
+        _onError?.call('网络连接失败!');
+        return false;
+      } else if (result.isAuthError == true) {
+        logger.e(
+          '$breedConfigType 类型数据 id:${data['id']} 上传失败: ${result.message}',
+        );
+        _onError?.call('鉴权失败!', isAuthError: true);
+        return false;
+      } else {
+        logger.e(
+          '$breedConfigType 类型数据 id:${data['id']} 上传失败: ${result.message}',
+        );
+        _onError?.call(result.message);
+        return false;
+      }
+    }
+    return true;
+  }
+
   Future<void> uploadPendingOperations({
     UploadProgressCallback? onProgress,
     UploadCompleteCallback? onComplete,
@@ -192,23 +199,28 @@ class UploadService {
     isHideUpload = false;
   }
 
-  bool _isAuthError(dynamic error) {
-    // 检查是否为鉴权错误 (401 或 403)
-    if (error is DioException) {
-      return error.response?.statusCode == 401 ||
-          error.response?.statusCode == 403;
-    }
-    // 根据错误消息判断是否为鉴权错误
-    final errorString = error.toString().toLowerCase();
-    return errorString.contains('unauthorized') ||
-        errorString.contains('forbidden') ||
-        errorString.contains('401') ||
-        errorString.contains('403');
-  }
-
-  bool _isNetworkError(dynamic error) {
-    // 复用 ApiService 中的网络错误判断逻辑
-    final apiService = ApiService();
-    return (apiService as dynamic)._isNetworkError(error);
-  }
+  // bool _isAuthError(dynamic error) {
+  //   // 检查是否为鉴权错误 (401 或 403)
+  //   if (error is DioException) {
+  //     return error.response?.statusCode == 401 ||
+  //         error.response?.statusCode == 403;
+  //   }
+  //   // 根据错误消息判断是否为鉴权错误
+  //   final errorString = error.toString().toLowerCase();
+  //   return errorString.contains('unauthorized') ||
+  //       errorString.contains('forbidden') ||
+  //       errorString.contains('401') ||
+  //       errorString.contains('403');
+  // }
+
+  // bool _isAuthErrorString(String? message) {
+  //   if (message == null) return false;
+  //   final errorString = message.toLowerCase();
+  //   return errorString.contains('登录') ||
+  //       errorString.contains('权限') ||
+  //       errorString.contains('unauthorized') ||
+  //       errorString.contains('forbidden') ||
+  //       errorString.contains('401') ||
+  //       errorString.contains('403');
+  // }
 }

+ 4 - 2
UI/CF.APP/chicken_farm/lib/core/utils/service_checker.dart

@@ -1,5 +1,6 @@
 import 'dart:async';
 import 'package:chicken_farm/core/config/app_config.dart';
+import 'package:chicken_farm/core/utils/logger.dart';
 import 'package:dio/dio.dart';
 
 /// 基于 Dio 的服务检查工具(固定地址 + 防抖)
@@ -19,7 +20,6 @@ class ServiceChecker {
   }
 
   // 核心配置
-  static final String _fixedServiceUrl = AppConfig.baseUrl; // 固定检查地址
   static const int _timeoutMs = 3000; // 超时时间(3秒)
   static const int _debounceDurationMs = 5000; // 防抖间隔(5秒)
 
@@ -34,6 +34,7 @@ class ServiceChecker {
   /// 检查固定地址服务是否可用
   /// 返回:true=可用,false=不可用/超时/异常
   Future<bool> checkService() async {
+    final serviceUrl = AppConfig.baseUrl;
     // 1. 防抖逻辑:5秒内重复调用直接返回上次结果
     final now = DateTime.now();
     if (_lastCheckTime != null &&
@@ -54,7 +55,7 @@ class ServiceChecker {
     try {
       // Dio 发送 HEAD 请求(轻量,仅检查状态码)
       final response = await _dio.head(
-        _fixedServiceUrl,
+        serviceUrl,
         // 自定义响应拦截(可选)
         options: Options(
           validateStatus: (status) =>
@@ -70,6 +71,7 @@ class ServiceChecker {
       _updateDebounceData(now, result);
       completer.complete(result);
     } catch (e) {
+      logger.e('服务连接失败[$serviceUrl]: $e');
       // 捕获所有异常:超时、网络错误、状态码异常等
       _updateDebounceData(now, false);
       completer.complete(false);

+ 1 - 3
UI/CF.APP/chicken_farm/lib/main.dart

@@ -9,9 +9,7 @@ void main() async {
   WidgetsFlutterBinding.ensureInitialized();
   await AppConfig.init(); // 初始化配置
 
-  if (AppConfig.isOffline) {
-    await SqliteManager().init(dbName: 'breeding.db');
-  }
+  await SqliteManager().init(dbName: 'breeding.db');
 
   final container = ProviderContainer();
   // 初始化LoadingUtil

+ 0 - 0
UI/CF.APP/chicken_farm/lib/modes/page/page_model.dart → UI/CF.APP/chicken_farm/lib/modes/api/page_model.dart


+ 28 - 20
UI/CF.APP/chicken_farm/lib/modes/api/result_model.dart

@@ -2,10 +2,18 @@ class ResultModel {
   bool success;
   String message;
   dynamic data;
+  bool? isNetError;
+  bool? isAuthError;
+  bool? isOffline;
 
   ResultModel({this.success = true, this.message = "", this.data});
   ResultModel.success(this.message, {this.data}) : success = true;
   ResultModel.fail(this.message) : success = false;
+  ResultModel.isNetError(this.message) : success = false, isNetError = true;
+  ResultModel.isAuthError(this.message) : success = false, isAuthError = true;
+  ResultModel.successOffline(this.message) : success = true, isOffline = true;
+  ResultModel.failOffline(this.message) : success = false, isOffline = true;
+  ResultModel.offline() : success = false, isOffline = true, message = "";
 
   ResultModel.fromJson(Map<String, dynamic> json)
     : success = json['success'],
@@ -13,27 +21,27 @@ class ResultModel {
       data = json['data'];
 }
 
-class ApiOfflineModel extends ResultModel {
-  bool isOffline;
-  dynamic _data;
+// class ApiOfflineModel extends ResultModel {
+//   bool isOffline;
+//   dynamic _data;
 
-  @override
-  dynamic get data => _data;
+//   @override
+//   dynamic get data => _data;
 
-  @override
-  set data(dynamic value) {
-    _data = value;
-  }
+//   @override
+//   set data(dynamic value) {
+//     _data = value;
+//   }
 
-  ApiOfflineModel({this.isOffline = true});
-  ApiOfflineModel.onLine(this._data, {this.isOffline = false})
-    : super(data: _data);
+//   ApiOfflineModel({this.isOffline = true});
+//   ApiOfflineModel.onLine(this._data, {this.isOffline = false})
+//     : super(data: _data);
 
-  ResultModel convert() {
-    if (success) {
-      return ResultModel.success(isOffline ? "离线保存成功" : "保存成功", data: _data);
-    } else {
-      return ResultModel.fail(isOffline ? "离线保存失败:$message" : "保存失败:$message");
-    }
-  }
-}
+//   ResultModel convert() {
+//     if (success) {
+//       return ResultModel.success(isOffline ? "离线保存成功" : "保存成功", data: _data);
+//     } else {
+//       return ResultModel.fail(isOffline ? "离线保存失败:$message" : "保存失败:$message");
+//     }
+//   }
+// }

+ 6 - 8
UI/CF.APP/chicken_farm/lib/pages/breeding/batch_create_page.dart

@@ -341,14 +341,12 @@ class _BatchCreatePageState extends State<BatchCreatePage> {
 
   // 提交数据
   void _handleSubmit() {
-    final data = _rfids.map((rfid) {
-      return {
-        'rfid': rfid,
-        'batchNum': _batchNum,
-        'familyId': _familyId,
-        'date': DateTimeUtil.format(DateTime.now()),
-      };
-    }).toList();
+    final data = {
+      'rfids': _rfids,
+      'batchNum': _batchNum,
+      'familyId': _familyId,
+      'date': DateTimeUtil.format(DateTime.now()),
+    };
     apis.breeding.submitApi.bindChicken(data).then((res) {
       if (res.success) {
         if (res.message.isNotEmpty) {

+ 0 - 76
UI/CF.APP/chicken_farm/lib/pages/excel/export_data_page.dart

@@ -23,7 +23,6 @@ class _ExcelExportPageState extends State<ExcelExportPage> {
   @override
   void initState() {
     super.initState();
-    _insetTestData();
     _refreshExportOptions();
   }
 
@@ -97,81 +96,6 @@ class _ExcelExportPageState extends State<ExcelExportPage> {
     }
   }
 
-  Future<void> _insetTestData() async {
-    for (int i = 0; i < 2; i++) {
-      apis.breeding.submitApi
-          .bindChicken({
-            'rfid': 'test_$i',
-            'batchNum': '666',
-            'familyId': '888',
-            'date': '2025-12-11',
-          })
-          .then(
-            (r) => {
-              if (r.success)
-                {logger.d('submitApi bindChicken: success')}
-              else
-                {logger.d('submitApi bindChicken: fail $i ${r.message}')},
-            },
-          );
-      apis.breeding.submitApi
-          .cageChange({
-            'rfids': ['test_1$i', 'test_2$i', 'test_3$i'],
-            'sourceCage': '12333',
-            'targetCage': '23333',
-            'date': '2025-12-11',
-          })
-          .then(
-            (r) => {
-              if (r.success)
-                {logger.d('submitApi cageChange: success')}
-              else
-                {logger.d('submitApi cageChange: fail $i ${r.message}')},
-            },
-          );
-      apis.breeding.submitApi
-          .weight({'rfid': 'test_$i', 'weight': '1.23', 'date': '2025-12-11'})
-          .then(
-            (r) => {
-              if (r.success)
-                {logger.d('submitApi weight: success')}
-              else
-                {logger.d('submitApi weight: fail $i ${r.message}')},
-            },
-          );
-      apis.breeding.submitApi
-          .cull({
-            'rfid': 'test_$i',
-            'cullReason': '1',
-            'disposalMethod': '1',
-            'date': '2025-12-11',
-          })
-          .then(
-            (r) => {
-              if (r.success)
-                {logger.d('submitApi cull: success')}
-              else
-                {logger.d('submitApi cull: fail $i ${r.message}')},
-            },
-          );
-      apis.breeding.submitApi
-          .batchCull({
-            'rfids': ['test_1$i', 'test_2$i', 'test_3$i'],
-            'cullReason': '1',
-            'disposalMethod': '1',
-            'date': '2025-12-11',
-          })
-          .then(
-            (r) => {
-              if (r.success)
-                {logger.d('submitApi batchCull: success')}
-              else
-                {logger.d('submitApi batchCull: fail $i ${r.message}')},
-            },
-          );
-    }
-  }
-
   @override
   Widget build(BuildContext context) {
     return Scaffold(

+ 5 - 5
UI/CF.APP/chicken_farm/lib/pages/home/_profile/clear_cache_button.dart

@@ -2,6 +2,7 @@ import 'package:chicken_farm/core/utils/storage.dart';
 import 'package:chicken_farm/core/utils/toast.dart';
 import 'package:chicken_farm/stores/config_store.dart';
 import 'package:chicken_farm/stores/dict_stroe.dart';
+import 'package:chicken_farm/test/test.dart';
 import 'package:flutter/material.dart';
 
 class ClearCacheButton extends StatelessWidget {
@@ -13,6 +14,8 @@ class ClearCacheButton extends StatelessWidget {
       width: double.infinity,
       child: ElevatedButton.icon(
         onPressed: () {
+          Test().insetTestData();
+
           ToastUtil.confirm('确定要清除所有缓存吗?', () async {
             await StorageUtils.removeWithPrefix("vb_");
             ConfigStore().clearAll();
@@ -25,13 +28,10 @@ class ClearCacheButton extends StatelessWidget {
         icon: const Icon(Icons.cleaning_services),
         label: const Text('清除缓存'),
         style: ElevatedButton.styleFrom(
-          padding: const EdgeInsets.symmetric(
-            horizontal: 30,
-            vertical: 15,
-          ),
+          padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 15),
           textStyle: const TextStyle(fontSize: 16),
         ),
       ),
     );
   }
-}
+}

+ 8 - 10
UI/CF.APP/chicken_farm/lib/pages/home/_profile/upload_data_button.dart

@@ -1,4 +1,4 @@
-import 'package:chicken_farm/core/services/offline_storage_service.dart';
+import 'package:chicken_farm/core/services/breeding_data_service.dart';
 import 'package:chicken_farm/core/services/upload_service.dart';
 import 'package:chicken_farm/core/utils/logger.dart';
 import 'package:chicken_farm/core/utils/service_checker.dart';
@@ -16,9 +16,7 @@ class UploadDataButton extends StatefulWidget {
 
 class _UploadDataButtonState extends State<UploadDataButton> {
   Future<void> _handleGoToUpload(BuildContext context) async {
-    final pendingOps = await OfflineStorageService().getPendingOperations();
-    final pendingCount = pendingOps.length;
-    if (pendingCount <= 0) {
+    if (await BreedingDataService().queryTotalCount() <= 0) {
       ToastUtil.warning('没有待上传数据');
       return;
     }
@@ -46,11 +44,11 @@ class _UploadDataButtonState extends State<UploadDataButton> {
 
   @override
   Widget build(BuildContext context) {
-    return FutureBuilder<List<OfflineOperation>>(
-      future: OfflineStorageService().getPendingOperations(),
+    return FutureBuilder<int>(
+      future: BreedingDataService().queryTotalCount(),
       builder: (context, snapshot) {
-        final pendingCount = snapshot.data?.length ?? 0;
-        logger.d('带上传数据数量: $pendingCount');
+        final count = snapshot.data ?? 0;
+        logger.d('带上传数据数量: $count');
         // 数据加载成功时显示正常的上传按钮
         return SizedBox(
           width: double.infinity,
@@ -58,9 +56,9 @@ class _UploadDataButtonState extends State<UploadDataButton> {
             onPressed: () => _handleGoToUpload(context),
             icon: const Icon(Icons.upload),
             label: Text(
-              UploadService().isHideUpload && pendingCount > 0
+              UploadService().isHideUpload && count > 0
                   ? "查看上传进度"
-                  : '上传数据${pendingCount > 0 ? '($pendingCount 条待上传)' : ''}',
+                  : '上传数据${count > 0 ? '($count 条待上传)' : ''}',
             ),
             style: ElevatedButton.styleFrom(
               padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 15),

+ 14 - 3
UI/CF.APP/chicken_farm/lib/pages/upload/upload_page.dart

@@ -2,6 +2,7 @@ import 'package:chicken_farm/core/services/offline_storage_service.dart';
 import 'package:chicken_farm/core/services/upload_service.dart';
 import 'package:chicken_farm/core/utils/logger.dart';
 import 'package:chicken_farm/core/utils/service_checker.dart';
+import 'package:chicken_farm/routes/app_routes.dart';
 import 'package:flutter/material.dart';
 import 'package:go_router/go_router.dart';
 
@@ -137,10 +138,14 @@ class _UploadPageState extends State<UploadPage> with WidgetsBindingObserver {
           setState(() {
             _uploadedOperations = uploaded;
             _totalOperations = total;
-            // 进度更新时不改变状态码,保持uploading状态
           });
         }
         logger.d('上传进度 $t:$uploaded/$total');
+        if (status != '上传中...') {
+          ScaffoldMessenger.of(context).showSnackBar(
+            SnackBar(content: Text(status), backgroundColor: Colors.lightBlue),
+          );
+        }
       },
       onComplete: () {
         if (mounted) {
@@ -151,10 +156,16 @@ class _UploadPageState extends State<UploadPage> with WidgetsBindingObserver {
           });
         }
         logger.d('上传完成');
+        ScaffoldMessenger.of(context).showSnackBar(
+          SnackBar(content: Text('上传完成'), backgroundColor: Colors.green),
+        );
       },
-      onError: (message) {
+      onError: (message, {isAuthError = false}) {
         logger.e('上传出错:$message');
-
+        if (isAuthError == true) {
+          // 认证错误,跳转到登录页面
+          context.pushNamed(AppRouteNames.login);
+        }
         if (mounted) {
           setState(() {
             _isUploading = false;

+ 78 - 0
UI/CF.APP/chicken_farm/lib/test/test.dart

@@ -0,0 +1,78 @@
+import 'package:chicken_farm/apis/index.dart';
+import 'package:chicken_farm/core/utils/logger.dart';
+
+class Test {
+  Future<void> insetTestData() async {
+    for (int i = 0; i < 2; i++) {
+      apis.breeding.submitApi
+          .bindChicken({
+            'rfids': ['test_1$i', 'test_2$i', 'test_3$i'],
+            'batchNum': '666',
+            'familyId': '888',
+            'date': '2025-12-11',
+          })
+          .then(
+            (r) => {
+              if (r.success)
+                {logger.d('submitApi bindChicken: success')}
+              else
+                {logger.d('submitApi bindChicken: fail $i ${r.message}')},
+            },
+          );
+      apis.breeding.submitApi
+          .cageChange({
+            'rfids': ['test_1$i', 'test_2$i', 'test_3$i'],
+            'targetCage': '23333',
+            'date': '2025-12-11',
+          })
+          .then(
+            (r) => {
+              if (r.success)
+                {logger.d('submitApi cageChange: success')}
+              else
+                {logger.d('submitApi cageChange: fail $i ${r.message}')},
+            },
+          );
+      apis.breeding.submitApi
+          .weight({'rfid': 'test_$i', 'weight': '1.23', 'date': '2025-12-11'})
+          .then(
+            (r) => {
+              if (r.success)
+                {logger.d('submitApi weight: success')}
+              else
+                {logger.d('submitApi weight: fail $i ${r.message}')},
+            },
+          );
+      apis.breeding.submitApi
+          .cull({
+            'rfid': 'test_$i',
+            'cullReason': '1',
+            'disposalMethod': '1',
+            'date': '2025-12-11',
+          })
+          .then(
+            (r) => {
+              if (r.success)
+                {logger.d('submitApi cull: success')}
+              else
+                {logger.d('submitApi cull: fail $i ${r.message}')},
+            },
+          );
+      apis.breeding.submitApi
+          .batchCull({
+            'rfids': ['test_1$i', 'test_2$i', 'test_3$i'],
+            'cullReason': '1',
+            'disposalMethod': '1',
+            'date': '2025-12-11',
+          })
+          .then(
+            (r) => {
+              if (r.success)
+                {logger.d('submitApi batchCull: success')}
+              else
+                {logger.d('submitApi batchCull: fail $i ${r.message}')},
+            },
+          );
+    }
+  }
+}