feat: add Flutter mobile clients and staff delivery API
This commit is contained in:
166
apps/service_app/lib/data/repositories/service_repository.dart
Normal file
166
apps/service_app/lib/data/repositories/service_repository.dart
Normal file
@@ -0,0 +1,166 @@
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
import '../../domain/models/service_models.dart';
|
||||
import '../services/api_client.dart';
|
||||
import '../services/location_service.dart';
|
||||
|
||||
class ServiceRepository {
|
||||
ServiceRepository(this._api, this._location);
|
||||
|
||||
static const root = '/heqi/client/v1/staff';
|
||||
final ApiClient _api;
|
||||
final LocationService _location;
|
||||
|
||||
Future<StaffProfile> profile() async =>
|
||||
StaffProfile.fromJson(jsonMap(await _api.get('$root/auth/profile')));
|
||||
|
||||
Future<PreflightResult> preflight() async =>
|
||||
PreflightResult.fromJson(jsonMap(await _api.get('$root/preflight')));
|
||||
|
||||
Future<Map<String, Object?>> wallet() async => jsonMap(await _api.get('$root/wallet'));
|
||||
|
||||
Future<void> attendance(String action, String deviceIdentity) async {
|
||||
final location = await _location.current();
|
||||
await _api.post(
|
||||
'$root/attendance',
|
||||
body: {
|
||||
'action': action,
|
||||
'occurred_at': location.occurredAt.toUtc().toIso8601String(),
|
||||
'longitude': location.longitude,
|
||||
'latitude': location.latitude,
|
||||
'device_identity': deviceIdentity,
|
||||
'request_no': const Uuid().v7(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<WorkItem>> tasks(String roleCode) async {
|
||||
if (roleCode == 'delivery') {
|
||||
return jsonList(
|
||||
await _api.get('$root/delivery/orders'),
|
||||
).map(WorkItem.delivery).toList(growable: false);
|
||||
}
|
||||
return jsonList(
|
||||
await _api.get('$root/tickets'),
|
||||
).map(WorkItem.ticket).toList(growable: false);
|
||||
}
|
||||
|
||||
Future<WorkItem> deliveryDetail(String identity) async {
|
||||
final details = jsonMap(await _api.get('$root/delivery/orders/$identity'));
|
||||
return WorkItem.delivery(jsonMap(details['order']));
|
||||
}
|
||||
|
||||
Future<WorkItem> ticketDetail(String identity) async =>
|
||||
WorkItem.ticket(jsonMap(await _api.get('$root/tickets/$identity')));
|
||||
|
||||
Future<void> start(WorkItem item, String roleCode) async {
|
||||
final path = roleCode == 'delivery'
|
||||
? '$root/delivery/orders/${item.identity}/start'
|
||||
: '$root/tickets/${item.identity}/start';
|
||||
await _api.post(path, body: {'reason': '工作人员开始执行'});
|
||||
}
|
||||
|
||||
Future<void> exception(WorkItem item, String roleCode, String reason) async {
|
||||
final path = roleCode == 'delivery'
|
||||
? '$root/delivery/orders/${item.identity}/exception'
|
||||
: '$root/tickets/${item.identity}/exception';
|
||||
await _api.post(path, body: {'reason': reason});
|
||||
}
|
||||
|
||||
Future<void> recover(WorkItem item, String roleCode, String reason) async {
|
||||
final path = roleCode == 'delivery'
|
||||
? '$root/delivery/orders/${item.identity}/recover'
|
||||
: '$root/tickets/${item.identity}/recover';
|
||||
await _api.post(path, body: {'reason': reason});
|
||||
}
|
||||
|
||||
Future<void> appendCurrentTrack(String identity) async {
|
||||
final point = await _location.current();
|
||||
await _api.post(
|
||||
'$root/delivery/orders/$identity/tracks',
|
||||
body: {
|
||||
'points': [_pointJson(point)],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> arrive(String identity) async {
|
||||
final point = await _location.current();
|
||||
await _api.post('$root/delivery/orders/$identity/arrive', body: _pointJson(point));
|
||||
}
|
||||
|
||||
Future<void> submitDeliveryReceipt({
|
||||
required String identity,
|
||||
required String recipientName,
|
||||
required String recipientPhone,
|
||||
required String proofFile,
|
||||
}) async {
|
||||
final proofUri = await _api.upload(proofFile);
|
||||
await _api.post(
|
||||
'$root/delivery/orders/$identity/submit-receipt',
|
||||
body: {
|
||||
'request_no': const Uuid().v7(),
|
||||
'confirm_type': 'signature',
|
||||
'recipient_name': recipientName,
|
||||
'recipient_phone': recipientPhone,
|
||||
'proof_uri': proofUri,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> submitTicketResult({
|
||||
required String identity,
|
||||
required String result,
|
||||
required String conclusion,
|
||||
required List<EvidenceInput> evidence,
|
||||
}) async {
|
||||
final location = await _location.current();
|
||||
final uploaded = <Map<String, Object?>>[];
|
||||
for (final item in evidence) {
|
||||
final uri = await _api.upload(
|
||||
item.filePath,
|
||||
contentType: item.mediaType == 'video' ? 'video/mp4' : 'image/jpeg',
|
||||
);
|
||||
uploaded.add({
|
||||
'evidence_type': item.evidenceType,
|
||||
'media_type': item.mediaType,
|
||||
'file_uri': uri,
|
||||
'captured_at': item.capturedAt.toUtc().toIso8601String(),
|
||||
'longitude': location.longitude,
|
||||
'latitude': location.latitude,
|
||||
'request_no': item.requestNo,
|
||||
});
|
||||
}
|
||||
await _api.post(
|
||||
'$root/tickets/$identity/submit-result',
|
||||
body: {'result': result, 'conclusion': conclusion, 'evidences': uploaded},
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, Object?> _pointJson(LocationPoint point) => {
|
||||
'request_no': const Uuid().v7(),
|
||||
'longitude': point.longitude,
|
||||
'latitude': point.latitude,
|
||||
'occurred_at': point.occurredAt.toUtc().toIso8601String(),
|
||||
'source': 'gps',
|
||||
'accuracy': point.accuracy,
|
||||
'speed': '',
|
||||
'direction': '',
|
||||
};
|
||||
}
|
||||
|
||||
class EvidenceInput {
|
||||
const EvidenceInput({
|
||||
required this.evidenceType,
|
||||
required this.mediaType,
|
||||
required this.filePath,
|
||||
required this.capturedAt,
|
||||
required this.requestNo,
|
||||
});
|
||||
|
||||
final String evidenceType;
|
||||
final String mediaType;
|
||||
final String filePath;
|
||||
final DateTime capturedAt;
|
||||
final String requestNo;
|
||||
}
|
||||
Reference in New Issue
Block a user