From 4a18fc6eb14b8f11c11ce1dd5a90af8bda0ae22c Mon Sep 17 00:00:00 2001 From: dzc <2388224167@qq.com> Date: Fri, 4 Sep 2026 13:15:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=B6=88=E8=B4=B9=E6=9C=BA=E6=89=93?= =?UTF-8?q?=E5=8D=B0=E6=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../facediscern/database/DatabaseHelper.java | 2 +- lib/common/api/hard_api.dart | 83 ++++ .../models/database/database_enum_models.dart | 6 + .../models/database/database_models.dart | 33 ++ .../models/database/database_models.g.dart | 25 + lib/common/models/mq/mq_models.dart | 17 + lib/common/models/mq/mq_models.g.dart | 10 + lib/common/routers/names.dart | 6 + lib/common/routers/pages.dart | 8 + .../database/dao/print_history_dao.dart | 20 + lib/common/services/database/database.dart | 2 +- lib/common/services/database/database.g.dart | 110 ++++- .../services/database/database_migration.dart | 25 + .../database/entity/print_history_entity.dart | 25 +- .../entity/print_history_entity.g.dart | 12 + lib/common/utils/app/app_mq_tool.dart | 35 +- lib/common/utils/app/app_network_tool.dart | 19 +- lib/common/utils/app/app_setting_tool.dart | 40 +- lib/common/utils/app/control/app_control.dart | 142 ++++-- lib/common/utils/manage/print_manage.dart | 70 ++- lib/common/utils/sync/sync_check_tool.dart | 53 ++- lib/common/utils/sync/sync_print_tool.dart | 337 ++++++++++---- lib/common/values/constants.dart | 4 +- lib/pages/index.dart | 2 + lib/pages/printer/view.dart | 438 ++++++++++++++++++ lib/pages/root/device_setup/view.dart | 163 +++++++ lib/pages/root/login/controller.dart | 33 +- .../terminal/order_history/controller.dart | 27 +- .../drago_usb_printer/android/build.gradle | 40 ++ .../android/src/main/AndroidManifest.xml | 3 + .../DragoUsbPrinterPlugin.kt | 202 ++++++++ .../lib/drago_usb_printer.dart | 61 +++ packages/drago_usb_printer/pubspec.yaml | 19 + pubspec.lock | 11 +- pubspec.yaml | 7 +- test/common/printer_task_test.dart | 146 ++++++ test/common/terminal_device_status_test.dart | 42 +- tmp/打印机文档.doc | Bin 0 -> 682496 bytes 39 files changed, 2061 insertions(+), 219 deletions(-) create mode 100644 lib/pages/printer/view.dart create mode 100644 lib/pages/root/device_setup/view.dart create mode 100644 packages/drago_usb_printer/android/build.gradle create mode 100644 packages/drago_usb_printer/android/src/main/AndroidManifest.xml create mode 100644 packages/drago_usb_printer/android/src/main/kotlin/app/sks/client/drago_usb_printer/DragoUsbPrinterPlugin.kt create mode 100644 packages/drago_usb_printer/lib/drago_usb_printer.dart create mode 100644 packages/drago_usb_printer/pubspec.yaml create mode 100644 test/common/printer_task_test.dart create mode 100644 tmp/打印机文档.doc diff --git a/README.md b/README.md index 07f196d..f99c3b3 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ flutter pub run build_runner build --delete-conflicting-outputs~ ## flutter打包 ``` -flutter build apk --build-name 1.4.6 --build-number 146 +flutter build apk --build-name 1.4.7 --build-number 147 --build-name为版本号(展示) --build-number为版本号(内部比较) ``` diff --git a/android/app/src/main/java/io/flutter/plugins/facediscern/database/DatabaseHelper.java b/android/app/src/main/java/io/flutter/plugins/facediscern/database/DatabaseHelper.java index 6abc702..b485b57 100644 --- a/android/app/src/main/java/io/flutter/plugins/facediscern/database/DatabaseHelper.java +++ b/android/app/src/main/java/io/flutter/plugins/facediscern/database/DatabaseHelper.java @@ -16,7 +16,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { private static final String DATABASE_PATH = "pj_cashier/database"; private static final String DATABASE_NAME = "cash_register.db"; /** 与 Flutter Floor 的 @Database 版本保持一致,避免原生层提前推进 user_version。 */ - private static final int DATABASE_VERSION = 12; + private static final int DATABASE_VERSION = 13; public DatabaseHelper(Context context) { super(context, getCustomDatabasePath(context), null, DATABASE_VERSION); diff --git a/lib/common/api/hard_api.dart b/lib/common/api/hard_api.dart index 0efcf6e..0ea0a12 100644 --- a/lib/common/api/hard_api.dart +++ b/lib/common/api/hard_api.dart @@ -284,6 +284,25 @@ abstract class HardApi { return ServiceGenerateInvoice.fromJson(res.data); } + /// 业务终端按 DEVICE-ID 创建打印任务,目标打印机只由服务端绑定关系决定。 + static Future 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 printInvoice(dynamic formData) async { final res = await HttpManage().dio.get( @@ -294,6 +313,70 @@ abstract class HardApi { return true; } + /// 读取当前 EY_PRINT 的服务端绑定和 USB/ESC-POS 参数。 + static Future> getPrinterBootstrap() 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 is! Map) throw const FormatException('打印终端启动配置格式错误'); + return Map.from(res.data as Map); + } + + /// 拉取当前打印终端未完成任务,补偿 MQTT 离线窗口。 + static Future> getPrinterTasks() async { + final setting = await AppSettingTool.getSysSetting(); + final hardId = setting.serviceId; + if (hardId == null || hardId.isEmpty) return const []; + 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 []; + return (res.data as List).whereType().map((item) { + final json = Map.from(item); + json['historyId'] ??= json['id']; + return MqttInvoiceMsg.fromJson(json); + }).toList(growable: false); + } + + /// 上报完整 USB 写入结果;请求失败由本地队列保留并在网络恢复后补报。 + static Future 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?> syncOfflineOrder(dynamic formData) async { final res = await HttpManage().dio.post( diff --git a/lib/common/models/database/database_enum_models.dart b/lib/common/models/database/database_enum_models.dart index 4f9e1ef..e14d232 100644 --- a/lib/common/models/database/database_enum_models.dart +++ b/lib/common/models/database/database_enum_models.dart @@ -17,6 +17,12 @@ enum SysType implements BaseEnum { const SysType(this.code, this.title, this.cnStr, this.usStr); } +/// 终端运行角色与业务类型分离,打印终端仍沿用绑定来源的 [SysType] 获取业务配置。 +enum TerminalRole { + cashier, + printer, +} + /// 订单类型 enum OrderType implements BaseEnum { BELOW("1", "线下购买"), diff --git a/lib/common/models/database/database_models.dart b/lib/common/models/database/database_models.dart index e4ccf28..e1f6df6 100644 --- a/lib/common/models/database/database_models.dart +++ b/lib/common/models/database/database_models.dart @@ -10,6 +10,10 @@ class SysSetting extends Equatable { /// 设备类型 SysType sysType; + /// 当前 APK 运行角色;旧配置默认保持收银终端行为。 + @JsonKey(defaultValue: TerminalRole.cashier) + TerminalRole terminalRole; + /// mac地址 String? androidId; @@ -22,6 +26,22 @@ class SysSetting extends Equatable { /// 商户id String? serviceMerchantId; + /// 打印终端所绑定的来源业务终端。 + String? printerSourceHardId; + + /// 打印终端所绑定的来源业务终端名称。 + String? printerSourceHardName; + + /// 真机打印机型号与 USB 参数,由服务端启动配置下发。 + String? printerModel; + int? printerUsbVendorId; + int? printerUsbProductId; + + /// 当前纸宽、切刀和图片指令模式。 + int printerPaperWidth; + bool printerAutoCut; + String printerImageMode; + /// 商户部门id String? merchantDeptId; @@ -131,6 +151,7 @@ class SysSetting extends Equatable { SysSetting({ required this.sysType, + this.terminalRole = TerminalRole.cashier, this.androidId, this.serviceId, this.mqSocketAddress, @@ -138,6 +159,9 @@ class SysSetting extends Equatable { this.sqlAddress, this.logAddress, this.hardGroupId, + this.printerPaperWidth = 80, + this.printerAutoCut = true, + this.printerImageMode = 'GS_V0', this.tenantList, this.manualHardwareInput = false, this.rootAccessStatus = RootAccessStatus.unknown, @@ -156,10 +180,19 @@ class SysSetting extends Equatable { @override List get props => [ sysType, + terminalRole, androidId, serviceId, serviceName, serviceMerchantId, + printerSourceHardId, + printerSourceHardName, + printerModel, + printerUsbVendorId, + printerUsbProductId, + printerPaperWidth, + printerAutoCut, + printerImageMode, merchantDeptId, hardGroupId, mqSocketAddress, diff --git a/lib/common/models/database/database_models.g.dart b/lib/common/models/database/database_models.g.dart index e065553..e9a8746 100644 --- a/lib/common/models/database/database_models.g.dart +++ b/lib/common/models/database/database_models.g.dart @@ -8,6 +8,9 @@ part of 'database_models.dart'; SysSetting _$SysSettingFromJson(Map json) => SysSetting( sysType: $enumDecode(_$SysTypeEnumMap, json['sysType']), + terminalRole: + $enumDecodeNullable(_$TerminalRoleEnumMap, json['terminalRole']) ?? + TerminalRole.cashier, androidId: json['androidId'] as String?, serviceId: json['serviceId'] as String?, mqSocketAddress: json['mqSocketAddress'] as String?, @@ -15,6 +18,9 @@ SysSetting _$SysSettingFromJson(Map json) => SysSetting( sqlAddress: json['sqlAddress'] as String?, logAddress: json['logAddress'] as String?, hardGroupId: json['hardGroupId'] as String?, + printerPaperWidth: (json['printerPaperWidth'] as num?)?.toInt() ?? 80, + printerAutoCut: json['printerAutoCut'] as bool? ?? true, + printerImageMode: json['printerImageMode'] as String? ?? 'GS_V0', tenantList: (json['tenantList'] as List?) ?.map((e) => ServiceTenant.fromJson(e as Map)) .toList(), @@ -38,6 +44,11 @@ SysSetting _$SysSettingFromJson(Map json) => SysSetting( ) ..serviceName = json['serviceName'] as String? ..serviceMerchantId = json['serviceMerchantId'] as String? + ..printerSourceHardId = json['printerSourceHardId'] as String? + ..printerSourceHardName = json['printerSourceHardName'] as String? + ..printerModel = json['printerModel'] as String? + ..printerUsbVendorId = (json['printerUsbVendorId'] as num?)?.toInt() + ..printerUsbProductId = (json['printerUsbProductId'] as num?)?.toInt() ..merchantDeptId = json['merchantDeptId'] as String? ..mqSocketPort = json['mqSocketPort'] as String? ..mqUsername = json['mqUsername'] as String? @@ -83,10 +94,19 @@ SysSetting _$SysSettingFromJson(Map json) => SysSetting( Map _$SysSettingToJson(SysSetting instance) => { 'sysType': _$SysTypeEnumMap[instance.sysType]!, + 'terminalRole': _$TerminalRoleEnumMap[instance.terminalRole]!, 'androidId': instance.androidId, 'serviceId': instance.serviceId, 'serviceName': instance.serviceName, 'serviceMerchantId': instance.serviceMerchantId, + 'printerSourceHardId': instance.printerSourceHardId, + 'printerSourceHardName': instance.printerSourceHardName, + 'printerModel': instance.printerModel, + 'printerUsbVendorId': instance.printerUsbVendorId, + 'printerUsbProductId': instance.printerUsbProductId, + 'printerPaperWidth': instance.printerPaperWidth, + 'printerAutoCut': instance.printerAutoCut, + 'printerImageMode': instance.printerImageMode, 'merchantDeptId': instance.merchantDeptId, 'hardGroupId': instance.hardGroupId, 'mqSocketAddress': instance.mqSocketAddress, @@ -135,6 +155,11 @@ const _$SysTypeEnumMap = { SysType.CANTEEN: 'CANTEEN', }; +const _$TerminalRoleEnumMap = { + TerminalRole.cashier: 'cashier', + TerminalRole.printer: 'printer', +}; + const _$RootAccessStatusEnumMap = { RootAccessStatus.unknown: 'unknown', RootAccessStatus.available: 'available', diff --git a/lib/common/models/mq/mq_models.dart b/lib/common/models/mq/mq_models.dart index 9944ff5..88b8e1e 100644 --- a/lib/common/models/mq/mq_models.dart +++ b/lib/common/models/mq/mq_models.dart @@ -40,6 +40,18 @@ class MqttInvoiceMsg { @JsonKey(fromJson: JsonTool.anyToString, toJson: JsonTool.stringToInt) final String? androidOrderCode; + /// 来源业务终端与实际目标打印终端。 + @JsonKey(fromJson: JsonTool.anyToString, toJson: JsonTool.stringToInt) + final String? sourceHardId; + + @JsonKey(fromJson: JsonTool.anyToString, toJson: JsonTool.stringToInt) + final String? targetHardId; + + /// 打印类型、同订单打印序号和人工重试版本。 + final String? printType; + final int? printNo; + final int? retryVersion; + // 图片地址url final String? pic; @@ -52,6 +64,11 @@ class MqttInvoiceMsg { this.userId, this.orderId, this.androidOrderCode, + this.sourceHardId, + this.targetHardId, + this.printType, + this.printNo, + this.retryVersion, this.pic, this.isGlobalFace, }); diff --git a/lib/common/models/mq/mq_models.g.dart b/lib/common/models/mq/mq_models.g.dart index 60b4b70..84cdbfc 100644 --- a/lib/common/models/mq/mq_models.g.dart +++ b/lib/common/models/mq/mq_models.g.dart @@ -34,6 +34,11 @@ MqttInvoiceMsg _$MqttInvoiceMsgFromJson(Map json) => userId: JsonTool.anyToString(json['userId']), orderId: JsonTool.anyToString(json['orderId']), androidOrderCode: JsonTool.anyToString(json['androidOrderCode']), + sourceHardId: JsonTool.anyToString(json['sourceHardId']), + targetHardId: JsonTool.anyToString(json['targetHardId']), + printType: json['printType'] as String?, + printNo: (json['printNo'] as num?)?.toInt(), + retryVersion: (json['retryVersion'] as num?)?.toInt(), pic: json['pic'] as String?, isGlobalFace: json['isGlobalFace'] as bool?, ); @@ -45,6 +50,11 @@ Map _$MqttInvoiceMsgToJson(MqttInvoiceMsg instance) => 'userId': JsonTool.stringToInt(instance.userId), 'orderId': JsonTool.stringToInt(instance.orderId), 'androidOrderCode': JsonTool.stringToInt(instance.androidOrderCode), + 'sourceHardId': JsonTool.stringToInt(instance.sourceHardId), + 'targetHardId': JsonTool.stringToInt(instance.targetHardId), + 'printType': instance.printType, + 'printNo': instance.printNo, + 'retryVersion': instance.retryVersion, 'pic': instance.pic, 'isGlobalFace': instance.isGlobalFace, }; diff --git a/lib/common/routers/names.dart b/lib/common/routers/names.dart index b035ab1..90a2395 100644 --- a/lib/common/routers/names.dart +++ b/lib/common/routers/names.dart @@ -6,6 +6,12 @@ class RouteNames { /// 登录 static const login = '/login'; + /// 登录后为尚未绑定的安装实例输入服务端设备 ID。 + static const deviceSetup = '/device-setup'; + + /// EY_PRINT 无人值守打印状态面板,不装载收银业务页面。 + static const printer = '/printer'; + /// 顾客屏全窗口预览,不装载终端侧栏和顶栏。 static const customer = '/customer'; diff --git a/lib/common/routers/pages.dart b/lib/common/routers/pages.dart index 5d140c7..8f30d60 100644 --- a/lib/common/routers/pages.dart +++ b/lib/common/routers/pages.dart @@ -20,6 +20,14 @@ class RoutePages { name: RouteNames.login, page: () => const LoginPage(), ), + GetPage( + name: RouteNames.deviceSetup, + page: () => const DeviceSetupPage(), + ), + GetPage( + name: RouteNames.printer, + page: () => const PrinterStatusPage(), + ), GetPage( name: RouteNames.customer, page: () => CustomerDisplayPreviewPage(arguments: Get.arguments), diff --git a/lib/common/services/database/dao/print_history_dao.dart b/lib/common/services/database/dao/print_history_dao.dart index 3cbfe6b..d1952c4 100644 --- a/lib/common/services/database/dao/print_history_dao.dart +++ b/lib/common/services/database/dao/print_history_dao.dart @@ -31,4 +31,24 @@ abstract class PrintHistoryDao { (:status = '-1' OR status = :status) ''') Future> getAll(String status); + + /// 以服务端任务 ID 唯一定位本地记录,作为 MQTT/HTTP 双通道去重依据。 + @Query( + 'SELECT * FROM print_history WHERE servicePrintId = :servicePrintId LIMIT 1') + Future findByServicePrintId(String servicePrintId); + + /// 查询等待补报成功或失败结果的任务。 + @Query( + 'SELECT * FROM print_history WHERE resultPending = 1 ORDER BY createTime ASC') + Future> getPendingResults(); + + @Query("SELECT COUNT(*) FROM print_history WHERE status = 'NONE'") + Future countPending(); + + @Query("SELECT COUNT(*) FROM print_history WHERE status = 'ERROR'") + Future countFailed(); + + @Query( + "SELECT * FROM print_history WHERE lastError IS NOT NULL AND lastError != '' ORDER BY id DESC LIMIT 1") + Future findLatestError(); } diff --git a/lib/common/services/database/database.dart b/lib/common/services/database/database.dart index 4f6a47d..6c4d6fe 100644 --- a/lib/common/services/database/database.dart +++ b/lib/common/services/database/database.dart @@ -48,7 +48,7 @@ const typeConverters = [ ]; /// 收银终端共享 SQLite 数据库入口,统一暴露各业务 DAO 和带迁移的建库方法。 -@Database(version: 12, entities: entities) +@Database(version: 13, entities: entities) @TypeConverters(typeConverters) abstract class AppDatabase extends FloorDatabase { /// 取餐广播队列 DAO。 diff --git a/lib/common/services/database/database.g.dart b/lib/common/services/database/database.g.dart index 84f6377..1d53a59 100644 --- a/lib/common/services/database/database.g.dart +++ b/lib/common/services/database/database.g.dart @@ -112,7 +112,7 @@ class _$AppDatabase extends AppDatabase { Callback? callback, ]) async { final databaseOptions = sqflite.OpenDatabaseOptions( - version: 12, + version: 13, onConfigure: (database) async { await database.execute('PRAGMA foreign_keys = ON'); await callback?.onConfigure?.call(database); @@ -148,7 +148,7 @@ class _$AppDatabase extends AppDatabase { await database.execute( 'CREATE TABLE IF NOT EXISTS `classify` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `serviceUniqueCode` TEXT NOT NULL, `sysType` TEXT NOT NULL, `name` TEXT NOT NULL, `categoryType` TEXT NOT NULL, `sort` INTEGER NOT NULL, `createTime` TEXT NOT NULL, `updateTime` TEXT NOT NULL, `delFlag` INTEGER NOT NULL)'); await database.execute( - 'CREATE TABLE IF NOT EXISTS `print_history` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `orderId` TEXT NOT NULL, `serviceOrderId` TEXT NOT NULL, `servicePrintId` TEXT NOT NULL, `invoiceUrl` TEXT NOT NULL, `status` TEXT NOT NULL, `printTime` TEXT, `syncTime` TEXT, `createTime` TEXT NOT NULL)'); + 'CREATE TABLE IF NOT EXISTS `print_history` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `orderId` TEXT NOT NULL, `serviceOrderId` TEXT NOT NULL, `servicePrintId` TEXT NOT NULL, `invoiceUrl` TEXT NOT NULL, `printType` TEXT NOT NULL, `printNo` INTEGER NOT NULL, `retryVersion` INTEGER NOT NULL, `attemptCount` INTEGER NOT NULL, `lastError` TEXT, `resultPending` INTEGER NOT NULL, `status` TEXT NOT NULL, `printTime` TEXT, `syncTime` TEXT, `createTime` TEXT NOT NULL)'); await database.execute( 'CREATE TABLE IF NOT EXISTS `user_record` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `userId` TEXT NOT NULL, `nickname` TEXT NOT NULL, `mobile` TEXT, `avatar` TEXT, `walletCardNumber` TEXT, `walletId` TEXT, `walletTypeId` TEXT, `walletBalance` INTEGER, `canteenId` TEXT, `discountsList` TEXT, `discountsUpdateTime` TEXT, `createTime` TEXT, `updateTime` TEXT)'); await database.execute( @@ -167,6 +167,8 @@ class _$AppDatabase extends AppDatabase { 'CREATE INDEX `index_canteen_spu_combo_item_parentServiceUniqueCode_saleDate` ON `canteen_spu_combo_item` (`parentServiceUniqueCode`, `saleDate`)'); await database.execute( 'CREATE UNIQUE INDEX `index_canteen_spu_combo_item_parentServiceUniqueCode_saleDate_childServiceUniqueCode` ON `canteen_spu_combo_item` (`parentServiceUniqueCode`, `saleDate`, `childServiceUniqueCode`)'); + await database.execute( + 'CREATE UNIQUE INDEX `index_print_history_servicePrintId` ON `print_history` (`servicePrintId`)'); await database.execute( 'CREATE INDEX `index_scheduled_task_log_taskCode_startedAt` ON `scheduled_task_log` (`taskCode`, `startedAt`)'); await database.execute( @@ -1580,6 +1582,12 @@ class _$PrintHistoryDao extends PrintHistoryDao { 'serviceOrderId': item.serviceOrderId, 'servicePrintId': item.servicePrintId, 'invoiceUrl': item.invoiceUrl, + 'printType': item.printType, + 'printNo': item.printNo, + 'retryVersion': item.retryVersion, + 'attemptCount': item.attemptCount, + 'lastError': item.lastError, + 'resultPending': _boolConverter.encode(item.resultPending), 'status': _printStatusConverter.encode(item.status), 'printTime': item.printTime, 'syncTime': _dateTimeEmptyConverter.encode(item.syncTime), @@ -1595,6 +1603,12 @@ class _$PrintHistoryDao extends PrintHistoryDao { 'serviceOrderId': item.serviceOrderId, 'servicePrintId': item.servicePrintId, 'invoiceUrl': item.invoiceUrl, + 'printType': item.printType, + 'printNo': item.printNo, + 'retryVersion': item.retryVersion, + 'attemptCount': item.attemptCount, + 'lastError': item.lastError, + 'resultPending': _boolConverter.encode(item.resultPending), 'status': _printStatusConverter.encode(item.status), 'printTime': item.printTime, 'syncTime': _dateTimeEmptyConverter.encode(item.syncTime), @@ -1610,6 +1624,12 @@ class _$PrintHistoryDao extends PrintHistoryDao { 'serviceOrderId': item.serviceOrderId, 'servicePrintId': item.servicePrintId, 'invoiceUrl': item.invoiceUrl, + 'printType': item.printType, + 'printNo': item.printNo, + 'retryVersion': item.retryVersion, + 'attemptCount': item.attemptCount, + 'lastError': item.lastError, + 'resultPending': _boolConverter.encode(item.resultPending), 'status': _printStatusConverter.encode(item.status), 'printTime': item.printTime, 'syncTime': _dateTimeEmptyConverter.encode(item.syncTime), @@ -1638,10 +1658,94 @@ class _$PrintHistoryDao extends PrintHistoryDao { Future> getAll(String status) async { return _queryAdapter.queryList( 'SELECT * FROM print_history WHERE (?1 = \'-1\' OR status = ?1)', - mapper: (Map row) => PrintHistoryEntity(id: row['id'] as int?, orderId: row['orderId'] as String, serviceOrderId: row['serviceOrderId'] as String, servicePrintId: row['servicePrintId'] as String, invoiceUrl: row['invoiceUrl'] as String, status: _printStatusConverter.decode(row['status'] as String), printTime: row['printTime'] as String?, syncTime: _dateTimeEmptyConverter.decode(row['syncTime'] as String), createTime: _dateTimeConverter.decode(row['createTime'] as String)), + mapper: (Map row) => PrintHistoryEntity(id: row['id'] as int?, orderId: row['orderId'] as String, serviceOrderId: row['serviceOrderId'] as String, servicePrintId: row['servicePrintId'] as String, invoiceUrl: row['invoiceUrl'] as String, printType: row['printType'] as String, printNo: row['printNo'] as int, retryVersion: row['retryVersion'] as int, attemptCount: row['attemptCount'] as int, lastError: row['lastError'] as String?, resultPending: _boolConverter.decode(row['resultPending'] as int), status: _printStatusConverter.decode(row['status'] as String), printTime: row['printTime'] as String?, syncTime: _dateTimeEmptyConverter.decode(row['syncTime'] as String), createTime: _dateTimeConverter.decode(row['createTime'] as String)), arguments: [status]); } + @override + Future findByServicePrintId( + String servicePrintId) async { + return _queryAdapter.query( + 'SELECT * FROM print_history WHERE servicePrintId = ?1 LIMIT 1', + mapper: (Map row) => PrintHistoryEntity( + id: row['id'] as int?, + orderId: row['orderId'] as String, + serviceOrderId: row['serviceOrderId'] as String, + servicePrintId: row['servicePrintId'] as String, + invoiceUrl: row['invoiceUrl'] as String, + printType: row['printType'] as String, + printNo: row['printNo'] as int, + retryVersion: row['retryVersion'] as int, + attemptCount: row['attemptCount'] as int, + lastError: row['lastError'] as String?, + resultPending: _boolConverter.decode(row['resultPending'] as int), + status: _printStatusConverter.decode(row['status'] as String), + printTime: row['printTime'] as String?, + syncTime: _dateTimeEmptyConverter.decode(row['syncTime'] as String), + createTime: _dateTimeConverter.decode(row['createTime'] as String)), + arguments: [servicePrintId]); + } + + @override + Future> getPendingResults() async { + return _queryAdapter.queryList( + 'SELECT * FROM print_history WHERE resultPending = 1 ORDER BY createTime ASC', + mapper: (Map row) => PrintHistoryEntity( + id: row['id'] as int?, + orderId: row['orderId'] as String, + serviceOrderId: row['serviceOrderId'] as String, + servicePrintId: row['servicePrintId'] as String, + invoiceUrl: row['invoiceUrl'] as String, + printType: row['printType'] as String, + printNo: row['printNo'] as int, + retryVersion: row['retryVersion'] as int, + attemptCount: row['attemptCount'] as int, + lastError: row['lastError'] as String?, + resultPending: _boolConverter.decode(row['resultPending'] as int), + status: _printStatusConverter.decode(row['status'] as String), + printTime: row['printTime'] as String?, + syncTime: _dateTimeEmptyConverter.decode(row['syncTime'] as String), + createTime: + _dateTimeConverter.decode(row['createTime'] as String))); + } + + @override + Future countPending() async { + return _queryAdapter.query( + 'SELECT COUNT(*) FROM print_history WHERE status = \'NONE\'', + mapper: (Map row) => row.values.first as int); + } + + @override + Future countFailed() async { + return _queryAdapter.query( + 'SELECT COUNT(*) FROM print_history WHERE status = \'ERROR\'', + mapper: (Map row) => row.values.first as int); + } + + @override + Future findLatestError() async { + return _queryAdapter.query( + 'SELECT * FROM print_history WHERE lastError IS NOT NULL AND lastError != \'\' ORDER BY id DESC LIMIT 1', + mapper: (Map row) => PrintHistoryEntity( + id: row['id'] as int?, + orderId: row['orderId'] as String, + serviceOrderId: row['serviceOrderId'] as String, + servicePrintId: row['servicePrintId'] as String, + invoiceUrl: row['invoiceUrl'] as String, + printType: row['printType'] as String, + printNo: row['printNo'] as int, + retryVersion: row['retryVersion'] as int, + attemptCount: row['attemptCount'] as int, + lastError: row['lastError'] as String?, + resultPending: _boolConverter.decode(row['resultPending'] as int), + status: _printStatusConverter.decode(row['status'] as String), + printTime: row['printTime'] as String?, + syncTime: _dateTimeEmptyConverter.decode(row['syncTime'] as String), + createTime: + _dateTimeConverter.decode(row['createTime'] as String))); + } + @override Future save(PrintHistoryEntity entity) async { await _printHistoryEntityInsertionAdapter.insert( diff --git a/lib/common/services/database/database_migration.dart b/lib/common/services/database/database_migration.dart index 11b153f..f02eb87 100644 --- a/lib/common/services/database/database_migration.dart +++ b/lib/common/services/database/database_migration.dart @@ -386,6 +386,30 @@ abstract class DatabaseMigration { ); }); + /// 版本13把旧发票记录升级为可幂等恢复、可补报结果的打印任务队列。 + static Migration migration12To13 = Migration(12, 13, (database) async { + await database.execute( + "ALTER TABLE print_history ADD COLUMN printType TEXT NOT NULL DEFAULT '1'"); + await database.execute( + 'ALTER TABLE print_history ADD COLUMN printNo INTEGER NOT NULL DEFAULT 1'); + await database.execute( + 'ALTER TABLE print_history ADD COLUMN retryVersion INTEGER NOT NULL DEFAULT 0'); + await database.execute( + 'ALTER TABLE print_history ADD COLUMN attemptCount INTEGER NOT NULL DEFAULT 0'); + await database + .execute('ALTER TABLE print_history ADD COLUMN lastError TEXT'); + await database.execute( + 'ALTER TABLE print_history ADD COLUMN resultPending INTEGER NOT NULL DEFAULT 0'); + // 旧版本没有唯一约束,先保留每个服务端任务最新一条,再建立幂等索引。 + await database.execute(''' + DELETE FROM print_history + WHERE id NOT IN (SELECT MAX(id) FROM print_history GROUP BY servicePrintId) + '''); + await database.execute( + 'CREATE UNIQUE INDEX IF NOT EXISTS index_print_history_servicePrintId ON print_history(servicePrintId)', + ); + }); + /// 按起始版本升序返回全部迁移,Floor 打开旧数据库时据此连续升级到当前版本。 static List getDatabaseMigrations() { return [ @@ -400,6 +424,7 @@ abstract class DatabaseMigration { migration9To10, migration10To11, migration11To12, + migration12To13, ]; } } diff --git a/lib/common/services/database/entity/print_history_entity.dart b/lib/common/services/database/entity/print_history_entity.dart index dcc44c7..6a46e4b 100644 --- a/lib/common/services/database/entity/print_history_entity.dart +++ b/lib/common/services/database/entity/print_history_entity.dart @@ -6,7 +6,12 @@ import 'package:pj_supermarket_cloud/common/index.dart'; part 'print_history_entity.g.dart'; @JsonSerializable() -@Entity(tableName: "print_history") +@Entity( + tableName: "print_history", + indices: [ + Index(value: ['servicePrintId'], unique: true) + ], +) class PrintHistoryEntity { /// 自增ID @PrimaryKey(autoGenerate: true) @@ -24,6 +29,18 @@ class PrintHistoryEntity { /// 发票图片地址 String invoiceUrl; + /// 服务端凭证类型、打印序号与重试版本。 + String printType; + int printNo; + int retryVersion; + + /// 当前重试版本实际执行的 USB 尝试次数和最近错误。 + int attemptCount; + String? lastError; + + /// 打印结果尚未可靠提交到服务端时保持为 true,网络恢复后继续补报。 + bool resultPending; + /// 打印状态 PrintStatus status; @@ -42,6 +59,12 @@ class PrintHistoryEntity { required this.serviceOrderId, required this.servicePrintId, required this.invoiceUrl, + this.printType = '1', + this.printNo = 1, + this.retryVersion = 0, + this.attemptCount = 0, + this.lastError, + this.resultPending = false, required this.status, this.printTime, this.syncTime, diff --git a/lib/common/services/database/entity/print_history_entity.g.dart b/lib/common/services/database/entity/print_history_entity.g.dart index 6c5fac9..70b5777 100644 --- a/lib/common/services/database/entity/print_history_entity.g.dart +++ b/lib/common/services/database/entity/print_history_entity.g.dart @@ -13,6 +13,12 @@ PrintHistoryEntity _$PrintHistoryEntityFromJson(Map json) => serviceOrderId: json['serviceOrderId'] as String, servicePrintId: json['servicePrintId'] as String, invoiceUrl: json['invoiceUrl'] as String, + printType: json['printType'] as String? ?? '1', + printNo: (json['printNo'] as num?)?.toInt() ?? 1, + retryVersion: (json['retryVersion'] as num?)?.toInt() ?? 0, + attemptCount: (json['attemptCount'] as num?)?.toInt() ?? 0, + lastError: json['lastError'] as String?, + resultPending: json['resultPending'] as bool? ?? false, status: $enumDecode(_$PrintStatusEnumMap, json['status']), printTime: json['printTime'] as String?, syncTime: json['syncTime'] == null @@ -28,6 +34,12 @@ Map _$PrintHistoryEntityToJson(PrintHistoryEntity instance) => 'serviceOrderId': instance.serviceOrderId, 'servicePrintId': instance.servicePrintId, 'invoiceUrl': instance.invoiceUrl, + 'printType': instance.printType, + 'printNo': instance.printNo, + 'retryVersion': instance.retryVersion, + 'attemptCount': instance.attemptCount, + 'lastError': instance.lastError, + 'resultPending': instance.resultPending, 'status': _$PrintStatusEnumMap[instance.status]!, 'printTime': instance.printTime, 'syncTime': instance.syncTime?.toIso8601String(), diff --git a/lib/common/utils/app/app_mq_tool.dart b/lib/common/utils/app/app_mq_tool.dart index bc83875..3e13f7f 100644 --- a/lib/common/utils/app/app_mq_tool.dart +++ b/lib/common/utils/app/app_mq_tool.dart @@ -14,13 +14,16 @@ class AppMqTool { /// 校验网络和设备绑定后重建 MQTT 连接,并注册当前设备的消息监听。 static Future mqRefresh() async { + final app = Get.find(); + if (!app.authenticatedServicesEnabled) { + logger.w('|$_tag|当前没有有效登录会话,暂不连接消息服务'); + return; + } final isNetwork = AppNetworkTool.isAutoNetwork(); if (!isNetwork) { logger.e("|$_tag|mqtt监听消息错误没有网络"); return; } - final app = Get.find(); - final sysInfo = await AppSettingTool.getSysSetting(); if (ObjectUtil.isEmpty(sysInfo.serviceId) || ObjectUtil.isEmpty(sysInfo.merchantDeptId) || @@ -76,6 +79,9 @@ class AppMqTool { logger.e("|$_tag|mqtt监听消息错误error:|$e|"); } }); + if (sysInfo.terminalRole == TerminalRole.printer) { + unawaited(SyncPrintTool.pullPendingTasks()); + } } /// 根据协议动作分发已完成设备 Topic 校验的 MQTT 消息。 @@ -85,9 +91,21 @@ class AppMqTool { if (ObjectUtil.isEmpty(msg.action)) return; final app = Get.find(); final mqUpdateEventBus = app.mqUpdateEventBus; + final setting = await AppSettingTool.getSysSetting(); + if (setting.terminalRole == TerminalRole.printer && + msg.action != MqtActionEnum.CONFIG && + msg.action != MqtActionEnum.PRINT && + msg.action != MqtActionEnum.PULL_TERMINAL_LOG) { + logger.w('|$_tag|打印角色忽略非打印消息action:${msg.action}|'); + return; + } switch (msg.action!) { case MqtActionEnum.CONFIG: await SyncConfigTool.autoSyncSysInfo(); + if (setting.terminalRole == TerminalRole.printer) { + final bootstrap = await HardApi.getPrinterBootstrap(); + await AppSettingTool.updatePrinterBootstrap(bootstrap); + } mqUpdateEventBus.fire(MqtActionEnum.CONFIG); break; case MqtActionEnum.CANTEEN_PRODUCT: @@ -123,14 +141,19 @@ class AppMqTool { final msg = MqttInvoiceMsg.fromJson(json.decode(orgStr)); if (ObjectUtil.isNotEmpty(msg.historyId) && ObjectUtil.isNotEmpty(msg.orderId) && - ObjectUtil.isNotEmpty(msg.androidOrderCode) && + setting.terminalRole == TerminalRole.printer && + (msg.targetHardId == null || + msg.targetHardId == setting.serviceId) && ObjectUtil.isNotEmpty(msg.pic)) { - SyncPrintTool.addTask( + unawaited(SyncPrintTool.addTask( orderId: msg.orderId!, - androidOrderId: msg.androidOrderCode!, + androidOrderId: msg.androidOrderCode, historyId: msg.historyId!, pic: msg.pic!, - ); + printType: msg.printType ?? '1', + printNo: msg.printNo ?? 1, + retryVersion: msg.retryVersion ?? 0, + )); } break; case MqtActionEnum.TAKE_FOOD_BROADCAST: diff --git a/lib/common/utils/app/app_network_tool.dart b/lib/common/utils/app/app_network_tool.dart index e06fc62..8d0378a 100644 --- a/lib/common/utils/app/app_network_tool.dart +++ b/lib/common/utils/app/app_network_tool.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:typed_data'; +import 'package:common_utils/common_utils.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; @@ -89,15 +90,25 @@ abstract class AppNetworkTool { /// 网络恢复后的消息连接和离线订单同步允许失败,不能产生未处理异常中断应用。 static Future _restoreNetworkServices() async { + final app = Get.find(); + if (!app.authenticatedServicesEnabled) return; + final userSetting = await AppSettingTool.getUserSetting(); + // 登录页只需要网络状态;退出后禁止网络变化重新连接 MQTT 或调用打印待办接口。 + if (ObjectUtil.isEmpty(userSetting.accessToken)) return; try { await AppMqTool.mqRefresh(); + await SyncPrintTool.pullPendingTasks(); + await SyncPrintTool.doCheckSync(); } catch (e, stack) { logger.e('|$_tag|网络恢复后连接消息服务失败error:|$e|stack:|$stack|'); } - try { - await SyncOrderTool.syncOfflineOrder(); - } catch (e, stack) { - logger.e('|$_tag|网络恢复后同步离线订单失败error:|$e|stack:|$stack|'); + final setting = await AppSettingTool.getSysSetting(); + if (setting.terminalRole == TerminalRole.cashier) { + try { + await SyncOrderTool.syncOfflineOrder(); + } catch (e, stack) { + logger.e('|$_tag|网络恢复后同步离线订单失败error:|$e|stack:|$stack|'); + } } } diff --git a/lib/common/utils/app/app_setting_tool.dart b/lib/common/utils/app/app_setting_tool.dart index 6915bdc..3c05170 100644 --- a/lib/common/utils/app/app_setting_tool.dart +++ b/lib/common/utils/app/app_setting_tool.dart @@ -755,9 +755,14 @@ abstract class AppSettingTool { } final type = data.type.toString(); if (ObjectUtil.isNotEmpty(type)) { - final t = SysType.values.fromCode(type); - if (ObjectUtil.isNotEmpty(t)) { - sysSetting.sysType = t!; + if (type == '12') { + sysSetting.terminalRole = TerminalRole.printer; + } else { + final t = SysType.values.fromCode(type); + if (ObjectUtil.isNotEmpty(t)) { + sysSetting.sysType = t!; + sysSetting.terminalRole = TerminalRole.cashier; + } } } final merchantId = data.merchantId; @@ -816,6 +821,35 @@ abstract class AppSettingTool { await _setSetting(sysJson: sysSetting); } + /// 将服务端校验过的 EY_PRINT 一对一绑定和打印参数持久化到本机。 + static Future updatePrinterBootstrap( + Map bootstrap, + ) async { + final setting = await getSysSetting(); + final sourceType = bootstrap['sourceHardType']?.toString(); + final businessType = SysType.values.fromCode(sourceType ?? ''); + if (businessType == null) { + throw const FormatException('打印终端绑定来源不是超市或食堂设备'); + } + final config = bootstrap['printerConfig'] is Map + ? Map.from(bootstrap['printerConfig'] as Map) + : {}; + setting.terminalRole = TerminalRole.printer; + setting.sysType = businessType; + setting.printerSourceHardId = bootstrap['sourceHardId']?.toString(); + setting.printerSourceHardName = bootstrap['sourceHardName']?.toString(); + setting.printerModel = config['printerModel']?.toString(); + setting.printerUsbVendorId = int.tryParse('${config['usbVendorId'] ?? ''}'); + setting.printerUsbProductId = + int.tryParse('${config['usbProductId'] ?? ''}'); + setting.printerPaperWidth = + int.tryParse('${config['paperWidth'] ?? ''}') ?? 80; + setting.printerAutoCut = '${config['autoCut'] ?? '1'}' != '0'; + setting.printerImageMode = config['imageMode']?.toString() ?? 'GS_V0'; + await _setSetting(sysJson: setting); + return setting; + } + /// 绑定设备信息 static Future bindSys(int serviceId) async { var androidId = await AppSysTool.getAndroidId(); diff --git a/lib/common/utils/app/control/app_control.dart b/lib/common/utils/app/control/app_control.dart index d208755..85233cb 100644 --- a/lib/common/utils/app/control/app_control.dart +++ b/lib/common/utils/app/control/app_control.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'package:common_utils/common_utils.dart'; import 'package:event_bus/event_bus.dart'; import 'package:permission_handler/permission_handler.dart'; @@ -97,6 +98,12 @@ class AppControl extends GetxController { bool _initializing = false; bool _databaseInitialized = false; + /// 当前账号是否允许启动需要鉴权或设备身份的 MQTT、心跳和后台同步任务。 + bool _authenticatedServicesEnabled = false; + + /// 网络监听和消息连接在执行前读取的会话开关;退出动作会先关闭它再释放资源。 + bool get authenticatedServicesEnabled => _authenticatedServicesEnabled; + int get startupCompletedCount => startupSteps .where((item) => item.status == AppStartupStepStatus.success || @@ -129,6 +136,7 @@ class AppControl extends GetxController { @override void onClose() { super.onClose(); + _authenticatedServicesEnabled = false; if (printManageInitialized) printManage.close(); if (serialCardManageInitialized) serialCardManage.close(); if (steelyardManageInitialized) srialSteelyardManage.close(); @@ -184,9 +192,6 @@ class AppControl extends GetxController { detail: '正在读取运行环境与本地终端配置', ); await AppRuntimeTool.initializePlatform(); - // 插件会在 Android 注册阶段自动创建副屏;这里统一确认可用状态并记录常驻标记, - // 即使无副屏或权限不足也只影响设备状态,不阻塞后续启动流程。 - unawaited(AppSubScreenTool.show()); await AppSettingTool.removeSettingCache(); _setStartupStep( @@ -199,13 +204,22 @@ class AppControl extends GetxController { _databaseInitialized = true; } - var storageGranted = - await Permission.manageExternalStorage.request().isGranted; - if (!storageGranted) { - storageGranted = await Permission.storage.request().isGranted; - } - final setting = await AppSettingTool.getSysSetting(); + final isPrinterTerminal = setting.terminalRole == TerminalRole.printer; + // 打印终端没有顾客副屏,不能在启动时创建第二 Flutter Engine。 + if (!isPrinterTerminal) { + // 插件会在 Android 注册阶段自动创建副屏;这里统一确认可用状态并记录常驻标记, + // 即使无副屏或权限不足也只影响设备状态,不阻塞后续启动流程。 + unawaited(AppSubScreenTool.show()); + } + var storageGranted = true; + if (!isPrinterTerminal) { + storageGranted = + await Permission.manageExternalStorage.request().isGranted; + if (!storageGranted) { + storageGranted = await Permission.storage.request().isGranted; + } + } AppRuntimeTool.applyManualHardwareInput(setting.manualHardwareInput); final environment = _resolveEnvironment(setting); startupEnvironmentDescription.value = environment.title; @@ -230,28 +244,37 @@ class AppControl extends GetxController { detail: '正在创建打印、读卡与电子秤管理服务', ); if (!printManageInitialized) printManage = PrintManage(); - if (!serialCardManageInitialized) serialCardManage = SerialCardManage(); - if (!steelyardManageInitialized) { - srialSteelyardManage = SerialSteelyardManage(); - } await AppDeviceStatusTool.loadCachedStatus(); - // 硬件检查失败只能影响状态展示,不能阻塞收银终端进入业务页面。 - unawaited( - AppDeviceStatusTool.refreshAll() - .catchError((Object error, StackTrace stack) { - logger.w('|$_tag|首次检查设备状态失败error:|$error|stack:|$stack|'); - return AppDeviceStatusTool.status.value; - }), - ); - await TtsManage.initialize(); + if (isPrinterTerminal) { + // 打印角色只探测 USB 打印机,避免初始化读卡、电子秤、人脸和副屏插件。 + unawaited(printManage.checkAvailable().then( + (available) => AppDeviceStatusTool.updatePrinter(available), + )); + } else { + if (!serialCardManageInitialized) serialCardManage = SerialCardManage(); + if (!steelyardManageInitialized) { + srialSteelyardManage = SerialSteelyardManage(); + } + // 硬件检查失败只能影响状态展示,不能阻塞收银终端进入业务页面。 + unawaited( + AppDeviceStatusTool.refreshAll() + .catchError((Object error, StackTrace stack) { + logger.w('|$_tag|首次检查设备状态失败error:|$error|stack:|$stack|'); + return AppDeviceStatusTool.status.value; + }), + ); + await TtsManage.initialize(); + } _setStartupStep( 2, status: environment.manualHardwareInput ? AppStartupStepStatus.warning : AppStartupStepStatus.success, - detail: environment.manualHardwareInput - ? '当前为手动硬件输入模式,实体设备将在业务使用时按需连接' - : '设备管理服务已就绪,实体设备将在业务使用时按需连接', + detail: isPrinterTerminal + ? 'USB 打印服务已就绪,未启动收银外设' + : environment.manualHardwareInput + ? '当前为手动硬件输入模式,实体设备将在业务使用时按需连接' + : '设备管理服务已就绪,实体设备将在业务使用时按需连接', ); _setStartupStep( @@ -259,19 +282,28 @@ class AppControl extends GetxController { status: AppStartupStepStatus.running, detail: '正在检查激活参数并装载本地人脸库', ); - final facePreparation = await AppFaceTool.prepareForStartup(); - faceStartupPreparationResult.value = facePreparation; - _setStartupStep( - 3, - status: facePreparation.status == FaceStartupPreparationStatus.success - ? AppStartupStepStatus.success - : AppStartupStepStatus.warning, - detail: _facePreparationDetail(facePreparation), - ); + if (isPrinterTerminal) { + _setStartupStep( + 3, + status: AppStartupStepStatus.success, + detail: '打印角色无需加载人脸识别能力', + ); + } else { + final facePreparation = await AppFaceTool.prepareForStartup(); + faceStartupPreparationResult.value = facePreparation; + _setStartupStep( + 3, + status: facePreparation.status == FaceStartupPreparationStatus.success + ? AppStartupStepStatus.success + : AppStartupStepStatus.warning, + detail: _facePreparationDetail(facePreparation), + ); + } - final backupSucceeded = - storageGranted ? await AppFileTool.backupSqlFile() : false; - if (storageGranted && !backupSucceeded) { + final backupSucceeded = !isPrinterTerminal && storageGranted + ? await AppFileTool.backupSqlFile() + : true; + if (!isPrinterTerminal && storageGranted && !backupSucceeded) { _setStartupStep( 1, status: AppStartupStepStatus.warning, @@ -283,9 +315,16 @@ class AppControl extends GetxController { if (AppNetworkTool.subscription == null) { AppNetworkTool.listenNetwork(); } - await SyncCheckTool.instance.startAll(); + final userSetting = await AppSettingTool.getUserSetting(); + _authenticatedServicesEnabled = + ObjectUtil.isNotEmpty(userSetting.accessToken); + if (_authenticatedServicesEnabled) { + await SyncCheckTool.instance.startAll( + terminalRole: setting.terminalRole, + ); + TerminalHeartbeatTool.start(); + } await TerminalAutoUpdateTool.recoverPendingUpdate(); - TerminalHeartbeatTool.start(); startupFinished.value = true; startupStatusLabel.value = startupSteps @@ -321,8 +360,25 @@ class AppControl extends GetxController { await initAsync(destination: destination, force: true); } + /// 登录或设备角色切换后恢复该角色允许的后台任务与在线心跳。 + Future resumeAuthenticatedServices(TerminalRole terminalRole) async { + _authenticatedServicesEnabled = true; + await SyncCheckTool.instance.startAll(terminalRole: terminalRole); + TerminalHeartbeatTool.start(); + } + + /// 退出登录时停止所有需要账号或设备身份的后台任务,并断开消息订阅。 + Future suspendAuthenticatedServices() async { + _authenticatedServicesEnabled = false; + await SyncCheckTool.instance.dispose(); + TerminalHeartbeatTool.stop(); + AppMqTool.mqListenClose(); + await MqManager.closeMqtt(); + } + /// 关闭已创建的设备和后台资源,供核心启动异常后的重试流程重新初始化。 void _closePartiallyInitializedResources() { + _authenticatedServicesEnabled = false; if (printManageInitialized) printManage.close(); if (serialCardManageInitialized) serialCardManage.close(); if (steelyardManageInitialized) srialSteelyardManage.close(); @@ -374,6 +430,14 @@ class AppControl extends GetxController { String stepDetail, bool manualHardwareInput, }) _resolveEnvironment(SysSetting setting) { + if (setting.terminalRole == TerminalRole.printer) { + return ( + title: '打印终端模式', + detail: '仅启用网络、MQTT、本地队列和 USB 打印能力。', + stepDetail: '已读取尔印打印终端配置', + manualHardwareInput: false, + ); + } final manualHardwareInput = AppRuntimeTool.isEmulator || setting.manualHardwareInput; if (AppRuntimeTool.isEmulator) { diff --git a/lib/common/utils/manage/print_manage.dart b/lib/common/utils/manage/print_manage.dart index 4c055e1..72a4a78 100644 --- a/lib/common/utils/manage/print_manage.dart +++ b/lib/common/utils/manage/print_manage.dart @@ -1,38 +1,63 @@ import 'package:common_utils/common_utils.dart'; import 'package:drago_pos_printer/drago_pos_printer.dart'; import 'package:get/get.dart'; +import 'package:image/image.dart' as imglib; import 'package:pj_supermarket_cloud/common/index.dart'; +/// USB ESC/POS 打印机管理器,负责设备发现、连接、图片指令生成和完整字节写入。 class PrintManage extends BaseFacilityManage { + /// 打印机发现、连接和输出日志使用的稳定模块标签。 static const String _tag = "打印机管理"; + /// 当前已选择并连接的 USB 打印管理器;未连接时为空。 USBPrinterManager? manager; + + /// 当前 ESC/POS 能力配置,成功打开打印机后加载。 CapabilityProfile? profile; + + /// ESC/POS 生成器使用的纸宽,首期固定为 80mm。 int paperSizeWidthMM = PaperSizeWidth.mm80; + + /// 80mm 纸宽对应的文本列数,由图片模式之外的兼容命令使用。 int maxPerLine = PaperSizeMaxPerLine.mm80; + /// 最近一次发现、连接、下载或 USB 写入错误,供无人值守状态面板展示。 + String? lastError; + + /// 历史收银设备自动发现使用的已知 Vendor ID;后台未配置 VID/PID 时才允许回退。 static const List _printVendorIds = [ "19267", "4070", "10473", ]; + /// 返回设备状态体系中使用的打印机名称。 @override String setName() => UsbFacilityNames.print.title; /// 只发现 USB 设备,不建立打印连接,避免后台状态任务占用实际打印通道。 Future checkAvailable() async { try { + final setting = await AppSettingTool.getSysSetting(); final printers = await USBPrinterManager.discover(); - return _findTarget(printers) != null; + return _findTarget(printers, setting) != null; } catch (e) { + lastError = e.toString(); logger.e("|$_tag|检查打印机失败error|$e|"); return false; } } - USBPrinter? _findTarget(List printers) { + /// 优先按后台 VID/PID 精确匹配;未配置时才沿用已知厂商或唯一设备回退规则。 + USBPrinter? _findTarget(List printers, SysSetting setting) { + final configuredVendor = setting.printerUsbVendorId; + final configuredProduct = setting.printerUsbProductId; + if (configuredVendor != null && configuredProduct != null) { + return printers.firstWhereOrNull((printer) => + printer.vendorId == configuredVendor && + printer.productId == configuredProduct); + } USBPrinter? target = printers.firstWhereOrNull((printer) { return _printVendorIds.contains(printer.vendorId?.toString() ?? "-1"); }); @@ -40,6 +65,7 @@ class PrintManage extends BaseFacilityManage { return target; } + /// 打开目标 USB 打印机并装载 ESC/POS 能力;失败只更新设备状态,不改变订单结果。 @override Future open() async { final check = await super.open(); @@ -48,8 +74,13 @@ class PrintManage extends BaseFacilityManage { return false; } try { - final target = _findTarget(await USBPrinterManager.discover()); + lastError = null; + final setting = await AppSettingTool.getSysSetting(); + final target = _findTarget(await USBPrinterManager.discover(), setting); if (target == null) { + lastError = setting.printerUsbVendorId == null + ? '未发现可用 USB 打印机' + : '未发现后台配置 VID/PID 对应的 USB 打印机'; AppDeviceStatusTool.updatePrinter(false); return false; } @@ -59,24 +90,36 @@ class PrintManage extends BaseFacilityManage { AppDeviceStatusTool.updatePrinter(true); return true; } catch (e) { + lastError = e.toString(); AppDeviceStatusTool.updatePrinter(false); logger.e("|$_tag|打开打印机失败error|$e|"); return false; } } - /// 下载发票图片并写入已连接的 USB 打印机;仅实际写入周期在顶栏展示“打印中”。 + /// 下载打印凭证图片,按 576px 等比缩放后生成 GS v 0 或 ESC * 字节并完整写入 USB。 + /// + /// 返回 true 只表示图片、走纸和可选切刀命令均被 USB 接口接受,不代表从硬件层确认出纸。 Future printImg(String img) async { if (ObjectUtil.isEmpty(manager)) { AppDeviceStatusTool.updatePrinterWorking(false); return false; } try { - final image = await AppNetworkTool.loadImageFromNetwork(img); - if (ObjectUtil.isEmpty(image)) { - Toast.error(msg: "找不到发票图片"); + final imageBytes = await AppNetworkTool.downloadImage(img); + final image = imageBytes == null ? null : imglib.decodeImage(imageBytes); + if (image == null) { + lastError = '无法下载或解码打印图片'; return false; } + final setting = await AppSettingTool.getSysSetting(); + final normalized = image.width == 576 + ? image + : imglib.copyResize( + image, + width: 576, + interpolation: imglib.Interpolation.linear, + ); final generator = EscGenerator( paperSizeWidthMM, maxPerLine, @@ -84,15 +127,19 @@ class PrintManage extends BaseFacilityManage { ); final bytes = [ ...generator.feed(1), - ...generator.image(image!), + ...(setting.printerImageMode == 'ESC_STAR' + ? generator.image(normalized) + : generator.imageRaster(normalized)), ...generator.feed(1), - ...generator.cut(), + if (setting.printerAutoCut) ...generator.cut(), ]; AppDeviceStatusTool.updatePrinterWorking(true); await manager!.writeBytes(bytes); + lastError = null; AppDeviceStatusTool.updatePrinter(true); return true; } catch (e) { + lastError = e.toString(); AppDeviceStatusTool.updatePrinter(false); logger.e("|$_tag|打印机失败error|$e|"); return false; @@ -101,22 +148,25 @@ class PrintManage extends BaseFacilityManage { } } + /// 关闭当前 USB 连接并清除“打印中”状态;重复调用不报错。 @override Future close() async { AppDeviceStatusTool.updatePrinterWorking(false); if (ObjectUtil.isNotEmpty(manager)) { if (manager != null) { - manager?.disconnect(); + await manager?.disconnect(); } manager = null; } } + /// USB 授权由项目内 Android 插件在连接时申请,此处无需前置 Flutter 权限。 @override Future checkPermission() async { return true; } + /// USB 授权由项目内 Android 插件负责,保留基类接口并返回可继续连接。 @override Future applyPermission() async { return true; diff --git a/lib/common/utils/sync/sync_check_tool.dart b/lib/common/utils/sync/sync_check_tool.dart index ac40cf1..56abc1c 100644 --- a/lib/common/utils/sync/sync_check_tool.dart +++ b/lib/common/utils/sync/sync_check_tool.dart @@ -19,6 +19,16 @@ class SyncCheckTool { /// 应用级任务调度单例,由 AppControl 初始化并由设置页读取响应式状态。 static final SyncCheckTool instance = SyncCheckTool._(); + /// 打印角色允许注册的后台任务;其余商品、人脸、订单和取餐任务属于收银角色。 + static const Set _printerTaskTypes = { + ScheduledTaskType.mqReconnect, + ScheduledTaskType.printCheck, + ScheduledTaskType.deviceStatus, + ScheduledTaskType.autoUpdate, + ScheduledTaskType.terminalLogUpload, + ScheduledTaskType.expiredLogCleanup, + }; + /// 当前已加载的任务配置,以稳定任务类型为键并向设置页提供响应式更新。 final configs = {}.obs; @@ -52,6 +62,16 @@ class SyncCheckTool { /// 是否已经完成本轮启动,防止 AppControl 重复注册相同 Cron 任务。 bool _started = false; + /// 当前调度任务对应的终端角色;设备首次绑定改变角色时据此重建任务集合。 + TerminalRole? _activeRole; + + /// 当前已经注册的终端角色,供状态诊断和测试确认角色切换结果。 + TerminalRole? get activeRole => _activeRole; + + /// 当前注册到 Cron 的任务类型快照。 + Set get scheduledTaskTypes => + Set.unmodifiable(_scheduledTasks.keys); + /// 当前进程最近一次补排部门人脸同步的本地日期,保证同一天最多自动补排一次。 DateTime? _lastDepartmentFaceCatchUpDate; @@ -333,19 +353,33 @@ class SyncCheckTool { ), ]; - /// 初始化结构化配置并注册全部已启用任务;同一生命周期内重复调用直接返回。 - Future startAll() async { - if (_started) return; + /// 初始化结构化配置并注册全部已启用任务;角色改变时先释放旧角色任务再重新注册。 + Future startAll( + {TerminalRole terminalRole = TerminalRole.cashier}) async { await _disposeFuture; + if (_started && _activeRole == terminalRole) return; + if (_started) await dispose(); _started = true; + _activeRole = terminalRole; _cron ??= Cron(); await _loadState(); for (final config in configs.values) { + if (!supportsTask(terminalRole, config.type)) { + continue; + } await _rebuildTask(config); } - queueMissedDepartmentFaceSync(); + if (terminalRole == TerminalRole.cashier) { + queueMissedDepartmentFaceSync(); + } } + /// 判断指定角色是否允许执行某类后台任务,打印角色只保留通信、打印和运维任务。 + static bool supportsTask( + TerminalRole terminalRole, ScheduledTaskType taskType) => + terminalRole == TerminalRole.cashier || + _printerTaskTypes.contains(taskType); + /// 在每天 00:40 之后检查当天是否成功完成部门人脸同步,并在首次启动或恢复时补排一次。 /// /// 补排只加入同一人脸任务队列并要求完整空闲,不会阻塞启动或打断支付、识别和业务弹框。 @@ -787,6 +821,7 @@ class SyncCheckTool { if (!AppNetworkTool.isAutoNetwork()) { return const _ScheduledTaskActionResult.skipped('网络不可用,本次跳过'); } + await SyncPrintTool.pullPendingTasks(); await SyncPrintTool.doCheckPrint(); await SyncPrintTool.doCheckSync(); return const _ScheduledTaskActionResult.success('打印任务检查完成'); @@ -804,7 +839,14 @@ class SyncCheckTool { ); return const _ScheduledTaskActionResult.success('取餐广播检查完成'); case ScheduledTaskType.deviceStatus: - await AppDeviceStatusTool.refreshAll(); + final setting = await AppSettingTool.getSysSetting(); + if (setting.terminalRole == TerminalRole.printer) { + final available = + await Get.find().printManage.checkAvailable(); + AppDeviceStatusTool.updatePrinter(available); + } else { + await AppDeviceStatusTool.refreshAll(); + } return const _ScheduledTaskActionResult.success('设备状态检查完成'); case ScheduledTaskType.autoUpdate: final update = await TerminalAutoUpdateTool.run(); @@ -1044,6 +1086,7 @@ class SyncCheckTool { /// 取消轮询和全部 Cron 句柄、清空待执行状态并关闭 Cron 实例。 Future _dispose() async { _started = false; + _activeRole = null; _idleTimer?.cancel(); _idleTimer = null; for (final task in _scheduledTasks.values) { diff --git a/lib/common/utils/sync/sync_print_tool.dart b/lib/common/utils/sync/sync_print_tool.dart index e076829..95bdfb6 100644 --- a/lib/common/utils/sync/sync_print_tool.dart +++ b/lib/common/utils/sync/sync_print_tool.dart @@ -1,132 +1,289 @@ -import 'dart:convert'; +import 'dart:async'; import 'package:common_utils/common_utils.dart'; import 'package:get/get.dart'; import 'package:pj_supermarket_cloud/common/index.dart'; +/// EY_PRINT 本地打印队列,负责双通道入队、版本幂等、串行 USB 重试和结果补报。 abstract class SyncPrintTool { - static const _tag = "sync打印工具"; + /// 打印队列诊断日志使用的稳定模块标签。 + static const _tag = 'sync打印工具'; + /// 是否已有串行打印循环正在执行,防止两个任务同时占用 USB 通道。 static bool _isPrint = false; + /// 是否已有打印结果补报正在执行,防止网络恢复和定时任务重复提交。 static bool _isSync = false; + /// 是否已有 HTTP 待办补拉正在执行,防止 MQTT 重连和页面恢复重复拉取。 + static bool _isPulling = false; + + /// 将 MQTT 与 HTTP 的并发入队串行化,避免唯一索引插入竞争。 + static Future _enqueueTail = Future.value(); + + /// 三次 USB 尝试前的等待时间:首次立即执行,后两次失败后分别等待 5 秒和 30 秒。 + /// + /// 方案同时限定“最多三次 USB 尝试”,因此不会再安排会形成第 4 次出纸的 120 秒重试。 + static const attemptDelays = [ + Duration.zero, + Duration(seconds: 5), + Duration(seconds: 30), + ]; + + /// 将 MQTT 或 HTTP 待办按服务端任务 ID 转换为唯一的本地任务。 + /// + /// [orderId] 是服务端订单 ID,[androidOrderId] 允许线上预约订单为空;[historyId] + /// 是本地唯一去重键,[retryVersion] 只有高于本地失败版本时才允许再次进入打印队列。 static Future addTask({ required String orderId, - required String androidOrderId, + String? androidOrderId, required String historyId, required String pic, + String printType = '1', + int printNo = 1, + int retryVersion = 0, }) async { - final appC = Get.find(); - final printHistoryDao = appC.dataBase.getPrintHistoryDao; - final saveObj = PrintHistoryEntity( - orderId: androidOrderId, - serviceOrderId: orderId, - servicePrintId: historyId, - invoiceUrl: pic, - status: PrintStatus.NONE, - createTime: DateTime.now(), + final operation = _enqueueTail.then((_) => _addTask( + orderId: orderId, + androidOrderId: androidOrderId, + historyId: historyId, + pic: pic, + printType: printType, + printNo: printNo, + retryVersion: retryVersion, + )); + // 队尾吞掉上一项异常以保证后续任务仍可入队,当前调用者仍等待原 operation 获得真实结果。 + _enqueueTail = operation.then( + (_) {}, + onError: (Object _, StackTrace __) {}, ); - await printHistoryDao.save(saveObj); - logger.i("|$_tag|添加到打印记录到本地数据库,json:|${saveObj.toJson()}|"); - await SyncPrintTool.doCheckPrint(); + await operation; } - // 检查打印 - static Future doCheckPrint() async { - if (_isPrint) { - logger.i("|$_tag|检查打印,正在打印中"); + /// 在串行入队区内执行任务新增或失败版本更新。 + static Future _addTask({ + required String orderId, + String? androidOrderId, + required String historyId, + required String pic, + required String printType, + required int printNo, + required int retryVersion, + }) async { + final dao = Get.find().dataBase.getPrintHistoryDao; + final existing = await dao.findByServicePrintId(historyId); + if (existing != null) { + if (!canApplyTaskVersion(existing, retryVersion)) { + if (retryVersion > existing.retryVersion && + existing.status != PrintStatus.ERROR) { + logger.w( + '|$_tag|忽略非失败任务的超前版本|task:$historyId|status:${existing.status.name}|'); + return; + } + logger.i('|$_tag|忽略重复打印任务|task:$historyId|version:$retryVersion|'); + return; + } + existing + ..orderId = androidOrderId?.trim().isNotEmpty == true + ? androidOrderId! + : orderId + ..serviceOrderId = orderId + ..invoiceUrl = pic + ..printType = printType + ..printNo = printNo + ..retryVersion = retryVersion + ..attemptCount = 0 + ..lastError = null + ..resultPending = false + ..status = PrintStatus.NONE + ..printTime = null + ..syncTime = null; + await dao.edit(existing); + } else { + await dao.save(PrintHistoryEntity( + orderId: androidOrderId?.trim().isNotEmpty == true + ? androidOrderId! + : orderId, + serviceOrderId: orderId, + servicePrintId: historyId, + invoiceUrl: pic, + printType: printType, + printNo: printNo, + retryVersion: retryVersion, + status: PrintStatus.NONE, + createTime: DateTime.now(), + )); + } + logger.i('|$_tag|打印任务已入队|task:$historyId|version:$retryVersion|'); + unawaited(doCheckPrint()); + } + + /// 只有本地任务已失败且服务端版本更高时,才允许同一任务重新出纸。 + static bool canApplyTaskVersion( + PrintHistoryEntity existing, int incomingVersion) => + existing.status == PrintStatus.ERROR && + incomingVersion > existing.retryVersion; + + /// 在登录、MQTT 重连、应用恢复和周期检查时补拉本机待办。 + static Future pullPendingTasks() async { + if (_isPulling || !AppNetworkTool.isAutoNetwork()) { return; } - final appC = Get.find(); - final printHistoryDao = appC.dataBase.getPrintHistoryDao; - final printFacility = appC.printManage; + final setting = await AppSettingTool.getSysSetting(); + if (setting.terminalRole != TerminalRole.printer || + setting.serviceId == null) { + return; + } + _isPulling = true; try { - // 检查有没有需要打印的 - final list = await printHistoryDao.getAll(PrintStatus.NONE.name); - if (ObjectUtil.isEmptyList(list)) { - _isPrint = false; - logger.i("|$_tag|检查打印,没有需要打印的发票"); - return; + final tasks = await HardApi.getPrinterTasks(); + for (final task in tasks) { + if (task.historyId == null || + task.orderId == null || + task.pic == null) { + continue; + } + await addTask( + orderId: task.orderId!, + androidOrderId: task.androidOrderCode, + historyId: task.historyId!, + pic: task.pic!, + printType: task.printType ?? '1', + printNo: task.printNo ?? 1, + retryVersion: task.retryVersion ?? 0, + ); } - final isOpen = await printFacility.open(); - if (!isOpen) { - _isPrint = false; - logger.e("|$_tag|检查打印,打开打印机失败"); - return; + } catch (error, stack) { + logger.w('|$_tag|补拉打印待办失败|error:$error|stack:$stack|'); + } finally { + _isPulling = false; + } + } + + /// 串行处理全部待打印任务,同一时间只允许一个 USB 写入流程。 + static Future doCheckPrint() async { + if (_isPrint) { + return; + } + _isPrint = true; + final dao = Get.find().dataBase.getPrintHistoryDao; + try { + // 循环查库可接住打印期间新入队的 MQTT/HTTP 任务,不必等待下一次十分钟补偿扫描。 + while (true) { + final list = await dao.getAll(PrintStatus.NONE.name); + if (list.isEmpty) { + break; + } + for (final task in list) { + await _print(task); + } } - for (final v in list) { - await doPrint(v); - } - } catch (e) { - logger.e("|$_tag|检查打印,打印错误,error:|$e|"); + } catch (error, stack) { + logger.e('|$_tag|打印队列执行异常|error:$error|stack:$stack|'); } finally { _isPrint = false; } } - // 检查同步 + /// 补报本地已经完成的成功或失败结果,断网不会导致再次出纸。 static Future doCheckSync() async { - if (_isSync) { - logger.i("|$_tag|检查打印,正在打印中"); + if (_isSync || !AppNetworkTool.isAutoNetwork()) { return; } - final app = Get.find(); - final printHistoryDao = app.dataBase.getPrintHistoryDao; - final list = await printHistoryDao.getAll(PrintStatus.SUCCESS.name); - if (ObjectUtil.isEmptyList(list)) { - _isSync = false; - return; - } - for (final v in list) { - final printPa = { - "orderId": v.serviceOrderId, - "historyId": v.servicePrintId, - }; - try { - final flag = await HardApi.printInvoice(printPa); - if (!flag) Exception("|$_tag|同步失败api错误params|${v.toJson()}|"); - v.status = PrintStatus.SYNC; - v.syncTime = DateTime.now(); - await printHistoryDao.edit(v); - } catch (e) { - logger.e("|$_tag|检查同步,打印错误,error:|$e|"); - v.status = PrintStatus.ERROR; - await printHistoryDao.edit(v); - } finally { - _isSync = false; + _isSync = true; + final dao = Get.find().dataBase.getPrintHistoryDao; + try { + for (final task in await dao.getPendingResults()) { + await _report(task); } + } finally { + _isSync = false; } } - // 执行打印 - static Future doPrint(PrintHistoryEntity v) async { + /// 串行执行单个任务的最多三次 USB 尝试,并在最终状态落库后补报服务端。 + static Future _print(PrintHistoryEntity task) async { final app = Get.find(); - final printHistoryDao = app.dataBase.getPrintHistoryDao; - final printFacility = app.printManage; - try { - final isPrint = await printFacility.printImg(v.invoiceUrl); - if (!isPrint) { - v.status = PrintStatus.ERROR; - await printHistoryDao.edit(v); - throw Exception("|$_tag|打印失败params|${v.toJson()}|"); + final dao = app.dataBase.getPrintHistoryDao; + for (var index = 0; index < attemptDelays.length; index++) { + final delay = attemptDelays[index]; + if (delay > Duration.zero) { + await Future.delayed(delay); } - // 更新本地数据库 - v.status = PrintStatus.SUCCESS; - v.printTime = DateUtil.formatDate(DateTime.now()); - await printHistoryDao.edit(v); - // 同步到服务端 - final printPa = { - "orderId": v.serviceOrderId, - "historyId": v.servicePrintId, - }; - await HardApi.printInvoice(printPa); - v.status = PrintStatus.SYNC; - v.syncTime = DateTime.now(); - await printHistoryDao.edit(v); - } catch (e) { - logger.i("|$_tag|执行打印,打印错误,error:|$e|"); + task.attemptCount = index + 1; + await dao.edit(task); + var opened = false; + try { + opened = await app.printManage.open(); + if (!opened) { + throw StateError(app.printManage.lastError ?? 'USB 打印机连接失败'); + } + final accepted = await app.printManage.printImg(task.invoiceUrl); + if (!accepted) { + throw StateError(app.printManage.lastError ?? 'USB 未完整接收打印数据'); + } + task + ..status = PrintStatus.SUCCESS + ..printTime = DateUtil.formatDate(DateTime.now()) + ..lastError = null + ..resultPending = true; + await dao.edit(task); + await _report(task); + return; + } catch (error, stack) { + task.lastError = _errorText(error); + await dao.edit(task); + logger.w( + '|$_tag|USB 打印尝试失败|task:${task.servicePrintId}|attempt:${task.attemptCount}|error:$error|stack:$stack|'); + } finally { + if (opened) { + await app.printManage.close(); + } + } + } + task + ..status = PrintStatus.ERROR + ..resultPending = true; + await dao.edit(task); + await _report(task); + } + + /// 向服务端提交当前重试版本的最终结果;失败时保留 resultPending 等待网络恢复补报。 + static Future _report(PrintHistoryEntity task) async { + if (!AppNetworkTool.isAutoNetwork()) { + return; + } + final success = + task.status == PrintStatus.SUCCESS || task.status == PrintStatus.SYNC; + try { + final accepted = await HardApi.reportPrinterResult( + taskId: task.servicePrintId, + orderId: task.serviceOrderId, + retryVersion: task.retryVersion, + success: success, + attemptCount: task.attemptCount, + errorMessage: success ? null : task.lastError, + ); + if (!accepted) { + throw StateError('服务端拒绝打印结果'); + } + task + ..resultPending = false + ..syncTime = DateTime.now(); + if (success) { + task.status = PrintStatus.SYNC; + } + await Get.find().dataBase.getPrintHistoryDao.edit(task); + } catch (error, stack) { + logger.w( + '|$_tag|打印结果等待补报|task:${task.servicePrintId}|error:$error|stack:$stack|'); } } + + /// 将本地异常压缩成最多 500 个字符的单行可上报文本,避免持久化堆栈或敏感上下文。 + static String _errorText(Object error) { + final text = error.toString().replaceAll(RegExp(r'\s+'), ' ').trim(); + return text.length <= 500 ? text : text.substring(0, 500); + } } diff --git a/lib/common/values/constants.dart b/lib/common/values/constants.dart index 72c24a3..e22da94 100644 --- a/lib/common/values/constants.dart +++ b/lib/common/values/constants.dart @@ -15,8 +15,8 @@ class Constants { static const pwdEncKey = 'pigxpigxpigxpigx'; /// 服务 url 线上 - static const apiUrl = 'https://dzc-test-1.fy.takin.cc'; - // static const apiUrl = 'https://www.zianxin.com/api'; + // static const apiUrl = 'https://dzc-test-1.fy.takin.cc'; + static const apiUrl = 'https://www.zianxin.com/api'; // static const apiUrl = 'https://dzc-test-1.fy.takin.cc'; diff --git a/lib/pages/index.dart b/lib/pages/index.dart index 2b98a5c..e37c13b 100644 --- a/lib/pages/index.dart +++ b/lib/pages/index.dart @@ -4,4 +4,6 @@ export './root/load/controller.dart'; export './root/load/view.dart'; export './root/login/controller.dart'; export './root/login/view.dart'; +export './root/device_setup/view.dart'; +export './printer/view.dart'; export './terminal/view.dart'; diff --git a/lib/pages/printer/view.dart b/lib/pages/printer/view.dart new file mode 100644 index 0000000..8b36443 --- /dev/null +++ b/lib/pages/printer/view.dart @@ -0,0 +1,438 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:mqtt_client/mqtt_client.dart'; + +import 'package:pj_supermarket_cloud/common/index.dart'; + +/// EY_PRINT 专用状态面板,只承载绑定、连接、队列和故障恢复信息。 +class PrinterStatusPage extends StatefulWidget { + /// 创建打印终端常驻状态页。 + const PrinterStatusPage({super.key}); + + /// 创建负责生命周期恢复、轮询和本地队列统计的页面状态。 + @override + State createState() => _PrinterStatusPageState(); +} + +/// 打印终端状态页的进程内状态,页面销毁时必须取消轮询和生命周期观察。 +class _PrinterStatusPageState extends State + with WidgetsBindingObserver { + /// 每 30 秒刷新一次本地 USB 与队列状态的页面级计时器。 + Timer? _timer; + + /// 最近一次从本地设置读取的绑定和打印机配置快照。 + SysSetting? _setting; + + /// 本机尚未完成 USB 输出的任务数量。 + int _pending = 0; + + /// 本机等待后台人工重试的失败任务数量。 + int _failed = 0; + + /// 最近一次本地打印或连接错误;为空表示暂无已知错误。 + String? _lastError; + + /// 当前配置的 VID/PID 或自动发现的唯一打印机是否在线。 + bool _usbAvailable = false; + + /// 是否正在执行服务端配置刷新、MQTT 重连和待办补拉。 + bool _refreshing = false; + + /// 退出流程开始后为 true,用于阻止尚未结束的恢复任务重新连接消息服务。 + bool _sessionEnding = false; + + /// 注册应用恢复监听,并启动首次恢复和页面状态轮询。 + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + unawaited(_restoreConnections()); + _timer = Timer.periodic( + const Duration(seconds: 30), (_) => unawaited(_refreshStatus())); + } + + /// 取消所有页面级资源,避免退出后继续刷新已销毁的 State。 + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + _timer?.cancel(); + super.dispose(); + } + + /// 应用回到前台时恢复 MQTT、HTTP 待办和待补报结果。 + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.resumed) { + unawaited(_restoreConnections()); + } + } + + /// 刷新服务端绑定配置、恢复 MQTT,并按顺序补拉、补报和启动本地打印。 + Future _restoreConnections() async { + if (_refreshing || _sessionEnding) { + return; + } + _refreshing = true; + if (mounted) setState(() {}); + try { + if (AppNetworkTool.isAutoNetwork()) { + final bootstrap = await HardApi.getPrinterBootstrap(); + if (_sessionEnding) return; + await AppSettingTool.updatePrinterBootstrap(bootstrap); + if (_sessionEnding) return; + await AppMqTool.mqRefresh(); + if (_sessionEnding) return; + await SyncPrintTool.pullPendingTasks(); + await SyncPrintTool.doCheckSync(); + } + if (_sessionEnding) return; + unawaited(SyncPrintTool.doCheckPrint()); + } catch (error, stack) { + logger.w('|打印终端恢复连接失败|error:$error|stack:$stack|'); + _lastError = error.toString(); + } finally { + _refreshing = false; + if (!_sessionEnding) await _refreshStatus(); + } + } + + /// 汇总本地设置、USB 可用性和队列数量,并以一次 setState 更新面板。 + Future _refreshStatus() async { + if (_sessionEnding) return; + final app = Get.find(); + final dao = app.dataBase.getPrintHistoryDao; + final setting = await AppSettingTool.getSysSetting(); + final latestError = await dao.findLatestError(); + final usb = await app.printManage.checkAvailable(); + final pending = await dao.countPending() ?? 0; + final failed = await dao.countFailed() ?? 0; + if (!mounted) { + return; + } + setState(() { + _setting = setting; + _usbAvailable = usb; + _lastError = + latestError?.lastError ?? app.printManage.lastError ?? _lastError; + _pending = pending; + _failed = failed; + }); + } + + /// 展示由 PC 管理端维护的只读打印机配置摘要。 + Future _showSettings() async { + final setting = _setting ?? await AppSettingTool.getSysSetting(); + if (!mounted) return; + await showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('打印机配置'), + content: Text( + '型号:${setting.printerModel ?? "未填写"}\n' + 'USB VID/PID:${setting.printerUsbVendorId ?? "未填写"} / ${setting.printerUsbProductId ?? "未填写"}\n' + '纸宽:${setting.printerPaperWidth}mm\n' + '图片模式:${setting.printerImageMode}\n' + '自动切刀:${setting.printerAutoCut ? "开启" : "关闭"}', + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), child: const Text('关闭')) + ], + ), + ); + } + + /// 二次确认后关闭 MQTT、清理登录态并返回登录页。 + Future _logout() async { + final confirmed = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('退出打印终端'), + content: const Text('退出后将停止接收新的打印任务。'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, false), + child: const Text('取消')), + FilledButton( + onPressed: () => Navigator.pop(context, true), + child: const Text('退出')), + ], + ), + ); + if (confirmed != true) return; + _sessionEnding = true; + await AppSettingTool.setUserSetting(UserSetting()); + await Get.find().suspendAuthenticatedServices(); + Get.offAllNamed(RouteNames.login); + } + + /// 构建打印角色专用的绑定、连接、队列和故障恢复面板。 + @override + Widget build(BuildContext context) { + final setting = _setting; + final mqtt = MqManager.state == MqttConnectionState.connected; + return Scaffold( + backgroundColor: XfjDesignTokens.canvas, + body: SafeArea( + child: Padding( + padding: const EdgeInsets.all(28), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Icon(Icons.print, + size: 36, color: XfjDesignTokens.primary), + const SizedBox(width: 14), + const Expanded( + child: Text('尔印打印终端', + style: TextStyle( + fontSize: 28, + fontWeight: FontWeight.w800, + color: XfjDesignTokens.ink))), + Obx(() { + final network = Get.find().isNetwork.value; + return _StatusBadge( + label: network ? '网络正常' : '网络断开', + ok: network, + ); + }), + const SizedBox(width: 8), + _StatusBadge(label: mqtt ? 'MQTT 已连接' : 'MQTT 未连接', ok: mqtt), + const SizedBox(width: 8), + _StatusBadge( + label: _usbAvailable ? 'USB 已连接' : 'USB 未连接', + ok: _usbAvailable), + ], + ), + const SizedBox(height: 24), + Expanded( + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded( + flex: 3, + child: _Panel( + title: '设备绑定', + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _InfoRow( + label: '本机设备', + value: setting?.serviceName ?? '--'), + _InfoRow( + label: '设备 ID', + value: setting?.serviceId ?? '--'), + _InfoRow( + label: '来源终端', + value: setting?.printerSourceHardName ?? '--'), + _InfoRow( + label: '业务类型', + value: setting?.sysType.title ?? '--'), + _InfoRow( + label: '打印机型号', + value: setting?.printerModel ?? '未填写'), + ], + ), + ), + ), + const SizedBox(width: 18), + Expanded( + flex: 2, + child: Column( + children: [ + Expanded( + child: _MetricPanel( + label: '待处理任务', + value: _pending, + color: XfjDesignTokens.primary)), + const SizedBox(height: 18), + Expanded( + child: _MetricPanel( + label: '失败任务', + value: _failed, + color: + _failed > 0 ? Colors.red : Colors.green)), + ], + ), + ), + ], + ), + ), + const SizedBox(height: 18), + _Panel( + title: '最近错误', + child: Text(_lastError ?? '暂无错误', + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: _lastError == null + ? XfjDesignTokens.muted + : Colors.red.shade700)), + ), + const SizedBox(height: 18), + Row( + children: [ + FilledButton.icon( + onPressed: _refreshing ? null : _restoreConnections, + icon: const Icon(Icons.refresh), + label: Text(_refreshing ? '连接中' : '重新连接')), + const SizedBox(width: 12), + OutlinedButton.icon( + onPressed: _refreshing ? null : _restoreConnections, + icon: const Icon(Icons.replay), + label: const Text('同步并重试')), + const SizedBox(width: 12), + OutlinedButton.icon( + onPressed: _showSettings, + icon: const Icon(Icons.settings), + label: const Text('设置')), + const Spacer(), + TextButton.icon( + onPressed: _logout, + icon: const Icon(Icons.logout), + label: const Text('退出')), + ], + ), + ], + ), + ), + ), + ); + } +} + +class _StatusBadge extends StatelessWidget { + /// 创建固定高度的连接状态标签。 + const _StatusBadge({required this.label, required this.ok}); + + /// 状态标签文案。 + final String label; + + /// 是否使用正常状态颜色。 + final bool ok; + + /// 构建不会因文案变化而改变外部工具栏高度的状态标签。 + @override + Widget build(BuildContext context) => Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration( + color: ok ? Colors.green.shade50 : Colors.red.shade50, + borderRadius: BorderRadius.circular(6)), + child: Text(label, + style: TextStyle( + color: ok ? Colors.green.shade800 : Colors.red.shade800, + fontWeight: FontWeight.w700)), + ); +} + +class _Panel extends StatelessWidget { + /// 创建与现有新 UI 主题一致的单层信息面板。 + const _Panel({required this.title, required this.child}); + + /// 面板标题。 + final String title; + + /// 面板正文内容。 + final Widget child; + + /// 构建带边框的单层内容容器。 + @override + Widget build(BuildContext context) => Container( + width: double.infinity, + padding: const EdgeInsets.all(22), + decoration: BoxDecoration( + color: XfjDesignTokens.surface, + border: Border.all(color: XfjDesignTokens.line), + borderRadius: BorderRadius.circular(XfjDesignTokens.cardRadius)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text(title, + style: const TextStyle( + fontSize: 18, fontWeight: FontWeight.w800)), + const SizedBox(height: 16), + child + ]), + ); +} + +class _MetricPanel extends StatelessWidget { + /// 创建填满父布局高度的队列数量面板。 + const _MetricPanel( + {required this.label, required this.value, required this.color}); + + /// 指标名称。 + final String label; + + /// 当前任务数量。 + final int value; + + /// 数值强调色。 + final Color color; + + /// 构建独立指标卡;不复用 `_Panel`,避免在受限高度内嵌套 Expanded。 + @override + Widget build(BuildContext context) => Container( + width: double.infinity, + padding: const EdgeInsets.all(22), + decoration: BoxDecoration( + color: XfjDesignTokens.surface, + border: Border.all(color: XfjDesignTokens.line), + borderRadius: BorderRadius.circular(XfjDesignTokens.cardRadius), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w800), + ), + Expanded( + child: Align( + alignment: Alignment.bottomLeft, + child: Text( + '$value', + style: TextStyle( + fontSize: 54, + fontWeight: FontWeight.w800, + color: color, + ), + ), + ), + ), + ], + ), + ); +} + +/// 绑定信息面板中的固定标签与可截断值行。 +class _InfoRow extends StatelessWidget { + /// 创建一行绑定信息。 + const _InfoRow({required this.label, required this.value}); + + /// 左侧固定宽度标签。 + final String label; + + /// 右侧允许省略展示的服务端或本地配置值。 + final String value; + + /// 构建适合横屏状态面板密度的信息行。 + @override + Widget build(BuildContext context) => Padding( + padding: const EdgeInsets.only(bottom: 12), + child: Row(children: [ + SizedBox( + width: 110, + child: Text(label, + style: const TextStyle(color: XfjDesignTokens.muted))), + Expanded( + child: Text(value, + overflow: TextOverflow.ellipsis, + style: const TextStyle(fontWeight: FontWeight.w700))) + ]), + ); +} diff --git a/lib/pages/root/device_setup/view.dart b/lib/pages/root/device_setup/view.dart new file mode 100644 index 0000000..0fc4288 --- /dev/null +++ b/lib/pages/root/device_setup/view.dart @@ -0,0 +1,163 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import 'package:pj_supermarket_cloud/common/index.dart'; + +/// 登录后的服务端设备绑定页,同时识别 cashier/printer 终端角色。 +class DeviceSetupPage extends StatefulWidget { + /// 创建登录后首次绑定服务端设备的页面。 + const DeviceSetupPage({super.key}); + + /// 创建设备 ID 输入、提交和错误反馈状态。 + @override + State createState() => _DeviceSetupPageState(); +} + +/// 首次设备绑定页面状态;绑定成功后按服务端类型路由到收银或打印角色。 +class _DeviceSetupPageState extends State { + /// PC 设备管理中生成的服务端设备 ID 输入控制器。 + final _controller = TextEditingController(); + + /// 是否正在提交绑定并加载角色配置,用于禁用重复操作。 + bool _submitting = false; + + /// 最近一次校验或服务端绑定错误;为空时不展示错误文案。 + String? _error; + + /// 释放设备 ID 输入控制器。 + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + /// 校验设备 ID、绑定设备并根据服务端返回的终端角色完成首次路由。 + Future _bind() async { + final id = int.tryParse(_controller.text.trim()); + if (id == null || id <= 0) { + setState(() => _error = '请输入有效的设备 ID'); + return; + } + setState(() { + _submitting = true; + _error = null; + }); + try { + final bound = await AppSettingTool.bindSys(id); + if (!bound) throw StateError('设备绑定失败,请确认设备 ID、单位以及后台绑定关系'); + var setting = await AppSettingTool.getSysSetting(); + if (setting.terminalRole == TerminalRole.printer) { + setting = await AppSettingTool.updatePrinterBootstrap( + await HardApi.getPrinterBootstrap(), + ); + } + await Get.find().resumeAuthenticatedServices( + setting.terminalRole, + ); + if (setting.terminalRole == TerminalRole.printer) { + await AppMqTool.mqRefresh(); + await SyncPrintTool.pullPendingTasks(); + Get.offAllNamed(RouteNames.printer); + } else { + Get.offAllNamed(RouteNames.terminal); + } + } catch (error) { + if (mounted) { + setState( + () => _error = error.toString().replaceFirst('Bad state: ', ''), + ); + } + } finally { + if (mounted) { + setState(() => _submitting = false); + } + } + } + + /// 清理账号登录态并返回登录页,不保留未完成的设备绑定输入。 + Future _logout() async { + await AppSettingTool.setUserSetting(UserSetting()); + await Get.find().suspendAuthenticatedServices(); + Get.offAllNamed(RouteNames.login); + } + + /// 构建适合终端横屏触控的设备 ID 绑定表单。 + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: XfjDesignTokens.canvas, + body: SafeArea( + child: Center( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 560), + child: Padding( + padding: const EdgeInsets.all(32), + child: Material( + color: XfjDesignTokens.surface, + shape: RoundedRectangleBorder( + side: const BorderSide(color: XfjDesignTokens.line), + borderRadius: + BorderRadius.circular(XfjDesignTokens.cardRadius), + ), + child: Padding( + padding: const EdgeInsets.all(32), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Icon(Icons.print_outlined, + size: 42, color: XfjDesignTokens.primary), + const SizedBox(height: 20), + const Text('绑定终端设备', + style: TextStyle( + fontSize: 28, + fontWeight: FontWeight.w800, + color: XfjDesignTokens.ink)), + const SizedBox(height: 8), + const Text('输入 PC 设备管理中创建的设备 ID', + style: TextStyle( + fontSize: 16, color: XfjDesignTokens.muted)), + const SizedBox(height: 28), + TextField( + controller: _controller, + enabled: !_submitting, + keyboardType: TextInputType.number, + textInputAction: TextInputAction.done, + onSubmitted: (_) => _bind(), + decoration: InputDecoration( + labelText: '设备 ID', + errorText: _error, + border: const OutlineInputBorder(), + ), + ), + const SizedBox(height: 24), + Row( + children: [ + TextButton( + onPressed: _submitting ? null : _logout, + child: const Text('退出登录')), + const Spacer(), + FilledButton.icon( + onPressed: _submitting ? null : _bind, + icon: _submitting + ? const SizedBox( + width: 18, + height: 18, + child: CircularProgressIndicator( + strokeWidth: 2)) + : const Icon(Icons.link), + label: const Text('绑定并继续'), + ), + ], + ), + ], + ), + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/root/login/controller.dart b/lib/pages/root/login/controller.dart index 74bfde0..1f31b48 100644 --- a/lib/pages/root/login/controller.dart +++ b/lib/pages/root/login/controller.dart @@ -179,9 +179,9 @@ class LoginController extends GetxController { if (visibleMsg) { Toast.success(msg: "登录成功"); } - // 启动页已经完成数据库与设备初始化,登录成功后直接进入终端,避免重复执行启动链路。 + // 启动页已经完成数据库与设备初始化,登录后按持久化终端角色进入独立工作区。 _clearForm(); - Get.offAllNamed(RouteNames.terminal); + await _openWorkspace(); } catch (error) { logger.e("|$_tag|登录错误error:|$error|"); } finally { @@ -208,7 +208,7 @@ class LoginController extends GetxController { Toast.success(msg: "登录成功"); EasyLoading.dismiss(); _clearForm(); - Get.offAllNamed(RouteNames.terminal); + await _openWorkspace(); return; } if (!isNetwork && serviceId == null) { @@ -218,6 +218,33 @@ class LoginController extends GetxController { await doLogin(true, automatic: true); } + /// 未绑定实例先进入设备 ID 流程;EY_PRINT 永远进入打印面板而非收银终端。 + Future _openWorkspace() async { + var setting = await AppSettingTool.getSysSetting(); + if (ObjectUtil.isEmpty(setting.serviceId)) { + Get.offAllNamed(RouteNames.deviceSetup); + return; + } + if (setting.terminalRole == TerminalRole.printer) { + try { + setting = await AppSettingTool.updatePrinterBootstrap( + await HardApi.getPrinterBootstrap(), + ); + } catch (error, stack) { + logger.w('|$_tag|刷新打印终端绑定失败,使用本地配置进入状态页|error:$error|stack:$stack|'); + } + await Get.find().resumeAuthenticatedServices( + setting.terminalRole, + ); + Get.offAllNamed(RouteNames.printer); + return; + } + await Get.find().resumeAuthenticatedServices( + setting.terminalRole, + ); + Get.offAllNamed(RouteNames.terminal); + } + /// 用户主动编辑账号或密码后,本轮初始化只保留人工登录,避免延迟的自动登录弹层关闭软键盘。 void markManualInputStarted() { _manualInputStarted = true; diff --git a/lib/pages/terminal/order_history/controller.dart b/lib/pages/terminal/order_history/controller.dart index 24af8e9..8e39e7c 100644 --- a/lib/pages/terminal/order_history/controller.dart +++ b/lib/pages/terminal/order_history/controller.dart @@ -394,36 +394,19 @@ class OrderHistoryController extends GetxController { /// 打印订单发票 void doPrint(OrderEntity v) async { - final isOpen = await printManage.open(); - if (!isOpen) { - Toast.error(msg: "打开打印机失败"); - return; - } EasyLoading.show( - status: '打印中...', + status: '正在提交打印任务...', maskType: EasyLoadingMaskType.black, ); try { - final orderId = int.tryParse(v.serviceUniqueCode); - final pa = {"orderId": orderId}; - final data = await HardApi.generateInvoice(pa); - if (data == null) throw Error(); - if (ObjectUtil.isEmpty(data.historyId) || ObjectUtil.isEmpty(data.pic)) { - throw Exception("服务端生成发票失败"); - } - await SyncPrintTool.addTask( - orderId: v.serviceUniqueCode, - androidOrderId: v.id.toString(), - historyId: data.historyId!, - pic: data.pic!, - ); + final accepted = + await HardApi.createPrinterTask(orderId: v.serviceUniqueCode); + if (!accepted) throw StateError('服务端未接受打印任务'); EasyLoading.dismiss(); - Toast.success(msg: "打印成功"); + Toast.success(msg: "打印任务已提交"); } catch (e, stack) { logger.e("打印订单发票失败error:|$e|stack:|$stack|"); EasyLoading.dismiss(); - } finally { - await printManage.close(); } } diff --git a/packages/drago_usb_printer/android/build.gradle b/packages/drago_usb_printer/android/build.gradle new file mode 100644 index 0000000..21dff80 --- /dev/null +++ b/packages/drago_usb_printer/android/build.gradle @@ -0,0 +1,40 @@ +group 'app.sks.client.drago_usb_printer' +version '1.0-SNAPSHOT' + +buildscript { + ext.kotlin_version = '1.8.22' + repositories { + google() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:8.1.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +rootProject.allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + namespace = 'app.sks.client.drago_usb_printer' + compileSdkVersion 35 + sourceSets { main.java.srcDirs += 'src/main/kotlin' } + defaultConfig { minSdkVersion 24 } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { jvmTarget = JavaVersion.VERSION_1_8.toString() } +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" +} diff --git a/packages/drago_usb_printer/android/src/main/AndroidManifest.xml b/packages/drago_usb_printer/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..1912c97 --- /dev/null +++ b/packages/drago_usb_printer/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + + diff --git a/packages/drago_usb_printer/android/src/main/kotlin/app/sks/client/drago_usb_printer/DragoUsbPrinterPlugin.kt b/packages/drago_usb_printer/android/src/main/kotlin/app/sks/client/drago_usb_printer/DragoUsbPrinterPlugin.kt new file mode 100644 index 0000000..ee45435 --- /dev/null +++ b/packages/drago_usb_printer/android/src/main/kotlin/app/sks/client/drago_usb_printer/DragoUsbPrinterPlugin.kt @@ -0,0 +1,202 @@ +package app.sks.client.drago_usb_printer + +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.hardware.usb.UsbConstants +import android.hardware.usb.UsbDevice +import android.hardware.usb.UsbDeviceConnection +import android.hardware.usb.UsbEndpoint +import android.hardware.usb.UsbInterface +import android.hardware.usb.UsbManager +import android.os.Build +import android.os.Handler +import android.os.Looper +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel + +/** 项目内 USB 打印插件;只有 Bulk OUT 接受全部字节后才向 Dart 返回成功。 */ +class DragoUsbPrinterPlugin : FlutterPlugin, MethodChannel.MethodCallHandler { + /** Flutter 插件使用的应用上下文。 */ + private lateinit var context: Context + + /** 与 Dart 侧约定为 drago_usb_printer 的方法通道。 */ + private lateinit var channel: MethodChannel + + /** Android USB Host 系统服务。 */ + private lateinit var usbManager: UsbManager + + /** 以 VID-PID 为键复用已经 claim 的打印机连接。 */ + private val connections = mutableMapOf() + + /** 将后台写入结果切回主线程提交给 Flutter。 */ + private val mainHandler = Handler(Looper.getMainLooper()) + + /** 注册方法通道并取得 USB Host 服务。 */ + override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { + context = binding.applicationContext + usbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager + channel = MethodChannel(binding.binaryMessenger, "drago_usb_printer") + channel.setMethodCallHandler(this) + } + + override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { + when (call.method) { + "getUSBDeviceList" -> result.success(listPrinterDevices()) + "connect" -> runCatching { connectionFor(call).connect() } + .fold(result::success) { result.error("USB_CONNECT", it.message, null) } + "disconnect" -> { + connections.remove(deviceKey(call))?.close() + result.success(true) + } + "write" -> { + val data = call.argument("data") + if (data == null) { + result.error("USB_DATA", "Missing USB payload", null) + return + } + Thread { + runCatching { connectionFor(call).writeFully(data) } + .fold( + { mainHandler.post { result.success(true) } }, + { error -> mainHandler.post { result.error("USB_WRITE", error.message, null) } }, + ) + }.start() + } + else -> result.notImplemented() + } + } + + /** 返回至少包含一个 Printer Class 接口的 USB 设备摘要。 */ + private fun listPrinterDevices(): List> = usbManager.deviceList.values + .filter(::isPrinterDevice) + .map { device -> + mapOf( + "deviceName" to device.deviceName, + "manufacturer" to safeValue { device.manufacturerName }, + "productName" to safeValue { device.productName }, + "deviceId" to device.deviceId.toString(), + "vendorId" to device.vendorId.toString(), + "productId" to device.productId.toString(), + ) + } + + /** 校验 VID/PID、设备类型和授权后取得可复用连接。 */ + private fun connectionFor(call: MethodCall): UsbPrinterConnection { + val vendorId = call.argument("vendorId") ?: error("Missing USB VID") + val productId = call.argument("productId") ?: error("Missing USB PID") + val device = usbManager.deviceList.values.firstOrNull { + it.vendorId == vendorId && it.productId == productId && isPrinterDevice(it) + } ?: error("Configured USB printer was not found") + if (!usbManager.hasPermission(device)) { + requestPermission(device) + error("USB permission requested; retry after permission is granted") + } + return connections.getOrPut("$vendorId-$productId") { + UsbPrinterConnection(usbManager, device) + } + } + + /** 从方法参数构造连接缓存键。 */ + private fun deviceKey(call: MethodCall): String = + "${call.argument("vendorId")}-${call.argument("productId")}" + + /** 请求 Android USB 授权;授权完成后由上层恢复流程再次连接。 */ + private fun requestPermission(device: UsbDevice) { + val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_MUTABLE else 0 + val intent = PendingIntent.getBroadcast( + context, + 0, + Intent("${context.packageName}.USB_PERMISSION"), + flags, + ) + usbManager.requestPermission(device, intent) + } + + /** 判断设备是否公开标准 USB Printer Class 接口。 */ + private fun isPrinterDevice(device: UsbDevice): Boolean = + (0 until device.interfaceCount).any { + device.getInterface(it).interfaceClass == UsbConstants.USB_CLASS_PRINTER + } + + /** 兼容部分设备描述符读取异常,异常字段按空值返回。 */ + private fun safeValue(read: () -> String?): String? = runCatching(read).getOrNull() + + /** 释放全部已 claim 的接口和连接,并注销方法通道。 */ + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + connections.values.forEach(UsbPrinterConnection::close) + connections.clear() + channel.setMethodCallHandler(null) + } +} + +/** 持有一个已 claim 的打印机接口;Bulk OUT 必需,Bulk IN 明确为可选。 */ +private class UsbPrinterConnection( + /** Android USB Host 系统服务。 */ + private val manager: UsbManager, + + /** 当前 VID/PID 对应的 USB 设备。 */ + private val device: UsbDevice, +) { + /** 已打开的设备连接。 */ + private var connection: UsbDeviceConnection? = null + + /** 已 claim 的 Printer Class 接口。 */ + private var printerInterface: UsbInterface? = null + + /** 当前打印接口的 Bulk OUT 端点。 */ + private var bulkOut: UsbEndpoint? = null + + /** 打开设备并 claim Printer Class 接口,缺少 Bulk OUT 时直接失败。 */ + @Synchronized + fun connect(): Boolean { + if (connection != null && bulkOut != null) return true + val targetInterface = (0 until device.interfaceCount) + .map(device::getInterface) + .firstOrNull { it.interfaceClass == UsbConstants.USB_CLASS_PRINTER } + ?: error("USB printer interface is missing") + val output = (0 until targetInterface.endpointCount) + .map(targetInterface::getEndpoint) + .firstOrNull { + it.type == UsbConstants.USB_ENDPOINT_XFER_BULK && + it.direction == UsbConstants.USB_DIR_OUT + } ?: error("USB Bulk OUT endpoint is missing") + val opened = manager.openDevice(device) ?: error("Unable to open USB printer") + if (!opened.claimInterface(targetInterface, true)) { + opened.close() + error("Unable to claim USB printer interface") + } + connection = opened + printerInterface = targetInterface + bulkOut = output + return true + } + + @Synchronized + fun writeFully(data: ByteArray): Int { + connect() + val opened = connection ?: error("USB connection is closed") + val output = bulkOut ?: error("USB Bulk OUT endpoint is missing") + var offset = 0 + while (offset < data.size) { + val written = opened.bulkTransfer(output, data, offset, data.size - offset, 5000) + if (written <= 0) error("USB write stopped at $offset/${data.size} bytes (result $written)") + offset += written + } + if (offset != data.size) error("USB write incomplete: $offset/${data.size} bytes") + return offset + } + + @Synchronized + fun close() { + printerInterface?.let { connection?.releaseInterface(it) } + connection?.close() + connection = null + printerInterface = null + bulkOut = null + } +} + /** 分发设备发现、连接、关闭和完整写入四类原生调用。 */ + /** 循环处理 bulkTransfer 部分写入,并核对最终累计长度。 */ + /** 释放接口并关闭设备连接,重复调用保持幂等。 */ diff --git a/packages/drago_usb_printer/lib/drago_usb_printer.dart b/packages/drago_usb_printer/lib/drago_usb_printer.dart new file mode 100644 index 0000000..151c9ef --- /dev/null +++ b/packages/drago_usb_printer/lib/drago_usb_printer.dart @@ -0,0 +1,61 @@ +import 'dart:io'; + +import 'package:flutter/services.dart'; + +/// `drago_pos_printer` 在 Android 上使用的项目内 USB Bulk 传输实现。 +class DragoUsbPrinter { + /// 与原生插件约定的稳定 MethodChannel 名称。 + static const MethodChannel _channel = MethodChannel('drago_usb_printer'); + + /// 当前连接目标的 USB Vendor ID,连接前为 0。 + int vendorId = 0; + + /// 当前连接目标的 USB Product ID,连接前为 0。 + int productId = 0; + + /// 列出 Android 上具有 Printer Class 接口的 USB 设备,授权仍由 [connect] 触发。 + static Future>> getUSBDeviceList() async { + if (!Platform.isAndroid) return >[]; + final devices = + await _channel.invokeListMethod('getUSBDeviceList'); + return (devices ?? const []) + .cast>() + .map((item) => Map.from(item)) + .toList(growable: false); + } + + /// 按准确 VID/PID 打开设备,并要求目标接口至少存在一个 Bulk OUT 端点。 + Future connect(int vendorId, int productId) async { + this.vendorId = vendorId; + this.productId = productId; + return await _channel.invokeMethod('connect', _params) == true; + } + + /// 释放当前原生 USB Interface 与连接;重复关闭按成功处理。 + Future close() async { + return await _channel.invokeMethod('disconnect', _params) == true; + } + + /// 等待原生层完整接收全部字节后返回;部分写、零字节或负数结果均抛出异常。 + Future write(Uint8List data) async { + if (data.isEmpty) return true; + final completed = await _channel.invokeMethod('write', { + ..._params, + 'data': data, + }); + if (completed != true) { + throw StateError('USB did not accept the complete payload'); + } + return true; + } + + /// 保留上游可能使用的文本兼容入口;生产图片打印直接调用 [write]。 + Future printText(String text) => + write(Uint8List.fromList(text.codeUnits)); + + /// 构造所有原生连接与关闭调用共用的目标设备参数。 + Map get _params => { + 'vendorId': vendorId, + 'productId': productId, + }; +} diff --git a/packages/drago_usb_printer/pubspec.yaml b/packages/drago_usb_printer/pubspec.yaml new file mode 100644 index 0000000..d90216c --- /dev/null +++ b/packages/drago_usb_printer/pubspec.yaml @@ -0,0 +1,19 @@ +name: drago_usb_printer +description: Project-owned Android USB transport with complete-write verification. +version: 0.1.2+zax.1 +publish_to: none + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" + +dependencies: + flutter: + sdk: flutter + +flutter: + plugin: + platforms: + android: + package: app.sks.client.drago_usb_printer + pluginClass: DragoUsbPrinterPlugin diff --git a/pubspec.lock b/pubspec.lock index 4299a67..0d9cebb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -343,13 +343,12 @@ packages: source: hosted version: "1.0.5" drago_usb_printer: - dependency: transitive + dependency: "direct overridden" description: - name: drago_usb_printer - sha256: "08272fe48797fe513cbdd57747dfe2edb2814cc438f9623311ec5e2601fcd1ab" - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.1.2" + path: "packages/drago_usb_printer" + relative: true + source: path + version: "0.1.2+zax.1" ducafe_ui_core: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 890c786..f3482ac 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: pj_supermarket_cloud description: "资安信智慧后勤-测试" publish_to: "none" -version: 1.4.6+146 +version: 1.4.7+147 environment: sdk: ^3.6.0 @@ -93,6 +93,11 @@ dev_dependencies: # json json_serializable: 6.8.0 +# drago_pos_printer 通过该传递依赖写入 Android USB。修复版随项目发布,禁止修改全局 pub-cache。 +dependency_overrides: + drago_usb_printer: + path: packages/drago_usb_printer + flutter: uses-material-design: true diff --git a/test/common/printer_task_test.dart b/test/common/printer_task_test.dart new file mode 100644 index 0000000..f96e308 --- /dev/null +++ b/test/common/printer_task_test.dart @@ -0,0 +1,146 @@ +import 'package:flutter_test/flutter_test.dart'; +// ignore_for_file: depend_on_referenced_packages + +import 'package:sqflite_common_ffi/sqflite_ffi.dart'; + +import 'package:pj_supermarket_cloud/common/index.dart'; +import 'package:pj_supermarket_cloud/common/services/database/database_migration.dart'; + +void main() { + sqfliteFfiInit(); + + setUpAll(() { + databaseFactory = databaseFactoryFfi; + }); + + test('MQTT 打印消息允许线上预约缺少 androidOrderCode', () { + final message = MqttInvoiceMsg.fromJson(const { + 'action': 'PRINT', + 'historyId': 9001, + 'orderId': 8001, + 'androidOrderCode': null, + 'sourceHardId': 101, + 'targetHardId': 202, + 'printType': '2', + 'printNo': 3, + 'retryVersion': 4, + 'pic': 'https://example.test/voucher.png', + }); + + expect(message.androidOrderCode, isNull); + expect(message.historyId, '9001'); + expect(message.orderId, '8001'); + expect(message.sourceHardId, '101'); + expect(message.targetHardId, '202'); + expect(message.printType, '2'); + expect(message.printNo, 3); + expect(message.retryVersion, 4); + expect(message.toJson(), containsPair('androidOrderCode', null)); + }); + + test('打印任务最多尝试三次且不安排第四次出纸', () { + expect( + SyncPrintTool.attemptDelays, + const [ + Duration.zero, + Duration(seconds: 5), + Duration(seconds: 30), + ], + ); + }); + + test('同版本重复任务被拒绝,只有失败任务的更高版本可重试', () { + final task = PrintHistoryEntity( + orderId: 'local-order', + serviceOrderId: '8001', + servicePrintId: '9001', + invoiceUrl: 'https://example.test/voucher.png', + retryVersion: 2, + status: PrintStatus.ERROR, + createTime: DateTime(2026, 9, 4), + ); + + expect(SyncPrintTool.canApplyTaskVersion(task, 2), isFalse); + expect(SyncPrintTool.canApplyTaskVersion(task, 1), isFalse); + expect(SyncPrintTool.canApplyTaskVersion(task, 3), isTrue); + + task.status = PrintStatus.SUCCESS; + expect(SyncPrintTool.canApplyTaskVersion(task, 3), isFalse); + task.status = PrintStatus.SYNC; + expect(SyncPrintTool.canApplyTaskVersion(task, 3), isFalse); + }); + + test('12到13迁移保留每个服务端任务最新记录并建立唯一索引', () async { + final database = + await databaseFactoryFfi.openDatabase(inMemoryDatabasePath); + addTearDown(database.close); + await database.execute(''' + CREATE TABLE print_history ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + orderId TEXT NOT NULL, + serviceOrderId TEXT NOT NULL, + servicePrintId TEXT NOT NULL, + invoiceUrl TEXT NOT NULL, + status TEXT NOT NULL, + printTime TEXT, + syncTime TEXT, + createTime TEXT NOT NULL + ) + '''); + await database.insert('print_history', { + 'orderId': 'old-1', + 'serviceOrderId': '8001', + 'servicePrintId': '9001', + 'invoiceUrl': 'old.png', + 'status': 'ERROR', + 'createTime': '2026-09-04T08:00:00.000', + }); + await database.insert('print_history', { + 'orderId': 'old-2', + 'serviceOrderId': '8001', + 'servicePrintId': '9001', + 'invoiceUrl': 'new.png', + 'status': 'SUCCESS', + 'createTime': '2026-09-04T08:01:00.000', + }); + + await DatabaseMigration.migration12To13.migrate(database); + + final rows = await database.query('print_history'); + final columns = await database.rawQuery('PRAGMA table_info(print_history)'); + final indexes = await database.rawQuery('PRAGMA index_list(print_history)'); + expect(rows, hasLength(1)); + expect(rows.single['orderId'], 'old-2'); + expect(rows.single['printType'], '1'); + expect(rows.single['printNo'], 1); + expect(rows.single['retryVersion'], 0); + expect(rows.single['attemptCount'], 0); + expect(rows.single['resultPending'], 0); + expect( + columns.map((column) => column['name']), + containsAll(const { + 'printType', + 'printNo', + 'retryVersion', + 'attemptCount', + 'lastError', + 'resultPending', + }), + ); + expect( + indexes.map((index) => index['name']), + contains('index_print_history_servicePrintId'), + ); + await expectLater( + database.insert('print_history', { + 'orderId': 'duplicate', + 'serviceOrderId': '8001', + 'servicePrintId': '9001', + 'invoiceUrl': 'duplicate.png', + 'status': 'NONE', + 'createTime': '2026-09-04T08:02:00.000', + }), + throwsA(isA()), + ); + }); +} diff --git a/test/common/terminal_device_status_test.dart b/test/common/terminal_device_status_test.dart index bc59e7e..2f6a0a6 100644 --- a/test/common/terminal_device_status_test.dart +++ b/test/common/terminal_device_status_test.dart @@ -222,27 +222,27 @@ void main() { expect(AppDeviceStatusTool.progress.value.running, isFalse); }); - test('自动检查任务避免重复启动并在释放时取消全部定时器', () { - final tool = SyncCheckTool.instance; - tool.dispose(); - addTearDown(tool.dispose); + test('打印角色只保留通信、打印和运维 Cron 任务', () { + const expected = { + ScheduledTaskType.mqReconnect, + ScheduledTaskType.printCheck, + ScheduledTaskType.deviceStatus, + ScheduledTaskType.autoUpdate, + ScheduledTaskType.terminalLogUpload, + ScheduledTaskType.expiredLogCleanup, + }; - tool.startAll(); - final mqTimer = tool.mqTimer; - final printTimer = tool.printTimer; - final broadcastTimer = tool.broadcastTimer; - final deviceStatusTimer = tool.deviceStatusTimer; - - tool.startAll(); - expect(identical(tool.mqTimer, mqTimer), isTrue); - expect(identical(tool.printTimer, printTimer), isTrue); - expect(identical(tool.broadcastTimer, broadcastTimer), isTrue); - expect(identical(tool.deviceStatusTimer, deviceStatusTimer), isTrue); - - tool.dispose(); - expect(tool.mqTimer, isNull); - expect(tool.printTimer, isNull); - expect(tool.broadcastTimer, isNull); - expect(tool.deviceStatusTimer, isNull); + for (final type in ScheduledTaskType.values) { + expect( + SyncCheckTool.supportsTask(TerminalRole.printer, type), + expected.contains(type), + reason: '打印角色任务过滤错误: $type', + ); + expect( + SyncCheckTool.supportsTask(TerminalRole.cashier, type), + isTrue, + reason: '收银角色应保留完整任务: $type', + ); + } }); } diff --git a/tmp/打印机文档.doc b/tmp/打印机文档.doc new file mode 100644 index 0000000000000000000000000000000000000000..0510a53e2ca9813d53b418c145202679c1e54c23 GIT binary patch literal 682496 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 Na&EhE{eM~y{1?c>!}$OJ literal 0 HcmV?d00001