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 { 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 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> 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> 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> 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> 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 _requireBaseUrl() async { final baseUrl = await _loadApiUrl() ?? ''; if (baseUrl.isEmpty) return null; return baseUrl; } BoxingActionResult _missingApiUrl() { return const BoxingActionResult.error( kind: BoxingActionErrorKind.missingApiUrl, message: missingApiUrlMessage, ); } BoxingActionResult _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; } }