feat(mobile-app): 添加合同签署 API 详细调试日志
- 为 signContract、lateSignContract、markScrollComplete、acknowledgeContract 添加详细日志 - 记录请求参数(签名图片大小、IP、设备信息、位置) - 记录响应状态码和完整响应数据 - 修复响应解析 bug:正确从 response.data['data'] 提取任务数据 - 增强错误日志,捕获堆栈信息便于调试 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
846bf5b061
commit
accc043ff0
|
|
@ -403,7 +403,8 @@
|
||||||
"Bash(frontend/mobile-app/lib/features/contract_signing/ )",
|
"Bash(frontend/mobile-app/lib/features/contract_signing/ )",
|
||||||
"Bash(frontend/mobile-app/lib/features/home/presentation/pages/home_shell_page.dart )",
|
"Bash(frontend/mobile-app/lib/features/home/presentation/pages/home_shell_page.dart )",
|
||||||
"Bash(git branch:*)",
|
"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": [],
|
"deny": [],
|
||||||
"ask": []
|
"ask": []
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -257,20 +257,34 @@ class ContractSigningService {
|
||||||
Future<ContractSigningTask> markScrollComplete(String orderNo) async {
|
Future<ContractSigningTask> markScrollComplete(String orderNo) async {
|
||||||
try {
|
try {
|
||||||
debugPrint('[ContractSigningService] 标记已滚动到底部: $orderNo');
|
debugPrint('[ContractSigningService] 标记已滚动到底部: $orderNo');
|
||||||
|
debugPrint('[ContractSigningService] 请求 URL: /planting/contract-signing/tasks/$orderNo/scroll-complete');
|
||||||
|
|
||||||
final response = await _apiClient.post(
|
final response = await _apiClient.post(
|
||||||
'/planting/contract-signing/tasks/$orderNo/scroll-complete',
|
'/planting/contract-signing/tasks/$orderNo/scroll-complete',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
debugPrint('[ContractSigningService] 响应状态码: ${response.statusCode}');
|
||||||
|
debugPrint('[ContractSigningService] 响应数据: ${response.data}');
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final data = response.data as Map<String, dynamic>;
|
final responseData = response.data as Map<String, dynamic>;
|
||||||
debugPrint('[ContractSigningService] 滚动标记成功');
|
if (responseData['success'] == true && responseData['data'] != null) {
|
||||||
|
final data = responseData['data'] as Map<String, dynamic>;
|
||||||
|
debugPrint('[ContractSigningService] 滚动标记成功,任务状态: ${data['status']}');
|
||||||
return ContractSigningTask.fromJson(data);
|
return ContractSigningTask.fromJson(data);
|
||||||
}
|
}
|
||||||
|
// 兼容直接返回任务数据的情况
|
||||||
|
if (responseData['orderNo'] != null) {
|
||||||
|
debugPrint('[ContractSigningService] 滚动标记成功(直接返回数据)');
|
||||||
|
return ContractSigningTask.fromJson(responseData);
|
||||||
|
}
|
||||||
|
throw Exception('标记滚动失败: ${responseData['message'] ?? '响应格式错误'}');
|
||||||
|
}
|
||||||
|
|
||||||
throw Exception('标记滚动失败: ${response.statusCode}');
|
throw Exception('标记滚动失败: ${response.statusCode}');
|
||||||
} catch (e) {
|
} catch (e, stackTrace) {
|
||||||
debugPrint('[ContractSigningService] 标记滚动失败: $e');
|
debugPrint('[ContractSigningService] 标记滚动失败: $e');
|
||||||
|
debugPrint('[ContractSigningService] 堆栈: $stackTrace');
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -279,20 +293,34 @@ class ContractSigningService {
|
||||||
Future<ContractSigningTask> acknowledgeContract(String orderNo) async {
|
Future<ContractSigningTask> acknowledgeContract(String orderNo) async {
|
||||||
try {
|
try {
|
||||||
debugPrint('[ContractSigningService] 确认法律效力: $orderNo');
|
debugPrint('[ContractSigningService] 确认法律效力: $orderNo');
|
||||||
|
debugPrint('[ContractSigningService] 请求 URL: /planting/contract-signing/tasks/$orderNo/acknowledge');
|
||||||
|
|
||||||
final response = await _apiClient.post(
|
final response = await _apiClient.post(
|
||||||
'/planting/contract-signing/tasks/$orderNo/acknowledge',
|
'/planting/contract-signing/tasks/$orderNo/acknowledge',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
debugPrint('[ContractSigningService] 响应状态码: ${response.statusCode}');
|
||||||
|
debugPrint('[ContractSigningService] 响应数据: ${response.data}');
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final data = response.data as Map<String, dynamic>;
|
final responseData = response.data as Map<String, dynamic>;
|
||||||
debugPrint('[ContractSigningService] 确认法律效力成功');
|
if (responseData['success'] == true && responseData['data'] != null) {
|
||||||
|
final data = responseData['data'] as Map<String, dynamic>;
|
||||||
|
debugPrint('[ContractSigningService] 确认法律效力成功,任务状态: ${data['status']}');
|
||||||
return ContractSigningTask.fromJson(data);
|
return ContractSigningTask.fromJson(data);
|
||||||
}
|
}
|
||||||
|
// 兼容直接返回任务数据的情况
|
||||||
|
if (responseData['orderNo'] != null) {
|
||||||
|
debugPrint('[ContractSigningService] 确认法律效力成功(直接返回数据)');
|
||||||
|
return ContractSigningTask.fromJson(responseData);
|
||||||
|
}
|
||||||
|
throw Exception('确认法律效力失败: ${responseData['message'] ?? '响应格式错误'}');
|
||||||
|
}
|
||||||
|
|
||||||
throw Exception('确认法律效力失败: ${response.statusCode}');
|
throw Exception('确认法律效力失败: ${response.statusCode}');
|
||||||
} catch (e) {
|
} catch (e, stackTrace) {
|
||||||
debugPrint('[ContractSigningService] 确认法律效力失败: $e');
|
debugPrint('[ContractSigningService] 确认法律效力失败: $e');
|
||||||
|
debugPrint('[ContractSigningService] 堆栈: $stackTrace');
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -309,31 +337,51 @@ class ContractSigningService {
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
debugPrint('[ContractSigningService] 签署合同: $orderNo');
|
debugPrint('[ContractSigningService] 签署合同: $orderNo');
|
||||||
|
debugPrint('[ContractSigningService] 签名图片大小: ${signatureImage.length} bytes');
|
||||||
|
debugPrint('[ContractSigningService] IP: $ipAddress, 设备: $deviceInfo');
|
||||||
|
debugPrint('[ContractSigningService] 位置: lat=$latitude, lng=$longitude');
|
||||||
|
|
||||||
// 将签名图片转为 base64
|
// 将签名图片转为 base64
|
||||||
final signatureBase64 = base64Encode(signatureImage);
|
final signatureBase64 = base64Encode(signatureImage);
|
||||||
|
debugPrint('[ContractSigningService] Base64 长度: ${signatureBase64.length}');
|
||||||
|
|
||||||
final response = await _apiClient.post(
|
final requestData = {
|
||||||
'/planting/contract-signing/tasks/$orderNo/sign',
|
|
||||||
data: {
|
|
||||||
'signatureImage': signatureBase64,
|
'signatureImage': signatureBase64,
|
||||||
'ipAddress': ipAddress,
|
'ipAddress': ipAddress,
|
||||||
'deviceInfo': deviceInfo,
|
'deviceInfo': deviceInfo,
|
||||||
'userAgent': userAgent,
|
'userAgent': userAgent,
|
||||||
'latitude': latitude,
|
'latitude': latitude,
|
||||||
'longitude': longitude,
|
'longitude': longitude,
|
||||||
},
|
};
|
||||||
|
debugPrint('[ContractSigningService] 请求 URL: /planting/contract-signing/tasks/$orderNo/sign');
|
||||||
|
|
||||||
|
final response = await _apiClient.post(
|
||||||
|
'/planting/contract-signing/tasks/$orderNo/sign',
|
||||||
|
data: requestData,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
debugPrint('[ContractSigningService] 响应状态码: ${response.statusCode}');
|
||||||
|
debugPrint('[ContractSigningService] 响应数据: ${response.data}');
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final data = response.data as Map<String, dynamic>;
|
final responseData = response.data as Map<String, dynamic>;
|
||||||
debugPrint('[ContractSigningService] 签署成功');
|
if (responseData['success'] == true && responseData['data'] != null) {
|
||||||
|
final data = responseData['data'] as Map<String, dynamic>;
|
||||||
|
debugPrint('[ContractSigningService] 签署成功,任务状态: ${data['status']}');
|
||||||
return ContractSigningTask.fromJson(data);
|
return ContractSigningTask.fromJson(data);
|
||||||
}
|
}
|
||||||
|
// 兼容直接返回任务数据的情况
|
||||||
|
if (responseData['orderNo'] != null) {
|
||||||
|
debugPrint('[ContractSigningService] 签署成功(直接返回数据)');
|
||||||
|
return ContractSigningTask.fromJson(responseData);
|
||||||
|
}
|
||||||
|
throw Exception('签署合同失败: ${responseData['message'] ?? '响应格式错误'}');
|
||||||
|
}
|
||||||
|
|
||||||
throw Exception('签署合同失败: ${response.statusCode}');
|
throw Exception('签署合同失败: ${response.statusCode}');
|
||||||
} catch (e) {
|
} catch (e, stackTrace) {
|
||||||
debugPrint('[ContractSigningService] 签署合同失败: $e');
|
debugPrint('[ContractSigningService] 签署合同失败: $e');
|
||||||
|
debugPrint('[ContractSigningService] 堆栈: $stackTrace');
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -350,30 +398,50 @@ class ContractSigningService {
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
debugPrint('[ContractSigningService] 补签合同: $orderNo');
|
debugPrint('[ContractSigningService] 补签合同: $orderNo');
|
||||||
|
debugPrint('[ContractSigningService] 签名图片大小: ${signatureImage.length} bytes');
|
||||||
|
debugPrint('[ContractSigningService] IP: $ipAddress, 设备: $deviceInfo');
|
||||||
|
debugPrint('[ContractSigningService] 位置: lat=$latitude, lng=$longitude');
|
||||||
|
|
||||||
final signatureBase64 = base64Encode(signatureImage);
|
final signatureBase64 = base64Encode(signatureImage);
|
||||||
|
debugPrint('[ContractSigningService] Base64 长度: ${signatureBase64.length}');
|
||||||
|
|
||||||
final response = await _apiClient.post(
|
final requestData = {
|
||||||
'/planting/contract-signing/tasks/$orderNo/late-sign',
|
|
||||||
data: {
|
|
||||||
'signatureImage': signatureBase64,
|
'signatureImage': signatureBase64,
|
||||||
'ipAddress': ipAddress,
|
'ipAddress': ipAddress,
|
||||||
'deviceInfo': deviceInfo,
|
'deviceInfo': deviceInfo,
|
||||||
'userAgent': userAgent,
|
'userAgent': userAgent,
|
||||||
'latitude': latitude,
|
'latitude': latitude,
|
||||||
'longitude': longitude,
|
'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: requestData,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
debugPrint('[ContractSigningService] 响应状态码: ${response.statusCode}');
|
||||||
|
debugPrint('[ContractSigningService] 响应数据: ${response.data}');
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final data = response.data as Map<String, dynamic>;
|
final responseData = response.data as Map<String, dynamic>;
|
||||||
debugPrint('[ContractSigningService] 补签成功');
|
if (responseData['success'] == true && responseData['data'] != null) {
|
||||||
|
final data = responseData['data'] as Map<String, dynamic>;
|
||||||
|
debugPrint('[ContractSigningService] 补签成功,任务状态: ${data['status']}');
|
||||||
return ContractSigningTask.fromJson(data);
|
return ContractSigningTask.fromJson(data);
|
||||||
}
|
}
|
||||||
|
// 兼容直接返回任务数据的情况
|
||||||
|
if (responseData['orderNo'] != null) {
|
||||||
|
debugPrint('[ContractSigningService] 补签成功(直接返回数据)');
|
||||||
|
return ContractSigningTask.fromJson(responseData);
|
||||||
|
}
|
||||||
|
throw Exception('补签合同失败: ${responseData['message'] ?? '响应格式错误'}');
|
||||||
|
}
|
||||||
|
|
||||||
throw Exception('补签合同失败: ${response.statusCode}');
|
throw Exception('补签合同失败: ${response.statusCode}');
|
||||||
} catch (e) {
|
} catch (e, stackTrace) {
|
||||||
debugPrint('[ContractSigningService] 补签合同失败: $e');
|
debugPrint('[ContractSigningService] 补签合同失败: $e');
|
||||||
|
debugPrint('[ContractSigningService] 堆栈: $stackTrace');
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import connectivity_plus
|
||||||
import device_info_plus
|
import device_info_plus
|
||||||
import file_selector_macos
|
import file_selector_macos
|
||||||
import flutter_secure_storage_macos
|
import flutter_secure_storage_macos
|
||||||
|
import geolocator_apple
|
||||||
import local_auth_darwin
|
import local_auth_darwin
|
||||||
import mobile_scanner
|
import mobile_scanner
|
||||||
import package_info_plus
|
import package_info_plus
|
||||||
|
|
@ -24,6 +25,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||||
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||||
|
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
|
||||||
LocalAuthPlugin.register(with: registry.registrar(forPlugin: "LocalAuthPlugin"))
|
LocalAuthPlugin.register(with: registry.registrar(forPlugin: "LocalAuthPlugin"))
|
||||||
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
|
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
|
||||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
||||||
#include <file_selector_windows/file_selector_windows.h>
|
#include <file_selector_windows/file_selector_windows.h>
|
||||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||||
|
#include <geolocator_windows/geolocator_windows.h>
|
||||||
#include <local_auth_windows/local_auth_plugin.h>
|
#include <local_auth_windows/local_auth_plugin.h>
|
||||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||||
#include <sentry_flutter/sentry_flutter_plugin.h>
|
#include <sentry_flutter/sentry_flutter_plugin.h>
|
||||||
|
|
@ -22,6 +23,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||||
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||||
|
GeolocatorWindowsRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("GeolocatorWindows"));
|
||||||
LocalAuthPluginRegisterWithRegistrar(
|
LocalAuthPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("LocalAuthPlugin"));
|
registry->GetRegistrarForPlugin("LocalAuthPlugin"));
|
||||||
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
connectivity_plus
|
connectivity_plus
|
||||||
file_selector_windows
|
file_selector_windows
|
||||||
flutter_secure_storage_windows
|
flutter_secure_storage_windows
|
||||||
|
geolocator_windows
|
||||||
local_auth_windows
|
local_auth_windows
|
||||||
permission_handler_windows
|
permission_handler_windows
|
||||||
sentry_flutter
|
sentry_flutter
|
||||||
|
|
|
||||||
Binary file not shown.
Loading…
Reference in New Issue