feat: improve one-to-many boxing workflow

This commit is contained in:
Misaka_Company
2026-05-13 11:22:49 +08:00
parent e4a8816b98
commit b975b15ba7
2 changed files with 462 additions and 49 deletions

View File

@@ -32,11 +32,13 @@ class RegistrationResult {
/// 箱号内单个总排号明细
class BoxItemData {
final int? boxItemId;
final String zongpaiNo;
final String? workOrderNo;
final int quantity;
BoxItemData({
this.boxItemId,
required this.zongpaiNo,
this.workOrderNo,
required this.quantity,
@@ -44,6 +46,7 @@ class BoxItemData {
factory BoxItemData.fromJson(Map<String, dynamic> 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,
@@ -68,6 +71,27 @@ class BoxDetailData {
}
}
/// 当前总排号已分配的装箱明细
class CurrentZongpaiBoxData {
final int boxItemId;
final int boxNo;
final int quantity;
CurrentZongpaiBoxData({
required this.boxItemId,
required this.boxNo,
required this.quantity,
});
factory CurrentZongpaiBoxData.fromJson(Map<String, dynamic> json) {
return CurrentZongpaiBoxData(
boxItemId: json['box_item_id'] as int,
boxNo: json['box_no'] as int,
quantity: json['quantity'] as int,
);
}
}
/// 装箱信息查询结果
class BoxInfoResult {
final bool success;
@@ -76,6 +100,7 @@ class BoxInfoResult {
final String? paichanNo;
final String? workOrderNo;
final int? quantity;
final List<CurrentZongpaiBoxData> currentZongpaiBoxes;
final List<BoxDetailData> existingBoxes;
final int maxBoxNo;
final int suggestedBoxNo;
@@ -87,6 +112,7 @@ class BoxInfoResult {
this.paichanNo,
this.workOrderNo,
this.quantity,
this.currentZongpaiBoxes = const [],
this.existingBoxes = const [],
this.maxBoxNo = 0,
this.suggestedBoxNo = 1,
@@ -98,12 +124,20 @@ class BoxInfoResult {
?.map((b) => BoxDetailData.fromJson(b as Map<String, dynamic>))
.toList() ??
[];
final currentBoxes =
(json['current_zongpai_boxes'] as List<dynamic>?)
?.map(
(b) => CurrentZongpaiBoxData.fromJson(b as Map<String, dynamic>),
)
.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,
@@ -120,22 +154,28 @@ class BoxSaveResult {
final bool success;
final bool isDuplicate;
final String? errorMessage;
final int? boxItemId;
final String? paichanNo;
final int? boxNo;
final int? quantity;
BoxSaveResult({
required this.success,
this.isDuplicate = false,
this.errorMessage,
this.boxItemId,
this.paichanNo,
this.boxNo,
this.quantity,
});
factory BoxSaveResult.ok(Map<String, dynamic> json) {
return BoxSaveResult(
success: true,
boxItemId: json['box_item_id'] as int?,
paichanNo: json['paichan_no'] as String?,
boxNo: json['box_no'] as int?,
quantity: json['quantity'] as int?,
);
}
@@ -153,6 +193,13 @@ class BoxSaveResult {
}
}
class BoxDeleteResult {
final bool success;
final String? errorMessage;
BoxDeleteResult({required this.success, this.errorMessage});
}
class ApiService {
final http.Client _client;
final Duration timeout;
@@ -275,6 +322,75 @@ class ApiService {
}
}
/// 更新装箱明细 — PATCH /CargoTrace/box/{boxItemId}
Future<BoxSaveResult> 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,
'box_mode': 'one-to-many',
}),
)
.timeout(timeout);
switch (response.statusCode) {
case 200:
final body = jsonDecode(response.body) as Map<String, dynamic>;
return BoxSaveResult.ok(body);
case 400:
final body = jsonDecode(response.body) as Map<String, dynamic>;
final msg = body['message']?.toString() ?? '请求参数错误';
return BoxSaveResult.error(msg);
case 409:
final body = jsonDecode(response.body) as Map<String, dynamic>;
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<BoxDeleteResult> 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: '网络异常,请检查网络连接');
}
}
/// Test connectivity by making a HEAD request to the base URL.
Future<bool> testConnection(String baseUrl) async {
try {