diff --git a/.claude/settings.local.json b/.claude/settings.local.json index dbce671f..fa7c1c48 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -403,7 +403,8 @@ "Bash(frontend/mobile-app/lib/features/contract_signing/ )", "Bash(frontend/mobile-app/lib/features/home/presentation/pages/home_shell_page.dart )", "Bash(git branch:*)", - "Bash(echo \"docker exec rwa-planting-service npx prisma db execute --stdin <<< \"\"SELECT template_id, version, title, is_active FROM contract_templates;\"\"\")" + "Bash(echo \"docker exec rwa-planting-service npx prisma db execute --stdin <<< \"\"SELECT template_id, version, title, is_active FROM contract_templates;\"\"\")", + "Bash(npm uninstall:*)" ], "deny": [], "ask": [] diff --git a/STKAITI.TTF b/STKAITI.TTF new file mode 100644 index 00000000..50441161 Binary files /dev/null and b/STKAITI.TTF differ diff --git a/frontend/mobile-app/lib/core/services/contract_signing_service.dart b/frontend/mobile-app/lib/core/services/contract_signing_service.dart index 14f5a5aa..5f80fa8d 100644 --- a/frontend/mobile-app/lib/core/services/contract_signing_service.dart +++ b/frontend/mobile-app/lib/core/services/contract_signing_service.dart @@ -257,20 +257,34 @@ class ContractSigningService { Future markScrollComplete(String orderNo) async { try { debugPrint('[ContractSigningService] 标记已滚动到底部: $orderNo'); + debugPrint('[ContractSigningService] 请求 URL: /planting/contract-signing/tasks/$orderNo/scroll-complete'); final response = await _apiClient.post( '/planting/contract-signing/tasks/$orderNo/scroll-complete', ); + debugPrint('[ContractSigningService] 响应状态码: ${response.statusCode}'); + debugPrint('[ContractSigningService] 响应数据: ${response.data}'); + if (response.statusCode == 200) { - final data = response.data as Map; - debugPrint('[ContractSigningService] 滚动标记成功'); - return ContractSigningTask.fromJson(data); + final responseData = response.data as Map; + if (responseData['success'] == true && responseData['data'] != null) { + final data = responseData['data'] as Map; + debugPrint('[ContractSigningService] 滚动标记成功,任务状态: ${data['status']}'); + return ContractSigningTask.fromJson(data); + } + // 兼容直接返回任务数据的情况 + if (responseData['orderNo'] != null) { + debugPrint('[ContractSigningService] 滚动标记成功(直接返回数据)'); + return ContractSigningTask.fromJson(responseData); + } + throw Exception('标记滚动失败: ${responseData['message'] ?? '响应格式错误'}'); } throw Exception('标记滚动失败: ${response.statusCode}'); - } catch (e) { + } catch (e, stackTrace) { debugPrint('[ContractSigningService] 标记滚动失败: $e'); + debugPrint('[ContractSigningService] 堆栈: $stackTrace'); rethrow; } } @@ -279,20 +293,34 @@ class ContractSigningService { Future acknowledgeContract(String orderNo) async { try { debugPrint('[ContractSigningService] 确认法律效力: $orderNo'); + debugPrint('[ContractSigningService] 请求 URL: /planting/contract-signing/tasks/$orderNo/acknowledge'); final response = await _apiClient.post( '/planting/contract-signing/tasks/$orderNo/acknowledge', ); + debugPrint('[ContractSigningService] 响应状态码: ${response.statusCode}'); + debugPrint('[ContractSigningService] 响应数据: ${response.data}'); + if (response.statusCode == 200) { - final data = response.data as Map; - debugPrint('[ContractSigningService] 确认法律效力成功'); - return ContractSigningTask.fromJson(data); + final responseData = response.data as Map; + if (responseData['success'] == true && responseData['data'] != null) { + final data = responseData['data'] as Map; + debugPrint('[ContractSigningService] 确认法律效力成功,任务状态: ${data['status']}'); + return ContractSigningTask.fromJson(data); + } + // 兼容直接返回任务数据的情况 + if (responseData['orderNo'] != null) { + debugPrint('[ContractSigningService] 确认法律效力成功(直接返回数据)'); + return ContractSigningTask.fromJson(responseData); + } + throw Exception('确认法律效力失败: ${responseData['message'] ?? '响应格式错误'}'); } throw Exception('确认法律效力失败: ${response.statusCode}'); - } catch (e) { + } catch (e, stackTrace) { debugPrint('[ContractSigningService] 确认法律效力失败: $e'); + debugPrint('[ContractSigningService] 堆栈: $stackTrace'); rethrow; } } @@ -309,31 +337,51 @@ class ContractSigningService { }) async { try { debugPrint('[ContractSigningService] 签署合同: $orderNo'); + debugPrint('[ContractSigningService] 签名图片大小: ${signatureImage.length} bytes'); + debugPrint('[ContractSigningService] IP: $ipAddress, 设备: $deviceInfo'); + debugPrint('[ContractSigningService] 位置: lat=$latitude, lng=$longitude'); // 将签名图片转为 base64 final signatureBase64 = base64Encode(signatureImage); + debugPrint('[ContractSigningService] Base64 长度: ${signatureBase64.length}'); + + final requestData = { + 'signatureImage': signatureBase64, + 'ipAddress': ipAddress, + 'deviceInfo': deviceInfo, + 'userAgent': userAgent, + 'latitude': latitude, + 'longitude': longitude, + }; + debugPrint('[ContractSigningService] 请求 URL: /planting/contract-signing/tasks/$orderNo/sign'); final response = await _apiClient.post( '/planting/contract-signing/tasks/$orderNo/sign', - data: { - 'signatureImage': signatureBase64, - 'ipAddress': ipAddress, - 'deviceInfo': deviceInfo, - 'userAgent': userAgent, - 'latitude': latitude, - 'longitude': longitude, - }, + data: requestData, ); + debugPrint('[ContractSigningService] 响应状态码: ${response.statusCode}'); + debugPrint('[ContractSigningService] 响应数据: ${response.data}'); + if (response.statusCode == 200) { - final data = response.data as Map; - debugPrint('[ContractSigningService] 签署成功'); - return ContractSigningTask.fromJson(data); + final responseData = response.data as Map; + if (responseData['success'] == true && responseData['data'] != null) { + final data = responseData['data'] as Map; + debugPrint('[ContractSigningService] 签署成功,任务状态: ${data['status']}'); + return ContractSigningTask.fromJson(data); + } + // 兼容直接返回任务数据的情况 + if (responseData['orderNo'] != null) { + debugPrint('[ContractSigningService] 签署成功(直接返回数据)'); + return ContractSigningTask.fromJson(responseData); + } + throw Exception('签署合同失败: ${responseData['message'] ?? '响应格式错误'}'); } throw Exception('签署合同失败: ${response.statusCode}'); - } catch (e) { + } catch (e, stackTrace) { debugPrint('[ContractSigningService] 签署合同失败: $e'); + debugPrint('[ContractSigningService] 堆栈: $stackTrace'); rethrow; } } @@ -350,30 +398,50 @@ class ContractSigningService { }) async { try { debugPrint('[ContractSigningService] 补签合同: $orderNo'); + debugPrint('[ContractSigningService] 签名图片大小: ${signatureImage.length} bytes'); + debugPrint('[ContractSigningService] IP: $ipAddress, 设备: $deviceInfo'); + debugPrint('[ContractSigningService] 位置: lat=$latitude, lng=$longitude'); final signatureBase64 = base64Encode(signatureImage); + debugPrint('[ContractSigningService] Base64 长度: ${signatureBase64.length}'); + + final requestData = { + 'signatureImage': signatureBase64, + 'ipAddress': ipAddress, + 'deviceInfo': deviceInfo, + 'userAgent': userAgent, + 'latitude': latitude, + 'longitude': longitude, + }; + debugPrint('[ContractSigningService] 请求 URL: /planting/contract-signing/tasks/$orderNo/late-sign'); final response = await _apiClient.post( '/planting/contract-signing/tasks/$orderNo/late-sign', - data: { - 'signatureImage': signatureBase64, - 'ipAddress': ipAddress, - 'deviceInfo': deviceInfo, - 'userAgent': userAgent, - 'latitude': latitude, - 'longitude': longitude, - }, + data: requestData, ); + debugPrint('[ContractSigningService] 响应状态码: ${response.statusCode}'); + debugPrint('[ContractSigningService] 响应数据: ${response.data}'); + if (response.statusCode == 200) { - final data = response.data as Map; - debugPrint('[ContractSigningService] 补签成功'); - return ContractSigningTask.fromJson(data); + final responseData = response.data as Map; + if (responseData['success'] == true && responseData['data'] != null) { + final data = responseData['data'] as Map; + debugPrint('[ContractSigningService] 补签成功,任务状态: ${data['status']}'); + return ContractSigningTask.fromJson(data); + } + // 兼容直接返回任务数据的情况 + if (responseData['orderNo'] != null) { + debugPrint('[ContractSigningService] 补签成功(直接返回数据)'); + return ContractSigningTask.fromJson(responseData); + } + throw Exception('补签合同失败: ${responseData['message'] ?? '响应格式错误'}'); } throw Exception('补签合同失败: ${response.statusCode}'); - } catch (e) { + } catch (e, stackTrace) { debugPrint('[ContractSigningService] 补签合同失败: $e'); + debugPrint('[ContractSigningService] 堆栈: $stackTrace'); rethrow; } } diff --git a/frontend/mobile-app/macos/Flutter/GeneratedPluginRegistrant.swift b/frontend/mobile-app/macos/Flutter/GeneratedPluginRegistrant.swift index 8111a6a0..ace84ae7 100644 --- a/frontend/mobile-app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/frontend/mobile-app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -9,6 +9,7 @@ import connectivity_plus import device_info_plus import file_selector_macos import flutter_secure_storage_macos +import geolocator_apple import local_auth_darwin import mobile_scanner import package_info_plus @@ -24,6 +25,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) + GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) LocalAuthPlugin.register(with: registry.registrar(forPlugin: "LocalAuthPlugin")) MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) diff --git a/frontend/mobile-app/windows/flutter/generated_plugin_registrant.cc b/frontend/mobile-app/windows/flutter/generated_plugin_registrant.cc index 6ea4aa7b..f583544a 100644 --- a/frontend/mobile-app/windows/flutter/generated_plugin_registrant.cc +++ b/frontend/mobile-app/windows/flutter/generated_plugin_registrant.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -22,6 +23,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("FileSelectorWindows")); FlutterSecureStorageWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); + GeolocatorWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("GeolocatorWindows")); LocalAuthPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("LocalAuthPlugin")); PermissionHandlerWindowsPluginRegisterWithRegistrar( diff --git a/frontend/mobile-app/windows/flutter/generated_plugins.cmake b/frontend/mobile-app/windows/flutter/generated_plugins.cmake index 434e0737..2c1d7009 100644 --- a/frontend/mobile-app/windows/flutter/generated_plugins.cmake +++ b/frontend/mobile-app/windows/flutter/generated_plugins.cmake @@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST connectivity_plus file_selector_windows flutter_secure_storage_windows + geolocator_windows local_auth_windows permission_handler_windows sentry_flutter diff --git a/榴莲树认种权益协议.pdf b/榴莲树认种权益协议.pdf new file mode 100644 index 00000000..0c369945 Binary files /dev/null and b/榴莲树认种权益协议.pdf differ