Files
ai-contract-platform/mobile/lib/features/contracts/application/contract_lifecycle_controller.dart
T
yuqei e376c1c436 feat: close mobile Android polish backlog — risk actions, lifecycle, file service, and screen polish
Review detail:
- Add review risk actions with status tracking and skip/apply support
- Pull-to-refresh and auto-navigate after review submit
- Report download, open, share, and copy-link actions

Contract lifecycle:
- Add contract lifecycle models and controller for mobile
- Version chain display and lifecycle status transitions
- Contract result screen now shows lifecycle tasks

File service:
- New unified file_service.dart for file management

API client:
- Add review risk actions and contract lifecycle endpoints

Screen polish:
- Login screen visual polish with brand assets
- Dashboard redesign with updated layout
- Profile screen updates
- Review entry/history screen improvements
- Theme and brand asset updates

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 00:06:38 +08:00

120 lines
3.2 KiB
Dart

import 'package:flutter/foundation.dart';
import '../../../shared/network/api_client.dart';
import '../../../shared/storage/session_store.dart';
import '../domain/contract_lifecycle_models.dart';
class ContractLifecycleController {
ContractLifecycleController({ApiClient? apiClient})
: _apiClient = apiClient ?? const ApiClient();
final ApiClient _apiClient;
final ValueNotifier<bool> isLoading = ValueNotifier<bool>(false);
final ValueNotifier<String?> errorMessage = ValueNotifier<String?>(null);
final ValueNotifier<ContractLifecycleTaskListResponse?> tasks =
ValueNotifier<ContractLifecycleTaskListResponse?>(null);
Future<void> loadTasks({
required String contractId,
String? statusValue,
String? taskType,
}) async {
final session = SessionStore.current;
if (session == null || !session.isAuthenticated) {
errorMessage.value = '请先登录';
return;
}
isLoading.value = true;
errorMessage.value = null;
try {
final response = await _apiClient.listContractLifecycleTasks(
accessToken: session.accessToken,
contractId: contractId,
statusValue: statusValue,
taskType: taskType,
);
tasks.value = response;
} on ApiException catch (error) {
if (error.isUnauthorized) {
await SessionStore.clear();
}
errorMessage.value = error.message;
} catch (_) {
errorMessage.value = '获取合同任务列表失败,请稍后重试';
} finally {
isLoading.value = false;
}
}
Future<void> updateTask({
required String contractId,
required String taskId,
String? taskType,
String? title,
String? description,
String? priority,
String? statusValue,
String? dueAt,
String? evidenceFileId,
String? evidenceNote,
}) async {
final session = SessionStore.current;
if (session == null || !session.isAuthenticated) {
errorMessage.value = '请先登录';
return;
}
isLoading.value = true;
errorMessage.value = null;
try {
await _apiClient.updateContractLifecycleTask(
accessToken: session.accessToken,
contractId: contractId,
taskId: taskId,
taskType: taskType,
title: title,
description: description,
priority: priority,
statusValue: statusValue,
dueAt: dueAt,
evidenceFileId: evidenceFileId,
evidenceNote: evidenceNote,
);
// Fire-and-forget refresh
_refreshTasks(contractId);
} on ApiException catch (error) {
errorMessage.value = error.message;
} catch (_) {
errorMessage.value = '更新合同任务失败,请稍后重试';
} finally {
isLoading.value = false;
}
}
Future<void> _refreshTasks(String contractId) async {
final session = SessionStore.current;
if (session == null || !session.isAuthenticated) {
return;
}
try {
final response = await _apiClient.listContractLifecycleTasks(
accessToken: session.accessToken,
contractId: contractId,
);
tasks.value = response;
} catch (_) {
// Non-blocking refresh failure
}
}
void dispose() {
isLoading.dispose();
errorMessage.dispose();
tasks.dispose();
}
}