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((ref) => BillApi(ref.watch(dioProvider))); class BillApi { final Dio _dio; BillApi(this._dio); /// 账单列表,可选日期区间(yyyy-MM-dd)。 /// 后端返回裸数组,无分页。 Future> getBills({String? startDate, String? endDate}) async { try { final params = {}; 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 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 createBill(BillRequest bill) async { try { await _dio.post('/bills', data: bill.toJson()); } on DioException catch (e) { throw _unwrap(e); } } /// 批量创建账单(OCR 直接存储) Future createBillsBatch(List 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 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 deleteBill(int id) async { try { await _dio.delete('/bills/$id'); } on DioException catch (e) { throw _unwrap(e); } } /// 兼容裸数组与 {code,data:[...]} 两种形态 List> _parseList(Object? data) { if (data is List) { return data.whereType>().toList(); } if (data is Map && data['data'] is List) { return (data['data'] as List).whereType>().toList(); } throw ApiException(-1, '响应格式异常'); } Map _parseObject(Object? data) { if (data is Map) return data; if (data is Map && data['data'] is Map) return Map.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, '请求失败'); } }