import 'dart:convert'; import 'package:http/http.dart' as http; /// Result of a registration API call. class RegistrationResult { final bool success; final bool isDuplicate; final bool isAlreadyOffShelf; final bool isOffShelfSuccess; final String? errorMessage; final Map? duplicateInfo; final Map? conflictInfo; RegistrationResult({ required this.success, this.isDuplicate = false, this.isAlreadyOffShelf = false, this.isOffShelfSuccess = false, this.errorMessage, this.duplicateInfo, this.conflictInfo, }); factory RegistrationResult.ok() => RegistrationResult(success: true); factory RegistrationResult.offShelfSuccess() => RegistrationResult(success: true, isOffShelfSuccess: true); factory RegistrationResult.duplicate(Map info) => RegistrationResult( success: false, isDuplicate: true, duplicateInfo: info, conflictInfo: info, ); factory RegistrationResult.alreadyOffShelf(Map info) => RegistrationResult( success: false, isAlreadyOffShelf: true, errorMessage: info['message']?.toString() ?? '该总排号已下架至转运区域,不可重新上架', conflictInfo: info, ); factory RegistrationResult.error(String message) => RegistrationResult(success: false, errorMessage: message); } class PaichaOverviewItem { final String zongpaiNo; final String? workOrderNo; final int quantity; final String? locationCode; final String status; PaichaOverviewItem({ required this.zongpaiNo, this.workOrderNo, required this.quantity, this.locationCode, required this.status, }); factory PaichaOverviewItem.fromJson(Map json) { return PaichaOverviewItem( zongpaiNo: json['zongpai_no'] as String, workOrderNo: json['work_order_no']?.toString(), quantity: json['quantity'] as int? ?? 0, locationCode: json['location_code']?.toString(), status: json['status']?.toString() ?? 'not_shelved', ); } } class PaichaOverviewResult { final bool success; final bool notFound; final String? errorMessage; final String? paichaNo; final int totalCount; final List items; PaichaOverviewResult({ required this.success, this.notFound = false, this.errorMessage, this.paichaNo, this.totalCount = 0, this.items = const [], }); factory PaichaOverviewResult.ok(Map json) { final items = (json['items'] as List?) ?.map( (item) => PaichaOverviewItem.fromJson(item as Map), ) .toList() ?? []; return PaichaOverviewResult( success: true, paichaNo: json['paicha_no'] as String?, totalCount: json['total_count'] as int? ?? items.length, items: items, ); } factory PaichaOverviewResult.notFoundResult() => PaichaOverviewResult( success: false, notFound: true, errorMessage: '暂无排产信息', ); factory PaichaOverviewResult.error(String message) => PaichaOverviewResult(success: false, errorMessage: message); } // === 装箱模块数据类 === /// 待装箱附件 class PendingAccessory { final int accessoryId; final String accessoryType; final int quantity; final String? locationCode; PendingAccessory({ required this.accessoryId, required this.accessoryType, required this.quantity, this.locationCode, }); factory PendingAccessory.fromJson(Map json) { return PendingAccessory( accessoryId: json['accessory_id'] as int, accessoryType: json['accessory_type'] as String, quantity: json['quantity'] as int, locationCode: json['location_code']?.toString(), ); } } /// 箱号内单个总排号明细 class BoxItemData { final int? boxItemId; final String zongpaiNo; final String? workOrderNo; final int quantity; final int? totalQuantity; final String itemType; BoxItemData({ this.boxItemId, required this.zongpaiNo, this.workOrderNo, required this.quantity, this.totalQuantity, this.itemType = 'product', }); factory BoxItemData.fromJson(Map json) { return BoxItemData( boxItemId: json['box_item_id'] as int?, zongpaiNo: json['zongpai_no'] as String, workOrderNo: json['work_order_no']?.toString(), quantity: json['quantity'] as int, totalQuantity: json['total_quantity'] as int?, itemType: json['item_type'] as String? ?? 'product', ); } } /// 箱号明细 class BoxDetailData { final int boxNo; final List items; BoxDetailData({required this.boxNo, required this.items}); factory BoxDetailData.fromJson(Map json) { final items = (json['items'] as List?) ?.map((i) => BoxItemData.fromJson(i as Map)) .toList() ?? []; return BoxDetailData(boxNo: json['box_no'] as int, items: items); } } /// 当前总排号已分配的装箱明细 class CurrentZongpaiBoxData { final int boxItemId; final int boxNo; final int quantity; final String itemType; CurrentZongpaiBoxData({ required this.boxItemId, required this.boxNo, required this.quantity, this.itemType = 'product', }); factory CurrentZongpaiBoxData.fromJson(Map json) { return CurrentZongpaiBoxData( boxItemId: json['box_item_id'] as int, boxNo: json['box_no'] as int, quantity: json['quantity'] as int, itemType: json['item_type'] as String? ?? 'product', ); } } /// 装箱信息查询结果 class BoxInfoResult { final bool success; final String? errorMessage; final String? zongpaiNo; final String? paichanNo; final String? workOrderNo; final int? quantity; final List currentZongpaiBoxes; final List existingBoxes; final int maxBoxNo; final int suggestedBoxNo; final List pendingAccessories; BoxInfoResult({ required this.success, this.errorMessage, this.zongpaiNo, this.paichanNo, this.workOrderNo, this.quantity, this.currentZongpaiBoxes = const [], this.existingBoxes = const [], this.maxBoxNo = 0, this.suggestedBoxNo = 1, this.pendingAccessories = const [], }); factory BoxInfoResult.ok(Map json) { final boxes = (json['existing_boxes'] as List?) ?.map((b) => BoxDetailData.fromJson(b as Map)) .toList() ?? []; final currentBoxes = (json['current_zongpai_boxes'] as List?) ?.map( (b) => CurrentZongpaiBoxData.fromJson(b as Map), ) .toList() ?? []; final pendingAccessories = (json['pending_accessories'] as List?) ?.map((a) => PendingAccessory.fromJson(a as Map)) .toList() ?? []; return BoxInfoResult( success: true, zongpaiNo: json['zongpai_no'] as String?, paichanNo: json['paichan_no'] as String?, workOrderNo: json['work_order_no']?.toString(), quantity: json['quantity'] as int?, currentZongpaiBoxes: currentBoxes, existingBoxes: boxes, maxBoxNo: json['max_box_no'] as int? ?? 0, suggestedBoxNo: json['suggested_box_no'] as int? ?? 1, pendingAccessories: pendingAccessories, ); } factory BoxInfoResult.error(String message) { return BoxInfoResult(success: false, errorMessage: message); } } /// 装箱保存结果 class BoxSaveResult { final bool success; final bool isDuplicate; final String? errorMessage; final String? errorCode; final int? boxItemId; final String? paichanNo; final String? zongpaiNo; final int? boxNo; final int? quantity; BoxSaveResult({ required this.success, this.isDuplicate = false, this.errorMessage, this.errorCode, this.boxItemId, this.paichanNo, this.zongpaiNo, this.boxNo, this.quantity, }); factory BoxSaveResult.ok(Map json) { return BoxSaveResult( success: true, boxItemId: json['box_item_id'] as int?, paichanNo: json['paichan_no'] as String?, zongpaiNo: json['zongpai_no'] as String?, boxNo: json['box_no'] as int?, quantity: json['quantity'] as int?, ); } factory BoxSaveResult.duplicate(Map json) { return BoxSaveResult( success: false, isDuplicate: true, errorCode: json['error_code']?.toString(), errorMessage: json['message']?.toString(), paichanNo: json['paichan_no'] as String?, zongpaiNo: json['zongpai_no'] as String?, boxNo: json['box_no'] as int?, ); } factory BoxSaveResult.error(String message) { return BoxSaveResult(success: false, errorMessage: message); } } class BoxDeleteResult { final bool success; final String? errorMessage; BoxDeleteResult({required this.success, this.errorMessage}); } // === 附件模块数据类 === /// 工令号分组(工令号 → 总排号列表) class WorkOrderGroup { final String workOrderNo; final List zongpaiNos; WorkOrderGroup({required this.workOrderNo, required this.zongpaiNos}); factory WorkOrderGroup.fromJson(Map json) => WorkOrderGroup( workOrderNo: json['work_order_no'] as String, zongpaiNos: (json['zongpai_nos'] as List).map((e) => e as String).toList(), ); } /// 附件类型预设 class AccessoryTypeInfo { final int id; final String name; final int sortOrder; AccessoryTypeInfo({ required this.id, required this.name, required this.sortOrder, }); factory AccessoryTypeInfo.fromJson(Map json) => AccessoryTypeInfo( id: json['id'] as int, name: json['name'] as String, sortOrder: json['sort_order'] as int, ); } /// 附件记录 class AccessoryRecord { final int id; final String paichanNo; final String zongpaiNo; final String accessoryType; final int quantity; final String? locationCode; final bool isBoxed; final String createdAt; AccessoryRecord({ required this.id, required this.paichanNo, required this.zongpaiNo, required this.accessoryType, required this.quantity, this.locationCode, required this.isBoxed, required this.createdAt, }); factory AccessoryRecord.fromJson(Map json) => AccessoryRecord( id: json['id'] as int, paichanNo: json['paichan_no'] as String, zongpaiNo: json['zongpai_no'] as String, accessoryType: json['accessory_type'] as String, quantity: json['quantity'] as int, locationCode: json['location_code'] as String?, isBoxed: json['is_boxed'] as bool, createdAt: json['created_at'] as String, ); } // === 附件模块结果类 === class WorkOrderQueryResult { final bool success; final String? paichanNo; final List workOrders; final String? errorMessage; final String? errorCode; WorkOrderQueryResult({ required this.success, this.paichanNo, this.workOrders = const [], this.errorMessage, this.errorCode, }); } class AccessoryListResult { final bool success; final List items; final String? errorMessage; final String? errorCode; AccessoryListResult({ required this.success, this.items = const [], this.errorMessage, this.errorCode, }); } class AccessoryCreateResult { final bool success; final AccessoryRecord? record; final String? errorMessage; final String? errorCode; AccessoryCreateResult({ required this.success, this.record, this.errorMessage, this.errorCode, }); } class AccessoryUpdateResult { final bool success; final AccessoryRecord? record; final String? errorMessage; final String? errorCode; AccessoryUpdateResult({ required this.success, this.record, this.errorMessage, this.errorCode, }); } class AccessoryDeleteResult { final bool success; final String? errorMessage; final String? errorCode; AccessoryDeleteResult({ required this.success, this.errorMessage, this.errorCode, }); } class AccessoryTypeListResult { final bool success; final List types; final String? errorMessage; final String? errorCode; AccessoryTypeListResult({ required this.success, this.types = const [], this.errorMessage, this.errorCode, }); } class AccessoryTypeCreateResult { final bool success; final AccessoryTypeInfo? type; final String? errorMessage; final String? errorCode; AccessoryTypeCreateResult({ required this.success, this.type, this.errorMessage, this.errorCode, }); } class ApiService { final http.Client _client; final Duration timeout; ApiService({http.Client? client, this.timeout = const Duration(seconds: 5)}) : _client = client ?? http.Client(); /// Submit a shelf registration (上架登记). Future registerLocation({ required String baseUrl, required String zongpaiNo, required String locationCode, }) async { final uri = Uri.parse('$baseUrl/CargoTrace/location'); try { final response = await _client .post( uri, headers: {'Content-Type': 'application/json'}, body: jsonEncode({ 'zongpai_no': zongpaiNo, 'location_code': locationCode, }), ) .timeout(timeout); switch (response.statusCode) { case 200: final body = jsonDecode(response.body) as Map; if (body['previous_location'] != null) { return RegistrationResult.offShelfSuccess(); } return RegistrationResult.ok(); case 400: final body = jsonDecode(response.body) as Map; final msg = body['message']?.toString() ?? '请求参数错误'; return RegistrationResult.error(msg); case 409: final body = jsonDecode(response.body) as Map; final errorCode = body['error_code']?.toString(); if (errorCode == 'DUPLICATE_LOCATION') { return RegistrationResult.duplicate(body); } if (errorCode == 'ALREADY_OFF_SHELF') { return RegistrationResult.alreadyOffShelf(body); } final msg = body['message']?.toString() ?? '提交失败'; return RegistrationResult.error(msg); default: final body = jsonDecode(response.body) as Map; final msg = body['message'] ?? body['error'] ?? 'Unknown error (${response.statusCode})'; return RegistrationResult.error(msg.toString()); } } catch (e) { return RegistrationResult.error('网络异常,请检查网络连接'); } } Future fetchPaichaOverview({ required String baseUrl, required String zongpaiNo, }) async { final uri = Uri.parse( '$baseUrl/CargoTrace/location/paicha-overview', ).replace(queryParameters: {'zongpai_no': zongpaiNo}); try { final response = await _client .get(uri, headers: {'Content-Type': 'application/json'}) .timeout(timeout); switch (response.statusCode) { case 200: final body = jsonDecode(response.body) as Map; return PaichaOverviewResult.ok(body); case 400: return PaichaOverviewResult.error('无效的总排号格式'); case 404: return PaichaOverviewResult.notFoundResult(); default: return PaichaOverviewResult.error('加载失败,点击重试'); } } catch (e) { return PaichaOverviewResult.error('加载失败,点击重试'); } } /// 查询装箱信息 — GET /CargoTrace/box/info Future fetchBoxInfo({ required String baseUrl, required String zongpaiNo, }) async { final uri = Uri.parse( '$baseUrl/CargoTrace/box/info', ).replace(queryParameters: {'zongpai_no': zongpaiNo}); try { final response = await _client .get(uri, headers: {'Content-Type': 'application/json'}) .timeout(timeout); switch (response.statusCode) { case 200: final body = jsonDecode(response.body) as Map; return BoxInfoResult.ok(body); case 400: return BoxInfoResult.error('无效的总排号格式'); case 404: return BoxInfoResult.error('未找到该总排号对应的排产号信息'); default: return BoxInfoResult.error('查询失败 (${response.statusCode})'); } } catch (e) { return BoxInfoResult.error('网络异常,请检查网络连接'); } } /// 保存装箱记录 — POST /CargoTrace/box Future saveBoxRecord({ required String baseUrl, required String zongpaiNo, required int boxNo, required int quantity, String itemType = 'product', int? accessoryId, }) async { final uri = Uri.parse('$baseUrl/CargoTrace/box'); try { final bodyMap = { 'zongpai_no': zongpaiNo, 'box_no': boxNo, 'quantity': quantity, 'item_type': itemType, }; if (accessoryId != null) { bodyMap['accessory_id'] = accessoryId; } final response = await _client .post( uri, headers: {'Content-Type': 'application/json'}, body: jsonEncode(bodyMap), ) .timeout(timeout); switch (response.statusCode) { case 200: final body = jsonDecode(response.body) as Map; return BoxSaveResult.ok(body); case 400: final body = jsonDecode(response.body) as Map; final msg = body['message']?.toString() ?? '请求参数错误'; return BoxSaveResult.error(msg); case 409: final body = jsonDecode(response.body) as Map; return BoxSaveResult.duplicate(body); case 404: return BoxSaveResult.error('未找到该总排号对应的排产号信息'); default: return BoxSaveResult.error('提交失败 (${response.statusCode})'); } } catch (e) { return BoxSaveResult.error('网络异常,请检查网络连接'); } } /// 更新装箱明细 — PATCH /CargoTrace/box/{boxItemId} Future updateBoxRecord({ required String baseUrl, required int boxItemId, required int boxNo, required int quantity, }) async { final uri = Uri.parse('$baseUrl/CargoTrace/box/$boxItemId'); try { final response = await _client .patch( uri, headers: {'Content-Type': 'application/json'}, body: jsonEncode({'box_no': boxNo, 'quantity': quantity}), ) .timeout(timeout); switch (response.statusCode) { case 200: final body = jsonDecode(response.body) as Map; return BoxSaveResult.ok(body); case 400: final body = jsonDecode(response.body) as Map; final msg = body['message']?.toString() ?? '请求参数错误'; return BoxSaveResult.error(msg); case 409: final body = jsonDecode(response.body) as Map; return BoxSaveResult.duplicate(body); case 404: return BoxSaveResult.error('指定装箱明细不存在'); default: return BoxSaveResult.error('修改失败 (${response.statusCode})'); } } catch (e) { return BoxSaveResult.error('网络异常,请检查网络连接'); } } /// 删除装箱明细 — DELETE /CargoTrace/box/{boxItemId} Future deleteBoxRecord({ required String baseUrl, required int boxItemId, }) async { final uri = Uri.parse('$baseUrl/CargoTrace/box/$boxItemId'); try { final response = await _client .delete(uri, headers: {'Content-Type': 'application/json'}) .timeout(timeout); switch (response.statusCode) { case 200: return BoxDeleteResult(success: true); case 404: return BoxDeleteResult(success: false, errorMessage: '指定装箱明细不存在'); default: return BoxDeleteResult( success: false, errorMessage: '删除失败 (${response.statusCode})', ); } } catch (e) { return BoxDeleteResult(success: false, errorMessage: '网络异常,请检查网络连接'); } } // === 附件模块 API 方法 === /// 工令号查询 — GET /CargoTrace/accessory/work-orders Future queryWorkOrders({ required String baseUrl, required String paichanNo, }) async { final uri = Uri.parse( '$baseUrl/CargoTrace/accessory/work-orders', ).replace(queryParameters: {'paichan_no': paichanNo}); try { final response = await _client .get(uri, headers: {'Content-Type': 'application/json'}) .timeout(timeout); switch (response.statusCode) { case 200: final body = jsonDecode(response.body) as Map; final workOrders = (body['work_orders'] as List?) ?.map( (w) => WorkOrderGroup.fromJson(w as Map), ) .toList() ?? []; return WorkOrderQueryResult( success: true, paichanNo: body['paichan_no'] as String?, workOrders: workOrders, ); case 400: final body = jsonDecode(response.body) as Map; return WorkOrderQueryResult( success: false, errorMessage: body['message']?.toString() ?? '请求参数错误', errorCode: body['error_code']?.toString(), ); case 404: return WorkOrderQueryResult( success: false, errorMessage: '未找到该排产号对应的工令号信息', errorCode: 'NOT_FOUND', ); default: return WorkOrderQueryResult( success: false, errorMessage: '查询失败 (${response.statusCode})', ); } } catch (e) { return WorkOrderQueryResult(success: false, errorMessage: '网络异常,请检查网络连接'); } } /// 附件列表查询 — GET /CargoTrace/accessory Future listAccessories({ required String baseUrl, required String paichanNo, }) async { final uri = Uri.parse( '$baseUrl/CargoTrace/accessory', ).replace(queryParameters: {'paichan_no': paichanNo}); try { final response = await _client .get(uri, headers: {'Content-Type': 'application/json'}) .timeout(timeout); switch (response.statusCode) { case 200: final body = jsonDecode(response.body) as Map; final items = (body['items'] as List?) ?.map( (i) => AccessoryRecord.fromJson(i as Map), ) .toList() ?? []; return AccessoryListResult(success: true, items: items); case 400: final body = jsonDecode(response.body) as Map; return AccessoryListResult( success: false, errorMessage: body['message']?.toString() ?? '请求参数错误', errorCode: body['error_code']?.toString(), ); case 404: return AccessoryListResult( success: false, errorMessage: '未找到该排产号信息', errorCode: 'NOT_FOUND', ); default: return AccessoryListResult( success: false, errorMessage: '查询失败 (${response.statusCode})', ); } } catch (e) { return AccessoryListResult(success: false, errorMessage: '网络异常,请检查网络连接'); } } /// 附件创建(上架) — POST /CargoTrace/accessory Future createAccessory({ required String baseUrl, required String paichanNo, required String zongpaiNo, required String accessoryType, required int quantity, String? locationCode, }) async { final uri = Uri.parse('$baseUrl/CargoTrace/accessory'); try { final bodyMap = { 'paichan_no': paichanNo, 'zongpai_no': zongpaiNo, 'accessory_type': accessoryType, 'quantity': quantity, }; if (locationCode != null) { bodyMap['location_code'] = locationCode; } final response = await _client .post( uri, headers: {'Content-Type': 'application/json'}, body: jsonEncode(bodyMap), ) .timeout(timeout); switch (response.statusCode) { case 200: final body = jsonDecode(response.body) as Map; return AccessoryCreateResult( success: true, record: AccessoryRecord.fromJson(body), ); case 400: final body = jsonDecode(response.body) as Map; return AccessoryCreateResult( success: false, errorMessage: body['message']?.toString() ?? '请求参数错误', errorCode: body['error_code']?.toString(), ); case 404: return AccessoryCreateResult( success: false, errorMessage: '未找到该排产号对应的工令号信息', errorCode: 'NOT_FOUND', ); case 409: final body = jsonDecode(response.body) as Map; return AccessoryCreateResult( success: false, errorMessage: body['message']?.toString() ?? '冲突', errorCode: body['error_code']?.toString(), ); default: return AccessoryCreateResult( success: false, errorMessage: '创建失败 (${response.statusCode})', ); } } catch (e) { return AccessoryCreateResult( success: false, errorMessage: '网络异常,请检查网络连接', ); } } /// 附件更新 — PATCH /CargoTrace/accessory/{id} Future updateAccessory({ required String baseUrl, required int id, String? accessoryType, int? quantity, String? locationCode, }) async { final uri = Uri.parse('$baseUrl/CargoTrace/accessory/$id'); try { final bodyMap = {}; if (accessoryType != null) bodyMap['accessory_type'] = accessoryType; if (quantity != null) bodyMap['quantity'] = quantity; if (locationCode != null) bodyMap['location_code'] = locationCode; final response = await _client .patch( uri, headers: {'Content-Type': 'application/json'}, body: jsonEncode(bodyMap), ) .timeout(timeout); switch (response.statusCode) { case 200: final body = jsonDecode(response.body) as Map; return AccessoryUpdateResult( success: true, record: AccessoryRecord.fromJson(body), ); case 400: final body = jsonDecode(response.body) as Map; return AccessoryUpdateResult( success: false, errorMessage: body['message']?.toString() ?? '请求参数错误', errorCode: body['error_code']?.toString(), ); case 404: return AccessoryUpdateResult( success: false, errorMessage: '指定附件记录不存在', errorCode: 'NOT_FOUND', ); default: return AccessoryUpdateResult( success: false, errorMessage: '更新失败 (${response.statusCode})', ); } } catch (e) { return AccessoryUpdateResult( success: false, errorMessage: '网络异常,请检查网络连接', ); } } /// 附件删除 — DELETE /CargoTrace/accessory/{id} Future deleteAccessory({ required String baseUrl, required int id, }) async { final uri = Uri.parse('$baseUrl/CargoTrace/accessory/$id'); try { final response = await _client .delete(uri, headers: {'Content-Type': 'application/json'}) .timeout(timeout); switch (response.statusCode) { case 200: return AccessoryDeleteResult(success: true); case 404: return AccessoryDeleteResult( success: false, errorMessage: '指定附件记录不存在', errorCode: 'NOT_FOUND', ); default: return AccessoryDeleteResult( success: false, errorMessage: '删除失败 (${response.statusCode})', ); } } catch (e) { return AccessoryDeleteResult( success: false, errorMessage: '网络异常,请检查网络连接', ); } } /// 附件类型列表 — GET /CargoTrace/accessory-type Future listAccessoryTypes({ required String baseUrl, }) async { final uri = Uri.parse('$baseUrl/CargoTrace/accessory-type'); try { final response = await _client .get(uri, headers: {'Content-Type': 'application/json'}) .timeout(timeout); switch (response.statusCode) { case 200: final body = jsonDecode(response.body) as Map; final types = (body['types'] as List?) ?.map( (t) => AccessoryTypeInfo.fromJson(t as Map), ) .toList() ?? []; return AccessoryTypeListResult(success: true, types: types); case 400: final body = jsonDecode(response.body) as Map; return AccessoryTypeListResult( success: false, errorMessage: body['message']?.toString() ?? '请求参数错误', errorCode: body['error_code']?.toString(), ); default: return AccessoryTypeListResult( success: false, errorMessage: '查询失败 (${response.statusCode})', ); } } catch (e) { return AccessoryTypeListResult( success: false, errorMessage: '网络异常,请检查网络连接', ); } } /// 附件类型创建 — POST /CargoTrace/accessory-type Future createAccessoryType({ required String baseUrl, required String name, int sortOrder = 0, }) async { final uri = Uri.parse('$baseUrl/CargoTrace/accessory-type'); try { final response = await _client .post( uri, headers: {'Content-Type': 'application/json'}, body: jsonEncode({'name': name, 'sort_order': sortOrder}), ) .timeout(timeout); switch (response.statusCode) { case 200: final body = jsonDecode(response.body) as Map; return AccessoryTypeCreateResult( success: true, type: AccessoryTypeInfo.fromJson(body), ); case 400: final body = jsonDecode(response.body) as Map; return AccessoryTypeCreateResult( success: false, errorMessage: body['message']?.toString() ?? '请求参数错误', errorCode: body['error_code']?.toString(), ); case 409: final body = jsonDecode(response.body) as Map; return AccessoryTypeCreateResult( success: false, errorMessage: body['message']?.toString() ?? '附件类型已存在', errorCode: body['error_code']?.toString(), ); default: return AccessoryTypeCreateResult( success: false, errorMessage: '创建失败 (${response.statusCode})', ); } } catch (e) { return AccessoryTypeCreateResult( success: false, errorMessage: '网络异常,请检查网络连接', ); } } /// 附件类型删除 — DELETE /CargoTrace/accessory-type/{id} Future deleteAccessoryType({ required String baseUrl, required int id, }) async { final uri = Uri.parse('$baseUrl/CargoTrace/accessory-type/$id'); try { final response = await _client .delete(uri, headers: {'Content-Type': 'application/json'}) .timeout(timeout); switch (response.statusCode) { case 200: return AccessoryDeleteResult(success: true); case 404: return AccessoryDeleteResult( success: false, errorMessage: '指定附件类型不存在', errorCode: 'NOT_FOUND', ); case 409: final body = jsonDecode(response.body) as Map; return AccessoryDeleteResult( success: false, errorMessage: body['message']?.toString() ?? '该类型下存在附件记录,无法删除', errorCode: body['error_code']?.toString(), ); default: return AccessoryDeleteResult( success: false, errorMessage: '删除失败 (${response.statusCode})', ); } } catch (e) { return AccessoryDeleteResult( success: false, errorMessage: '网络异常,请检查网络连接', ); } } /// Test connectivity by making a HEAD request to the base URL. Future testConnection(String baseUrl) async { try { final uri = Uri.parse(baseUrl); final response = await _client.head(uri).timeout(timeout); return response.statusCode < 500; } catch (_) { return false; } } }