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:
hailin 2025-12-25 03:14:10 -08:00
parent 846bf5b061
commit accc043ff0
7 changed files with 108 additions and 33 deletions

View File

@ -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": []

BIN
STKAITI.TTF Normal file

Binary file not shown.

View File

@ -257,20 +257,34 @@ class ContractSigningService {
Future<ContractSigningTask> 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<String, dynamic>;
debugPrint('[ContractSigningService] 滚动标记成功');
final responseData = response.data as Map<String, dynamic>;
if (responseData['success'] == true && responseData['data'] != null) {
final data = responseData['data'] as Map<String, dynamic>;
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<ContractSigningTask> 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<String, dynamic>;
debugPrint('[ContractSigningService] 确认法律效力成功');
final responseData = response.data as Map<String, dynamic>;
if (responseData['success'] == true && responseData['data'] != null) {
final data = responseData['data'] as Map<String, dynamic>;
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 response = await _apiClient.post(
'/planting/contract-signing/tasks/$orderNo/sign',
data: {
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: requestData,
);
debugPrint('[ContractSigningService] 响应状态码: ${response.statusCode}');
debugPrint('[ContractSigningService] 响应数据: ${response.data}');
if (response.statusCode == 200) {
final data = response.data as Map<String, dynamic>;
debugPrint('[ContractSigningService] 签署成功');
final responseData = response.data as Map<String, dynamic>;
if (responseData['success'] == true && responseData['data'] != null) {
final data = responseData['data'] as Map<String, dynamic>;
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 response = await _apiClient.post(
'/planting/contract-signing/tasks/$orderNo/late-sign',
data: {
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: requestData,
);
debugPrint('[ContractSigningService] 响应状态码: ${response.statusCode}');
debugPrint('[ContractSigningService] 响应数据: ${response.data}');
if (response.statusCode == 200) {
final data = response.data as Map<String, dynamic>;
debugPrint('[ContractSigningService] 补签成功');
final responseData = response.data as Map<String, dynamic>;
if (responseData['success'] == true && responseData['data'] != null) {
final data = responseData['data'] as Map<String, dynamic>;
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;
}
}

View File

@ -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"))

View File

@ -9,6 +9,7 @@
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <file_selector_windows/file_selector_windows.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 <permission_handler_windows/permission_handler_windows_plugin.h>
#include <sentry_flutter/sentry_flutter_plugin.h>
@ -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(

View File

@ -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

Binary file not shown.