Files
Aq-Accounting-Flutter/lib/api/bill_api.dart
T
2026-09-21 17:37:53 +08:00

105 lines
3.1 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:dio/dio.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../core/network/api_exception.dart';
import '../core/network/dio_client.dart';
import '../models/bill.dart';
import '../models/bill_request.dart';
final billApiProvider = Provider<BillApi>((ref) => BillApi(ref.watch(dioProvider)));
class BillApi {
final Dio _dio;
BillApi(this._dio);
/// 账单列表,可选日期区间(yyyy-MM-dd)。
/// 后端返回裸数组,无分页。
Future<List<Bill>> getBills({String? startDate, String? endDate}) async {
try {
final params = <String, dynamic>{};
if (startDate != null) params['startDate'] = startDate;
if (endDate != null) params['endDate'] = endDate;
final res = await _dio.get('/bills', queryParameters: params);
return _parseList(res.data).map(Bill.fromJson).toList();
} on DioException catch (e) {
throw _unwrap(e);
}
}
/// 账单详情
Future<Bill> getBill(int id) async {
try {
final res = await _dio.get('/bills/$id');
return Bill.fromJson(_parseObject(res.data));
} on DioException catch (e) {
throw _unwrap(e);
}
}
/// 创建账单
Future<void> createBill(BillRequest bill) async {
try {
await _dio.post('/bills', data: bill.toJson());
} on DioException catch (e) {
throw _unwrap(e);
}
}
/// 批量创建账单(OCR 直接存储)
Future<void> createBillsBatch(List<BillRequest> bills) async {
try {
await _dio.post('/bills/batch', data: {
'bills': bills.map((b) => b.toJson()).toList(),
});
} on DioException catch (e) {
throw _unwrap(e);
}
}
/// 更新账单
///
/// 注意:后端对 description 为 null 会覆盖成 null,所以必须回传全部字段
/// (BillRequest.toJson 不含 imageUrl,后端按「null 保留原值」处理,正好保留原图)。
Future<Bill> updateBill(int id, BillRequest bill) async {
try {
final res = await _dio.put('/bills/$id', data: bill.toJson());
return Bill.fromJson(_parseObject(res.data));
} on DioException catch (e) {
throw _unwrap(e);
}
}
/// 删除账单(后端为物理删除,不可恢复)
Future<void> deleteBill(int id) async {
try {
await _dio.delete('/bills/$id');
} on DioException catch (e) {
throw _unwrap(e);
}
}
/// 兼容裸数组与 {code,data:[...]} 两种形态
List<Map<String, dynamic>> _parseList(Object? data) {
if (data is List) {
return data.whereType<Map<String, dynamic>>().toList();
}
if (data is Map && data['data'] is List) {
return (data['data'] as List).whereType<Map<String, dynamic>>().toList();
}
throw ApiException(-1, '响应格式异常');
}
Map<String, dynamic> _parseObject(Object? data) {
if (data is Map<String, dynamic>) return data;
if (data is Map && data['data'] is Map) return Map<String, dynamic>.from(data['data'] as Map);
throw ApiException(-1, '响应格式异常');
}
Never _unwrap(DioException e) {
final err = e.error;
if (err is ApiException) throw err;
throw ApiException(-1, '请求失败');
}
}