174 lines
7.0 KiB
Dart
174 lines
7.0 KiB
Dart
// lib/services/submission_ftp_service.dart
|
|
|
|
import 'dart:io';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'dart:convert'; // Added for jsonEncode
|
|
import 'package:path/path.dart' as p; // Added for basename
|
|
|
|
import 'package:environment_monitoring_app/services/user_preferences_service.dart';
|
|
import 'package:environment_monitoring_app/services/ftp_service.dart';
|
|
import 'package:environment_monitoring_app/services/retry_service.dart';
|
|
// Import necessary services and models if needed for queueFtpTasksForSkippedAttempt
|
|
import 'package:environment_monitoring_app/services/zipping_service.dart';
|
|
import 'package:environment_monitoring_app/services/api_service.dart'; // For DatabaseHelper
|
|
|
|
/// A generic, reusable service for handling the FTP submission process.
|
|
/// It respects user preferences for enabled destinations for any given module.
|
|
class SubmissionFtpService {
|
|
final UserPreferencesService _userPreferencesService = UserPreferencesService();
|
|
final FtpService _ftpService = FtpService();
|
|
final RetryService _retryService = RetryService();
|
|
// Add ZippingService and DatabaseHelper if queueFtpTasksForSkippedAttempt needs them
|
|
final ZippingService _zippingService = ZippingService();
|
|
final DatabaseHelper _dbHelper = DatabaseHelper();
|
|
|
|
|
|
/// Submits a file to all enabled FTP destinations for a given module.
|
|
///
|
|
/// This method works differently from the API service. It attempts to upload
|
|
/// to ALL enabled destinations. It returns a summary of success/failure for each.
|
|
/// If any upload fails, it is queued for individual retry.
|
|
/// The overall result is considered successful if there are no hard errors
|
|
/// during the process, even if some uploads are queued.
|
|
Future<Map<String, dynamic>> submit({
|
|
required String moduleName,
|
|
required File fileToUpload,
|
|
required String remotePath,
|
|
}) async {
|
|
final destinations = await _userPreferencesService.getEnabledFtpConfigsForModule(moduleName);
|
|
|
|
if (destinations.isEmpty) {
|
|
debugPrint("SubmissionFtpService: No enabled FTP destinations for module '$moduleName'. Skipping.");
|
|
// Return success with a specific status indicating no config
|
|
return {
|
|
'success': true, // Process succeeded because there was nothing to do
|
|
'message': 'No FTP destinations enabled for this module.',
|
|
'statuses': [{'status': 'Not Configured', 'message': 'No destinations enabled.', 'success': true}]
|
|
};
|
|
}
|
|
|
|
final List<Map<String, dynamic>> statuses = [];
|
|
bool allSucceededOrNotConfigured = true; // Track if all attempts either succeeded or weren't configured
|
|
|
|
for (final dest in destinations) {
|
|
final configName = dest['config_name'] as String? ?? 'Unknown FTP';
|
|
final int? configId = dest['ftp_config_id'] as int?; // Get the config ID
|
|
|
|
// Skip if config ID is missing (should not happen with DB data)
|
|
if (configId == null) {
|
|
debugPrint("SubmissionFtpService: Skipping destination '$configName' due to missing config ID.");
|
|
statuses.add({
|
|
'config_name': configName,
|
|
'status': 'Error',
|
|
'success': false,
|
|
'message': 'Configuration ID missing.',
|
|
});
|
|
allSucceededOrNotConfigured = false;
|
|
continue;
|
|
}
|
|
|
|
debugPrint("SubmissionFtpService: Attempting to upload to '$configName' (ID: $configId)");
|
|
|
|
final result = await _ftpService.uploadFile(
|
|
config: dest,
|
|
fileToUpload: fileToUpload,
|
|
remotePath: remotePath,
|
|
);
|
|
|
|
statuses.add({
|
|
'config_name': configName,
|
|
'ftp_config_id': configId, // Include ID in status
|
|
'success': result['success'],
|
|
'message': result['message'],
|
|
'status': result['success'] ? 'Success' : 'Failed', // Add status text
|
|
});
|
|
|
|
if (result['success'] != true) {
|
|
allSucceededOrNotConfigured = false;
|
|
// If an individual upload fails, queue it for manual retry.
|
|
debugPrint("SubmissionFtpService: Upload to '$configName' (ID: $configId) failed. Queuing for retry.");
|
|
// --- START FIX: Add ftpConfigId ---
|
|
await _retryService.addFtpToQueue(
|
|
localFilePath: fileToUpload.path,
|
|
remotePath: remotePath,
|
|
ftpConfigId: configId, // Pass the specific config ID
|
|
);
|
|
// --- END FIX ---
|
|
}
|
|
}
|
|
|
|
if (allSucceededOrNotConfigured) {
|
|
return {
|
|
'success': true,
|
|
'message': 'File successfully uploaded to all enabled FTP destinations.',
|
|
'statuses': statuses,
|
|
};
|
|
} else {
|
|
return {
|
|
'success': true, // The process itself succeeded (attempted all), even if some uploads were queued.
|
|
'message': 'One or more FTP uploads failed and have been queued for retry.',
|
|
'statuses': statuses,
|
|
};
|
|
}
|
|
}
|
|
|
|
/// Manually queues FTP tasks when the initial FTP attempt is skipped (e.g., due to session expiry).
|
|
Future<void> queueFtpTasksForSkippedAttempt({
|
|
required String moduleName,
|
|
required Map<String, dynamic> dataJson, // The data model converted to JSON (toDbJson)
|
|
required Map<String, File> imageFiles,
|
|
required String baseFileName, // Base name for zip files
|
|
}) async {
|
|
debugPrint("Manually queuing FTP tasks for skipped attempt (Module: $moduleName).");
|
|
final ftpConfigs = await _dbHelper.loadFtpConfigs() ?? [];
|
|
if (ftpConfigs.isEmpty) {
|
|
debugPrint("Cannot queue skipped FTP tasks: No FTP configurations found.");
|
|
return;
|
|
}
|
|
|
|
// 1. Create Data ZIP (in temp directory)
|
|
final dataZip = await _zippingService.createDataZip(
|
|
// Adapt jsonDataMap based on module if needed, using dataJson
|
|
jsonDataMap: {'db.json': jsonEncode(dataJson)}, // Default, adjust per module if needed
|
|
baseFileName: baseFileName,
|
|
destinationDir: null, // Save to temp dir
|
|
);
|
|
|
|
// 2. Create Image ZIP (in temp directory)
|
|
File? imageZip;
|
|
if (imageFiles.isNotEmpty) {
|
|
imageZip = await _zippingService.createImageZip(
|
|
imageFiles: imageFiles.values.toList(),
|
|
baseFileName: baseFileName,
|
|
destinationDir: null, // Save to temp dir
|
|
);
|
|
}
|
|
|
|
// 3. Queue uploads for each config
|
|
for (final config in ftpConfigs) {
|
|
final configId = config['ftp_config_id'];
|
|
if (configId != null) {
|
|
// Queue data zip upload
|
|
if (dataZip != null) {
|
|
await _retryService.addFtpToQueue(
|
|
localFilePath: dataZip.path,
|
|
remotePath: '/${p.basename(dataZip.path)}',
|
|
ftpConfigId: configId
|
|
);
|
|
debugPrint("Queued skipped data ZIP upload for FTP config ID $configId");
|
|
}
|
|
// Queue image zip upload
|
|
if (imageZip != null) {
|
|
await _retryService.addFtpToQueue(
|
|
localFilePath: imageZip.path,
|
|
remotePath: '/${p.basename(imageZip.path)}',
|
|
ftpConfigId: configId
|
|
);
|
|
debugPrint("Queued skipped image ZIP upload for FTP config ID $configId");
|
|
}
|
|
}
|
|
}
|
|
// Temporary ZIP files will be cleaned up by OS eventually, or handled by retry logic upon success/failure.
|
|
}
|
|
|
|
} |