refactor: extract API actions into BoxingApiActions with error handling
Centralize all API call logic (fetchBoxInfo, saveBoxRecord, updateBoxRecord, deleteBoxRecord) into BoxingApiActions class with unified error categorization (network, duplicate, missingApiUrl). Add helper methods to reduce repetitive error handling in boxing_page. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
150
lib/pages/boxing/boxing_api_actions.dart
Normal file
150
lib/pages/boxing/boxing_api_actions.dart
Normal file
@@ -0,0 +1,150 @@
|
||||
import 'package:pad_scanner/services/api_service.dart';
|
||||
import 'package:pad_scanner/services/app_config_service.dart';
|
||||
|
||||
enum BoxingActionErrorKind { missingApiUrl, network, duplicate, backend }
|
||||
|
||||
class BoxingActionResult<T> {
|
||||
final bool success;
|
||||
final T? data;
|
||||
final BoxingActionErrorKind? errorKind;
|
||||
final String? errorMessage;
|
||||
final int? boxNo;
|
||||
|
||||
const BoxingActionResult._({
|
||||
required this.success,
|
||||
this.data,
|
||||
this.errorKind,
|
||||
this.errorMessage,
|
||||
this.boxNo,
|
||||
});
|
||||
|
||||
const BoxingActionResult.ok(T data) : this._(success: true, data: data);
|
||||
|
||||
const BoxingActionResult.error({
|
||||
required BoxingActionErrorKind kind,
|
||||
required String message,
|
||||
int? boxNo,
|
||||
}) : this._(
|
||||
success: false,
|
||||
errorKind: kind,
|
||||
errorMessage: message,
|
||||
boxNo: boxNo,
|
||||
);
|
||||
}
|
||||
|
||||
typedef ApiUrlLoader = Future<String?> Function();
|
||||
|
||||
class BoxingApiActions {
|
||||
static const networkErrorMessage = '网络异常,请检查网络连接';
|
||||
static const missingApiUrlMessage = '未配置 API 地址,请前往设置';
|
||||
|
||||
final ApiService _apiService;
|
||||
final ApiUrlLoader _loadApiUrl;
|
||||
|
||||
BoxingApiActions({required ApiService apiService, ApiUrlLoader? loadApiUrl})
|
||||
: _apiService = apiService,
|
||||
_loadApiUrl =
|
||||
loadApiUrl ?? (() => AppConfigService().getString('api_url'));
|
||||
|
||||
Future<BoxingActionResult<BoxInfoResult>> fetchBoxInfo(String zongpai) async {
|
||||
final baseUrl = await _requireBaseUrl();
|
||||
if (baseUrl == null) return _missingApiUrl();
|
||||
|
||||
final result = await _apiService.fetchBoxInfo(
|
||||
baseUrl: baseUrl,
|
||||
zongpaiNo: zongpai,
|
||||
);
|
||||
if (result.success) return BoxingActionResult.ok(result);
|
||||
return BoxingActionResult.error(
|
||||
kind: _kindForMessage(result.errorMessage),
|
||||
message: result.errorMessage ?? '查询失败',
|
||||
);
|
||||
}
|
||||
|
||||
Future<BoxingActionResult<BoxSaveResult>> saveBoxRecord({
|
||||
required String zongpaiNo,
|
||||
required int boxNo,
|
||||
required int quantity,
|
||||
}) async {
|
||||
final baseUrl = await _requireBaseUrl();
|
||||
if (baseUrl == null) return _missingApiUrl();
|
||||
|
||||
final result = await _apiService.saveBoxRecord(
|
||||
baseUrl: baseUrl,
|
||||
zongpaiNo: zongpaiNo,
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
);
|
||||
return _saveResult(result, fallbackMessage: '提交失败');
|
||||
}
|
||||
|
||||
Future<BoxingActionResult<BoxSaveResult>> updateBoxRecord({
|
||||
required int boxItemId,
|
||||
required int boxNo,
|
||||
required int quantity,
|
||||
}) async {
|
||||
final baseUrl = await _requireBaseUrl();
|
||||
if (baseUrl == null) return _missingApiUrl();
|
||||
|
||||
final result = await _apiService.updateBoxRecord(
|
||||
baseUrl: baseUrl,
|
||||
boxItemId: boxItemId,
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
);
|
||||
return _saveResult(result, fallbackMessage: '修改失败');
|
||||
}
|
||||
|
||||
Future<BoxingActionResult<BoxDeleteResult>> deleteBoxRecord({
|
||||
required int boxItemId,
|
||||
}) async {
|
||||
final baseUrl = await _requireBaseUrl();
|
||||
if (baseUrl == null) return _missingApiUrl();
|
||||
|
||||
final result = await _apiService.deleteBoxRecord(
|
||||
baseUrl: baseUrl,
|
||||
boxItemId: boxItemId,
|
||||
);
|
||||
if (result.success) return BoxingActionResult.ok(result);
|
||||
return BoxingActionResult.error(
|
||||
kind: _kindForMessage(result.errorMessage),
|
||||
message: result.errorMessage ?? '删除失败',
|
||||
);
|
||||
}
|
||||
|
||||
Future<String?> _requireBaseUrl() async {
|
||||
final baseUrl = await _loadApiUrl() ?? '';
|
||||
if (baseUrl.isEmpty) return null;
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
BoxingActionResult<T> _missingApiUrl<T>() {
|
||||
return const BoxingActionResult.error(
|
||||
kind: BoxingActionErrorKind.missingApiUrl,
|
||||
message: missingApiUrlMessage,
|
||||
);
|
||||
}
|
||||
|
||||
BoxingActionResult<BoxSaveResult> _saveResult(
|
||||
BoxSaveResult result, {
|
||||
required String fallbackMessage,
|
||||
}) {
|
||||
if (result.success) return BoxingActionResult.ok(result);
|
||||
if (result.isDuplicate) {
|
||||
return BoxingActionResult.error(
|
||||
kind: BoxingActionErrorKind.duplicate,
|
||||
message: result.errorMessage ?? fallbackMessage,
|
||||
boxNo: result.boxNo,
|
||||
);
|
||||
}
|
||||
return BoxingActionResult.error(
|
||||
kind: _kindForMessage(result.errorMessage),
|
||||
message: result.errorMessage ?? fallbackMessage,
|
||||
);
|
||||
}
|
||||
|
||||
BoxingActionErrorKind _kindForMessage(String? message) {
|
||||
if (message == networkErrorMessage) return BoxingActionErrorKind.network;
|
||||
return BoxingActionErrorKind.backend;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user