feat:对接读写卡器

feat:优化用户下拉框搜索
feat:优化下单卡顿问题
feat:优化商户列表样式
This commit is contained in:
dzc committed 2026-10-13 17:26:11 +08:00
1 parent ceff6c8831
commit ba7e31dbd8
3 files changed
+116 -3

No files matched your search

+22
View File
@@ -273,6 +273,28 @@ abstract class HardApi {
return ServiceHardOrder.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: {
'androidOrderCode': androidOrderCode,
'hardId': hardId,
},
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<List<ServiceCar>?> getCarByOrderId(
dynamic formData,
@@ -1097,6 +1097,40 @@ class ServiceHardCreateHardQrCodeScanOrder {
_$ServiceHardCreateHardQrCodeScanOrderToJson(this);
}
/// 终端按本地订单号对账查询到的服务端订单最小状态。
///
/// 创建订单请求超时后用于确认服务端是否已建单扣款;手写 fromJson,
/// 不参与 json_serializable 代码生成。
class ServiceOrderByAndroidCode {
/// 服务端订单 ID
String orderId;
/// 订单状态编码(5=已支付)
String orderStatus;
/// 支付方式编码
String payType;
/// 实付金额(分)
int payPrice;
ServiceOrderByAndroidCode({
required this.orderId,
required this.orderStatus,
required this.payType,
required this.payPrice,
});
factory ServiceOrderByAndroidCode.fromJson(Map<String, dynamic> json) {
return ServiceOrderByAndroidCode(
orderId: '${json['orderId']}',
orderStatus: '${json['orderStatus']}',
payType: '${json['payType']}',
payPrice: (json['payPrice'] as num?)?.toInt() ?? 0,
);
}
}
/// 收银终端支付宝、微信聚合订单码创建结果。
///
/// 该模型只接收 `AGGREGATE_ORDER_PAY` HTTPS 收银地址,不承载自有钱包码或平台原生码。
@@ -488,9 +488,25 @@ mixin OrderMixin {
sqlOrderDetail.serviceUniqueCode = data.orderId;
result = data as T;
} else {
final data = await HardApi.createHardwareOrder(pa);
if (data == null) throw Error();
if (ObjectUtil.isEmpty(data)) throw Error();
String? data;
try {
data = await HardApi.createHardwareOrder(pa);
if (data == null) throw Error();
if (ObjectUtil.isEmpty(data)) throw Error();
} on DioException catch (e) {
// 统一网络层会把服务端业务拒绝(库存不足等)也包装成 DioException 抛出,
// 只有超时/连接类异常才存在"服务端已建单扣款但响应丢失"的可能,需要先对账。
final isTimeoutLike = e.type == DioExceptionType.connectionTimeout ||
e.type == DioExceptionType.sendTimeout ||
e.type == DioExceptionType.receiveTimeout ||
e.type == DioExceptionType.connectionError;
if (!isTimeoutLike) rethrow;
data = await _recoverTimeoutCreateOrder(
serviceId: serviceId!,
order: sqlOrderDetail,
);
if (data == null) rethrow;
}
logger.i("|$_tag||有网络|创建api订单,下单成功res:|$data|");
sqlOrderDetail.status = OrderStatus.PAY;
sqlOrderDetail.serviceUniqueCode = data;
@@ -596,6 +612,47 @@ mixin OrderMixin {
return null;
}
/// 创建订单请求超时后,按终端本地订单号反查服务端订单结果。
///
/// 网络抖动时服务端处理可能长达数十秒,终端 10 秒就超时,但订单可能已建单扣款成功;
/// 此结果必须找回并按成功收尾,否则收银员重试会造成重复下单。服务端已按本地订单号
/// 幂等(重复提交返回原订单),即使对账期间收银员再次点击也不会重复扣款。
/// 最多轮询 3 次(间隔 3 秒)等待服务端长事务提交;查到同号已支付且金额一致的订单时
/// 返回服务端订单号,否则返回 null 由调用方按原失败流程处理。
Future<String?> _recoverTimeoutCreateOrder({
required String serviceId,
required OrderEntity order,
}) async {
final expectedPayPrice = order.price + order.extraPrice;
for (var attempt = 0; attempt < 3; attempt++) {
if (attempt > 0) {
await Future<void>.delayed(const Duration(seconds: 3));
}
try {
final remote = await HardApi.getOrderByAndroidCode(
hardId: serviceId,
androidOrderCode: order.uniqueCode,
);
if (remote == null) continue;
// 状态 5=已支付,且金额与本次结算一致才认定为本次订单。
if (remote.orderStatus == '5' && remote.payPrice == expectedPayPrice) {
logger.i(
"|$_tag|超时对账命中已支付订单,orderId:|${remote.orderId}|attempt:|$attempt|",
);
return remote.orderId;
}
// 已查到订单但状态或金额不符(如已被退款、金额被调整),不再轮询等待。
logger.w(
"|$_tag|超时对账查到订单但状态或金额不符,orderStatus:|${remote.orderStatus}|payPrice:|${remote.payPrice}|expected:|$expectedPayPrice|",
);
return null;
} catch (error) {
logger.w("|$_tag|超时对账查询失败,attempt:|$attempt|error:|$error|");
}
}
return null;
}
/// 支付成功后从 SQLite 重读当前食堂分类,使扣减后的库存立即展示在商品卡片上。
///
/// 页面刷新失败不改变已经确认的支付结果;现有 `productSync` 会在后续空闲周期重新校准