Files
pj_supermarket_cloud/lib/common/api/hard_api.dart
T
2026-10-22 19:08:46 +08:00

587 lines
21 KiB
Dart

import 'dart:io';
import 'package:dio/dio.dart' as dio;
import 'package:pj_supermarket_cloud/common/index.dart';
abstract class HardApi {
/// 按终端订单号或服务端订单 ID 对账识别证据状态;后台任务失败不弹业务 Toast。
static Future<ServiceOrderEvidenceResolve?> resolveOrderEvidence({
required String hardId,
String? androidOrderCode,
String? orderId,
}) async {
final res = await HttpManage().dio.get(
'/hard/hard-order-evidence/ad/resolve',
queryParameters: {
if (androidOrderCode != null) 'androidOrderCode': androidOrderCode,
if (orderId != null) 'orderId': orderId,
},
options: dio.Options(
headers: {'DEVICE-ID': hardId},
extra: {HttpManage.silentRequestKey: true},
),
);
if (res.data == null) return null;
return ServiceOrderEvidenceResolve.fromJson(res.data);
}
/// 提交识别元数据、触发参考图复制或报告本地图片永久缺失。
static Future<ServiceOrderEvidenceUploadResult?> submitOrderEvidence({
required String hardId,
required OrderEvidenceRequest request,
}) async {
final res = await HttpManage().dio.post(
'/hard/hard-order-evidence/ad/submit',
data: request.toJson(),
options: dio.Options(
headers: {'DEVICE-ID': hardId},
extra: {HttpManage.silentRequestKey: true},
),
);
if (res.data == null) return null;
return ServiceOrderEvidenceUploadResult.fromJson(res.data);
}
/// 使用统一 Dio 鉴权和租户头上传现场图片,multipart 文件名不包含终端完整路径。
static Future<ServiceOrderEvidenceUploadResult?> uploadOrderEvidence({
required String hardId,
required OrderEvidenceRequest request,
required String filePath,
}) async {
final file = File(filePath);
final fileName = file.uri.pathSegments.last;
final form = dio.FormData.fromMap({
...request.toJson(),
'file': await dio.MultipartFile.fromFile(filePath, filename: fileName),
});
final res = await HttpManage().dio.post(
'/hard/hard-order-evidence/ad/upload',
data: form,
options: dio.Options(
headers: {'DEVICE-ID': hardId},
contentType: 'multipart/form-data',
extra: {HttpManage.silentRequestKey: true},
),
);
if (res.data == null) return null;
return ServiceOrderEvidenceUploadResult.fromJson(res.data);
}
/// 静默上报食堂、超市 Android 终端最新运行状态;后台失败不得弹出业务 Toast。
static Future<void> terminalHeartbeat({
required String hardId,
required Map<String, dynamic> data,
}) async {
await HttpManage().dio.post(
'/hard/hard-api/ad/terminal-heartbeat',
data: data,
options: dio.Options(
headers: {'DEVICE-ID': hardId},
extra: {HttpManage.silentRequestKey: true},
),
);
}
/// 获取硬件配置信息
static Future<ServiceHardware?> bind(dynamic pa) async {
final res = await HttpManage().dio.post(
"/hard/hard-api/ad/bind",
data: pa,
);
if (res.data == null) return null;
return ServiceHardware.fromJson(res.data);
}
/// 获取硬件配置信息
static Future<ServiceHardware?> get(dynamic pa) async {
final res = await HttpManage().dio.get(
"/hard/hard-api/ad/get",
queryParameters: pa,
);
if (res.data == null) return null;
return ServiceHardware.fromJson(res.data);
}
/// 获取用户信息
static Future<ServiceUser?> getUserInfoById(
dynamic formData,
) async {
final res = await HttpManage().dio.get(
"/hard/hard-api/ad/get-user",
queryParameters: formData,
);
if (res.data == null) return null;
return ServiceUser.fromJson(res.data);
}
/// 获取用户信息
static Future<ServiceUser?> getUserByCard(
dynamic formData,
) async {
final res = await HttpManage().dio.get(
"/hard/hard-api/ad/get-user-by-card",
queryParameters: formData,
);
if (res.data == null) return null;
return ServiceUser.fromJson(res.data);
}
/// 按当前设备绑定部门树(或全租户)拉取脱敏后的离线人员固定上界游标快照。
///
/// 部门范围、钱包类型全部由 DEVICE-ID 解析;姓名脱敏且不含手机号头像。
/// [allTenant] 仅非生产调试使用,生产环境服务端会强制收敛为部门范围。
/// 业务失败必须抛出,由调用方在同步中断时保留已写入的本地数据。
static Future<ServiceOfflineUserSyncSnapshot> getOfflineUserSyncSnapshot({
required String afterId,
required int limit,
String? snapshotMaxId,
bool allTenant = false,
}) async {
final setting = await AppSettingTool.getSysSetting();
final hardId = setting.serviceId;
if (hardId == null) throw StateError('终端尚未绑定设备');
final res = await HttpManage().dio.post(
'/hard/hard-api/ad/offline-user/sync-snapshot',
data: {
'afterId': afterId,
'limit': limit,
if (snapshotMaxId != null) 'snapshotMaxId': snapshotMaxId,
if (allTenant) 'allTenant': true,
},
options: dio.Options(
headers: {'DEVICE-ID': hardId},
extra: {HttpManage.rejectBusinessErrorKey: true},
),
);
if (res.data is! Map) {
throw const FormatException('服务端未返回有效的离线人员快照');
}
return ServiceOfflineUserSyncSnapshot.fromJson(
Map<String, dynamic>.from(res.data as Map),
);
}
/// 仅按当前终端和识别身份查询本人预约,调用方不得补充商户或其他用户参数。
static Future<ServiceReservationMenuQuery?> queryReservationMenu(
ReservationMenuQueryRequest request,
) async {
final res = await HttpManage().dio.post(
"/hard/hard-api/ad/reservation-menu/query",
data: request.toJson(),
);
if (res.data == null) return null;
return ServiceReservationMenuQuery.fromJson(res.data);
}
/// 只按查询结果中的订单 ID 核销一笔预约订单。
static Future<bool> cancelReservationOrder({
required int orderId,
}) async {
final setting = await AppSettingTool.getSysSetting();
final res = await HttpManage().dio.post(
'/hard/hard-api/ad/reservation-menu/cancel',
data: {'orderId': orderId},
options: dio.Options(
headers: {
if (setting.serviceId != null) 'DEVICE-ID': setting.serviceId,
},
),
);
return res.data == true;
}
/// 一次提交多笔同一用户预约订单,服务端在同一事务内核销并合并生成一张兑换券。
static Future<bool> cancelReservationOrders({
required List<int> orderIds,
}) async {
final setting = await AppSettingTool.getSysSetting();
final hardId = setting.serviceId;
if (hardId == null || hardId.isEmpty) {
throw StateError('当前业务终端尚未绑定');
}
final res = await HttpManage().dio.post(
'/hard/hard-api/ad/reservation-menu/cancel-batch',
data: {'orderIds': orderIds},
options: dio.Options(headers: {'DEVICE-ID': hardId}),
);
return res.data == true;
}
/// 分页查询当前设备的预约核销记录,设备范围由服务端再次校验。
static Future<ServiceVerificationRecordPage?> verificationRecordPage({
required int current,
required int size,
required VerificationRecordQueryRequest request,
}) async {
final res = await HttpManage().dio.post(
'/hard/hard-api/ad/verification-record/page',
queryParameters: {'current': current, 'size': size},
data: request.toJson(),
options: dio.Options(headers: {'DEVICE-ID': request.hardId}),
);
if (res.data == null) return null;
return ServiceVerificationRecordPage.fromJson(res.data);
}
/// 分页查询当前设备的交易记录;scope=MERCHANT 时按终端所属商户查询全部设备。
static Future<ServiceTerminalOrderRecordPage?> terminalOrderRecordPage({
required int current,
required int size,
required TerminalOrderRecordQueryRequest request,
}) async {
final res = await HttpManage().dio.post(
'/hard/hard-api/ad/order-record/page',
queryParameters: {'current': current, 'size': size},
data: request.toJson(),
options: dio.Options(headers: {'DEVICE-ID': '${request.hardId}'}),
);
if (res.data == null) return null;
return ServiceTerminalOrderRecordPage.fromJson(res.data);
}
/// 超时后按原本地订单号读取已提交结果;新接口失败时不得自动重新发起支付。
static Future<ServiceOrderByAndroidCode?> getOrderByAndroidCode({
required String hardId,
required String androidOrderCode,
}) async {
final res = await HttpManage().dio.get(
'/hard/hard-api/ad/get-order-by-android-code',
queryParameters: {
'hardId': hardId,
'androidOrderCode': androidOrderCode,
},
options: dio.Options(
headers: {'DEVICE-ID': hardId},
extra: {HttpManage.silentRequestKey: true},
),
);
if (res.data == null) return null;
return ServiceOrderByAndroidCode.fromJson(
Map<String, dynamic>.from(res.data as Map),
);
}
/// 创建硬件订单
static Future<String?> createHardwareOrder(dynamic formData) async {
final res = await HttpManage().dio.post(
"/hard/hard-api/ad/create-order",
data: formData,
options: dio.Options(
extra: {HttpManage.rejectBusinessErrorKey: true},
),
);
if (res.data == null) return null;
return res.data;
}
/// 创建硬件订单
static Future<ServiceHardCreateHardQrCodeScanOrder?>
createHardQrCodeScanOrder(dynamic formData) async {
final res = await HttpManage().dio.post(
"/hard/hard-api/ad/create-hard-qr-code-order",
data: formData,
options: dio.Options(
extra: {HttpManage.rejectBusinessErrorKey: true},
),
);
if (res.data == null) return null;
return ServiceHardCreateHardQrCodeScanOrder.fromJson(res.data);
}
/// 创建与 Hardware 业务订单绑定的支付宝、微信聚合订单码。
static Future<ServiceHardAggregateOrderCodePay?> createAggregateOrderCodePay(
dynamic formData) async {
final res = await HttpManage().dio.post(
"/hard/hard-api/ad/create-aggregate-order-code-pay",
data: formData,
options: dio.Options(
extra: {HttpManage.rejectBusinessErrorKey: true},
),
);
if (res.data == null) return null;
return ServiceHardAggregateOrderCodePay.fromJson(res.data);
}
/// 查询聚合支付状态;成功响应表示 Hardware 业务订单已同步完成结算。
static Future<ServiceHardAggregateOrderCodePayStatus?>
getAggregateOrderCodePayStatus({
required String orderId,
required String aggregateNo,
}) async {
final res = await HttpManage().dio.get(
"/hard/hard-api/ad/aggregate-order-code-pay/status",
queryParameters: {
"orderId": orderId,
"aggregateNo": aggregateNo,
},
);
if (res.data == null) return null;
return ServiceHardAggregateOrderCodePayStatus.fromJson(res.data);
}
/// 根据订单id,获取订单详情
static Future<ServiceHardOrder?> getOrder(dynamic formData) async {
final res = await HttpManage().dio.get(
"/hard/hard-api/ad/get-order",
queryParameters: formData,
);
if (res.data == null) return null;
return ServiceHardOrder.fromJson(res.data);
}
/// 获取订单购物车
static Future<List<ServiceCar>?> getCarByOrderId(
dynamic formData,
) async {
final res = await HttpManage().dio.get(
"/hard/hard-api/ad/get-by-order-id",
queryParameters: formData,
);
if (res.data == null) return null;
return res.data?.map<ServiceCar>((e) => ServiceCar.fromJson(e)).toList();
}
/// 核销订单
static Future<bool> cancelQrCode(dynamic formData) async {
final res = await HttpManage().dio.post(
"/hard/hard-api/ad/cancel-code",
data: formData,
);
if (res.data == null) return false;
return true;
}
/// 核销活动订单
static Future<bool> cancelActQrCode(dynamic formData) async {
final res = await HttpManage().dio.post(
"/hard/hard-api/ad/cancel-act-code",
data: formData,
);
if (res.data == null) return false;
return true;
}
/// 生成发票
static Future<ServiceGenerateInvoice?> generateInvoice(
dynamic formData,
) async {
final res = await HttpManage().dio.get(
"/hard/hard-api/ad/generate-invoice",
queryParameters: formData,
);
if (res.data == null) return null;
return ServiceGenerateInvoice.fromJson(res.data);
}
/// 业务终端按 DEVICE-ID 创建打印任务,目标打印机只由服务端绑定关系决定。
static Future<bool> createPrinterTask({
required String orderId,
String printType = '1',
}) async {
final setting = await AppSettingTool.getSysSetting();
final hardId = setting.serviceId;
if (hardId == null || hardId.isEmpty) throw StateError('当前业务终端尚未绑定');
final res = await HttpManage().dio.post(
'/hard/hard-api/ad/printer/task',
data: {
'orderId': int.parse(orderId),
'printType': printType,
},
options: dio.Options(headers: {'DEVICE-ID': hardId}),
);
return res.data != null;
}
/// 在线查询当前业务终端为指定订单生成的打印凭证,不读取终端本地打印记录。
static Future<List<ServicePrintRecord>> getPrinterRecords({
required String orderId,
}) async {
final setting = await AppSettingTool.getSysSetting();
final hardId = setting.serviceId;
if (hardId == null || hardId.isEmpty) throw StateError('当前业务终端尚未绑定');
final res = await HttpManage().dio.get(
'/hard/hard-api/ad/printer/records',
queryParameters: {'orderId': int.parse(orderId)},
options: dio.Options(headers: {'DEVICE-ID': hardId}),
);
if (res.data == null) return const [];
if (res.data is! List) throw const FormatException('打印凭证记录格式错误');
return (res.data as List)
.map((item) => ServicePrintRecord.fromJson(
Map<String, dynamic>.from(item as Map),
))
.toList(growable: false);
}
/// 提交当前业务终端已打印兑换券的人工重打原因并取得新任务。
static Future<ServicePrintRecord?> reprintVoucher({
required String historyId,
required String reason,
}) async {
final setting = await AppSettingTool.getSysSetting();
final hardId = setting.serviceId;
if (hardId == null || hardId.isEmpty) throw StateError('当前业务终端尚未绑定');
final res = await HttpManage().dio.post(
'/hard/hard-api/ad/printer/reprint',
data: {
'historyId': int.parse(historyId),
'reprintReason': reason,
},
options: dio.Options(headers: {'DEVICE-ID': hardId}),
);
if (res.data == null) return null;
if (res.data is! Map) throw const FormatException('兑换券重打任务格式错误');
return ServicePrintRecord.fromJson(
Map<String, dynamic>.from(res.data as Map),
);
}
/// 打印成功
static Future<bool> printInvoice(dynamic formData) async {
final res = await HttpManage().dio.get(
"/hard/hard-api/ad/print-invoice",
queryParameters: formData,
);
if (res.data == null) return false;
return true;
}
/// 读取当前食堂或超市终端绑定的逻辑打印机及 USB/ESC-POS 参数。
static Future<Map<String, dynamic>?> getPrinterBinding() async {
final setting = await AppSettingTool.getSysSetting();
final hardId = setting.serviceId;
if (hardId == null || hardId.isEmpty) throw StateError('当前业务终端尚未绑定设备 ID');
final res = await HttpManage().dio.get(
'/hard/hard-api/ad/printer/bootstrap',
options: dio.Options(headers: {'DEVICE-ID': hardId}),
);
if (res.data == null) return null;
if (res.data is! Map) throw const FormatException('打印机绑定配置格式错误');
return Map<String, dynamic>.from(res.data as Map);
}
/// 由当前业务终端把实际枚举到的 USB 打印机型号和 VID/PID 上报到绑定的 EY_PRINT 配置。
static Future<Map<String, dynamic>> reportPrinterInfo({
required String hardId,
required PrinterInfoReportRequest request,
}) async {
final res = await HttpManage().dio.post(
'/hard/hard-api/ad/printer/info',
data: request.toJson(),
options: dio.Options(headers: {'DEVICE-ID': hardId}),
);
if (res.data is! Map) throw const FormatException('打印机信息上报响应格式错误');
return Map<String, dynamic>.from(res.data as Map);
}
/// 拉取当前业务终端所绑定打印机的未完成任务,补偿 MQTT 离线窗口。
static Future<List<MqttInvoiceMsg>> getPrinterTasks() async {
final setting = await AppSettingTool.getSysSetting();
final hardId = setting.serviceId;
if (hardId == null || hardId.isEmpty) return const <MqttInvoiceMsg>[];
final res = await HttpManage().dio.get(
'/hard/hard-api/ad/printer/tasks',
options: dio.Options(
headers: {'DEVICE-ID': hardId},
extra: {HttpManage.silentRequestKey: true},
),
);
if (res.data is! List) return const <MqttInvoiceMsg>[];
return (res.data as List).whereType<Map>().map((item) {
final json = Map<String, dynamic>.from(item);
json['historyId'] ??= json['id'];
return MqttInvoiceMsg.fromJson(json);
}).toList(growable: false);
}
/// 由当前业务终端上报完整 USB 写入结果;请求失败由本地队列保留并在网络恢复后补报。
static Future<bool> reportPrinterResult({
required String taskId,
required String orderId,
required int retryVersion,
required bool success,
required int attemptCount,
String? errorMessage,
}) async {
final setting = await AppSettingTool.getSysSetting();
final hardId = setting.serviceId;
if (hardId == null || hardId.isEmpty) throw StateError('业务终端设备 ID 缺失');
final res = await HttpManage().dio.post(
'/hard/hard-api/ad/printer/result',
data: {
'taskId': int.parse(taskId),
'orderId': int.parse(orderId),
'retryVersion': retryVersion,
'success': success,
'attemptCount': attemptCount,
if (errorMessage != null && errorMessage.isNotEmpty)
'errorMessage': errorMessage,
},
options: dio.Options(
headers: {'DEVICE-ID': hardId},
extra: {HttpManage.silentRequestKey: true},
),
);
return res.data == true;
}
/// 离线同步
static Future<List<String>?> syncOfflineOrder(dynamic formData) async {
final res = await HttpManage().dio.post(
"/hard/hard-api/ad/sync-offline-order",
data: formData,
);
if (res.data == null) return null;
return res.data?.map<String>((e) => e.toString()).toList();
}
/// 统计每天、每周、每月、每年的交易人数和次数
static Future<ServiceChartLatitude?> chartLatitude(
dynamic formData,
) async {
final res = await HttpManage().dio.get(
"/hard/hard-api/ad/chart-latitude",
queryParameters: formData,
);
if (res.data == null) return null;
return ServiceChartLatitude.fromJson(res.data);
}
/// android查询今日的广播
static Future<ServiceBroadcastTodayPage?> broadcastToday(
dynamic formData,
) async {
final res = await HttpManage().dio.get(
"/hard/hard-broadcast/ad/today",
queryParameters: formData,
data: {"hardId": formData["hardId"]},
);
if (res.data == null) return null;
return ServiceBroadcastTodayPage.fromJson(res.data);
}
/// android完成一轮广播
static Future<ServiceBroadcastTodayPage?> broadcast(
dynamic formData,
) async {
final res = await HttpManage()
.dio
.post("/hard/hard-broadcast/ad/broadcast", data: formData);
if (res.data == null) return null;
return ServiceBroadcastTodayPage.fromJson(res.data);
}
/// 消费机限时退款
static Future<bool> timeLimitRefund(
dynamic formData,
) async {
final res = await HttpManage().dio.get(
"/hard/hard-after-sale/ad/time-limit-refund",
queryParameters: formData,
);
if (res.data == null) return false;
return true;
}
}